diff --git a/.DS_Store b/.DS_Store
index 6b5f894..ee33b5b 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/vendor/.DS_Store b/vendor/.DS_Store
index f375826..0ffbd46 100644
Binary files a/vendor/.DS_Store and b/vendor/.DS_Store differ
diff --git a/vendor/PhpSpreadsheet/Calculation/Calculation.php b/vendor/PhpSpreadsheet/Calculation/Calculation.php
deleted file mode 100644
index 99260e3..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Calculation.php
+++ /dev/null
@@ -1,5327 +0,0 @@
-=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
- // Cell reference (with or without a sheet reference) ensuring absolute/relative
- const CALCULATION_REGEXP_CELLREF_RELATIVE = '((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?\b[a-z]{1,3})(\$?\d{1,7})(?![\w.])';
- // Cell ranges ensuring absolute/relative
- const CALCULATION_REGEXP_COLUMNRANGE_RELATIVE = '(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
- const CALCULATION_REGEXP_ROWRANGE_RELATIVE = '(\$?\d{1,7}):(\$?\d{1,7})';
- // Defined Names: Named Range of cells, or Named Formulae
- const CALCULATION_REGEXP_DEFINEDNAME = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_\p{L}][_\p{L}\p{N}\.]*)';
- // Error
- const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?';
-
- /** constants */
- const RETURN_ARRAY_AS_ERROR = 'error';
- const RETURN_ARRAY_AS_VALUE = 'value';
- const RETURN_ARRAY_AS_ARRAY = 'array';
-
- const FORMULA_OPEN_FUNCTION_BRACE = '{';
- const FORMULA_CLOSE_FUNCTION_BRACE = '}';
- const FORMULA_STRING_QUOTE = '"';
-
- private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
-
- /**
- * Instance of this class.
- *
- * @var Calculation
- */
- private static $instance;
-
- /**
- * Instance of the spreadsheet this Calculation Engine is using.
- *
- * @var Spreadsheet
- */
- private $spreadsheet;
-
- /**
- * Calculation cache.
- *
- * @var array
- */
- private $calculationCache = [];
-
- /**
- * Calculation cache enabled.
- *
- * @var bool
- */
- private $calculationCacheEnabled = true;
-
- /**
- * Used to generate unique store keys.
- *
- * @var int
- */
- private $branchStoreKeyCounter = 0;
-
- private $branchPruningEnabled = true;
-
- /**
- * List of operators that can be used within formulae
- * The true/false value indicates whether it is a binary operator or a unary operator.
- *
- * @var array
- */
- private static $operators = [
- '+' => true, '-' => true, '*' => true, '/' => true,
- '^' => true, '&' => true, '%' => false, '~' => false,
- '>' => true, '<' => true, '=' => true, '>=' => true,
- '<=' => true, '<>' => true, '|' => true, ':' => true,
- ];
-
- /**
- * List of binary operators (those that expect two operands).
- *
- * @var array
- */
- private static $binaryOperators = [
- '+' => true, '-' => true, '*' => true, '/' => true,
- '^' => true, '&' => true, '>' => true, '<' => true,
- '=' => true, '>=' => true, '<=' => true, '<>' => true,
- '|' => true, ':' => true,
- ];
-
- /**
- * The debug log generated by the calculation engine.
- *
- * @var Logger
- */
- private $debugLog;
-
- /**
- * Flag to determine how formula errors should be handled
- * If true, then a user error will be triggered
- * If false, then an exception will be thrown.
- *
- * @var bool
- */
- public $suppressFormulaErrors = false;
-
- /**
- * Error message for any error that was raised/thrown by the calculation engine.
- *
- * @var string
- */
- public $formulaError;
-
- /**
- * Reference Helper.
- *
- * @var ReferenceHelper
- */
- private static $referenceHelper;
-
- /**
- * An array of the nested cell references accessed by the calculation engine, used for the debug log.
- *
- * @var CyclicReferenceStack
- */
- private $cyclicReferenceStack;
-
- private $cellStack = [];
-
- /**
- * Current iteration counter for cyclic formulae
- * If the value is 0 (or less) then cyclic formulae will throw an exception,
- * otherwise they will iterate to the limit defined here before returning a result.
- *
- * @var int
- */
- private $cyclicFormulaCounter = 1;
-
- private $cyclicFormulaCell = '';
-
- /**
- * Number of iterations for cyclic formulae.
- *
- * @var int
- */
- public $cyclicFormulaCount = 1;
-
- /**
- * Epsilon Precision used for comparisons in calculations.
- *
- * @var float
- */
- private $delta = 0.1e-12;
-
- /**
- * The current locale setting.
- *
- * @var string
- */
- private static $localeLanguage = 'en_us'; // US English (default locale)
-
- /**
- * List of available locale settings
- * Note that this is read for the locale subdirectory only when requested.
- *
- * @var string[]
- */
- private static $validLocaleLanguages = [
- 'en', // English (default language)
- ];
-
- /**
- * Locale-specific argument separator for function arguments.
- *
- * @var string
- */
- private static $localeArgumentSeparator = ',';
-
- private static $localeFunctions = [];
-
- /**
- * Locale-specific translations for Excel constants (True, False and Null).
- *
- * @var string[]
- */
- public static $localeBoolean = [
- 'TRUE' => 'TRUE',
- 'FALSE' => 'FALSE',
- 'NULL' => 'NULL',
- ];
-
- /**
- * Excel constant string translations to their PHP equivalents
- * Constant conversion from text name/value to actual (datatyped) value.
- *
- * @var string[]
- */
- private static $excelConstants = [
- 'TRUE' => true,
- 'FALSE' => false,
- 'NULL' => null,
- ];
-
- // PhpSpreadsheet functions
- private static $phpSpreadsheetFunctions = [
- 'ABS' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'abs',
- 'argumentCount' => '1',
- ],
- 'ACCRINT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ACCRINT'],
- 'argumentCount' => '4-7',
- ],
- 'ACCRINTM' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ACCRINTM'],
- 'argumentCount' => '3-5',
- ],
- 'ACOS' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'acos',
- 'argumentCount' => '1',
- ],
- 'ACOSH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'acosh',
- 'argumentCount' => '1',
- ],
- 'ACOT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ACOT'],
- 'argumentCount' => '1',
- ],
- 'ACOTH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ACOTH'],
- 'argumentCount' => '1',
- ],
- 'ADDRESS' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'cellAddress'],
- 'argumentCount' => '2-5',
- ],
- 'AGGREGATE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3+',
- ],
- 'AMORDEGRC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'AMORDEGRC'],
- 'argumentCount' => '6,7',
- ],
- 'AMORLINC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'AMORLINC'],
- 'argumentCount' => '6,7',
- ],
- 'AND' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalAnd'],
- 'argumentCount' => '1+',
- ],
- 'ARABIC' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ARABIC'],
- 'argumentCount' => '1',
- ],
- 'AREAS' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'ASC' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'ASIN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'asin',
- 'argumentCount' => '1',
- ],
- 'ASINH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'asinh',
- 'argumentCount' => '1',
- ],
- 'ATAN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'atan',
- 'argumentCount' => '1',
- ],
- 'ATAN2' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ATAN2'],
- 'argumentCount' => '2',
- ],
- 'ATANH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'atanh',
- 'argumentCount' => '1',
- ],
- 'AVEDEV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVEDEV'],
- 'argumentCount' => '1+',
- ],
- 'AVERAGE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGE'],
- 'argumentCount' => '1+',
- ],
- 'AVERAGEA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGEA'],
- 'argumentCount' => '1+',
- ],
- 'AVERAGEIF' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'AVERAGEIF'],
- 'argumentCount' => '2,3',
- ],
- 'AVERAGEIFS' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3+',
- ],
- 'BAHTTEXT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'BASE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'BASE'],
- 'argumentCount' => '2,3',
- ],
- 'BESSELI' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELI'],
- 'argumentCount' => '2',
- ],
- 'BESSELJ' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELJ'],
- 'argumentCount' => '2',
- ],
- 'BESSELK' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELK'],
- 'argumentCount' => '2',
- ],
- 'BESSELY' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BESSELY'],
- 'argumentCount' => '2',
- ],
- 'BETADIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETADIST'],
- 'argumentCount' => '3-5',
- ],
- 'BETA.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '4-6',
- ],
- 'BETAINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETAINV'],
- 'argumentCount' => '3-5',
- ],
- 'BETA.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BETAINV'],
- 'argumentCount' => '3-5',
- ],
- 'BIN2DEC' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTODEC'],
- 'argumentCount' => '1',
- ],
- 'BIN2HEX' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTOHEX'],
- 'argumentCount' => '1,2',
- ],
- 'BIN2OCT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BINTOOCT'],
- 'argumentCount' => '1,2',
- ],
- 'BINOMDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BINOMDIST'],
- 'argumentCount' => '4',
- ],
- 'BINOM.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'BINOMDIST'],
- 'argumentCount' => '4',
- ],
- 'BINOM.DIST.RANGE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3,4',
- ],
- 'BINOM.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'BITAND' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITAND'],
- 'argumentCount' => '2',
- ],
- 'BITOR' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITOR'],
- 'argumentCount' => '2',
- ],
- 'BITXOR' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITOR'],
- 'argumentCount' => '2',
- ],
- 'BITLSHIFT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITLSHIFT'],
- 'argumentCount' => '2',
- ],
- 'BITRSHIFT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'BITRSHIFT'],
- 'argumentCount' => '2',
- ],
- 'CEILING' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CEILING'],
- 'argumentCount' => '2',
- ],
- 'CEILING.MATH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'CEILING.PRECISE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'CELL' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1,2',
- ],
- 'CHAR' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CHARACTER'],
- 'argumentCount' => '1',
- ],
- 'CHIDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIDIST'],
- 'argumentCount' => '2',
- ],
- 'CHISQ.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'CHISQ.DIST.RT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIDIST'],
- 'argumentCount' => '2',
- ],
- 'CHIINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIINV'],
- 'argumentCount' => '2',
- ],
- 'CHISQ.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'CHISQ.INV.RT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CHIINV'],
- 'argumentCount' => '2',
- ],
- 'CHITEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'CHISQ.TEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'CHOOSE' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'CHOOSE'],
- 'argumentCount' => '2+',
- ],
- 'CLEAN' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TRIMNONPRINTABLE'],
- 'argumentCount' => '1',
- ],
- 'CODE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'ASCIICODE'],
- 'argumentCount' => '1',
- ],
- 'COLUMN' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'COLUMN'],
- 'argumentCount' => '-1',
- 'passByReference' => [true],
- ],
- 'COLUMNS' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'COLUMNS'],
- 'argumentCount' => '1',
- ],
- 'COMBIN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COMBIN'],
- 'argumentCount' => '2',
- ],
- 'COMBINA' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'COMPLEX' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'COMPLEX'],
- 'argumentCount' => '2,3',
- ],
- 'CONCAT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CONCATENATE'],
- 'argumentCount' => '1+',
- ],
- 'CONCATENATE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CONCATENATE'],
- 'argumentCount' => '1+',
- ],
- 'CONFIDENCE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CONFIDENCE'],
- 'argumentCount' => '3',
- ],
- 'CONFIDENCE.NORM' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CONFIDENCE'],
- 'argumentCount' => '3',
- ],
- 'CONFIDENCE.T' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'CONVERT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'CONVERTUOM'],
- 'argumentCount' => '3',
- ],
- 'CORREL' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CORREL'],
- 'argumentCount' => '2',
- ],
- 'COS' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'cos',
- 'argumentCount' => '1',
- ],
- 'COSH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'cosh',
- 'argumentCount' => '1',
- ],
- 'COT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COT'],
- 'argumentCount' => '1',
- ],
- 'COTH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'COTH'],
- 'argumentCount' => '1',
- ],
- 'COUNT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNT'],
- 'argumentCount' => '1+',
- ],
- 'COUNTA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTA'],
- 'argumentCount' => '1+',
- ],
- 'COUNTBLANK' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTBLANK'],
- 'argumentCount' => '1',
- ],
- 'COUNTIF' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTIF'],
- 'argumentCount' => '2',
- ],
- 'COUNTIFS' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COUNTIFS'],
- 'argumentCount' => '2+',
- ],
- 'COUPDAYBS' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYBS'],
- 'argumentCount' => '3,4',
- ],
- 'COUPDAYS' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYS'],
- 'argumentCount' => '3,4',
- ],
- 'COUPDAYSNC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPDAYSNC'],
- 'argumentCount' => '3,4',
- ],
- 'COUPNCD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPNCD'],
- 'argumentCount' => '3,4',
- ],
- 'COUPNUM' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPNUM'],
- 'argumentCount' => '3,4',
- ],
- 'COUPPCD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'COUPPCD'],
- 'argumentCount' => '3,4',
- ],
- 'COVAR' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COVAR'],
- 'argumentCount' => '2',
- ],
- 'COVARIANCE.P' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'COVAR'],
- 'argumentCount' => '2',
- ],
- 'COVARIANCE.S' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'CRITBINOM' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CRITBINOM'],
- 'argumentCount' => '3',
- ],
- 'CSC' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CSC'],
- 'argumentCount' => '1',
- ],
- 'CSCH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'CSCH'],
- 'argumentCount' => '1',
- ],
- 'CUBEKPIMEMBER' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUBEMEMBER' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUBEMEMBERPROPERTY' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUBERANKEDMEMBER' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUBESET' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUBESETCOUNT' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUBEVALUE' => [
- 'category' => Category::CATEGORY_CUBE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '?',
- ],
- 'CUMIPMT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'CUMIPMT'],
- 'argumentCount' => '6',
- ],
- 'CUMPRINC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'CUMPRINC'],
- 'argumentCount' => '6',
- ],
- 'DATE' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATE'],
- 'argumentCount' => '3',
- ],
- 'DATEDIF' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATEDIF'],
- 'argumentCount' => '2,3',
- ],
- 'DATEVALUE' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATEVALUE'],
- 'argumentCount' => '1',
- ],
- 'DAVERAGE' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DAVERAGE'],
- 'argumentCount' => '3',
- ],
- 'DAY' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYOFMONTH'],
- 'argumentCount' => '1',
- ],
- 'DAYS' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYS'],
- 'argumentCount' => '2',
- ],
- 'DAYS360' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DAYS360'],
- 'argumentCount' => '2,3',
- ],
- 'DB' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DB'],
- 'argumentCount' => '4,5',
- ],
- 'DBCS' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'DCOUNT' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DCOUNT'],
- 'argumentCount' => '3',
- ],
- 'DCOUNTA' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DCOUNTA'],
- 'argumentCount' => '3',
- ],
- 'DDB' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DDB'],
- 'argumentCount' => '4,5',
- ],
- 'DEC2BIN' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOBIN'],
- 'argumentCount' => '1,2',
- ],
- 'DEC2HEX' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOHEX'],
- 'argumentCount' => '1,2',
- ],
- 'DEC2OCT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DECTOOCT'],
- 'argumentCount' => '1,2',
- ],
- 'DECIMAL' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'DEGREES' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'rad2deg',
- 'argumentCount' => '1',
- ],
- 'DELTA' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'DELTA'],
- 'argumentCount' => '1,2',
- ],
- 'DEVSQ' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'DEVSQ'],
- 'argumentCount' => '1+',
- ],
- 'DGET' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DGET'],
- 'argumentCount' => '3',
- ],
- 'DISC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DISC'],
- 'argumentCount' => '4,5',
- ],
- 'DMAX' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DMAX'],
- 'argumentCount' => '3',
- ],
- 'DMIN' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DMIN'],
- 'argumentCount' => '3',
- ],
- 'DOLLAR' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'DOLLAR'],
- 'argumentCount' => '1,2',
- ],
- 'DOLLARDE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DOLLARDE'],
- 'argumentCount' => '2',
- ],
- 'DOLLARFR' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'DOLLARFR'],
- 'argumentCount' => '2',
- ],
- 'DPRODUCT' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DPRODUCT'],
- 'argumentCount' => '3',
- ],
- 'DSTDEV' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSTDEV'],
- 'argumentCount' => '3',
- ],
- 'DSTDEVP' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSTDEVP'],
- 'argumentCount' => '3',
- ],
- 'DSUM' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DSUM'],
- 'argumentCount' => '3',
- ],
- 'DURATION' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '5,6',
- ],
- 'DVAR' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DVAR'],
- 'argumentCount' => '3',
- ],
- 'DVARP' => [
- 'category' => Category::CATEGORY_DATABASE,
- 'functionCall' => [Database::class, 'DVARP'],
- 'argumentCount' => '3',
- ],
- 'EDATE' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'EDATE'],
- 'argumentCount' => '2',
- ],
- 'EFFECT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'EFFECT'],
- 'argumentCount' => '2',
- ],
- 'ENCODEURL' => [
- 'category' => Category::CATEGORY_WEB,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'EOMONTH' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'EOMONTH'],
- 'argumentCount' => '2',
- ],
- 'ERF' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERF'],
- 'argumentCount' => '1,2',
- ],
- 'ERF.PRECISE' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFPRECISE'],
- 'argumentCount' => '1',
- ],
- 'ERFC' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFC'],
- 'argumentCount' => '1',
- ],
- 'ERFC.PRECISE' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'ERFC'],
- 'argumentCount' => '1',
- ],
- 'ERROR.TYPE' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'errorType'],
- 'argumentCount' => '1',
- ],
- 'EVEN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'EVEN'],
- 'argumentCount' => '1',
- ],
- 'EXACT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'EXACT'],
- 'argumentCount' => '2',
- ],
- 'EXP' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'exp',
- 'argumentCount' => '1',
- ],
- 'EXPONDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'EXPONDIST'],
- 'argumentCount' => '3',
- ],
- 'EXPON.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'EXPONDIST'],
- 'argumentCount' => '3',
- ],
- 'FACT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FACT'],
- 'argumentCount' => '1',
- ],
- 'FACTDOUBLE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FACTDOUBLE'],
- 'argumentCount' => '1',
- ],
- 'FALSE' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'FALSE'],
- 'argumentCount' => '0',
- ],
- 'FDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'F.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FDIST2'],
- 'argumentCount' => '4',
- ],
- 'F.DIST.RT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'FILTER' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3+',
- ],
- 'FILTERXML' => [
- 'category' => Category::CATEGORY_WEB,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'FIND' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'],
- 'argumentCount' => '2,3',
- ],
- 'FINDB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'],
- 'argumentCount' => '2,3',
- ],
- 'FINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'F.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'F.INV.RT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'FISHER' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FISHER'],
- 'argumentCount' => '1',
- ],
- 'FISHERINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FISHERINV'],
- 'argumentCount' => '1',
- ],
- 'FIXED' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'FIXEDFORMAT'],
- 'argumentCount' => '1-3',
- ],
- 'FLOOR' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOOR'],
- 'argumentCount' => '2',
- ],
- 'FLOOR.MATH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOORMATH'],
- 'argumentCount' => '3',
- ],
- 'FLOOR.PRECISE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'FLOORPRECISE'],
- 'argumentCount' => '2',
- ],
- 'FORECAST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FORECAST'],
- 'argumentCount' => '3',
- ],
- 'FORECAST.ETS' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3-6',
- ],
- 'FORECAST.ETS.CONFINT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3-6',
- ],
- 'FORECAST.ETS.SEASONALITY' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2-4',
- ],
- 'FORECAST.ETS.STAT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3-6',
- ],
- 'FORECAST.LINEAR' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'FORECAST'],
- 'argumentCount' => '3',
- ],
- 'FORMULATEXT' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'FORMULATEXT'],
- 'argumentCount' => '1',
- 'passCellReference' => true,
- 'passByReference' => [true],
- ],
- 'FREQUENCY' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'FTEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'F.TEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'FV' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'FV'],
- 'argumentCount' => '3-5',
- ],
- 'FVSCHEDULE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'FVSCHEDULE'],
- 'argumentCount' => '2',
- ],
- 'GAMMA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAFunction'],
- 'argumentCount' => '1',
- ],
- 'GAMMADIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMADIST'],
- 'argumentCount' => '4',
- ],
- 'GAMMA.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMADIST'],
- 'argumentCount' => '4',
- ],
- 'GAMMAINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAINV'],
- 'argumentCount' => '3',
- ],
- 'GAMMA.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMAINV'],
- 'argumentCount' => '3',
- ],
- 'GAMMALN' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMALN'],
- 'argumentCount' => '1',
- ],
- 'GAMMALN.PRECISE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAMMALN'],
- 'argumentCount' => '1',
- ],
- 'GAUSS' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GAUSS'],
- 'argumentCount' => '1',
- ],
- 'GCD' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'GCD'],
- 'argumentCount' => '1+',
- ],
- 'GEOMEAN' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GEOMEAN'],
- 'argumentCount' => '1+',
- ],
- 'GESTEP' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'GESTEP'],
- 'argumentCount' => '1,2',
- ],
- 'GETPIVOTDATA' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2+',
- ],
- 'GROWTH' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'GROWTH'],
- 'argumentCount' => '1-4',
- ],
- 'HARMEAN' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'HARMEAN'],
- 'argumentCount' => '1+',
- ],
- 'HEX2BIN' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTOBIN'],
- 'argumentCount' => '1,2',
- ],
- 'HEX2DEC' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTODEC'],
- 'argumentCount' => '1',
- ],
- 'HEX2OCT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'HEXTOOCT'],
- 'argumentCount' => '1,2',
- ],
- 'HLOOKUP' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'HLOOKUP'],
- 'argumentCount' => '3,4',
- ],
- 'HOUR' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'HOUROFDAY'],
- 'argumentCount' => '1',
- ],
- 'HYPERLINK' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'HYPERLINK'],
- 'argumentCount' => '1,2',
- 'passCellReference' => true,
- ],
- 'HYPGEOMDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'HYPGEOMDIST'],
- 'argumentCount' => '4',
- ],
- 'HYPGEOM.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '5',
- ],
- 'IF' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'statementIf'],
- 'argumentCount' => '1-3',
- ],
- 'IFERROR' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFERROR'],
- 'argumentCount' => '2',
- ],
- 'IFNA' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFNA'],
- 'argumentCount' => '2',
- ],
- 'IFS' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'IFS'],
- 'argumentCount' => '2+',
- ],
- 'IMABS' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMABS'],
- 'argumentCount' => '1',
- ],
- 'IMAGINARY' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMAGINARY'],
- 'argumentCount' => '1',
- ],
- 'IMARGUMENT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMARGUMENT'],
- 'argumentCount' => '1',
- ],
- 'IMCONJUGATE' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCONJUGATE'],
- 'argumentCount' => '1',
- ],
- 'IMCOS' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOS'],
- 'argumentCount' => '1',
- ],
- 'IMCOSH' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOSH'],
- 'argumentCount' => '1',
- ],
- 'IMCOT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCOT'],
- 'argumentCount' => '1',
- ],
- 'IMCSC' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCSC'],
- 'argumentCount' => '1',
- ],
- 'IMCSCH' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMCSCH'],
- 'argumentCount' => '1',
- ],
- 'IMDIV' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMDIV'],
- 'argumentCount' => '2',
- ],
- 'IMEXP' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMEXP'],
- 'argumentCount' => '1',
- ],
- 'IMLN' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLN'],
- 'argumentCount' => '1',
- ],
- 'IMLOG10' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLOG10'],
- 'argumentCount' => '1',
- ],
- 'IMLOG2' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMLOG2'],
- 'argumentCount' => '1',
- ],
- 'IMPOWER' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMPOWER'],
- 'argumentCount' => '2',
- ],
- 'IMPRODUCT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMPRODUCT'],
- 'argumentCount' => '1+',
- ],
- 'IMREAL' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMREAL'],
- 'argumentCount' => '1',
- ],
- 'IMSEC' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSEC'],
- 'argumentCount' => '1',
- ],
- 'IMSECH' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSECH'],
- 'argumentCount' => '1',
- ],
- 'IMSIN' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSIN'],
- 'argumentCount' => '1',
- ],
- 'IMSINH' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSINH'],
- 'argumentCount' => '1',
- ],
- 'IMSQRT' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSQRT'],
- 'argumentCount' => '1',
- ],
- 'IMSUB' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSUB'],
- 'argumentCount' => '2',
- ],
- 'IMSUM' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMSUM'],
- 'argumentCount' => '1+',
- ],
- 'IMTAN' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'IMTAN'],
- 'argumentCount' => '1',
- ],
- 'INDEX' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'INDEX'],
- 'argumentCount' => '1-4',
- ],
- 'INDIRECT' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'INDIRECT'],
- 'argumentCount' => '1,2',
- 'passCellReference' => true,
- ],
- 'INFO' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'INT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'INT'],
- 'argumentCount' => '1',
- ],
- 'INTERCEPT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'INTERCEPT'],
- 'argumentCount' => '2',
- ],
- 'INTRATE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'INTRATE'],
- 'argumentCount' => '4,5',
- ],
- 'IPMT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'IPMT'],
- 'argumentCount' => '4-6',
- ],
- 'IRR' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'IRR'],
- 'argumentCount' => '1,2',
- ],
- 'ISBLANK' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isBlank'],
- 'argumentCount' => '1',
- ],
- 'ISERR' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isErr'],
- 'argumentCount' => '1',
- ],
- 'ISERROR' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isError'],
- 'argumentCount' => '1',
- ],
- 'ISEVEN' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isEven'],
- 'argumentCount' => '1',
- ],
- 'ISFORMULA' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isFormula'],
- 'argumentCount' => '1',
- 'passCellReference' => true,
- 'passByReference' => [true],
- ],
- 'ISLOGICAL' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isLogical'],
- 'argumentCount' => '1',
- ],
- 'ISNA' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isNa'],
- 'argumentCount' => '1',
- ],
- 'ISNONTEXT' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isNonText'],
- 'argumentCount' => '1',
- ],
- 'ISNUMBER' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isNumber'],
- 'argumentCount' => '1',
- ],
- 'ISO.CEILING' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1,2',
- ],
- 'ISODD' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isOdd'],
- 'argumentCount' => '1',
- ],
- 'ISOWEEKNUM' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'ISOWEEKNUM'],
- 'argumentCount' => '1',
- ],
- 'ISPMT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'ISPMT'],
- 'argumentCount' => '4',
- ],
- 'ISREF' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'ISTEXT' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'isText'],
- 'argumentCount' => '1',
- ],
- 'JIS' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'KURT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'KURT'],
- 'argumentCount' => '1+',
- ],
- 'LARGE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LARGE'],
- 'argumentCount' => '2',
- ],
- 'LCM' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'LCM'],
- 'argumentCount' => '1+',
- ],
- 'LEFT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LEFT'],
- 'argumentCount' => '1,2',
- ],
- 'LEFTB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LEFT'],
- 'argumentCount' => '1,2',
- ],
- 'LEN' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'STRINGLENGTH'],
- 'argumentCount' => '1',
- ],
- 'LENB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'STRINGLENGTH'],
- 'argumentCount' => '1',
- ],
- 'LINEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LINEST'],
- 'argumentCount' => '1-4',
- ],
- 'LN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'log',
- 'argumentCount' => '1',
- ],
- 'LOG' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'logBase'],
- 'argumentCount' => '1,2',
- ],
- 'LOG10' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'log10',
- 'argumentCount' => '1',
- ],
- 'LOGEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGEST'],
- 'argumentCount' => '1-4',
- ],
- 'LOGINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGINV'],
- 'argumentCount' => '3',
- ],
- 'LOGNORMDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGNORMDIST'],
- 'argumentCount' => '3',
- ],
- 'LOGNORM.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGNORMDIST2'],
- 'argumentCount' => '4',
- ],
- 'LOGNORM.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'LOGINV'],
- 'argumentCount' => '3',
- ],
- 'LOOKUP' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'LOOKUP'],
- 'argumentCount' => '2,3',
- ],
- 'LOWER' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'LOWERCASE'],
- 'argumentCount' => '1',
- ],
- 'MATCH' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'MATCH'],
- 'argumentCount' => '2,3',
- ],
- 'MAX' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAX'],
- 'argumentCount' => '1+',
- ],
- 'MAXA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXA'],
- 'argumentCount' => '1+',
- ],
- 'MAXIFS' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXIFS'],
- 'argumentCount' => '3+',
- ],
- 'MDETERM' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MDETERM'],
- 'argumentCount' => '1',
- ],
- 'MDURATION' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '5,6',
- ],
- 'MEDIAN' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MEDIAN'],
- 'argumentCount' => '1+',
- ],
- 'MEDIANIF' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2+',
- ],
- 'MID' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'MID'],
- 'argumentCount' => '3',
- ],
- 'MIDB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'MID'],
- 'argumentCount' => '3',
- ],
- 'MIN' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MIN'],
- 'argumentCount' => '1+',
- ],
- 'MINA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINA'],
- 'argumentCount' => '1+',
- ],
- 'MINIFS' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINIFS'],
- 'argumentCount' => '3+',
- ],
- 'MINUTE' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'MINUTE'],
- 'argumentCount' => '1',
- ],
- 'MINVERSE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MINVERSE'],
- 'argumentCount' => '1',
- ],
- 'MIRR' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'MIRR'],
- 'argumentCount' => '3',
- ],
- 'MMULT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MMULT'],
- 'argumentCount' => '2',
- ],
- 'MOD' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MOD'],
- 'argumentCount' => '2',
- ],
- 'MODE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MODE'],
- 'argumentCount' => '1+',
- ],
- 'MODE.MULT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1+',
- ],
- 'MODE.SNGL' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MODE'],
- 'argumentCount' => '1+',
- ],
- 'MONTH' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'MONTHOFYEAR'],
- 'argumentCount' => '1',
- ],
- 'MROUND' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MROUND'],
- 'argumentCount' => '2',
- ],
- 'MULTINOMIAL' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'MULTINOMIAL'],
- 'argumentCount' => '1+',
- ],
- 'MUNIT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'N' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'n'],
- 'argumentCount' => '1',
- ],
- 'NA' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'NA'],
- 'argumentCount' => '0',
- ],
- 'NEGBINOMDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NEGBINOMDIST'],
- 'argumentCount' => '3',
- ],
- 'NEGBINOM.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '4',
- ],
- 'NETWORKDAYS' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'NETWORKDAYS'],
- 'argumentCount' => '2-3',
- ],
- 'NETWORKDAYS.INTL' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2-4',
- ],
- 'NOMINAL' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NOMINAL'],
- 'argumentCount' => '2',
- ],
- 'NORMDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMDIST'],
- 'argumentCount' => '4',
- ],
- 'NORM.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMDIST'],
- 'argumentCount' => '4',
- ],
- 'NORMINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMINV'],
- 'argumentCount' => '3',
- ],
- 'NORM.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMINV'],
- 'argumentCount' => '3',
- ],
- 'NORMSDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSDIST'],
- 'argumentCount' => '1',
- ],
- 'NORM.S.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSDIST2'],
- 'argumentCount' => '1,2',
- ],
- 'NORMSINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSINV'],
- 'argumentCount' => '1',
- ],
- 'NORM.S.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'NORMSINV'],
- 'argumentCount' => '1',
- ],
- 'NOT' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'NOT'],
- 'argumentCount' => '1',
- ],
- 'NOW' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATETIMENOW'],
- 'argumentCount' => '0',
- ],
- 'NPER' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NPER'],
- 'argumentCount' => '3-5',
- ],
- 'NPV' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'NPV'],
- 'argumentCount' => '2+',
- ],
- 'NUMBERVALUE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'NUMBERVALUE'],
- 'argumentCount' => '1+',
- ],
- 'OCT2BIN' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTOBIN'],
- 'argumentCount' => '1,2',
- ],
- 'OCT2DEC' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTODEC'],
- 'argumentCount' => '1',
- ],
- 'OCT2HEX' => [
- 'category' => Category::CATEGORY_ENGINEERING,
- 'functionCall' => [Engineering::class, 'OCTTOHEX'],
- 'argumentCount' => '1,2',
- ],
- 'ODD' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ODD'],
- 'argumentCount' => '1',
- ],
- 'ODDFPRICE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '8,9',
- ],
- 'ODDFYIELD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '8,9',
- ],
- 'ODDLPRICE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '7,8',
- ],
- 'ODDLYIELD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '7,8',
- ],
- 'OFFSET' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'OFFSET'],
- 'argumentCount' => '3-5',
- 'passCellReference' => true,
- 'passByReference' => [true],
- ],
- 'OR' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalOr'],
- 'argumentCount' => '1+',
- ],
- 'PDURATION' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PDURATION'],
- 'argumentCount' => '3',
- ],
- 'PEARSON' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'CORREL'],
- 'argumentCount' => '2',
- ],
- 'PERCENTILE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTILE'],
- 'argumentCount' => '2',
- ],
- 'PERCENTILE.EXC' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'PERCENTILE.INC' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTILE'],
- 'argumentCount' => '2',
- ],
- 'PERCENTRANK' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTRANK'],
- 'argumentCount' => '2,3',
- ],
- 'PERCENTRANK.EXC' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2,3',
- ],
- 'PERCENTRANK.INC' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERCENTRANK'],
- 'argumentCount' => '2,3',
- ],
- 'PERMUT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'PERMUT'],
- 'argumentCount' => '2',
- ],
- 'PERMUTATIONA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'PHONETIC' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'PHI' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1',
- ],
- 'PI' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'pi',
- 'argumentCount' => '0',
- ],
- 'PMT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PMT'],
- 'argumentCount' => '3-5',
- ],
- 'POISSON' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'POISSON'],
- 'argumentCount' => '3',
- ],
- 'POISSON.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'POISSON'],
- 'argumentCount' => '3',
- ],
- 'POWER' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'POWER'],
- 'argumentCount' => '2',
- ],
- 'PPMT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PPMT'],
- 'argumentCount' => '4-6',
- ],
- 'PRICE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICE'],
- 'argumentCount' => '6,7',
- ],
- 'PRICEDISC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICEDISC'],
- 'argumentCount' => '4,5',
- ],
- 'PRICEMAT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PRICEMAT'],
- 'argumentCount' => '5,6',
- ],
- 'PROB' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3,4',
- ],
- 'PRODUCT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'PRODUCT'],
- 'argumentCount' => '1+',
- ],
- 'PROPER' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'PROPERCASE'],
- 'argumentCount' => '1',
- ],
- 'PV' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'PV'],
- 'argumentCount' => '3-5',
- ],
- 'QUARTILE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'QUARTILE'],
- 'argumentCount' => '2',
- ],
- 'QUARTILE.EXC' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'QUARTILE.INC' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'QUARTILE'],
- 'argumentCount' => '2',
- ],
- 'QUOTIENT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'QUOTIENT'],
- 'argumentCount' => '2',
- ],
- 'RADIANS' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'deg2rad',
- 'argumentCount' => '1',
- ],
- 'RAND' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'RAND'],
- 'argumentCount' => '0',
- ],
- 'RANDARRAY' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '0-5',
- ],
- 'RANDBETWEEN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'RAND'],
- 'argumentCount' => '2',
- ],
- 'RANK' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RANK'],
- 'argumentCount' => '2,3',
- ],
- 'RANK.AVG' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2,3',
- ],
- 'RANK.EQ' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RANK'],
- 'argumentCount' => '2,3',
- ],
- 'RATE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RATE'],
- 'argumentCount' => '3-6',
- ],
- 'RECEIVED' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RECEIVED'],
- 'argumentCount' => '4-5',
- ],
- 'REPLACE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'REPLACE'],
- 'argumentCount' => '4',
- ],
- 'REPLACEB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'REPLACE'],
- 'argumentCount' => '4',
- ],
- 'REPT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => 'str_repeat',
- 'argumentCount' => '2',
- ],
- 'RIGHT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RIGHT'],
- 'argumentCount' => '1,2',
- ],
- 'RIGHTB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RIGHT'],
- 'argumentCount' => '1,2',
- ],
- 'ROMAN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROMAN'],
- 'argumentCount' => '1,2',
- ],
- 'ROUND' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'round',
- 'argumentCount' => '2',
- ],
- 'ROUNDDOWN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROUNDDOWN'],
- 'argumentCount' => '2',
- ],
- 'ROUNDUP' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'ROUNDUP'],
- 'argumentCount' => '2',
- ],
- 'ROW' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'ROW'],
- 'argumentCount' => '-1',
- 'passByReference' => [true],
- ],
- 'ROWS' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'ROWS'],
- 'argumentCount' => '1',
- ],
- 'RRI' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'RRI'],
- 'argumentCount' => '3',
- ],
- 'RSQ' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'RSQ'],
- 'argumentCount' => '2',
- ],
- 'RTD' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1+',
- ],
- 'SEARCH' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'],
- 'argumentCount' => '2,3',
- ],
- 'SEARCHB' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SEARCHINSENSITIVE'],
- 'argumentCount' => '2,3',
- ],
- 'SEC' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SEC'],
- 'argumentCount' => '1',
- ],
- 'SECH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SECH'],
- 'argumentCount' => '1',
- ],
- 'SECOND' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'SECOND'],
- 'argumentCount' => '1',
- ],
- 'SEQUENCE' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'SERIESSUM' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SERIESSUM'],
- 'argumentCount' => '4',
- ],
- 'SHEET' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '0,1',
- ],
- 'SHEETS' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '0,1',
- ],
- 'SIGN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SIGN'],
- 'argumentCount' => '1',
- ],
- 'SIN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sin',
- 'argumentCount' => '1',
- ],
- 'SINH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sinh',
- 'argumentCount' => '1',
- ],
- 'SKEW' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SKEW'],
- 'argumentCount' => '1+',
- ],
- 'SKEW.P' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1+',
- ],
- 'SLN' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'SLN'],
- 'argumentCount' => '3',
- ],
- 'SLOPE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SLOPE'],
- 'argumentCount' => '2',
- ],
- 'SMALL' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'SMALL'],
- 'argumentCount' => '2',
- ],
- 'SORT' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1+',
- ],
- 'SORTBY' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2+',
- ],
- 'SQRT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'sqrt',
- 'argumentCount' => '1',
- ],
- 'SQRTPI' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SQRTPI'],
- 'argumentCount' => '1',
- ],
- 'STANDARDIZE' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STANDARDIZE'],
- 'argumentCount' => '3',
- ],
- 'STDEV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEV'],
- 'argumentCount' => '1+',
- ],
- 'STDEV.S' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEV'],
- 'argumentCount' => '1+',
- ],
- 'STDEV.P' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVP'],
- 'argumentCount' => '1+',
- ],
- 'STDEVA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVA'],
- 'argumentCount' => '1+',
- ],
- 'STDEVP' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVP'],
- 'argumentCount' => '1+',
- ],
- 'STDEVPA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STDEVPA'],
- 'argumentCount' => '1+',
- ],
- 'STEYX' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'STEYX'],
- 'argumentCount' => '2',
- ],
- 'SUBSTITUTE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'SUBSTITUTE'],
- 'argumentCount' => '3,4',
- ],
- 'SUBTOTAL' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUBTOTAL'],
- 'argumentCount' => '2+',
- 'passCellReference' => true,
- ],
- 'SUM' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUM'],
- 'argumentCount' => '1+',
- ],
- 'SUMIF' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMIF'],
- 'argumentCount' => '2,3',
- ],
- 'SUMIFS' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMIFS'],
- 'argumentCount' => '3+',
- ],
- 'SUMPRODUCT' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMPRODUCT'],
- 'argumentCount' => '1+',
- ],
- 'SUMSQ' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMSQ'],
- 'argumentCount' => '1+',
- ],
- 'SUMX2MY2' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMX2MY2'],
- 'argumentCount' => '2',
- ],
- 'SUMX2PY2' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMX2PY2'],
- 'argumentCount' => '2',
- ],
- 'SUMXMY2' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'SUMXMY2'],
- 'argumentCount' => '2',
- ],
- 'SWITCH' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'statementSwitch'],
- 'argumentCount' => '3+',
- ],
- 'SYD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'SYD'],
- 'argumentCount' => '4',
- ],
- 'T' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'RETURNSTRING'],
- 'argumentCount' => '1',
- ],
- 'TAN' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'tan',
- 'argumentCount' => '1',
- ],
- 'TANH' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => 'tanh',
- 'argumentCount' => '1',
- ],
- 'TBILLEQ' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLEQ'],
- 'argumentCount' => '3',
- ],
- 'TBILLPRICE' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLPRICE'],
- 'argumentCount' => '3',
- ],
- 'TBILLYIELD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'TBILLYIELD'],
- 'argumentCount' => '3',
- ],
- 'TDIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TDIST'],
- 'argumentCount' => '3',
- ],
- 'T.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3',
- ],
- 'T.DIST.2T' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'T.DIST.RT' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'TEXT' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TEXTFORMAT'],
- 'argumentCount' => '2',
- ],
- 'TEXTJOIN' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TEXTJOIN'],
- 'argumentCount' => '3+',
- ],
- 'TIME' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'TIME'],
- 'argumentCount' => '3',
- ],
- 'TIMEVALUE' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'TIMEVALUE'],
- 'argumentCount' => '1',
- ],
- 'TINV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TINV'],
- 'argumentCount' => '2',
- ],
- 'T.INV' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TINV'],
- 'argumentCount' => '2',
- ],
- 'T.INV.2T' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'TODAY' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'DATENOW'],
- 'argumentCount' => '0',
- ],
- 'TRANSPOSE' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'TRANSPOSE'],
- 'argumentCount' => '1',
- ],
- 'TREND' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TREND'],
- 'argumentCount' => '1-4',
- ],
- 'TRIM' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'TRIMSPACES'],
- 'argumentCount' => '1',
- ],
- 'TRIMMEAN' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'TRIMMEAN'],
- 'argumentCount' => '2',
- ],
- 'TRUE' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'TRUE'],
- 'argumentCount' => '0',
- ],
- 'TRUNC' => [
- 'category' => Category::CATEGORY_MATH_AND_TRIG,
- 'functionCall' => [MathTrig::class, 'TRUNC'],
- 'argumentCount' => '1,2',
- ],
- 'TTEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '4',
- ],
- 'T.TEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '4',
- ],
- 'TYPE' => [
- 'category' => Category::CATEGORY_INFORMATION,
- 'functionCall' => [Functions::class, 'TYPE'],
- 'argumentCount' => '1',
- ],
- 'UNICHAR' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'CHARACTER'],
- 'argumentCount' => '1',
- ],
- 'UNICODE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'ASCIICODE'],
- 'argumentCount' => '1',
- ],
- 'UNIQUE' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1+',
- ],
- 'UPPER' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'UPPERCASE'],
- 'argumentCount' => '1',
- ],
- 'USDOLLAR' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2',
- ],
- 'VALUE' => [
- 'category' => Category::CATEGORY_TEXT_AND_DATA,
- 'functionCall' => [TextData::class, 'VALUE'],
- 'argumentCount' => '1',
- ],
- 'VAR' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARFunc'],
- 'argumentCount' => '1+',
- ],
- 'VAR.P' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARP'],
- 'argumentCount' => '1+',
- ],
- 'VAR.S' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARFunc'],
- 'argumentCount' => '1+',
- ],
- 'VARA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARA'],
- 'argumentCount' => '1+',
- ],
- 'VARP' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARP'],
- 'argumentCount' => '1+',
- ],
- 'VARPA' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'VARPA'],
- 'argumentCount' => '1+',
- ],
- 'VDB' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '5-7',
- ],
- 'VLOOKUP' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [LookupRef::class, 'VLOOKUP'],
- 'argumentCount' => '3,4',
- ],
- 'WEBSERVICE' => [
- 'category' => Category::CATEGORY_WEB,
- 'functionCall' => [Web::class, 'WEBSERVICE'],
- 'argumentCount' => '1',
- ],
- 'WEEKDAY' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WEEKDAY'],
- 'argumentCount' => '1,2',
- ],
- 'WEEKNUM' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WEEKNUM'],
- 'argumentCount' => '1,2',
- ],
- 'WEIBULL' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'WEIBULL'],
- 'argumentCount' => '4',
- ],
- 'WEIBULL.DIST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'WEIBULL'],
- 'argumentCount' => '4',
- ],
- 'WORKDAY' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'WORKDAY'],
- 'argumentCount' => '2-3',
- ],
- 'WORKDAY.INTL' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2-4',
- ],
- 'XIRR' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'XIRR'],
- 'argumentCount' => '2,3',
- ],
- 'XLOOKUP' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '3-6',
- ],
- 'XNPV' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'XNPV'],
- 'argumentCount' => '3',
- ],
- 'XMATCH' => [
- 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '2,3',
- ],
- 'XOR' => [
- 'category' => Category::CATEGORY_LOGICAL,
- 'functionCall' => [Logical::class, 'logicalXor'],
- 'argumentCount' => '1+',
- ],
- 'YEAR' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'YEAR'],
- 'argumentCount' => '1',
- ],
- 'YEARFRAC' => [
- 'category' => Category::CATEGORY_DATE_AND_TIME,
- 'functionCall' => [DateTime::class, 'YEARFRAC'],
- 'argumentCount' => '2,3',
- ],
- 'YIELD' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '6,7',
- ],
- 'YIELDDISC' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'YIELDDISC'],
- 'argumentCount' => '4,5',
- ],
- 'YIELDMAT' => [
- 'category' => Category::CATEGORY_FINANCIAL,
- 'functionCall' => [Financial::class, 'YIELDMAT'],
- 'argumentCount' => '5,6',
- ],
- 'ZTEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'ZTEST'],
- 'argumentCount' => '2-3',
- ],
- 'Z.TEST' => [
- 'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'ZTEST'],
- 'argumentCount' => '2-3',
- ],
- ];
-
- // Internal functions used for special control purposes
- private static $controlFunctions = [
- 'MKMATRIX' => [
- 'argumentCount' => '*',
- 'functionCall' => [__CLASS__, 'mkMatrix'],
- ],
- 'NAME.ERROR' => [
- 'argumentCount' => '*',
- 'functionCall' => [Functions::class, 'NAME'],
- ],
- ];
-
- public function __construct(?Spreadsheet $spreadsheet = null)
- {
- $this->delta = 1 * 10 ** (0 - ini_get('precision'));
-
- $this->spreadsheet = $spreadsheet;
- $this->cyclicReferenceStack = new CyclicReferenceStack();
- $this->debugLog = new Logger($this->cyclicReferenceStack);
- self::$referenceHelper = ReferenceHelper::getInstance();
- }
-
- private static function loadLocales(): void
- {
- $localeFileDirectory = __DIR__ . '/locale/';
- foreach (glob($localeFileDirectory . '*', GLOB_ONLYDIR) as $filename) {
- $filename = substr($filename, strlen($localeFileDirectory));
- if ($filename != 'en') {
- self::$validLocaleLanguages[] = $filename;
- }
- }
- }
-
- /**
- * Get an instance of this class.
- *
- * @param Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
- * or NULL to create a standalone claculation engine
- *
- * @return Calculation
- */
- public static function getInstance(?Spreadsheet $spreadsheet = null)
- {
- if ($spreadsheet !== null) {
- $instance = $spreadsheet->getCalculationEngine();
- if (isset($instance)) {
- return $instance;
- }
- }
-
- if (!isset(self::$instance) || (self::$instance === null)) {
- self::$instance = new self();
- }
-
- return self::$instance;
- }
-
- /**
- * Flush the calculation cache for any existing instance of this class
- * but only if a Calculation instance exists.
- */
- public function flushInstance(): void
- {
- $this->clearCalculationCache();
- $this->clearBranchStore();
- }
-
- /**
- * Get the Logger for this calculation engine instance.
- *
- * @return Logger
- */
- public function getDebugLog()
- {
- return $this->debugLog;
- }
-
- /**
- * __clone implementation. Cloning should not be allowed in a Singleton!
- */
- final public function __clone()
- {
- throw new Exception('Cloning the calculation engine is not allowed!');
- }
-
- /**
- * Return the locale-specific translation of TRUE.
- *
- * @return string locale-specific translation of TRUE
- */
- public static function getTRUE()
- {
- return self::$localeBoolean['TRUE'];
- }
-
- /**
- * Return the locale-specific translation of FALSE.
- *
- * @return string locale-specific translation of FALSE
- */
- public static function getFALSE()
- {
- return self::$localeBoolean['FALSE'];
- }
-
- /**
- * Set the Array Return Type (Array or Value of first element in the array).
- *
- * @param string $returnType Array return type
- *
- * @return bool Success or failure
- */
- public static function setArrayReturnType($returnType)
- {
- if (
- ($returnType == self::RETURN_ARRAY_AS_VALUE) ||
- ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
- ($returnType == self::RETURN_ARRAY_AS_ARRAY)
- ) {
- self::$returnArrayAsType = $returnType;
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Return the Array Return Type (Array or Value of first element in the array).
- *
- * @return string $returnType Array return type
- */
- public static function getArrayReturnType()
- {
- return self::$returnArrayAsType;
- }
-
- /**
- * Is calculation caching enabled?
- *
- * @return bool
- */
- public function getCalculationCacheEnabled()
- {
- return $this->calculationCacheEnabled;
- }
-
- /**
- * Enable/disable calculation cache.
- *
- * @param bool $pValue
- */
- public function setCalculationCacheEnabled($pValue): void
- {
- $this->calculationCacheEnabled = $pValue;
- $this->clearCalculationCache();
- }
-
- /**
- * Enable calculation cache.
- */
- public function enableCalculationCache(): void
- {
- $this->setCalculationCacheEnabled(true);
- }
-
- /**
- * Disable calculation cache.
- */
- public function disableCalculationCache(): void
- {
- $this->setCalculationCacheEnabled(false);
- }
-
- /**
- * Clear calculation cache.
- */
- public function clearCalculationCache(): void
- {
- $this->calculationCache = [];
- }
-
- /**
- * Clear calculation cache for a specified worksheet.
- *
- * @param string $worksheetName
- */
- public function clearCalculationCacheForWorksheet($worksheetName): void
- {
- if (isset($this->calculationCache[$worksheetName])) {
- unset($this->calculationCache[$worksheetName]);
- }
- }
-
- /**
- * Rename calculation cache for a specified worksheet.
- *
- * @param string $fromWorksheetName
- * @param string $toWorksheetName
- */
- public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName): void
- {
- if (isset($this->calculationCache[$fromWorksheetName])) {
- $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
- unset($this->calculationCache[$fromWorksheetName]);
- }
- }
-
- /**
- * Enable/disable calculation cache.
- *
- * @param mixed $enabled
- */
- public function setBranchPruningEnabled($enabled): void
- {
- $this->branchPruningEnabled = $enabled;
- }
-
- public function enableBranchPruning(): void
- {
- $this->setBranchPruningEnabled(true);
- }
-
- public function disableBranchPruning(): void
- {
- $this->setBranchPruningEnabled(false);
- }
-
- public function clearBranchStore(): void
- {
- $this->branchStoreKeyCounter = 0;
- }
-
- /**
- * Get the currently defined locale code.
- *
- * @return string
- */
- public function getLocale()
- {
- return self::$localeLanguage;
- }
-
- /**
- * Set the locale code.
- *
- * @param string $locale The locale to use for formula translation, eg: 'en_us'
- *
- * @return bool
- */
- public function setLocale($locale)
- {
- // Identify our locale and language
- $language = $locale = strtolower($locale);
- if (strpos($locale, '_') !== false) {
- [$language] = explode('_', $locale);
- }
- if (count(self::$validLocaleLanguages) == 1) {
- self::loadLocales();
- }
- // Test whether we have any language data for this language (any locale)
- if (in_array($language, self::$validLocaleLanguages)) {
- // initialise language/locale settings
- self::$localeFunctions = [];
- self::$localeArgumentSeparator = ',';
- self::$localeBoolean = ['TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL'];
- // Default is English, if user isn't requesting english, then read the necessary data from the locale files
- if ($locale != 'en_us') {
- // Search for a file with a list of function names for locale
- $functionNamesFile = __DIR__ . '/locale/' . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'functions';
- if (!file_exists($functionNamesFile)) {
- // If there isn't a locale specific function file, look for a language specific function file
- $functionNamesFile = __DIR__ . '/locale/' . $language . DIRECTORY_SEPARATOR . 'functions';
- if (!file_exists($functionNamesFile)) {
- return false;
- }
- }
- // Retrieve the list of locale or language specific function names
- $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- foreach ($localeFunctions as $localeFunction) {
- [$localeFunction] = explode('##', $localeFunction); // Strip out comments
- if (strpos($localeFunction, '=') !== false) {
- [$fName, $lfName] = explode('=', $localeFunction);
- $fName = trim($fName);
- $lfName = trim($lfName);
- if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
- self::$localeFunctions[$fName] = $lfName;
- }
- }
- }
- // Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
- if (isset(self::$localeFunctions['TRUE'])) {
- self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE'];
- }
- if (isset(self::$localeFunctions['FALSE'])) {
- self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
- }
-
- $configFile = __DIR__ . '/locale/' . str_replace('_', DIRECTORY_SEPARATOR, $locale) . DIRECTORY_SEPARATOR . 'config';
- if (!file_exists($configFile)) {
- $configFile = __DIR__ . '/locale/' . $language . DIRECTORY_SEPARATOR . 'config';
- }
- if (file_exists($configFile)) {
- $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
- foreach ($localeSettings as $localeSetting) {
- [$localeSetting] = explode('##', $localeSetting); // Strip out comments
- if (strpos($localeSetting, '=') !== false) {
- [$settingName, $settingValue] = explode('=', $localeSetting);
- $settingName = strtoupper(trim($settingName));
- switch ($settingName) {
- case 'ARGUMENTSEPARATOR':
- self::$localeArgumentSeparator = trim($settingValue);
-
- break;
- }
- }
- }
- }
- }
-
- self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
- self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
- self::$localeLanguage = $locale;
-
- return true;
- }
-
- return false;
- }
-
- /**
- * @param string $fromSeparator
- * @param string $toSeparator
- * @param string $formula
- * @param bool $inBraces
- *
- * @return string
- */
- public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
- {
- $strlen = mb_strlen($formula);
- for ($i = 0; $i < $strlen; ++$i) {
- $chr = mb_substr($formula, $i, 1);
- switch ($chr) {
- case self::FORMULA_OPEN_FUNCTION_BRACE:
- $inBraces = true;
-
- break;
- case self::FORMULA_CLOSE_FUNCTION_BRACE:
- $inBraces = false;
-
- break;
- case $fromSeparator:
- if (!$inBraces) {
- $formula = mb_substr($formula, 0, $i) . $toSeparator . mb_substr($formula, $i + 1);
- }
- }
- }
-
- return $formula;
- }
-
- /**
- * @param string[] $from
- * @param string[] $to
- * @param string $formula
- * @param string $fromSeparator
- * @param string $toSeparator
- *
- * @return string
- */
- private static function translateFormula(array $from, array $to, $formula, $fromSeparator, $toSeparator)
- {
- // Convert any Excel function names to the required language
- if (self::$localeLanguage !== 'en_us') {
- $inBraces = false;
- // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
- if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) {
- // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
- // the formula
- $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
- $i = false;
- foreach ($temp as &$value) {
- // Only count/replace in alternating array entries
- if ($i = !$i) {
- $value = preg_replace($from, $to, $value);
- $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces);
- }
- }
- unset($value);
- // Then rebuild the formula string
- $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
- } else {
- // If there's no quoted strings, then we do a simple count/replace
- $formula = preg_replace($from, $to, $formula);
- $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inBraces);
- }
- }
-
- return $formula;
- }
-
- private static $functionReplaceFromExcel = null;
-
- private static $functionReplaceToLocale = null;
-
- public function _translateFormulaToLocale($formula)
- {
- if (self::$functionReplaceFromExcel === null) {
- self::$functionReplaceFromExcel = [];
- foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
- self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelFunctionName, '/') . '([\s]*\()/Ui';
- }
- foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
- self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/Ui';
- }
- }
-
- if (self::$functionReplaceToLocale === null) {
- self::$functionReplaceToLocale = [];
- foreach (self::$localeFunctions as $localeFunctionName) {
- self::$functionReplaceToLocale[] = '$1' . trim($localeFunctionName) . '$2';
- }
- foreach (self::$localeBoolean as $localeBoolean) {
- self::$functionReplaceToLocale[] = '$1' . trim($localeBoolean) . '$2';
- }
- }
-
- return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
- }
-
- private static $functionReplaceFromLocale = null;
-
- private static $functionReplaceToExcel = null;
-
- public function _translateFormulaToEnglish($formula)
- {
- if (self::$functionReplaceFromLocale === null) {
- self::$functionReplaceFromLocale = [];
- foreach (self::$localeFunctions as $localeFunctionName) {
- self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($localeFunctionName, '/') . '([\s]*\()/Ui';
- }
- foreach (self::$localeBoolean as $excelBoolean) {
- self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/Ui';
- }
- }
-
- if (self::$functionReplaceToExcel === null) {
- self::$functionReplaceToExcel = [];
- foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
- self::$functionReplaceToExcel[] = '$1' . trim($excelFunctionName) . '$2';
- }
- foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
- self::$functionReplaceToExcel[] = '$1' . trim($excelBoolean) . '$2';
- }
- }
-
- return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
- }
-
- public static function localeFunc($function)
- {
- if (self::$localeLanguage !== 'en_us') {
- $functionName = trim($function, '(');
- if (isset(self::$localeFunctions[$functionName])) {
- $brace = ($functionName != $function);
- $function = self::$localeFunctions[$functionName];
- if ($brace) {
- $function .= '(';
- }
- }
- }
-
- return $function;
- }
-
- /**
- * Wrap string values in quotes.
- *
- * @param mixed $value
- *
- * @return mixed
- */
- public static function wrapResult($value)
- {
- if (is_string($value)) {
- // Error values cannot be "wrapped"
- if (preg_match('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
- // Return Excel errors "as is"
- return $value;
- }
- // Return strings wrapped in quotes
- return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
- } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
- // Convert numeric errors to NaN error
- return Functions::NAN();
- }
-
- return $value;
- }
-
- /**
- * Remove quotes used as a wrapper to identify string values.
- *
- * @param mixed $value
- *
- * @return mixed
- */
- public static function unwrapResult($value)
- {
- if (is_string($value)) {
- if ((isset($value[0])) && ($value[0] == self::FORMULA_STRING_QUOTE) && (substr($value, -1) == self::FORMULA_STRING_QUOTE)) {
- return substr($value, 1, -1);
- }
- // Convert numeric errors to NAN error
- } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
- return Functions::NAN();
- }
-
- return $value;
- }
-
- /**
- * Calculate cell value (using formula from a cell ID)
- * Retained for backward compatibility.
- *
- * @param Cell $pCell Cell to calculate
- *
- * @return mixed
- */
- public function calculate(?Cell $pCell = null)
- {
- try {
- return $this->calculateCellValue($pCell);
- } catch (\Exception $e) {
- throw new Exception($e->getMessage());
- }
- }
-
- /**
- * Calculate the value of a cell formula.
- *
- * @param Cell $pCell Cell to calculate
- * @param bool $resetLog Flag indicating whether the debug log should be reset or not
- *
- * @return mixed
- */
- public function calculateCellValue(?Cell $pCell = null, $resetLog = true)
- {
- if ($pCell === null) {
- return null;
- }
-
- $returnArrayAsType = self::$returnArrayAsType;
- if ($resetLog) {
- // Initialise the logging settings if requested
- $this->formulaError = null;
- $this->debugLog->clearLog();
- $this->cyclicReferenceStack->clear();
- $this->cyclicFormulaCounter = 1;
-
- self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
- }
-
- // Execute the calculation for the cell formula
- $this->cellStack[] = [
- 'sheet' => $pCell->getWorksheet()->getTitle(),
- 'cell' => $pCell->getCoordinate(),
- ];
-
- try {
- $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
- $cellAddress = array_pop($this->cellStack);
- $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
- } catch (\Exception $e) {
- $cellAddress = array_pop($this->cellStack);
- $this->spreadsheet->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
-
- throw new Exception($e->getMessage());
- }
-
- if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
- self::$returnArrayAsType = $returnArrayAsType;
- $testResult = Functions::flattenArray($result);
- if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
- return Functions::VALUE();
- }
- // If there's only a single cell in the array, then we allow it
- if (count($testResult) != 1) {
- // If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
- $r = array_keys($result);
- $r = array_shift($r);
- if (!is_numeric($r)) {
- return Functions::VALUE();
- }
- if (is_array($result[$r])) {
- $c = array_keys($result[$r]);
- $c = array_shift($c);
- if (!is_numeric($c)) {
- return Functions::VALUE();
- }
- }
- }
- $result = array_shift($testResult);
- }
- self::$returnArrayAsType = $returnArrayAsType;
-
- if ($result === null && $pCell->getWorksheet()->getSheetView()->getShowZeros()) {
- return 0;
- } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
- return Functions::NAN();
- }
-
- return $result;
- }
-
- /**
- * Validate and parse a formula string.
- *
- * @param string $formula Formula to parse
- *
- * @return array|bool
- */
- public function parseFormula($formula)
- {
- // Basic validation that this is indeed a formula
- // We return an empty array if not
- $formula = trim($formula);
- if ((!isset($formula[0])) || ($formula[0] != '=')) {
- return [];
- }
- $formula = ltrim(substr($formula, 1));
- if (!isset($formula[0])) {
- return [];
- }
-
- // Parse the formula and return the token stack
- return $this->internalParseFormula($formula);
- }
-
- /**
- * Calculate the value of a formula.
- *
- * @param string $formula Formula to parse
- * @param string $cellID Address of the cell to calculate
- * @param Cell $pCell Cell to calculate
- *
- * @return mixed
- */
- public function calculateFormula($formula, $cellID = null, ?Cell $pCell = null)
- {
- // Initialise the logging settings
- $this->formulaError = null;
- $this->debugLog->clearLog();
- $this->cyclicReferenceStack->clear();
-
- $resetCache = $this->getCalculationCacheEnabled();
- if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
- $cellID = 'A1';
- $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
- } else {
- // Disable calculation cacheing because it only applies to cell calculations, not straight formulae
- // But don't actually flush any cache
- $this->calculationCacheEnabled = false;
- }
-
- // Execute the calculation
- try {
- $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
- } catch (\Exception $e) {
- throw new Exception($e->getMessage());
- }
-
- if ($this->spreadsheet === null) {
- // Reset calculation cacheing to its previous state
- $this->calculationCacheEnabled = $resetCache;
- }
-
- return $result;
- }
-
- /**
- * @param string $cellReference
- * @param mixed $cellValue
- *
- * @return bool
- */
- public function getValueFromCache($cellReference, &$cellValue)
- {
- // Is calculation cacheing enabled?
- // Is the value present in calculation cache?
- $this->debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
- if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
- $this->debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
- // Return the cached result
-
- $cellValue = $this->calculationCache[$cellReference];
-
- return true;
- }
-
- return false;
- }
-
- /**
- * @param string $cellReference
- * @param mixed $cellValue
- */
- public function saveValueToCache($cellReference, $cellValue): void
- {
- if ($this->calculationCacheEnabled) {
- $this->calculationCache[$cellReference] = $cellValue;
- }
- }
-
- /**
- * Parse a cell formula and calculate its value.
- *
- * @param string $formula The formula to parse and calculate
- * @param string $cellID The ID (e.g. A3) of the cell that we are calculating
- * @param Cell $pCell Cell to calculate
- *
- * @return mixed
- */
- public function _calculateFormulaValue($formula, $cellID = null, ?Cell $pCell = null)
- {
- $cellValue = null;
-
- // Quote-Prefixed cell values cannot be formulae, but are treated as strings
- if ($pCell !== null && $pCell->getStyle()->getQuotePrefix() === true) {
- return self::wrapResult((string) $formula);
- }
-
- if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
- return self::wrapResult($formula);
- }
-
- // Basic validation that this is indeed a formula
- // We simply return the cell value if not
- $formula = trim($formula);
- if ($formula[0] != '=') {
- return self::wrapResult($formula);
- }
- $formula = ltrim(substr($formula, 1));
- if (!isset($formula[0])) {
- return self::wrapResult($formula);
- }
-
- $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
- $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
- $wsCellReference = $wsTitle . '!' . $cellID;
-
- if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
- return $cellValue;
- }
- $this->debugLog->writeDebugLog('Evaluating formula for cell ', $wsCellReference);
-
- if (($wsTitle[0] !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
- if ($this->cyclicFormulaCount <= 0) {
- $this->cyclicFormulaCell = '';
-
- return $this->raiseFormulaError('Cyclic Reference in Formula');
- } elseif ($this->cyclicFormulaCell === $wsCellReference) {
- ++$this->cyclicFormulaCounter;
- if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
- $this->cyclicFormulaCell = '';
-
- return $cellValue;
- }
- } elseif ($this->cyclicFormulaCell == '') {
- if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
- return $cellValue;
- }
- $this->cyclicFormulaCell = $wsCellReference;
- }
- }
-
- $this->debugLog->writeDebugLog('Formula for cell ', $wsCellReference, ' is ', $formula);
- // Parse the formula onto the token stack and calculate the value
- $this->cyclicReferenceStack->push($wsCellReference);
- $cellValue = $this->processTokenStack($this->internalParseFormula($formula, $pCell), $cellID, $pCell);
- $this->cyclicReferenceStack->pop();
-
- // Save to calculation cache
- if ($cellID !== null) {
- $this->saveValueToCache($wsCellReference, $cellValue);
- }
-
- // Return the calculated value
- return $cellValue;
- }
-
- /**
- * Ensure that paired matrix operands are both matrices and of the same size.
- *
- * @param mixed &$operand1 First matrix operand
- * @param mixed &$operand2 Second matrix operand
- * @param int $resize Flag indicating whether the matrices should be resized to match
- * and (if so), whether the smaller dimension should grow or the
- * larger should shrink.
- * 0 = no resize
- * 1 = shrink to fit
- * 2 = extend to fit
- *
- * @return array
- */
- private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1)
- {
- // Examine each of the two operands, and turn them into an array if they aren't one already
- // Note that this function should only be called if one or both of the operand is already an array
- if (!is_array($operand1)) {
- [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand2);
- $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
- $resize = 0;
- } elseif (!is_array($operand2)) {
- [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand1);
- $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
- $resize = 0;
- }
-
- [$matrix1Rows, $matrix1Columns] = self::getMatrixDimensions($operand1);
- [$matrix2Rows, $matrix2Columns] = self::getMatrixDimensions($operand2);
- if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
- $resize = 1;
- }
-
- if ($resize == 2) {
- // Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
- self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
- } elseif ($resize == 1) {
- // Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
- self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
- }
-
- return [$matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns];
- }
-
- /**
- * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0.
- *
- * @param array &$matrix matrix operand
- *
- * @return int[] An array comprising the number of rows, and number of columns
- */
- public static function getMatrixDimensions(array &$matrix)
- {
- $matrixRows = count($matrix);
- $matrixColumns = 0;
- foreach ($matrix as $rowKey => $rowValue) {
- if (!is_array($rowValue)) {
- $matrix[$rowKey] = [$rowValue];
- $matrixColumns = max(1, $matrixColumns);
- } else {
- $matrix[$rowKey] = array_values($rowValue);
- $matrixColumns = max(count($rowValue), $matrixColumns);
- }
- }
- $matrix = array_values($matrix);
-
- return [$matrixRows, $matrixColumns];
- }
-
- /**
- * Ensure that paired matrix operands are both matrices of the same size.
- *
- * @param mixed &$matrix1 First matrix operand
- * @param mixed &$matrix2 Second matrix operand
- * @param int $matrix1Rows Row size of first matrix operand
- * @param int $matrix1Columns Column size of first matrix operand
- * @param int $matrix2Rows Row size of second matrix operand
- * @param int $matrix2Columns Column size of second matrix operand
- */
- private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
- {
- if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
- if ($matrix2Rows < $matrix1Rows) {
- for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
- unset($matrix1[$i]);
- }
- }
- if ($matrix2Columns < $matrix1Columns) {
- for ($i = 0; $i < $matrix1Rows; ++$i) {
- for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
- unset($matrix1[$i][$j]);
- }
- }
- }
- }
-
- if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
- if ($matrix1Rows < $matrix2Rows) {
- for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
- unset($matrix2[$i]);
- }
- }
- if ($matrix1Columns < $matrix2Columns) {
- for ($i = 0; $i < $matrix2Rows; ++$i) {
- for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
- unset($matrix2[$i][$j]);
- }
- }
- }
- }
- }
-
- /**
- * Ensure that paired matrix operands are both matrices of the same size.
- *
- * @param mixed &$matrix1 First matrix operand
- * @param mixed &$matrix2 Second matrix operand
- * @param int $matrix1Rows Row size of first matrix operand
- * @param int $matrix1Columns Column size of first matrix operand
- * @param int $matrix2Rows Row size of second matrix operand
- * @param int $matrix2Columns Column size of second matrix operand
- */
- private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
- {
- if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
- if ($matrix2Columns < $matrix1Columns) {
- for ($i = 0; $i < $matrix2Rows; ++$i) {
- $x = $matrix2[$i][$matrix2Columns - 1];
- for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
- $matrix2[$i][$j] = $x;
- }
- }
- }
- if ($matrix2Rows < $matrix1Rows) {
- $x = $matrix2[$matrix2Rows - 1];
- for ($i = 0; $i < $matrix1Rows; ++$i) {
- $matrix2[$i] = $x;
- }
- }
- }
-
- if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
- if ($matrix1Columns < $matrix2Columns) {
- for ($i = 0; $i < $matrix1Rows; ++$i) {
- $x = $matrix1[$i][$matrix1Columns - 1];
- for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
- $matrix1[$i][$j] = $x;
- }
- }
- }
- if ($matrix1Rows < $matrix2Rows) {
- $x = $matrix1[$matrix1Rows - 1];
- for ($i = 0; $i < $matrix2Rows; ++$i) {
- $matrix1[$i] = $x;
- }
- }
- }
- }
-
- /**
- * Format details of an operand for display in the log (based on operand type).
- *
- * @param mixed $value First matrix operand
- *
- * @return mixed
- */
- private function showValue($value)
- {
- if ($this->debugLog->getWriteDebugLog()) {
- $testArray = Functions::flattenArray($value);
- if (count($testArray) == 1) {
- $value = array_pop($testArray);
- }
-
- if (is_array($value)) {
- $returnMatrix = [];
- $pad = $rpad = ', ';
- foreach ($value as $row) {
- if (is_array($row)) {
- $returnMatrix[] = implode($pad, array_map([$this, 'showValue'], $row));
- $rpad = '; ';
- } else {
- $returnMatrix[] = $this->showValue($row);
- }
- }
-
- return '{ ' . implode($rpad, $returnMatrix) . ' }';
- } elseif (is_string($value) && (trim($value, self::FORMULA_STRING_QUOTE) == $value)) {
- return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
- } elseif (is_bool($value)) {
- return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
- }
- }
-
- return Functions::flattenSingleValue($value);
- }
-
- /**
- * Format type and details of an operand for display in the log (based on operand type).
- *
- * @param mixed $value First matrix operand
- *
- * @return null|string
- */
- private function showTypeDetails($value)
- {
- if ($this->debugLog->getWriteDebugLog()) {
- $testArray = Functions::flattenArray($value);
- if (count($testArray) == 1) {
- $value = array_pop($testArray);
- }
-
- if ($value === null) {
- return 'a NULL value';
- } elseif (is_float($value)) {
- $typeString = 'a floating point number';
- } elseif (is_int($value)) {
- $typeString = 'an integer number';
- } elseif (is_bool($value)) {
- $typeString = 'a boolean';
- } elseif (is_array($value)) {
- $typeString = 'a matrix';
- } else {
- if ($value == '') {
- return 'an empty string';
- } elseif ($value[0] == '#') {
- return 'a ' . $value . ' error';
- }
- $typeString = 'a string';
- }
-
- return $typeString . ' with a value of ' . $this->showValue($value);
- }
- }
-
- /**
- * @param string $formula
- *
- * @return false|string False indicates an error
- */
- private function convertMatrixReferences($formula)
- {
- static $matrixReplaceFrom = [self::FORMULA_OPEN_FUNCTION_BRACE, ';', self::FORMULA_CLOSE_FUNCTION_BRACE];
- static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'];
-
- // Convert any Excel matrix references to the MKMATRIX() function
- if (strpos($formula, self::FORMULA_OPEN_FUNCTION_BRACE) !== false) {
- // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
- if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) {
- // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
- // the formula
- $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
- // Open and Closed counts used for trapping mismatched braces in the formula
- $openCount = $closeCount = 0;
- $i = false;
- foreach ($temp as &$value) {
- // Only count/replace in alternating array entries
- if ($i = !$i) {
- $openCount += substr_count($value, self::FORMULA_OPEN_FUNCTION_BRACE);
- $closeCount += substr_count($value, self::FORMULA_CLOSE_FUNCTION_BRACE);
- $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
- }
- }
- unset($value);
- // Then rebuild the formula string
- $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
- } else {
- // If there's no quoted strings, then we do a simple count/replace
- $openCount = substr_count($formula, self::FORMULA_OPEN_FUNCTION_BRACE);
- $closeCount = substr_count($formula, self::FORMULA_CLOSE_FUNCTION_BRACE);
- $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
- }
- // Trap for mismatched braces and trigger an appropriate error
- if ($openCount < $closeCount) {
- if ($openCount > 0) {
- return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
- }
-
- return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered");
- } elseif ($openCount > $closeCount) {
- if ($closeCount > 0) {
- return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
- }
-
- return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered");
- }
- }
-
- return $formula;
- }
-
- private static function mkMatrix(...$args)
- {
- return $args;
- }
-
- // Binary Operators
- // These operators always work on two values
- // Array key is the operator, the value indicates whether this is a left or right associative operator
- private static $operatorAssociativity = [
- '^' => 0, // Exponentiation
- '*' => 0, '/' => 0, // Multiplication and Division
- '+' => 0, '-' => 0, // Addition and Subtraction
- '&' => 0, // Concatenation
- '|' => 0, ':' => 0, // Intersect and Range
- '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, // Comparison
- ];
-
- // Comparison (Boolean) Operators
- // These operators work on two values, but always return a boolean result
- private static $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true];
-
- // Operator Precedence
- // This list includes all valid operators, whether binary (including boolean) or unary (such as %)
- // Array key is the operator, the value is its precedence
- private static $operatorPrecedence = [
- ':' => 8, // Range
- '|' => 7, // Intersect
- '~' => 6, // Negation
- '%' => 5, // Percentage
- '^' => 4, // Exponentiation
- '*' => 3, '/' => 3, // Multiplication and Division
- '+' => 2, '-' => 2, // Addition and Subtraction
- '&' => 1, // Concatenation
- '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, // Comparison
- ];
-
- // Convert infix to postfix notation
-
- /**
- * @param string $formula
- *
- * @return bool
- */
- private function internalParseFormula($formula, ?Cell $pCell = null)
- {
- if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
- return false;
- }
-
- // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
- // so we store the parent worksheet so that we can re-attach it when necessary
- $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
-
- $regexpMatchString = '/^(' . self::CALCULATION_REGEXP_FUNCTION .
- '|' . self::CALCULATION_REGEXP_CELLREF .
- '|' . self::CALCULATION_REGEXP_NUMBER .
- '|' . self::CALCULATION_REGEXP_STRING .
- '|' . self::CALCULATION_REGEXP_OPENBRACE .
- '|' . self::CALCULATION_REGEXP_DEFINEDNAME .
- '|' . self::CALCULATION_REGEXP_ERROR .
- ')/sui';
-
- // Start with initialisation
- $index = 0;
- $stack = new Stack();
- $output = [];
- $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a
- // - is a negation or + is a positive operator rather than an operation
- $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand
- // should be null in a function call
-
- // IF branch pruning
- // currently pending storeKey (last item of the storeKeysStack
- $pendingStoreKey = null;
- // stores a list of storeKeys (string[])
- $pendingStoreKeysStack = [];
- $expectingConditionMap = []; // ['storeKey' => true, ...]
- $expectingThenMap = []; // ['storeKey' => true, ...]
- $expectingElseMap = []; // ['storeKey' => true, ...]
- $parenthesisDepthMap = []; // ['storeKey' => 4, ...]
-
- // The guts of the lexical parser
- // Loop through the formula extracting each operator and operand in turn
- while (true) {
- // Branch pruning: we adapt the output item to the context (it will
- // be used to limit its computation)
- $currentCondition = null;
- $currentOnlyIf = null;
- $currentOnlyIfNot = null;
- $previousStoreKey = null;
- $pendingStoreKey = end($pendingStoreKeysStack);
-
- if ($this->branchPruningEnabled) {
- // this is a condition ?
- if (isset($expectingConditionMap[$pendingStoreKey]) && $expectingConditionMap[$pendingStoreKey]) {
- $currentCondition = $pendingStoreKey;
- $stackDepth = count($pendingStoreKeysStack);
- if ($stackDepth > 1) { // nested if
- $previousStoreKey = $pendingStoreKeysStack[$stackDepth - 2];
- }
- }
- if (isset($expectingThenMap[$pendingStoreKey]) && $expectingThenMap[$pendingStoreKey]) {
- $currentOnlyIf = $pendingStoreKey;
- } elseif (isset($previousStoreKey)) {
- if (isset($expectingThenMap[$previousStoreKey]) && $expectingThenMap[$previousStoreKey]) {
- $currentOnlyIf = $previousStoreKey;
- }
- }
- if (isset($expectingElseMap[$pendingStoreKey]) && $expectingElseMap[$pendingStoreKey]) {
- $currentOnlyIfNot = $pendingStoreKey;
- } elseif (isset($previousStoreKey)) {
- if (isset($expectingElseMap[$previousStoreKey]) && $expectingElseMap[$previousStoreKey]) {
- $currentOnlyIfNot = $previousStoreKey;
- }
- }
- }
-
- $opCharacter = $formula[$index]; // Get the first character of the value at the current index position
-
- if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
- $opCharacter .= $formula[++$index];
- }
- // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
- $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
- if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
- // Put a negation on the stack
- $stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- ++$index; // and drop the negation symbol
- } elseif ($opCharacter == '%' && $expectingOperator) {
- // Put a percentage on the stack
- $stack->push('Unary Operator', '%', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- ++$index;
- } elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
- ++$index; // Drop the redundant plus symbol
- } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal
- return $this->raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression
- } elseif ((isset(self::$operators[$opCharacter]) || $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
- while (
- $stack->count() > 0 &&
- ($o2 = $stack->last()) &&
- isset(self::$operators[$o2['value']]) &&
- @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
- ) {
- $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
- }
-
- // Finally put our current operator onto the stack
- $stack->push('Binary Operator', $opCharacter, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
-
- ++$index;
- $expectingOperator = false;
- } elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
- $expectingOperand = false;
- while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
- if ($o2 === null) {
- return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"');
- }
- $output[] = $o2;
- }
- $d = $stack->last(2);
-
- // Branch pruning we decrease the depth whether is it a function
- // call or a parenthesis
- if (!empty($pendingStoreKey)) {
- --$parenthesisDepthMap[$pendingStoreKey];
- }
-
- if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'], $matches)) { // Did this parenthesis just close a function?
- if (!empty($pendingStoreKey) && $parenthesisDepthMap[$pendingStoreKey] == -1) {
- // we are closing an IF(
- if ($d['value'] != 'IF(') {
- return $this->raiseFormulaError('Parser bug we should be in an "IF("');
- }
- if ($expectingConditionMap[$pendingStoreKey]) {
- return $this->raiseFormulaError('We should not be expecting a condition');
- }
- $expectingThenMap[$pendingStoreKey] = false;
- $expectingElseMap[$pendingStoreKey] = false;
- --$parenthesisDepthMap[$pendingStoreKey];
- array_pop($pendingStoreKeysStack);
- unset($pendingStoreKey);
- }
-
- $functionName = $matches[1]; // Get the function name
- $d = $stack->pop();
- $argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack)
- $output[] = $d; // Dump the argument count on the output
- $output[] = $stack->pop(); // Pop the function and push onto the output
- if (isset(self::$controlFunctions[$functionName])) {
- $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
- $functionCall = self::$controlFunctions[$functionName]['functionCall'];
- } elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) {
- $expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount'];
- $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
- } else { // did we somehow push a non-function on the stack? this should never happen
- return $this->raiseFormulaError('Formula Error: Internal error, non-function on stack');
- }
- // Check the argument count
- $argumentCountError = false;
- if (is_numeric($expectedArgumentCount)) {
- if ($expectedArgumentCount < 0) {
- if ($argumentCount > abs($expectedArgumentCount)) {
- $argumentCountError = true;
- $expectedArgumentCountString = 'no more than ' . abs($expectedArgumentCount);
- }
- } else {
- if ($argumentCount != $expectedArgumentCount) {
- $argumentCountError = true;
- $expectedArgumentCountString = $expectedArgumentCount;
- }
- }
- } elseif ($expectedArgumentCount != '*') {
- $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
- switch ($argMatch[2]) {
- case '+':
- if ($argumentCount < $argMatch[1]) {
- $argumentCountError = true;
- $expectedArgumentCountString = $argMatch[1] . ' or more ';
- }
-
- break;
- case '-':
- if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
- $argumentCountError = true;
- $expectedArgumentCountString = 'between ' . $argMatch[1] . ' and ' . $argMatch[3];
- }
-
- break;
- case ',':
- if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
- $argumentCountError = true;
- $expectedArgumentCountString = 'either ' . $argMatch[1] . ' or ' . $argMatch[3];
- }
-
- break;
- }
- }
- if ($argumentCountError) {
- return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, " . $expectedArgumentCountString . ' expected');
- }
- }
- ++$index;
- } elseif ($opCharacter == ',') { // Is this the separator for function arguments?
- if (
- !empty($pendingStoreKey) &&
- $parenthesisDepthMap[$pendingStoreKey] == 0
- ) {
- // We must go to the IF next argument
- if ($expectingConditionMap[$pendingStoreKey]) {
- $expectingConditionMap[$pendingStoreKey] = false;
- $expectingThenMap[$pendingStoreKey] = true;
- } elseif ($expectingThenMap[$pendingStoreKey]) {
- $expectingThenMap[$pendingStoreKey] = false;
- $expectingElseMap[$pendingStoreKey] = true;
- } elseif ($expectingElseMap[$pendingStoreKey]) {
- return $this->raiseFormulaError('Reaching fourth argument of an IF');
- }
- }
- while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
- if ($o2 === null) {
- return $this->raiseFormulaError('Formula Error: Unexpected ,');
- }
- $output[] = $o2; // pop the argument expression stuff and push onto the output
- }
- // If we've a comma when we're expecting an operand, then what we actually have is a null operand;
- // so push a null onto the stack
- if (($expectingOperand) || (!$expectingOperator)) {
- $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
- }
- // make sure there was a function
- $d = $stack->last(2);
- if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'], $matches)) {
- return $this->raiseFormulaError('Formula Error: Unexpected ,');
- }
- $d = $stack->pop();
- $itemStoreKey = $d['storeKey'] ?? null;
- $itemOnlyIf = $d['onlyIf'] ?? null;
- $itemOnlyIfNot = $d['onlyIfNot'] ?? null;
- $stack->push($d['type'], ++$d['value'], $d['reference'], $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // increment the argument count
- $stack->push('Brace', '(', null, $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // put the ( back on, we'll need to pop back to it again
- $expectingOperator = false;
- $expectingOperand = true;
- ++$index;
- } elseif ($opCharacter == '(' && !$expectingOperator) {
- if (!empty($pendingStoreKey)) { // Branch pruning: we go deeper
- ++$parenthesisDepthMap[$pendingStoreKey];
- }
- $stack->push('Brace', '(', null, $currentCondition, $currentOnlyIf, $currentOnlyIf);
- ++$index;
- } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
- $expectingOperator = true;
- $expectingOperand = false;
- $val = $match[1];
- $length = strlen($val);
- if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
- $val = preg_replace('/\s/u', '', $val);
- if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
- $valToUpper = strtoupper($val);
- } else {
- $valToUpper = 'NAME.ERROR(';
- }
- // here $matches[1] will contain values like "IF"
- // and $val "IF("
- if ($this->branchPruningEnabled && ($valToUpper == 'IF(')) { // we handle a new if
- $pendingStoreKey = $this->getUnusedBranchStoreKey();
- $pendingStoreKeysStack[] = $pendingStoreKey;
- $expectingConditionMap[$pendingStoreKey] = true;
- $parenthesisDepthMap[$pendingStoreKey] = 0;
- } else { // this is not an if but we go deeper
- if (!empty($pendingStoreKey) && array_key_exists($pendingStoreKey, $parenthesisDepthMap)) {
- ++$parenthesisDepthMap[$pendingStoreKey];
- }
- }
-
- $stack->push('Function', $valToUpper, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- // tests if the function is closed right after opening
- $ax = preg_match('/^\s*\)/u', substr($formula, $index + $length));
- if ($ax) {
- $stack->push('Operand Count for Function ' . $valToUpper . ')', 0, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- $expectingOperator = true;
- } else {
- $stack->push('Operand Count for Function ' . $valToUpper . ')', 1, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- $expectingOperator = false;
- }
- $stack->push('Brace', '(');
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $val, $matches)) {
- // Watch for this case-change when modifying to allow cell references in different worksheets...
- // Should only be applied to the actual cell column, not the worksheet name
- // If the last entry on the stack was a : operator, then we have a cell range reference
- $testPrevOp = $stack->last(1);
- if ($testPrevOp !== null && $testPrevOp['value'] == ':') {
- // If we have a worksheet reference, then we're playing with a 3D reference
- if ($matches[2] == '') {
- // Otherwise, we 'inherit' the worksheet reference from the start cell reference
- // The start of the cell range reference should be the last entry in $output
- $rangeStartCellRef = $output[count($output) - 1]['value'];
- preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $rangeStartCellRef, $rangeStartMatches);
- if ($rangeStartMatches[2] > '') {
- $val = $rangeStartMatches[2] . '!' . $val;
- }
- } else {
- $rangeStartCellRef = $output[count($output) - 1]['value'];
- preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $rangeStartCellRef, $rangeStartMatches);
- if ($rangeStartMatches[2] !== $matches[2]) {
- return $this->raiseFormulaError('3D Range references are not yet supported');
- }
- }
- }
-
- $outputItem = $stack->getStackItem('Cell Reference', $val, $val, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
-
- $output[] = $outputItem;
- } else { // it's a variable, constant, string, number or boolean
- // If the last entry on the stack was a : operator, then we may have a row or column range reference
- $testPrevOp = $stack->last(1);
- if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
- $startRowColRef = $output[count($output) - 1]['value'];
- [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
- $rangeSheetRef = $rangeWS1;
- if ($rangeWS1 != '') {
- $rangeWS1 .= '!';
- }
- [$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
- if ($rangeWS2 != '') {
- $rangeWS2 .= '!';
- } else {
- $rangeWS2 = $rangeWS1;
- }
- $refSheet = $pCellParent;
- if ($pCellParent !== null && $rangeSheetRef !== $pCellParent->getTitle()) {
- $refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef);
- }
- if (
- (is_int($startRowColRef)) && (ctype_digit($val)) &&
- ($startRowColRef <= 1048576) && ($val <= 1048576)
- ) {
- // Row range
- $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
- $output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef;
- $val = $rangeWS2 . $endRowColRef . $val;
- } elseif (
- (ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
- (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)
- ) {
- // Column range
- $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
- $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1';
- $val = $rangeWS2 . $val . $endRowColRef;
- }
- }
-
- $localeConstant = false;
- $stackItemType = 'Value';
- $stackItemReference = null;
- if ($opCharacter == self::FORMULA_STRING_QUOTE) {
- // UnEscape any quotes within the string
- $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val)));
- } elseif (is_numeric($val)) {
- if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
- $val = (float) $val;
- } else {
- $val = (int) $val;
- }
- } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
- $stackItemType = 'Constant';
- $excelConstant = trim(strtoupper($val));
- $val = self::$excelConstants[$excelConstant];
- } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
- $stackItemType = 'Constant';
- $val = self::$excelConstants[$localeConstant];
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', $val, $match)) {
- $stackItemType = 'Defined Name';
- $stackItemReference = $val;
- }
- $details = $stack->getStackItem($stackItemType, $val, $stackItemReference, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
- if ($localeConstant) {
- $details['localeValue'] = $localeConstant;
- }
- $output[] = $details;
- }
- $index += $length;
- } elseif ($opCharacter == '$') { // absolute row or column range
- ++$index;
- } elseif ($opCharacter == ')') { // miscellaneous error checking
- if ($expectingOperand) {
- $output[] = ['type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null];
- $expectingOperand = false;
- $expectingOperator = true;
- } else {
- return $this->raiseFormulaError("Formula Error: Unexpected ')'");
- }
- } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
- return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
- } else { // I don't even want to know what you did to get here
- return $this->raiseFormulaError('Formula Error: An unexpected error occurred');
- }
- // Test for end of formula string
- if ($index == strlen($formula)) {
- // Did we end with an operator?.
- // Only valid for the % unary operator
- if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
- return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
- }
-
- break;
- }
- // Ignore white space
- while (($formula[$index] == "\n") || ($formula[$index] == "\r")) {
- ++$index;
- }
-
- if ($formula[$index] == ' ') {
- while ($formula[$index] == ' ') {
- ++$index;
- }
-
- // If we're expecting an operator, but only have a space between the previous and next operands (and both are
- // Cell References) then we have an INTERSECTION operator
- if (
- ($expectingOperator) &&
- ((preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
- ($output[count($output) - 1]['type'] == 'Cell Reference') ||
- (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match)) &&
- ($output[count($output) - 1]['type'] == 'Defined Name' || $output[count($output) - 1]['type'] == 'Value')
- )
- ) {
- while (
- $stack->count() > 0 &&
- ($o2 = $stack->last()) &&
- isset(self::$operators[$o2['value']]) &&
- @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
- ) {
- $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
- }
- $stack->push('Binary Operator', '|'); // Put an Intersect Operator on the stack
- $expectingOperator = false;
- }
- }
- }
-
- while (($op = $stack->pop()) !== null) { // pop everything off the stack and push onto output
- if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
- return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
- }
- $output[] = $op;
- }
-
- return $output;
- }
-
- private static function dataTestReference(&$operandData)
- {
- $operand = $operandData['value'];
- if (($operandData['reference'] === null) && (is_array($operand))) {
- $rKeys = array_keys($operand);
- $rowKey = array_shift($rKeys);
- $cKeys = array_keys(array_keys($operand[$rowKey]));
- $colKey = array_shift($cKeys);
- if (ctype_upper($colKey)) {
- $operandData['reference'] = $colKey . $rowKey;
- }
- }
-
- return $operand;
- }
-
- // evaluate postfix notation
-
- /**
- * @param mixed $tokens
- * @param null|string $cellID
- *
- * @return bool
- */
- private function processTokenStack($tokens, $cellID = null, ?Cell $pCell = null)
- {
- if ($tokens == false) {
- return false;
- }
-
- // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
- // so we store the parent cell collection so that we can re-attach it when necessary
- $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
- $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
- $stack = new Stack();
-
- // Stores branches that have been pruned
- $fakedForBranchPruning = [];
- // help us to know when pruning ['branchTestId' => true/false]
- $branchStore = [];
- // Loop through each token in turn
- foreach ($tokens as $tokenData) {
- $token = $tokenData['value'];
-
- // Branch pruning: skip useless resolutions
- $storeKey = $tokenData['storeKey'] ?? null;
- if ($this->branchPruningEnabled && isset($tokenData['onlyIf'])) {
- $onlyIfStoreKey = $tokenData['onlyIf'];
- $storeValue = $branchStore[$onlyIfStoreKey] ?? null;
- $storeValueAsBool = ($storeValue === null) ?
- true : (bool) Functions::flattenSingleValue($storeValue);
- if (is_array($storeValue)) {
- $wrappedItem = end($storeValue);
- $storeValue = end($wrappedItem);
- }
-
- if (
- isset($storeValue)
- && (
- !$storeValueAsBool
- || Functions::isError($storeValue)
- || ($storeValue === 'Pruned branch')
- )
- ) {
- // If branching value is not true, we don't need to compute
- if (!isset($fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey])) {
- $stack->push('Value', 'Pruned branch (only if ' . $onlyIfStoreKey . ') ' . $token);
- $fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey] = true;
- }
-
- if (isset($storeKey)) {
- // We are processing an if condition
- // We cascade the pruning to the depending branches
- $branchStore[$storeKey] = 'Pruned branch';
- $fakedForBranchPruning['onlyIfNot-' . $storeKey] = true;
- $fakedForBranchPruning['onlyIf-' . $storeKey] = true;
- }
-
- continue;
- }
- }
-
- if ($this->branchPruningEnabled && isset($tokenData['onlyIfNot'])) {
- $onlyIfNotStoreKey = $tokenData['onlyIfNot'];
- $storeValue = $branchStore[$onlyIfNotStoreKey] ?? null;
- $storeValueAsBool = ($storeValue === null) ?
- true : (bool) Functions::flattenSingleValue($storeValue);
- if (is_array($storeValue)) {
- $wrappedItem = end($storeValue);
- $storeValue = end($wrappedItem);
- }
- if (
- isset($storeValue)
- && (
- $storeValueAsBool
- || Functions::isError($storeValue)
- || ($storeValue === 'Pruned branch'))
- ) {
- // If branching value is true, we don't need to compute
- if (!isset($fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey])) {
- $stack->push('Value', 'Pruned branch (only if not ' . $onlyIfNotStoreKey . ') ' . $token);
- $fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey] = true;
- }
-
- if (isset($storeKey)) {
- // We are processing an if condition
- // We cascade the pruning to the depending branches
- $branchStore[$storeKey] = 'Pruned branch';
- $fakedForBranchPruning['onlyIfNot-' . $storeKey] = true;
- $fakedForBranchPruning['onlyIf-' . $storeKey] = true;
- }
-
- continue;
- }
- }
-
- // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
- if (isset(self::$binaryOperators[$token])) {
- // We must have two operands, error if we don't
- if (($operand2Data = $stack->pop()) === null) {
- return $this->raiseFormulaError('Internal error - Operand value missing from stack');
- }
- if (($operand1Data = $stack->pop()) === null) {
- return $this->raiseFormulaError('Internal error - Operand value missing from stack');
- }
-
- $operand1 = self::dataTestReference($operand1Data);
- $operand2 = self::dataTestReference($operand2Data);
-
- // Log what we're doing
- if ($token == ':') {
- $this->debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference']));
- } else {
- $this->debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2));
- }
-
- // Process the operation in the appropriate manner
- switch ($token) {
- // Comparison (Boolean) Operators
- case '>': // Greater than
- case '<': // Less than
- case '>=': // Greater than or Equal to
- case '<=': // Less than or Equal to
- case '=': // Equality
- case '<>': // Inequality
- $result = $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- // Binary Operators
- case ':': // Range
- if (strpos($operand1Data['reference'], '!') !== false) {
- [$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
- } else {
- $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
- }
-
- [$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
- if (empty($sheet2)) {
- $sheet2 = $sheet1;
- }
-
- if ($sheet1 == $sheet2) {
- if ($operand1Data['reference'] === null) {
- if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
- $operand1Data['reference'] = $pCell->getColumn() . $operand1Data['value'];
- } elseif (trim($operand1Data['reference']) == '') {
- $operand1Data['reference'] = $pCell->getCoordinate();
- } else {
- $operand1Data['reference'] = $operand1Data['value'] . $pCell->getRow();
- }
- }
- if ($operand2Data['reference'] === null) {
- if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
- $operand2Data['reference'] = $pCell->getColumn() . $operand2Data['value'];
- } elseif (trim($operand2Data['reference']) == '') {
- $operand2Data['reference'] = $pCell->getCoordinate();
- } else {
- $operand2Data['reference'] = $operand2Data['value'] . $pCell->getRow();
- }
- }
-
- $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
- $oCol = $oRow = [];
- foreach ($oData as $oDatum) {
- $oCR = Coordinate::coordinateFromString($oDatum);
- $oCol[] = Coordinate::columnIndexFromString($oCR[0]) - 1;
- $oRow[] = $oCR[1];
- }
- $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
- if ($pCellParent !== null) {
- $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false);
- } else {
- return $this->raiseFormulaError('Unable to access Cell Reference');
- }
- $stack->push('Cell Reference', $cellValue, $cellRef);
- } else {
- $stack->push('Error', Functions::REF(), null);
- }
-
- break;
- case '+': // Addition
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- case '-': // Subtraction
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- case '*': // Multiplication
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- case '/': // Division
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- case '^': // Exponential
- $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- case '&': // Concatenation
- // If either of the operands is a matrix, we need to treat them both as matrices
- // (converting the other operand to a matrix if need be); then perform the required
- // matrix operation
- if (is_bool($operand1)) {
- $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
- }
- if (is_bool($operand2)) {
- $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
- }
- if ((is_array($operand1)) || (is_array($operand2))) {
- // Ensure that both operands are arrays/matrices
- self::checkMatrixOperands($operand1, $operand2, 2);
-
- try {
- // Convert operand 1 from a PHP array to a matrix
- $matrix = new Shared\JAMA\Matrix($operand1);
- // Perform the required operation against the operand 1 matrix, passing in operand 2
- $matrixResult = $matrix->concat($operand2);
- $result = $matrixResult->getArray();
- } catch (\Exception $ex) {
- $this->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
- $result = '#VALUE!';
- }
- } else {
- $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
- }
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
- $stack->push('Value', $result);
-
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
-
- break;
- case '|': // Intersect
- $rowIntersect = array_intersect_key($operand1, $operand2);
- $cellIntersect = $oCol = $oRow = [];
- foreach (array_keys($rowIntersect) as $row) {
- $oRow[] = $row;
- foreach ($rowIntersect[$row] as $col => $data) {
- $oCol[] = Coordinate::columnIndexFromString($col) - 1;
- $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
- }
- }
- if (count(Functions::flattenArray($cellIntersect)) === 0) {
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
- $stack->push('Error', Functions::null(), null);
- } else {
- $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' .
- Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
- $stack->push('Value', $cellIntersect, $cellRef);
- }
-
- break;
- }
-
- // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
- } elseif (($token === '~') || ($token === '%')) {
- if (($arg = $stack->pop()) === null) {
- return $this->raiseFormulaError('Internal error - Operand value missing from stack');
- }
- $arg = $arg['value'];
- if ($token === '~') {
- $this->debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg));
- $multiplier = -1;
- } else {
- $this->debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg));
- $multiplier = 0.01;
- }
- if (is_array($arg)) {
- self::checkMatrixOperands($arg, $multiplier, 2);
-
- try {
- $matrix1 = new Shared\JAMA\Matrix($arg);
- $matrixResult = $matrix1->arrayTimesEquals($multiplier);
- $result = $matrixResult->getArray();
- } catch (\Exception $ex) {
- $this->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
- $result = '#VALUE!';
- }
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
- $stack->push('Value', $result);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
- } else {
- $this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack);
- }
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) {
- $cellRef = null;
- if (isset($matches[8])) {
- if ($pCell === null) {
- // We can't access the range, so return a REF error
- $cellValue = Functions::REF();
- } else {
- $cellRef = $matches[6] . $matches[7] . ':' . $matches[9] . $matches[10];
- if ($matches[2] > '') {
- $matches[2] = trim($matches[2], "\"'");
- if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
- // It's a Reference to an external spreadsheet (not currently supported)
- return $this->raiseFormulaError('Unable to access External Workbook');
- }
- $matches[2] = trim($matches[2], "\"'");
- $this->debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
- if ($pCellParent !== null) {
- $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
- } else {
- return $this->raiseFormulaError('Unable to access Cell Reference');
- }
- $this->debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
- } else {
- $this->debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
- if ($pCellParent !== null) {
- $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
- } else {
- return $this->raiseFormulaError('Unable to access Cell Reference');
- }
- $this->debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
- }
- }
- } else {
- if ($pCell === null) {
- // We can't access the cell, so return a REF error
- $cellValue = Functions::REF();
- } else {
- $cellRef = $matches[6] . $matches[7];
- if ($matches[2] > '') {
- $matches[2] = trim($matches[2], "\"'");
- if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
- // It's a Reference to an external spreadsheet (not currently supported)
- return $this->raiseFormulaError('Unable to access External Workbook');
- }
- $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
- if ($pCellParent !== null) {
- $cellSheet = $this->spreadsheet->getSheetByName($matches[2]);
- if ($cellSheet && $cellSheet->cellExists($cellRef)) {
- $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
- $pCell->attach($pCellParent);
- } else {
- $cellValue = null;
- }
- } else {
- return $this->raiseFormulaError('Unable to access Cell Reference');
- }
- $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
- } else {
- $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
- if ($pCellParent->has($cellRef)) {
- $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
- $pCell->attach($pCellParent);
- } else {
- $cellValue = null;
- }
- $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
- }
- }
- }
- $stack->push('Value', $cellValue, $cellRef);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $cellValue;
- }
-
- // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $token, $matches)) {
- if ($pCellParent) {
- $pCell->attach($pCellParent);
- }
-
- $functionName = $matches[1];
- $argCount = $stack->pop();
- $argCount = $argCount['value'];
- if ($functionName != 'MKMATRIX') {
- $this->debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
- }
- if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function
- if (isset(self::$phpSpreadsheetFunctions[$functionName])) {
- $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
- $passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']);
- $passCellReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passCellReference']);
- } elseif (isset(self::$controlFunctions[$functionName])) {
- $functionCall = self::$controlFunctions[$functionName]['functionCall'];
- $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
- $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
- }
- // get the arguments for this function
- $args = $argArrayVals = [];
- for ($i = 0; $i < $argCount; ++$i) {
- $arg = $stack->pop();
- $a = $argCount - $i - 1;
- if (
- ($passByReference) &&
- (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) &&
- (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])
- ) {
- if ($arg['reference'] === null) {
- $args[] = $cellID;
- if ($functionName != 'MKMATRIX') {
- $argArrayVals[] = $this->showValue($cellID);
- }
- } else {
- $args[] = $arg['reference'];
- if ($functionName != 'MKMATRIX') {
- $argArrayVals[] = $this->showValue($arg['reference']);
- }
- }
- } else {
- $args[] = self::unwrapResult($arg['value']);
- if ($functionName != 'MKMATRIX') {
- $argArrayVals[] = $this->showValue($arg['value']);
- }
- }
- }
-
- // Reverse the order of the arguments
- krsort($args);
-
- if (($passByReference) && ($argCount == 0)) {
- $args[] = $cellID;
- $argArrayVals[] = $this->showValue($cellID);
- }
-
- if ($functionName != 'MKMATRIX') {
- if ($this->debugLog->getWriteDebugLog()) {
- krsort($argArrayVals);
- $this->debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator . ' ', Functions::flattenArray($argArrayVals)), ' )');
- }
- }
-
- // Process the argument with the appropriate function call
- $args = $this->addCellReference($args, $passCellReference, $functionCall, $pCell);
-
- if (!is_array($functionCall)) {
- foreach ($args as &$arg) {
- $arg = Functions::flattenSingleValue($arg);
- }
- unset($arg);
- }
-
- $result = call_user_func_array($functionCall, $args);
-
- if ($functionName != 'MKMATRIX') {
- $this->debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
- }
- $stack->push('Value', self::wrapResult($result));
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
- }
- } else {
- // if the token is a number, boolean, string or an Excel error, push it onto the stack
- if (isset(self::$excelConstants[strtoupper($token)])) {
- $excelConstant = strtoupper($token);
- $stack->push('Constant Value', self::$excelConstants[$excelConstant]);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = self::$excelConstants[$excelConstant];
- }
- $this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
- } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == self::FORMULA_STRING_QUOTE) || ($token[0] == '#')) {
- $stack->push('Value', $token);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $token;
- }
- // if the token is a named range or formula, evaluate it and push the result onto the stack
- } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) {
- $definedName = $matches[6];
- if ($pCell === null || $pCellWorksheet === null) {
- return $this->raiseFormulaError("undefined name '$token'");
- }
-
- $this->debugLog->writeDebugLog('Evaluating Defined Name ', $definedName);
- $namedRange = DefinedName::resolveName($definedName, $pCellWorksheet);
- if ($namedRange === null) {
- return $this->raiseFormulaError("undefined name '$definedName'");
- }
-
- $result = $this->evaluateDefinedName($pCell, $namedRange, $pCellWorksheet, $stack);
- if (isset($storeKey)) {
- $branchStore[$storeKey] = $result;
- }
- } else {
- return $this->raiseFormulaError("undefined name '$token'");
- }
- }
- }
- // when we're out of tokens, the stack should have a single element, the final result
- if ($stack->count() != 1) {
- return $this->raiseFormulaError('internal error');
- }
- $output = $stack->pop();
- $output = $output['value'];
-
- return $output;
- }
-
- private function validateBinaryOperand(&$operand, &$stack)
- {
- if (is_array($operand)) {
- if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
- do {
- $operand = array_pop($operand);
- } while (is_array($operand));
- }
- }
- // Numbers, matrices and booleans can pass straight through, as they're already valid
- if (is_string($operand)) {
- // We only need special validations for the operand if it is a string
- // Start by stripping off the quotation marks we use to identify true excel string values internally
- if ($operand > '' && $operand[0] == self::FORMULA_STRING_QUOTE) {
- $operand = self::unwrapResult($operand);
- }
- // If the string is a numeric value, we treat it as a numeric, so no further testing
- if (!is_numeric($operand)) {
- // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
- if ($operand > '' && $operand[0] == '#') {
- $stack->push('Value', $operand);
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand));
-
- return false;
- } elseif (!Shared\StringHelper::convertToNumberIfFraction($operand)) {
- // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
- $stack->push('Error', '#VALUE!');
- $this->debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
-
- return false;
- }
- }
- }
-
- // return a true if the value of the operand is one that we can use in normal binary operations
- return true;
- }
-
- /**
- * @param null|string $cellID
- * @param mixed $operand1
- * @param mixed $operand2
- * @param string $operation
- * @param bool $recursingArrays
- *
- * @return mixed
- */
- private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false)
- {
- // If we're dealing with matrix operations, we want a matrix result
- if ((is_array($operand1)) || (is_array($operand2))) {
- $result = [];
- if ((is_array($operand1)) && (!is_array($operand2))) {
- foreach ($operand1 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
- $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- } elseif ((!is_array($operand1)) && (is_array($operand2))) {
- foreach ($operand2 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
- $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- } else {
- if (!$recursingArrays) {
- self::checkMatrixOperands($operand1, $operand2, 2);
- }
- foreach ($operand1 as $x => $operandData) {
- $this->debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
- $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
- $r = $stack->pop();
- $result[$x] = $r['value'];
- }
- }
- // Log the result details
- $this->debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
- // And push the result onto the stack
- $stack->push('Array', $result);
-
- return $result;
- }
-
- // Simple validate the two operands if they are string values
- if (is_string($operand1) && $operand1 > '' && $operand1[0] == self::FORMULA_STRING_QUOTE) {
- $operand1 = self::unwrapResult($operand1);
- }
- if (is_string($operand2) && $operand2 > '' && $operand2[0] == self::FORMULA_STRING_QUOTE) {
- $operand2 = self::unwrapResult($operand2);
- }
-
- // Use case insensitive comparaison if not OpenOffice mode
- if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
- if (is_string($operand1)) {
- $operand1 = strtoupper($operand1);
- }
- if (is_string($operand2)) {
- $operand2 = strtoupper($operand2);
- }
- }
-
- $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE;
-
- // execute the necessary operation
- switch ($operation) {
- // Greater than
- case '>':
- if ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
- } else {
- $result = ($operand1 > $operand2);
- }
-
- break;
- // Less than
- case '<':
- if ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
- } else {
- $result = ($operand1 < $operand2);
- }
-
- break;
- // Equality
- case '=':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = (abs($operand1 - $operand2) < $this->delta);
- } else {
- $result = strcmp($operand1, $operand2) == 0;
- }
-
- break;
- // Greater than or equal
- case '>=':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
- } elseif ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
- } else {
- $result = strcmp($operand1, $operand2) >= 0;
- }
-
- break;
- // Less than or equal
- case '<=':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
- } elseif ($useLowercaseFirstComparison) {
- $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
- } else {
- $result = strcmp($operand1, $operand2) <= 0;
- }
-
- break;
- // Inequality
- case '<>':
- if (is_numeric($operand1) && is_numeric($operand2)) {
- $result = (abs($operand1 - $operand2) > 1E-14);
- } else {
- $result = strcmp($operand1, $operand2) != 0;
- }
-
- break;
- }
-
- // Log the result details
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
- // And push the result onto the stack
- $stack->push('Value', $result);
-
- return $result;
- }
-
- /**
- * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
- *
- * @param string $str1 First string value for the comparison
- * @param string $str2 Second string value for the comparison
- *
- * @return int
- */
- private function strcmpLowercaseFirst($str1, $str2)
- {
- $inversedStr1 = Shared\StringHelper::strCaseReverse($str1);
- $inversedStr2 = Shared\StringHelper::strCaseReverse($str2);
-
- return strcmp($inversedStr1, $inversedStr2);
- }
-
- /**
- * @param mixed $operand1
- * @param mixed $operand2
- * @param mixed $operation
- * @param string $matrixFunction
- * @param mixed $stack
- *
- * @return bool|mixed
- */
- private function executeNumericBinaryOperation($operand1, $operand2, $operation, $matrixFunction, &$stack)
- {
- // Validate the two operands
- if (!$this->validateBinaryOperand($operand1, $stack)) {
- return false;
- }
- if (!$this->validateBinaryOperand($operand2, $stack)) {
- return false;
- }
-
- // If either of the operands is a matrix, we need to treat them both as matrices
- // (converting the other operand to a matrix if need be); then perform the required
- // matrix operation
- if ((is_array($operand1)) || (is_array($operand2))) {
- // Ensure that both operands are arrays/matrices of the same size
- self::checkMatrixOperands($operand1, $operand2, 2);
-
- try {
- // Convert operand 1 from a PHP array to a matrix
- $matrix = new Shared\JAMA\Matrix($operand1);
- // Perform the required operation against the operand 1 matrix, passing in operand 2
- $matrixResult = $matrix->$matrixFunction($operand2);
- $result = $matrixResult->getArray();
- } catch (\Exception $ex) {
- $this->debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
- $result = '#VALUE!';
- }
- } else {
- if (
- (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) &&
- ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1) > 0) ||
- (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0))
- ) {
- $result = Functions::VALUE();
- } else {
- // If we're dealing with non-matrix operations, execute the necessary operation
- switch ($operation) {
- // Addition
- case '+':
- $result = $operand1 + $operand2;
-
- break;
- // Subtraction
- case '-':
- $result = $operand1 - $operand2;
-
- break;
- // Multiplication
- case '*':
- $result = $operand1 * $operand2;
-
- break;
- // Division
- case '/':
- if ($operand2 == 0) {
- // Trap for Divide by Zero error
- $stack->push('Error', '#DIV/0!');
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
-
- return false;
- }
- $result = $operand1 / $operand2;
-
- break;
- // Power
- case '^':
- $result = $operand1 ** $operand2;
-
- break;
- }
- }
- }
-
- // Log the result details
- $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
- // And push the result onto the stack
- $stack->push('Value', $result);
-
- return $result;
- }
-
- // trigger an error, but nicely, if need be
- protected function raiseFormulaError($errorMessage)
- {
- $this->formulaError = $errorMessage;
- $this->cyclicReferenceStack->clear();
- if (!$this->suppressFormulaErrors) {
- throw new Exception($errorMessage);
- }
- trigger_error($errorMessage, E_USER_ERROR);
-
- return false;
- }
-
- /**
- * Extract range values.
- *
- * @param string &$pRange String based range representation
- * @param Worksheet $pSheet Worksheet
- * @param bool $resetLog Flag indicating whether calculation log should be reset or not
- *
- * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
- */
- public function extractCellRange(&$pRange = 'A1', ?Worksheet $pSheet = null, $resetLog = true)
- {
- // Return value
- $returnValue = [];
-
- if ($pSheet !== null) {
- $pSheetName = $pSheet->getTitle();
- if (strpos($pRange, '!') !== false) {
- [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
- $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
- }
-
- // Extract range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
- $pRange = $pSheetName . '!' . $pRange;
- if (!isset($aReferences[1])) {
- $currentCol = '';
- $currentRow = 0;
- // Single cell in range
- sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
- if ($pSheet->cellExists($aReferences[0])) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
- } else {
- $returnValue[$currentRow][$currentCol] = null;
- }
- } else {
- // Extract cell data for all cells in the range
- foreach ($aReferences as $reference) {
- $currentCol = '';
- $currentRow = 0;
- // Extract range
- sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
- if ($pSheet->cellExists($reference)) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
- } else {
- $returnValue[$currentRow][$currentCol] = null;
- }
- }
- }
- }
-
- return $returnValue;
- }
-
- /**
- * Extract range values.
- *
- * @param string &$pRange String based range representation
- * @param Worksheet $pSheet Worksheet
- * @param bool $resetLog Flag indicating whether calculation log should be reset or not
- *
- * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
- */
- public function extractNamedRange(&$pRange = 'A1', ?Worksheet $pSheet = null, $resetLog = true)
- {
- // Return value
- $returnValue = [];
-
- if ($pSheet !== null) {
- $pSheetName = $pSheet->getTitle();
- if (strpos($pRange, '!') !== false) {
- [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
- $pSheet = $this->spreadsheet->getSheetByName($pSheetName);
- }
-
- // Named range?
- $namedRange = DefinedName::resolveName($pRange, $pSheet);
- if ($namedRange === null) {
- return Functions::REF();
- }
-
- $pSheet = $namedRange->getWorksheet();
- $pRange = $namedRange->getValue();
- $splitRange = Coordinate::splitRange($pRange);
- // Convert row and column references
- if (ctype_alpha($splitRange[0][0])) {
- $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
- } elseif (ctype_digit($splitRange[0][0])) {
- $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
- }
-
- // Extract range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
- if (!isset($aReferences[1])) {
- // Single cell (or single column or row) in range
- [$currentCol, $currentRow] = Coordinate::coordinateFromString($aReferences[0]);
- if ($pSheet->cellExists($aReferences[0])) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
- } else {
- $returnValue[$currentRow][$currentCol] = null;
- }
- } else {
- // Extract cell data for all cells in the range
- foreach ($aReferences as $reference) {
- // Extract range
- [$currentCol, $currentRow] = Coordinate::coordinateFromString($reference);
- if ($pSheet->cellExists($reference)) {
- $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
- } else {
- $returnValue[$currentRow][$currentCol] = null;
- }
- }
- }
- }
-
- return $returnValue;
- }
-
- /**
- * Is a specific function implemented?
- *
- * @param string $pFunction Function Name
- *
- * @return bool
- */
- public function isImplemented($pFunction)
- {
- $pFunction = strtoupper($pFunction);
- $notImplemented = !isset(self::$phpSpreadsheetFunctions[$pFunction]) || (is_array(self::$phpSpreadsheetFunctions[$pFunction]['functionCall']) && self::$phpSpreadsheetFunctions[$pFunction]['functionCall'][1] === 'DUMMY');
-
- return !$notImplemented;
- }
-
- /**
- * Get a list of all implemented functions as an array of function objects.
- *
- * @return array of Category
- */
- public function getFunctions()
- {
- return self::$phpSpreadsheetFunctions;
- }
-
- /**
- * Get a list of implemented Excel function names.
- *
- * @return array
- */
- public function getImplementedFunctionNames()
- {
- $returnValue = [];
- foreach (self::$phpSpreadsheetFunctions as $functionName => $function) {
- if ($this->isImplemented($functionName)) {
- $returnValue[] = $functionName;
- }
- }
-
- return $returnValue;
- }
-
- /**
- * Add cell reference if needed while making sure that it is the last argument.
- *
- * @param bool $passCellReference
- * @param array|string $functionCall
- *
- * @return array
- */
- private function addCellReference(array $args, $passCellReference, $functionCall, ?Cell $pCell = null)
- {
- if ($passCellReference) {
- if (is_array($functionCall)) {
- $className = $functionCall[0];
- $methodName = $functionCall[1];
-
- $reflectionMethod = new ReflectionMethod($className, $methodName);
- $argumentCount = count($reflectionMethod->getParameters());
- while (count($args) < $argumentCount - 1) {
- $args[] = null;
- }
- }
-
- $args[] = $pCell;
- }
-
- return $args;
- }
-
- private function getUnusedBranchStoreKey()
- {
- $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
- ++$this->branchStoreKeyCounter;
-
- return $storeKeyValue;
- }
-
- private function getTokensAsString($tokens)
- {
- $tokensStr = array_map(function ($token) {
- $value = $token['value'] ?? 'no value';
- while (is_array($value)) {
- $value = array_pop($value);
- }
-
- return $value;
- }, $tokens);
-
- return '[ ' . implode(' | ', $tokensStr) . ' ]';
- }
-
- /**
- * @return mixed|string
- */
- private function evaluateDefinedName(Cell $pCell, DefinedName $namedRange, Worksheet $pCellWorksheet, Stack $stack)
- {
- $definedNameScope = $namedRange->getScope();
- if ($definedNameScope !== null && $definedNameScope !== $pCellWorksheet) {
- // The defined name isn't in our current scope, so #REF
- $result = Functions::REF();
- $stack->push('Error', $result, $namedRange->getName());
-
- return $result;
- }
-
- $definedNameValue = $namedRange->getValue();
- $definedNameType = $namedRange->isFormula() ? 'Formula' : 'Range';
- $definedNameWorksheet = $namedRange->getWorksheet();
-
- if ($definedNameValue[0] !== '=') {
- $definedNameValue = '=' . $definedNameValue;
- }
-
- $this->debugLog->writeDebugLog("Defined Name is a {$definedNameType} with a value of {$definedNameValue}");
-
- $recursiveCalculationCell = ($definedNameWorksheet !== null && $definedNameWorksheet !== $pCellWorksheet)
- ? $definedNameWorksheet->getCell('A1')
- : $pCell;
- $recursiveCalculationCellAddress = $recursiveCalculationCell !== null
- ? $recursiveCalculationCell->getCoordinate()
- : null;
-
- // Adjust relative references in ranges and formulae so that we execute the calculation for the correct rows and columns
- $definedNameValue = self::$referenceHelper->updateFormulaReferencesAnyWorksheet(
- $definedNameValue,
- Coordinate::columnIndexFromString($pCell->getColumn()) - 1,
- $pCell->getRow() - 1
- );
-
- $this->debugLog->writeDebugLog("Value adjusted for relative references is {$definedNameValue}");
-
- $recursiveCalculator = new self($this->spreadsheet);
- $recursiveCalculator->getDebugLog()->setWriteDebugLog($this->getDebugLog()->getWriteDebugLog());
- $recursiveCalculator->getDebugLog()->setEchoDebugLog($this->getDebugLog()->getEchoDebugLog());
- $result = $recursiveCalculator->_calculateFormulaValue($definedNameValue, $recursiveCalculationCellAddress, $recursiveCalculationCell);
-
- if ($this->getDebugLog()->getWriteDebugLog()) {
- $this->debugLog->mergeDebugLog(array_slice($recursiveCalculator->getDebugLog()->getLog(), 3));
- $this->debugLog->writeDebugLog("Evaluation Result for Named {$definedNameType} {$namedRange->getName()} is {$this->showTypeDetails($result)}");
- }
-
- $stack->push('Defined Name', $result, $namedRange->getName());
-
- return $result;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Category.php b/vendor/PhpSpreadsheet/Calculation/Category.php
deleted file mode 100644
index 96bb72a..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Category.php
+++ /dev/null
@@ -1,20 +0,0 @@
- $criteriaName) {
- $testCondition = [];
- $testConditionCount = 0;
- foreach ($criteria as $row => $criterion) {
- if ($criterion[$key] > '') {
- $testCondition[] = '[:' . $criteriaName . ']' . Functions::ifCondition($criterion[$key]);
- ++$testConditionCount;
- }
- }
- if ($testConditionCount > 1) {
- $testConditions[] = 'OR(' . implode(',', $testCondition) . ')';
- ++$testConditionsCount;
- } elseif ($testConditionCount == 1) {
- $testConditions[] = $testCondition[0];
- ++$testConditionsCount;
- }
- }
-
- if ($testConditionsCount > 1) {
- $testConditionSet = 'AND(' . implode(',', $testConditions) . ')';
- } elseif ($testConditionsCount == 1) {
- $testConditionSet = $testConditions[0];
- }
-
- // Loop through each row of the database
- foreach ($database as $dataRow => $dataValues) {
- // Substitute actual values from the database row for our [:placeholders]
- $testConditionList = $testConditionSet;
- foreach ($criteriaNames as $key => $criteriaName) {
- $k = array_search($criteriaName, $fieldNames);
- if (isset($dataValues[$k])) {
- $dataValue = $dataValues[$k];
- $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
- $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
- }
- }
- // evaluate the criteria against the row data
- $result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList);
- // If the row failed to meet the criteria, remove it from the database
- if (!$result) {
- unset($database[$dataRow]);
- }
- }
-
- return $database;
- }
-
- private static function getFilteredColumn($database, $field, $criteria)
- {
- // reduce the database to a set of rows that match all the criteria
- $database = self::filter($database, $criteria);
- // extract an array of values for the requested column
- $colData = [];
- foreach ($database as $row) {
- $colData[] = $row[$field];
- }
-
- return $colData;
- }
-
- /**
- * DAVERAGE.
- *
- * Averages the values in a column of a list or database that match conditions you specify.
- *
- * Excel Function:
- * DAVERAGE(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float|string
- */
- public static function DAVERAGE($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::AVERAGE(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DCOUNT.
- *
- * Counts the cells that contain numbers in a column of a list or database that match conditions
- * that you specify.
- *
- * Excel Function:
- * DCOUNT(database,[field],criteria)
- *
- * Excel Function:
- * DAVERAGE(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return int
- *
- * @TODO The field argument is optional. If field is omitted, DCOUNT counts all records in the
- * database that match the criteria.
- */
- public static function DCOUNT($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::COUNT(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DCOUNTA.
- *
- * Counts the nonblank cells in a column of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DCOUNTA(database,[field],criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return int
- *
- * @TODO The field argument is optional. If field is omitted, DCOUNTA counts all records in the
- * database that match the criteria.
- */
- public static function DCOUNTA($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // reduce the database to a set of rows that match all the criteria
- $database = self::filter($database, $criteria);
- // extract an array of values for the requested column
- $colData = [];
- foreach ($database as $row) {
- $colData[] = $row[$field];
- }
-
- // Return
- return Statistical::COUNTA(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DGET.
- *
- * Extracts a single value from a column of a list or database that matches conditions that you
- * specify.
- *
- * Excel Function:
- * DGET(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return mixed
- */
- public static function DGET($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- $colData = self::getFilteredColumn($database, $field, $criteria);
- if (count($colData) > 1) {
- return Functions::NAN();
- }
-
- return $colData[0];
- }
-
- /**
- * DMAX.
- *
- * Returns the largest number in a column of a list or database that matches conditions you that
- * specify.
- *
- * Excel Function:
- * DMAX(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float
- */
- public static function DMAX($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::MAX(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DMIN.
- *
- * Returns the smallest number in a column of a list or database that matches conditions you that
- * specify.
- *
- * Excel Function:
- * DMIN(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float
- */
- public static function DMIN($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::MIN(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DPRODUCT.
- *
- * Multiplies the values in a column of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DPRODUCT(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float
- */
- public static function DPRODUCT($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return MathTrig::PRODUCT(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DSTDEV.
- *
- * Estimates the standard deviation of a population based on a sample by using the numbers in a
- * column of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DSTDEV(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float|string
- */
- public static function DSTDEV($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::STDEV(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DSTDEVP.
- *
- * Calculates the standard deviation of a population based on the entire population by using the
- * numbers in a column of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DSTDEVP(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float|string
- */
- public static function DSTDEVP($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::STDEVP(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DSUM.
- *
- * Adds the numbers in a column of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DSUM(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float
- */
- public static function DSUM($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return MathTrig::SUM(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DVAR.
- *
- * Estimates the variance of a population based on a sample by using the numbers in a column
- * of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DVAR(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float|string (string if result is an error)
- */
- public static function DVAR($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::VARFunc(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-
- /**
- * DVARP.
- *
- * Calculates the variance of a population based on the entire population by using the numbers
- * in a column of a list or database that match conditions that you specify.
- *
- * Excel Function:
- * DVARP(database,field,criteria)
- *
- * @param mixed[] $database The range of cells that makes up the list or database.
- * A database is a list of related data in which rows of related
- * information are records, and columns of data are fields. The
- * first row of the list contains labels for each column.
- * @param int|string $field Indicates which column is used in the function. Enter the
- * column label enclosed between double quotation marks, such as
- * "Age" or "Yield," or a number (without quotation marks) that
- * represents the position of the column within the list: 1 for
- * the first column, 2 for the second column, and so on.
- * @param mixed[] $criteria The range of cells that contains the conditions you specify.
- * You can use any range for the criteria argument, as long as it
- * includes at least one column label and at least one cell below
- * the column label in which you specify a condition for the
- * column.
- *
- * @return float|string (string if result is an error)
- */
- public static function DVARP($database, $field, $criteria)
- {
- $field = self::fieldExtract($database, $field);
- if ($field === null) {
- return null;
- }
-
- // Return
- return Statistical::VARP(
- self::getFilteredColumn($database, $field, $criteria)
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/DateTime.php b/vendor/PhpSpreadsheet/Calculation/DateTime.php
deleted file mode 100644
index bfce2a0..0000000
--- a/vendor/PhpSpreadsheet/Calculation/DateTime.php
+++ /dev/null
@@ -1,1651 +0,0 @@
-format('m');
- $oYear = (int) $PHPDateObject->format('Y');
-
- $adjustmentMonthsString = (string) $adjustmentMonths;
- if ($adjustmentMonths > 0) {
- $adjustmentMonthsString = '+' . $adjustmentMonths;
- }
- if ($adjustmentMonths != 0) {
- $PHPDateObject->modify($adjustmentMonthsString . ' months');
- }
- $nMonth = (int) $PHPDateObject->format('m');
- $nYear = (int) $PHPDateObject->format('Y');
-
- $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
- if ($monthDiff != $adjustmentMonths) {
- $adjustDays = (int) $PHPDateObject->format('d');
- $adjustDaysString = '-' . $adjustDays . ' days';
- $PHPDateObject->modify($adjustDaysString);
- }
-
- return $PHPDateObject;
- }
-
- /**
- * DATETIMENOW.
- *
- * Returns the current date and time.
- * The NOW function is useful when you need to display the current date and time on a worksheet or
- * calculate a value based on the current date and time, and have that value updated each time you
- * open the worksheet.
- *
- * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
- * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
- *
- * Excel Function:
- * NOW()
- *
- * @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
- */
- public static function DATETIMENOW()
- {
- $saveTimeZone = date_default_timezone_get();
- date_default_timezone_set('UTC');
- $retValue = false;
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $retValue = (float) Date::PHPToExcel(time());
-
- break;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- $retValue = (int) time();
-
- break;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $retValue = new \DateTime();
-
- break;
- }
- date_default_timezone_set($saveTimeZone);
-
- return $retValue;
- }
-
- /**
- * DATENOW.
- *
- * Returns the current date.
- * The NOW function is useful when you need to display the current date and time on a worksheet or
- * calculate a value based on the current date and time, and have that value updated each time you
- * open the worksheet.
- *
- * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
- * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
- *
- * Excel Function:
- * TODAY()
- *
- * @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
- */
- public static function DATENOW()
- {
- $saveTimeZone = date_default_timezone_get();
- date_default_timezone_set('UTC');
- $retValue = false;
- $excelDateTime = floor(Date::PHPToExcel(time()));
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $retValue = (float) $excelDateTime;
-
- break;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- $retValue = (int) Date::excelToTimestamp($excelDateTime);
-
- break;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $retValue = Date::excelToDateTimeObject($excelDateTime);
-
- break;
- }
- date_default_timezone_set($saveTimeZone);
-
- return $retValue;
- }
-
- /**
- * DATE.
- *
- * The DATE function returns a value that represents a particular date.
- *
- * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
- * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
- *
- * Excel Function:
- * DATE(year,month,day)
- *
- * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
- * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
- * as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
- *
- * @param int $year The value of the year argument can include one to four digits.
- * Excel interprets the year argument according to the configured
- * date system: 1900 or 1904.
- * If year is between 0 (zero) and 1899 (inclusive), Excel adds that
- * value to 1900 to calculate the year. For example, DATE(108,1,2)
- * returns January 2, 2008 (1900+108).
- * If year is between 1900 and 9999 (inclusive), Excel uses that
- * value as the year. For example, DATE(2008,1,2) returns January 2,
- * 2008.
- * If year is less than 0 or is 10000 or greater, Excel returns the
- * #NUM! error value.
- * @param int $month A positive or negative integer representing the month of the year
- * from 1 to 12 (January to December).
- * If month is greater than 12, month adds that number of months to
- * the first month in the year specified. For example, DATE(2008,14,2)
- * returns the serial number representing February 2, 2009.
- * If month is less than 1, month subtracts the magnitude of that
- * number of months, plus 1, from the first month in the year
- * specified. For example, DATE(2008,-3,2) returns the serial number
- * representing September 2, 2007.
- * @param int $day A positive or negative integer representing the day of the month
- * from 1 to 31.
- * If day is greater than the number of days in the month specified,
- * day adds that number of days to the first day in the month. For
- * example, DATE(2008,1,35) returns the serial number representing
- * February 4, 2008.
- * If day is less than 1, day subtracts the magnitude that number of
- * days, plus one, from the first day of the month specified. For
- * example, DATE(2008,1,-15) returns the serial number representing
- * December 16, 2007.
- *
- * @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
- */
- public static function DATE($year = 0, $month = 1, $day = 1)
- {
- $year = Functions::flattenSingleValue($year);
- $month = Functions::flattenSingleValue($month);
- $day = Functions::flattenSingleValue($day);
-
- if (($month !== null) && (!is_numeric($month))) {
- $month = Date::monthStringToNumber($month);
- }
-
- if (($day !== null) && (!is_numeric($day))) {
- $day = Date::dayStringToNumber($day);
- }
-
- $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0;
- $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0;
- $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0;
- if (
- (!is_numeric($year)) ||
- (!is_numeric($month)) ||
- (!is_numeric($day))
- ) {
- return Functions::VALUE();
- }
- $year = (int) $year;
- $month = (int) $month;
- $day = (int) $day;
-
- $baseYear = Date::getExcelCalendar();
- // Validate parameters
- if ($year < ($baseYear - 1900)) {
- return Functions::NAN();
- }
- if ((($baseYear - 1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
- return Functions::NAN();
- }
-
- if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
- $year += 1900;
- }
-
- if ($month < 1) {
- // Handle year/month adjustment if month < 1
- --$month;
- $year += ceil($month / 12) - 1;
- $month = 13 - abs($month % 12);
- } elseif ($month > 12) {
- // Handle year/month adjustment if month > 12
- $year += floor($month / 12);
- $month = ($month % 12);
- }
-
- // Re-validate the year parameter after adjustments
- if (($year < $baseYear) || ($year >= 10000)) {
- return Functions::NAN();
- }
-
- // Execute function
- $excelDateValue = Date::formattedPHPToExcel($year, $month, $day);
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return Date::excelToDateTimeObject($excelDateValue);
- }
- }
-
- /**
- * 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 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 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 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
- *
- * @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
- */
- public static function TIME($hour = 0, $minute = 0, $second = 0)
- {
- $hour = Functions::flattenSingleValue($hour);
- $minute = Functions::flattenSingleValue($minute);
- $second = Functions::flattenSingleValue($second);
-
- if ($hour == '') {
- $hour = 0;
- }
- if ($minute == '') {
- $minute = 0;
- }
- if ($second == '') {
- $second = 0;
- }
-
- if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
- return Functions::VALUE();
- }
- $hour = (int) $hour;
- $minute = (int) $minute;
- $second = (int) $second;
-
- 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;
- }
- 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;
- }
-
- if ($hour > 23) {
- $hour = $hour % 24;
- } elseif ($hour < 0) {
- return Functions::NAN();
- }
-
- // Execute function
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- $date = 0;
- $calendar = Date::getExcelCalendar();
- if ($calendar != Date::CALENDAR_WINDOWS_1900) {
- $date = 1;
- }
-
- return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- $dayAdjust = 0;
- if ($hour < 0) {
- $dayAdjust = floor($hour / 24);
- $hour = 24 - abs($hour % 24);
- if ($hour == 24) {
- $hour = 0;
- }
- } elseif ($hour >= 24) {
- $dayAdjust = floor($hour / 24);
- $hour = $hour % 24;
- }
- $phpDateObject = new \DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
- if ($dayAdjust != 0) {
- $phpDateObject->modify($dayAdjust . ' days');
- }
-
- return $phpDateObject;
- }
- }
-
- /**
- * DATEVALUE.
- *
- * Returns a value that represents a particular date.
- * Use DATEVALUE to convert a date 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 date
- * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
- *
- * Excel Function:
- * DATEVALUE(dateValue)
- *
- * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
- * For example, "1/30/2008" or "30-Jan-2008" are text strings within
- * quotation marks that represent dates. Using the default date
- * system in Excel for Windows, date_text must represent a date from
- * January 1, 1900, to December 31, 9999. Using the default date
- * system in Excel for the Macintosh, date_text must represent a date
- * from January 1, 1904, to December 31, 9999. DATEVALUE returns the
- * #VALUE! error value if date_text is out of this range.
- *
- * @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
- */
- public static function DATEVALUE($dateValue = 1)
- {
- $dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
- // Strip any ordinals because they're allowed in Excel (English only)
- $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
- // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
- $dateValue = str_replace(['/', '.', '-', ' '], ' ', $dateValue);
-
- $yearFound = false;
- $t1 = explode(' ', $dateValue);
- foreach ($t1 as &$t) {
- if ((is_numeric($t)) && ($t > 31)) {
- if ($yearFound) {
- return Functions::VALUE();
- }
- if ($t < 100) {
- $t += 1900;
- }
- $yearFound = true;
- }
- }
- if ((count($t1) == 1) && (strpos($t, ':') !== false)) {
- // We've been fed a time value without any date
- return 0.0;
- } elseif (count($t1) == 2) {
- // We only have two parts of the date: either day/month or month/year
- if ($yearFound) {
- array_unshift($t1, 1);
- } else {
- if ($t1[1] > 29) {
- $t1[1] += 1900;
- array_unshift($t1, 1);
- } else {
- $t1[] = date('Y');
- }
- }
- }
- unset($t);
- $dateValue = implode(' ', $t1);
-
- $PHPDateArray = date_parse($dateValue);
- if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
- $testVal1 = strtok($dateValue, '- ');
- if ($testVal1 !== false) {
- $testVal2 = strtok('- ');
- if ($testVal2 !== false) {
- $testVal3 = strtok('- ');
- if ($testVal3 === false) {
- $testVal3 = strftime('%Y');
- }
- } else {
- return Functions::VALUE();
- }
- } else {
- return Functions::VALUE();
- }
- if ($testVal1 < 31 && $testVal2 < 12 && $testVal3 < 12 && strlen($testVal3) == 2) {
- $testVal3 += 2000;
- }
- $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
- if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
- $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
- if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
- return Functions::VALUE();
- }
- }
- }
-
- if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
- // Execute function
- if ($PHPDateArray['year'] == '') {
- $PHPDateArray['year'] = strftime('%Y');
- }
- if ($PHPDateArray['year'] < 1900) {
- return Functions::VALUE();
- }
- if ($PHPDateArray['month'] == '') {
- $PHPDateArray['month'] = strftime('%m');
- }
- if ($PHPDateArray['day'] == '') {
- $PHPDateArray['day'] = strftime('%d');
- }
- if (!checkdate($PHPDateArray['month'], $PHPDateArray['day'], $PHPDateArray['year'])) {
- return Functions::VALUE();
- }
- $excelDateValue = floor(
- Date::formattedPHPToExcel(
- $PHPDateArray['year'],
- $PHPDateArray['month'],
- $PHPDateArray['day'],
- $PHPDateArray['hour'],
- $PHPDateArray['minute'],
- $PHPDateArray['second']
- )
- );
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
- }
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 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.
- *
- * @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
- */
- public static function TIMEVALUE($timeValue)
- {
- $timeValue = trim(Functions::flattenSingleValue($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 = date_parse($timeValue);
- if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $excelDateValue = Date::formattedPHPToExcel(
- $PHPDateArray['year'],
- $PHPDateArray['month'],
- $PHPDateArray['day'],
- $PHPDateArray['hour'],
- $PHPDateArray['minute'],
- $PHPDateArray['second']
- );
- } else {
- $excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
- }
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $excelDateValue;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
- }
- }
-
- return Functions::VALUE();
- }
-
- /**
- * DATEDIF.
- *
- * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
- * or a standard date string
- * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
- * or a standard date string
- * @param string $unit
- *
- * @return int|string Interval between the dates
- */
- public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
- {
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- $unit = strtoupper(Functions::flattenSingleValue($unit));
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- // Validate parameters
- if ($startDate > $endDate) {
- return Functions::NAN();
- }
-
- // Execute function
- $difference = $endDate - $startDate;
-
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $startDays = $PHPStartDateObject->format('j');
- $startMonths = $PHPStartDateObject->format('n');
- $startYears = $PHPStartDateObject->format('Y');
-
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
- $endDays = $PHPEndDateObject->format('j');
- $endMonths = $PHPEndDateObject->format('n');
- $endYears = $PHPEndDateObject->format('Y');
-
- $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
-
- switch ($unit) {
- case 'D':
- $retVal = (int) $difference;
-
- break;
- case 'M':
- $retVal = (int) 12 * $PHPDiffDateObject->format('%y') + $PHPDiffDateObject->format('%m');
-
- break;
- case 'Y':
- $retVal = (int) $PHPDiffDateObject->format('%y');
-
- break;
- case 'MD':
- if ($endDays < $startDays) {
- $retVal = $endDays;
- $PHPEndDateObject->modify('-' . $endDays . ' days');
- $adjustDays = $PHPEndDateObject->format('j');
- $retVal += ($adjustDays - $startDays);
- } else {
- $retVal = (int) $PHPDiffDateObject->format('%d');
- }
-
- break;
- case 'YM':
- $retVal = (int) $PHPDiffDateObject->format('%m');
-
- break;
- case 'YD':
- $retVal = (int) $difference;
- if ($endYears > $startYears) {
- $isLeapStartYear = $PHPStartDateObject->format('L');
- $wasLeapEndYear = $PHPEndDateObject->format('L');
-
- // Adjust end year to be as close as possible as start year
- while ($PHPEndDateObject >= $PHPStartDateObject) {
- $PHPEndDateObject->modify('-1 year');
- $endYears = $PHPEndDateObject->format('Y');
- }
- $PHPEndDateObject->modify('+1 year');
-
- // Get the result
- $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
-
- // Adjust for leap years cases
- $isLeapEndYear = $PHPEndDateObject->format('L');
- $limit = new \DateTime($PHPEndDateObject->format('Y-02-29'));
- if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
- --$retVal;
- }
- }
-
- break;
- default:
- $retVal = Functions::VALUE();
- }
-
- return $retVal;
- }
-
- /**
- * DAYS.
- *
- * Returns the number of days between two dates
- *
- * Excel Function:
- * DAYS(endDate, startDate)
- *
- * @param DateTimeImmutable|float|int|string $endDate Excel date serial value (float),
- * PHP date timestamp (integer), PHP DateTime object, or a standard date string
- * @param DateTimeImmutable|float|int|string $startDate Excel date serial value (float),
- * PHP date timestamp (integer), PHP DateTime object, or a standard date string
- *
- * @return int|string Number of days between start date and end date or an error
- */
- public static function DAYS($endDate = 0, $startDate = 0)
- {
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
-
- $startDate = self::getDateValue($startDate);
- if (is_string($startDate)) {
- return Functions::VALUE();
- }
-
- $endDate = self::getDateValue($endDate);
- if (is_string($endDate)) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
-
- $diff = $PHPStartDateObject->diff($PHPEndDateObject);
- $days = $diff->days;
-
- if ($diff->invert) {
- $days = -$days;
- }
-
- return $days;
- }
-
- /**
- * DAYS360.
- *
- * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
- * which is used in some accounting calculations. Use this function to help compute payments if
- * your accounting system is based on twelve 30-day months.
- *
- * Excel Function:
- * DAYS360(startDate,endDate[,method])
- *
- * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param bool $method US or European Method
- * FALSE or omitted: U.S. (NASD) method. If the starting date is
- * the last day of a month, it becomes equal to the 30th of the
- * same month. If the ending date is the last day of a month and
- * the starting date is earlier than the 30th of a month, the
- * ending date becomes equal to the 1st of the next month;
- * otherwise the ending date becomes equal to the 30th of the
- * same month.
- * TRUE: European method. Starting dates and ending dates that
- * occur on the 31st of a month become equal to the 30th of the
- * same month.
- *
- * @return int|string Number of days between start date and end date
- */
- public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
- {
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
-
- if (!is_bool($method)) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPStartDateObject = Date::excelToDateTimeObject($startDate);
- $startDay = $PHPStartDateObject->format('j');
- $startMonth = $PHPStartDateObject->format('n');
- $startYear = $PHPStartDateObject->format('Y');
-
- $PHPEndDateObject = Date::excelToDateTimeObject($endDate);
- $endDay = $PHPEndDateObject->format('j');
- $endMonth = $PHPEndDateObject->format('n');
- $endYear = $PHPEndDateObject->format('Y');
-
- return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
- }
-
- /**
- * 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
- * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param 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
- *
- * @return float|string fraction of the year, or a string containing an error
- */
- public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
- {
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- $method = Functions::flattenSingleValue($method);
-
- if (is_string($startDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- if (is_string($endDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
- if ($startDate > $endDate) {
- $temp = $startDate;
- $startDate = $endDate;
- $endDate = $temp;
- }
-
- if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
- switch ($method) {
- case 0:
- return self::DAYS360($startDate, $endDate) / 360;
- case 1:
- $days = self::DATEDIF($startDate, $endDate);
- $startYear = self::YEAR($startDate);
- $endYear = self::YEAR($endDate);
- $years = $endYear - $startYear + 1;
- $startMonth = self::MONTHOFYEAR($startDate);
- $startDay = self::DAYOFMONTH($startDate);
- $endMonth = self::MONTHOFYEAR($endDate);
- $endDay = self::DAYOFMONTH($endDate);
- $startMonthDay = 100 * $startMonth + $startDay;
- $endMonthDay = 100 * $endMonth + $endDay;
- if ($years == 1) {
- if (self::isLeapYear($endYear)) {
- $tmpCalcAnnualBasis = 366;
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
- if (self::isLeapYear($startYear)) {
- if ($startMonthDay <= 229) {
- $tmpCalcAnnualBasis = 366;
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } elseif (self::isLeapYear($endYear)) {
- if ($endMonthDay >= 229) {
- $tmpCalcAnnualBasis = 366;
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } else {
- $tmpCalcAnnualBasis = 365;
- }
- } else {
- $tmpCalcAnnualBasis = 0;
- for ($year = $startYear; $year <= $endYear; ++$year) {
- $tmpCalcAnnualBasis += self::isLeapYear($year) ? 366 : 365;
- }
- $tmpCalcAnnualBasis /= $years;
- }
-
- return $days / $tmpCalcAnnualBasis;
- case 2:
- return self::DATEDIF($startDate, $endDate) / 360;
- case 3:
- return self::DATEDIF($startDate, $endDate) / 365;
- case 4:
- return self::DAYS360($startDate, $endDate, true) / 360;
- }
- }
-
- return Functions::VALUE();
- }
-
- /**
- * NETWORKDAYS.
- *
- * Returns the number of whole working days between start_date and end_date. Working days
- * exclude weekends and any dates identified in holidays.
- * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
- * worked during a specific term.
- *
- * Excel Function:
- * NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
- *
- * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- *
- * @return int|string Interval between the dates
- */
- public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
- {
- // Retrieve the mandatory start and end date that are referenced in the function definition
- $startDate = Functions::flattenSingleValue($startDate);
- $endDate = Functions::flattenSingleValue($endDate);
- // Get the optional days
- $dateArgs = Functions::flattenArray($dateArgs);
-
- // Validate the start and end dates
- if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
- return Functions::VALUE();
- }
- $startDate = (float) floor($startDate);
- if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
- return Functions::VALUE();
- }
- $endDate = (float) floor($endDate);
-
- if ($sDate > $eDate) {
- $startDate = $eDate;
- $endDate = $sDate;
- }
-
- // Execute function
- $startDoW = 6 - self::WEEKDAY($startDate, 2);
- if ($startDoW < 0) {
- $startDoW = 0;
- }
- $endDoW = self::WEEKDAY($endDate, 2);
- if ($endDoW >= 6) {
- $endDoW = 0;
- }
-
- $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
- $partWeekDays = $endDoW + $startDoW;
- if ($partWeekDays > 5) {
- $partWeekDays -= 5;
- }
-
- // Test any extra holiday parameters
- $holidayCountedArray = [];
- foreach ($dateArgs as $holidayDate) {
- if (is_string($holidayDate = self::getDateValue($holidayDate))) {
- return Functions::VALUE();
- }
- if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
- if ((self::WEEKDAY($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
- --$partWeekDays;
- $holidayCountedArray[] = $holidayDate;
- }
- }
- }
-
- if ($sDate > $eDate) {
- return 0 - ($wholeWeekDays + $partWeekDays);
- }
-
- return $wholeWeekDays + $partWeekDays;
- }
-
- /**
- * 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 mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param 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.
- *
- * @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
- */
- public static function WORKDAY($startDate, $endDays, ...$dateArgs)
- {
- // Retrieve the mandatory start date and days that are referenced in the function definition
- $startDate = Functions::flattenSingleValue($startDate);
- $endDays = Functions::flattenSingleValue($endDays);
- // Get the optional days
- $dateArgs = Functions::flattenArray($dateArgs);
-
- if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
- return Functions::VALUE();
- }
- $startDate = (float) floor($startDate);
- $endDays = (int) floor($endDays);
- // If endDays is 0, we always return startDate
- if ($endDays == 0) {
- return $startDate;
- }
-
- $decrementing = $endDays < 0;
-
- // Adjust the start date if it falls over a weekend
-
- $startDoW = self::WEEKDAY($startDate, 3);
- if (self::WEEKDAY($startDate, 3) >= 5) {
- $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
- ($decrementing) ? $endDays++ : $endDays--;
- }
-
- // Add endDays
- $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);
-
- // Adjust the calculated end date if it falls over a weekend
- $endDoW = self::WEEKDAY($endDate, 3);
- if ($endDoW >= 5) {
- $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
- }
-
- // Test any extra holiday parameters
- if (!empty($dateArgs)) {
- $holidayCountedArray = $holidayDates = [];
- foreach ($dateArgs as $holidayDate) {
- if (($holidayDate !== null) && (trim($holidayDate) > '')) {
- if (is_string($holidayDate = self::getDateValue($holidayDate))) {
- return Functions::VALUE();
- }
- if (self::WEEKDAY($holidayDate, 3) < 5) {
- $holidayDates[] = $holidayDate;
- }
- }
- }
- if ($decrementing) {
- rsort($holidayDates, SORT_NUMERIC);
- } else {
- sort($holidayDates, SORT_NUMERIC);
- }
- foreach ($holidayDates as $holidayDate) {
- if ($decrementing) {
- if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
- if (!in_array($holidayDate, $holidayCountedArray)) {
- --$endDate;
- $holidayCountedArray[] = $holidayDate;
- }
- }
- } else {
- 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::WEEKDAY($endDate, 3);
- if ($endDoW >= 5) {
- $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
- }
- }
- }
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) $endDate;
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp($endDate);
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return Date::excelToDateTimeObject($endDate);
- }
- }
-
- /**
- * DAYOFMONTH.
- *
- * Returns the day of the month, for a specified date. The day is given as an integer
- * ranging from 1 to 31.
- *
- * Excel Function:
- * DAY(dateValue)
- *
- * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- *
- * @return int|string Day of the month
- */
- public static function DAYOFMONTH($dateValue = 1)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
- if ($dateValue < 0.0) {
- return Functions::NAN();
- } elseif ($dateValue < 1.0) {
- return 0;
- }
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('j');
- }
-
- /**
- * 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 int $dateValue Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param int $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).
- *
- * @return int|string Day of the week value
- */
- public static function WEEKDAY($dateValue = 1, $style = 1)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
- $style = Functions::flattenSingleValue($style);
-
- if (!is_numeric($style)) {
- return Functions::VALUE();
- } elseif (($style < 1) || ($style > 3)) {
- return Functions::NAN();
- }
- $style = floor($style);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- $DoW = (int) $PHPDateObject->format('w');
-
- $firstDay = 1;
- switch ($style) {
- case 1:
- ++$DoW;
-
- break;
- case 2:
- if ($DoW === 0) {
- $DoW = 7;
- }
-
- break;
- case 3:
- if ($DoW === 0) {
- $DoW = 7;
- }
- $firstDay = 0;
- --$DoW;
-
- break;
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
- // Test for Excel's 1900 leap year, and introduce the error as required
- if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
- --$DoW;
- if ($DoW < $firstDay) {
- $DoW += 7;
- }
- }
- }
-
- return $DoW;
- }
-
- const STARTWEEK_SUNDAY = 1;
- const STARTWEEK_MONDAY = 2;
- const STARTWEEK_MONDAY_ALT = 11;
- const STARTWEEK_TUESDAY = 12;
- const STARTWEEK_WEDNESDAY = 13;
- const STARTWEEK_THURSDAY = 14;
- const STARTWEEK_FRIDAY = 15;
- const STARTWEEK_SATURDAY = 16;
- const STARTWEEK_SUNDAY_ALT = 17;
- const DOW_SUNDAY = 1;
- const DOW_MONDAY = 2;
- const DOW_TUESDAY = 3;
- const DOW_WEDNESDAY = 4;
- const DOW_THURSDAY = 5;
- const DOW_FRIDAY = 6;
- const DOW_SATURDAY = 7;
- const STARTWEEK_MONDAY_ISO = 21;
- const METHODARR = [
- self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
- self::DOW_MONDAY,
- self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
- self::DOW_TUESDAY,
- self::DOW_WEDNESDAY,
- self::DOW_THURSDAY,
- self::DOW_FRIDAY,
- self::DOW_SATURDAY,
- self::DOW_SUNDAY,
- self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
- ];
-
- /**
- * 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
- * @param 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).
- *
- * @return int|string Week Number
- */
- public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
- $method = Functions::flattenSingleValue($method);
-
- if (!is_numeric($method)) {
- return Functions::VALUE();
- }
- $method = (int) $method;
- if (!array_key_exists($method, self::METHODARR)) {
- return Functions::NaN();
- }
- $method = self::METHODARR[$method];
-
- $dateValue = self::getDateValue($dateValue);
- if (is_string($dateValue)) {
- return Functions::VALUE();
- }
- if ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
- if ($method == self::STARTWEEK_MONDAY_ISO) {
- return (int) $PHPDateObject->format('W');
- }
- $dayOfYear = $PHPDateObject->format('z');
- $PHPDateObject->modify('-' . $dayOfYear . ' days');
- $firstDayOfFirstWeek = $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
- *
- * @return int|string Week Number
- */
- public static function ISOWEEKNUM($dateValue = 1)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('W');
- }
-
- /**
- * MONTHOFYEAR.
- *
- * Returns the month of a date represented by a serial number.
- * The month is given as an integer, ranging from 1 (January) to 12 (December).
- *
- * Excel Function:
- * MONTH(dateValue)
- *
- * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- *
- * @return int|string Month of the year
- */
- public static function MONTHOFYEAR($dateValue = 1)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if (empty($dateValue)) {
- $dateValue = 1;
- }
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('n');
- }
-
- /**
- * YEAR.
- *
- * Returns the year corresponding to a date.
- * The year is returned as an integer in the range 1900-9999.
- *
- * Excel Function:
- * YEAR(dateValue)
- *
- * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- *
- * @return int|string Year
- */
- public static function YEAR($dateValue = 1)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
-
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
- return Functions::NAN();
- }
-
- // Execute function
- $PHPDateObject = Date::excelToDateTimeObject($dateValue);
-
- return (int) $PHPDateObject->format('Y');
- }
-
- /**
- * 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
- *
- * @return int|string Hour
- */
- public static function HOUROFDAY($timeValue = 0)
- {
- $timeValue = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('G', $timeValue);
- }
-
- /**
- * 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
- *
- * @return int|string Minute
- */
- public static function MINUTE($timeValue = 0)
- {
- $timeValue = $timeTester = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('i', $timeValue);
- }
-
- /**
- * SECOND.
- *
- * Returns the seconds of a time value.
- * The second is given as an integer in the range 0 (zero) 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
- *
- * @return int|string Second
- */
- public static function SECOND($timeValue = 0)
- {
- $timeValue = Functions::flattenSingleValue($timeValue);
-
- if (!is_numeric($timeValue)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $testVal = strtok($timeValue, '/-: ');
- if (strlen($testVal) < strlen($timeValue)) {
- return Functions::VALUE();
- }
- }
- $timeValue = self::getTimeValue($timeValue);
- if (is_string($timeValue)) {
- return Functions::VALUE();
- }
- }
- // Execute function
- if ($timeValue >= 1) {
- $timeValue = fmod($timeValue, 1);
- } elseif ($timeValue < 0.0) {
- return Functions::NAN();
- }
- $timeValue = Date::excelToTimestamp($timeValue);
-
- return (int) gmdate('s', $timeValue);
- }
-
- /**
- * EDATE.
- *
- * Returns the serial number that represents the date that is the indicated number of months
- * before or after a specified date (the start_date).
- * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
- * as the date of issue.
- *
- * Excel Function:
- * EDATE(dateValue,adjustmentMonths)
- *
- * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param int $adjustmentMonths The number of months before or after start_date.
- * A positive value for months yields a future date;
- * a negative value yields a past date.
- *
- * @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
- */
- public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
- $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
-
- if (!is_numeric($adjustmentMonths)) {
- return Functions::VALUE();
- }
- $adjustmentMonths = floor($adjustmentMonths);
-
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return $PHPDateObject;
- }
- }
-
- /**
- * EOMONTH.
- *
- * Returns the date value for the last day of the month that is the indicated number of months
- * before or after start_date.
- * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
- *
- * Excel Function:
- * EOMONTH(dateValue,adjustmentMonths)
- *
- * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
- * PHP DateTime object, or a standard date string
- * @param int $adjustmentMonths The number of months before or after start_date.
- * A positive value for months yields a future date;
- * a negative value yields a past date.
- *
- * @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
- */
- public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
- {
- $dateValue = Functions::flattenSingleValue($dateValue);
- $adjustmentMonths = Functions::flattenSingleValue($adjustmentMonths);
-
- if (!is_numeric($adjustmentMonths)) {
- return Functions::VALUE();
- }
- $adjustmentMonths = floor($adjustmentMonths);
-
- if (is_string($dateValue = self::getDateValue($dateValue))) {
- return Functions::VALUE();
- }
-
- // Execute function
- $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
- $adjustDays = (int) $PHPDateObject->format('d');
- $adjustDaysString = '-' . $adjustDays . ' days';
- $PHPDateObject->modify($adjustDaysString);
-
- switch (Functions::getReturnDateType()) {
- case Functions::RETURNDATE_EXCEL:
- return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_UNIX_TIMESTAMP:
- return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
- return $PHPDateObject;
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php b/vendor/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
deleted file mode 100644
index b688e05..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
+++ /dev/null
@@ -1,73 +0,0 @@
-stack);
- }
-
- /**
- * Push a new entry onto the stack.
- *
- * @param mixed $value
- */
- public function push($value): void
- {
- $this->stack[$value] = $value;
- }
-
- /**
- * Pop the last entry from the stack.
- *
- * @return mixed
- */
- public function pop()
- {
- return array_pop($this->stack);
- }
-
- /**
- * Test to see if a specified entry exists on the stack.
- *
- * @param mixed $value The value to test
- *
- * @return bool
- */
- public function onStack($value)
- {
- return isset($this->stack[$value]);
- }
-
- /**
- * Clear the stack.
- */
- public function clear(): void
- {
- $this->stack = [];
- }
-
- /**
- * Return an array of all entries on the stack.
- *
- * @return mixed[]
- */
- public function showStack()
- {
- return $this->stack;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Engine/Logger.php b/vendor/PhpSpreadsheet/Calculation/Engine/Logger.php
deleted file mode 100644
index 3c0f237..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Engine/Logger.php
+++ /dev/null
@@ -1,140 +0,0 @@
-cellStack = $stack;
- }
-
- /**
- * Enable/Disable Calculation engine logging.
- *
- * @param bool $pValue
- */
- public function setWriteDebugLog($pValue): void
- {
- $this->writeDebugLog = $pValue;
- }
-
- /**
- * Return whether calculation engine logging is enabled or disabled.
- *
- * @return bool
- */
- public function getWriteDebugLog()
- {
- return $this->writeDebugLog;
- }
-
- /**
- * Enable/Disable echoing of debug log information.
- *
- * @param bool $pValue
- */
- public function setEchoDebugLog($pValue): void
- {
- $this->echoDebugLog = $pValue;
- }
-
- /**
- * Return whether echoing of debug log information is enabled or disabled.
- *
- * @return bool
- */
- public function getEchoDebugLog()
- {
- return $this->echoDebugLog;
- }
-
- /**
- * Write an entry to the calculation engine debug log.
- */
- public function writeDebugLog(...$args): void
- {
- // Only write the debug log if logging is enabled
- if ($this->writeDebugLog) {
- $message = implode('', $args);
- $cellReference = implode(' -> ', $this->cellStack->showStack());
- if ($this->echoDebugLog) {
- echo $cellReference,
- ($this->cellStack->count() > 0 ? ' => ' : ''),
- $message,
- PHP_EOL;
- }
- $this->debugLog[] = $cellReference .
- ($this->cellStack->count() > 0 ? ' => ' : '') .
- $message;
- }
- }
-
- /**
- * Write a series of entries to the calculation engine debug log.
- *
- * @param string[] $args
- */
- public function mergeDebugLog(array $args): void
- {
- if ($this->writeDebugLog) {
- foreach ($args as $entry) {
- $this->writeDebugLog($entry);
- }
- }
- }
-
- /**
- * Clear the calculation engine debug log.
- */
- public function clearLog(): void
- {
- $this->debugLog = [];
- }
-
- /**
- * Return the calculation engine debug log.
- *
- * @return string[]
- */
- public function getLog()
- {
- return $this->debugLog;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Engineering.php b/vendor/PhpSpreadsheet/Calculation/Engineering.php
deleted file mode 100644
index 1256dd9..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Engineering.php
+++ /dev/null
@@ -1,2760 +0,0 @@
- ['Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => true],
- 'sg' => ['Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => false],
- 'lbm' => ['Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
- 'u' => ['Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
- 'ozm' => ['Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
- 'm' => ['Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => true],
- 'mi' => ['Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
- 'Nmi' => ['Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
- 'in' => ['Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => false],
- 'ft' => ['Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => false],
- 'yd' => ['Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => false],
- 'ang' => ['Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
- 'Pica' => ['Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
- 'yr' => ['Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => false],
- 'day' => ['Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => false],
- 'hr' => ['Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => false],
- 'mn' => ['Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => false],
- 'sec' => ['Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => true],
- 'Pa' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
- 'p' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
- 'atm' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
- 'at' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
- 'mmHg' => ['Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
- 'N' => ['Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => true],
- 'dyn' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
- 'dy' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
- 'lbf' => ['Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
- 'J' => ['Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => true],
- 'e' => ['Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => true],
- 'c' => ['Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
- 'cal' => ['Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
- 'eV' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
- 'ev' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
- 'HPh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
- 'hh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
- 'Wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
- 'wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
- 'flb' => ['Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
- 'BTU' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
- 'btu' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
- 'HP' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
- 'h' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
- 'W' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
- 'w' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
- 'T' => ['Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
- 'ga' => ['Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
- 'C' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
- 'cel' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
- 'F' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
- 'fah' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
- 'K' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
- 'kel' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
- 'tsp' => ['Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
- 'tbs' => ['Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
- 'oz' => ['Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
- 'cup' => ['Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => false],
- 'pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
- 'us_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
- 'uk_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
- 'qt' => ['Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => false],
- 'gal' => ['Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
- 'l' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
- 'lt' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
- ];
-
- /**
- * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
- *
- * @var mixed[]
- */
- private static $conversionMultipliers = [
- 'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
- 'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
- 'E' => ['multiplier' => 1E18, 'name' => 'exa'],
- 'P' => ['multiplier' => 1E15, 'name' => 'peta'],
- 'T' => ['multiplier' => 1E12, 'name' => 'tera'],
- 'G' => ['multiplier' => 1E9, 'name' => 'giga'],
- 'M' => ['multiplier' => 1E6, 'name' => 'mega'],
- 'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
- 'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
- 'e' => ['multiplier' => 1E1, 'name' => 'deka'],
- 'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
- 'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
- 'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
- 'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
- 'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
- 'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
- 'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
- 'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
- 'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
- 'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
- ];
-
- /**
- * Details of the Units of measure conversion factors, organised by group.
- *
- * @var mixed[]
- */
- private static $unitConversions = [
- 'Mass' => [
- 'g' => [
- 'g' => 1.0,
- 'sg' => 6.85220500053478E-05,
- 'lbm' => 2.20462291469134E-03,
- 'u' => 6.02217000000000E+23,
- 'ozm' => 3.52739718003627E-02,
- ],
- 'sg' => [
- 'g' => 1.45938424189287E+04,
- 'sg' => 1.0,
- 'lbm' => 3.21739194101647E+01,
- 'u' => 8.78866000000000E+27,
- 'ozm' => 5.14782785944229E+02,
- ],
- 'lbm' => [
- 'g' => 4.5359230974881148E+02,
- 'sg' => 3.10810749306493E-02,
- 'lbm' => 1.0,
- 'u' => 2.73161000000000E+26,
- 'ozm' => 1.60000023429410E+01,
- ],
- 'u' => [
- 'g' => 1.66053100460465E-24,
- 'sg' => 1.13782988532950E-28,
- 'lbm' => 3.66084470330684E-27,
- 'u' => 1.0,
- 'ozm' => 5.85735238300524E-26,
- ],
- 'ozm' => [
- 'g' => 2.83495152079732E+01,
- 'sg' => 1.94256689870811E-03,
- 'lbm' => 6.24999908478882E-02,
- 'u' => 1.70725600000000E+25,
- 'ozm' => 1.0,
- ],
- ],
- 'Distance' => [
- 'm' => [
- 'm' => 1.0,
- 'mi' => 6.21371192237334E-04,
- 'Nmi' => 5.39956803455724E-04,
- 'in' => 3.93700787401575E+01,
- 'ft' => 3.28083989501312E+00,
- 'yd' => 1.09361329797891E+00,
- 'ang' => 1.00000000000000E+10,
- 'Pica' => 2.83464566929116E+03,
- ],
- 'mi' => [
- 'm' => 1.60934400000000E+03,
- 'mi' => 1.0,
- 'Nmi' => 8.68976241900648E-01,
- 'in' => 6.33600000000000E+04,
- 'ft' => 5.28000000000000E+03,
- 'yd' => 1.76000000000000E+03,
- 'ang' => 1.60934400000000E+13,
- 'Pica' => 4.56191999999971E+06,
- ],
- 'Nmi' => [
- 'm' => 1.85200000000000E+03,
- 'mi' => 1.15077944802354E+00,
- 'Nmi' => 1.0,
- 'in' => 7.29133858267717E+04,
- 'ft' => 6.07611548556430E+03,
- 'yd' => 2.02537182785694E+03,
- 'ang' => 1.85200000000000E+13,
- 'Pica' => 5.24976377952723E+06,
- ],
- 'in' => [
- 'm' => 2.54000000000000E-02,
- 'mi' => 1.57828282828283E-05,
- 'Nmi' => 1.37149028077754E-05,
- 'in' => 1.0,
- 'ft' => 8.33333333333333E-02,
- 'yd' => 2.77777777686643E-02,
- 'ang' => 2.54000000000000E+08,
- 'Pica' => 7.19999999999955E+01,
- ],
- 'ft' => [
- 'm' => 3.04800000000000E-01,
- 'mi' => 1.89393939393939E-04,
- 'Nmi' => 1.64578833693305E-04,
- 'in' => 1.20000000000000E+01,
- 'ft' => 1.0,
- 'yd' => 3.33333333223972E-01,
- 'ang' => 3.04800000000000E+09,
- 'Pica' => 8.63999999999946E+02,
- ],
- 'yd' => [
- 'm' => 9.14400000300000E-01,
- 'mi' => 5.68181818368230E-04,
- 'Nmi' => 4.93736501241901E-04,
- 'in' => 3.60000000118110E+01,
- 'ft' => 3.00000000000000E+00,
- 'yd' => 1.0,
- 'ang' => 9.14400000300000E+09,
- 'Pica' => 2.59200000085023E+03,
- ],
- 'ang' => [
- 'm' => 1.00000000000000E-10,
- 'mi' => 6.21371192237334E-14,
- 'Nmi' => 5.39956803455724E-14,
- 'in' => 3.93700787401575E-09,
- 'ft' => 3.28083989501312E-10,
- 'yd' => 1.09361329797891E-10,
- 'ang' => 1.0,
- 'Pica' => 2.83464566929116E-07,
- ],
- 'Pica' => [
- 'm' => 3.52777777777800E-04,
- 'mi' => 2.19205948372629E-07,
- 'Nmi' => 1.90484761219114E-07,
- 'in' => 1.38888888888898E-02,
- 'ft' => 1.15740740740748E-03,
- 'yd' => 3.85802469009251E-04,
- 'ang' => 3.52777777777800E+06,
- 'Pica' => 1.0,
- ],
- ],
- 'Time' => [
- 'yr' => [
- 'yr' => 1.0,
- 'day' => 365.25,
- 'hr' => 8766.0,
- 'mn' => 525960.0,
- 'sec' => 31557600.0,
- ],
- 'day' => [
- 'yr' => 2.73785078713210E-03,
- 'day' => 1.0,
- 'hr' => 24.0,
- 'mn' => 1440.0,
- 'sec' => 86400.0,
- ],
- 'hr' => [
- 'yr' => 1.14077116130504E-04,
- 'day' => 4.16666666666667E-02,
- 'hr' => 1.0,
- 'mn' => 60.0,
- 'sec' => 3600.0,
- ],
- 'mn' => [
- 'yr' => 1.90128526884174E-06,
- 'day' => 6.94444444444444E-04,
- 'hr' => 1.66666666666667E-02,
- 'mn' => 1.0,
- 'sec' => 60.0,
- ],
- 'sec' => [
- 'yr' => 3.16880878140289E-08,
- 'day' => 1.15740740740741E-05,
- 'hr' => 2.77777777777778E-04,
- 'mn' => 1.66666666666667E-02,
- 'sec' => 1.0,
- ],
- ],
- 'Pressure' => [
- 'Pa' => [
- 'Pa' => 1.0,
- 'p' => 1.0,
- 'atm' => 9.86923299998193E-06,
- 'at' => 9.86923299998193E-06,
- 'mmHg' => 7.50061707998627E-03,
- ],
- 'p' => [
- 'Pa' => 1.0,
- 'p' => 1.0,
- 'atm' => 9.86923299998193E-06,
- 'at' => 9.86923299998193E-06,
- 'mmHg' => 7.50061707998627E-03,
- ],
- 'atm' => [
- 'Pa' => 1.01324996583000E+05,
- 'p' => 1.01324996583000E+05,
- 'atm' => 1.0,
- 'at' => 1.0,
- 'mmHg' => 760.0,
- ],
- 'at' => [
- 'Pa' => 1.01324996583000E+05,
- 'p' => 1.01324996583000E+05,
- 'atm' => 1.0,
- 'at' => 1.0,
- 'mmHg' => 760.0,
- ],
- 'mmHg' => [
- 'Pa' => 1.33322363925000E+02,
- 'p' => 1.33322363925000E+02,
- 'atm' => 1.31578947368421E-03,
- 'at' => 1.31578947368421E-03,
- 'mmHg' => 1.0,
- ],
- ],
- 'Force' => [
- 'N' => [
- 'N' => 1.0,
- 'dyn' => 1.0E+5,
- 'dy' => 1.0E+5,
- 'lbf' => 2.24808923655339E-01,
- ],
- 'dyn' => [
- 'N' => 1.0E-5,
- 'dyn' => 1.0,
- 'dy' => 1.0,
- 'lbf' => 2.24808923655339E-06,
- ],
- 'dy' => [
- 'N' => 1.0E-5,
- 'dyn' => 1.0,
- 'dy' => 1.0,
- 'lbf' => 2.24808923655339E-06,
- ],
- 'lbf' => [
- 'N' => 4.448222,
- 'dyn' => 4.448222E+5,
- 'dy' => 4.448222E+5,
- 'lbf' => 1.0,
- ],
- ],
- 'Energy' => [
- 'J' => [
- 'J' => 1.0,
- 'e' => 9.99999519343231E+06,
- 'c' => 2.39006249473467E-01,
- 'cal' => 2.38846190642017E-01,
- 'eV' => 6.24145700000000E+18,
- 'ev' => 6.24145700000000E+18,
- 'HPh' => 3.72506430801000E-07,
- 'hh' => 3.72506430801000E-07,
- 'Wh' => 2.77777916238711E-04,
- 'wh' => 2.77777916238711E-04,
- 'flb' => 2.37304222192651E+01,
- 'BTU' => 9.47815067349015E-04,
- 'btu' => 9.47815067349015E-04,
- ],
- 'e' => [
- 'J' => 1.00000048065700E-07,
- 'e' => 1.0,
- 'c' => 2.39006364353494E-08,
- 'cal' => 2.38846305445111E-08,
- 'eV' => 6.24146000000000E+11,
- 'ev' => 6.24146000000000E+11,
- 'HPh' => 3.72506609848824E-14,
- 'hh' => 3.72506609848824E-14,
- 'Wh' => 2.77778049754611E-11,
- 'wh' => 2.77778049754611E-11,
- 'flb' => 2.37304336254586E-06,
- 'BTU' => 9.47815522922962E-11,
- 'btu' => 9.47815522922962E-11,
- ],
- 'c' => [
- 'J' => 4.18399101363672E+00,
- 'e' => 4.18398900257312E+07,
- 'c' => 1.0,
- 'cal' => 9.99330315287563E-01,
- 'eV' => 2.61142000000000E+19,
- 'ev' => 2.61142000000000E+19,
- 'HPh' => 1.55856355899327E-06,
- 'hh' => 1.55856355899327E-06,
- 'Wh' => 1.16222030532950E-03,
- 'wh' => 1.16222030532950E-03,
- 'flb' => 9.92878733152102E+01,
- 'BTU' => 3.96564972437776E-03,
- 'btu' => 3.96564972437776E-03,
- ],
- 'cal' => [
- 'J' => 4.18679484613929E+00,
- 'e' => 4.18679283372801E+07,
- 'c' => 1.00067013349059E+00,
- 'cal' => 1.0,
- 'eV' => 2.61317000000000E+19,
- 'ev' => 2.61317000000000E+19,
- 'HPh' => 1.55960800463137E-06,
- 'hh' => 1.55960800463137E-06,
- 'Wh' => 1.16299914807955E-03,
- 'wh' => 1.16299914807955E-03,
- 'flb' => 9.93544094443283E+01,
- 'BTU' => 3.96830723907002E-03,
- 'btu' => 3.96830723907002E-03,
- ],
- 'eV' => [
- 'J' => 1.60219000146921E-19,
- 'e' => 1.60218923136574E-12,
- 'c' => 3.82933423195043E-20,
- 'cal' => 3.82676978535648E-20,
- 'eV' => 1.0,
- 'ev' => 1.0,
- 'HPh' => 5.96826078912344E-26,
- 'hh' => 5.96826078912344E-26,
- 'Wh' => 4.45053000026614E-23,
- 'wh' => 4.45053000026614E-23,
- 'flb' => 3.80206452103492E-18,
- 'BTU' => 1.51857982414846E-22,
- 'btu' => 1.51857982414846E-22,
- ],
- 'ev' => [
- 'J' => 1.60219000146921E-19,
- 'e' => 1.60218923136574E-12,
- 'c' => 3.82933423195043E-20,
- 'cal' => 3.82676978535648E-20,
- 'eV' => 1.0,
- 'ev' => 1.0,
- 'HPh' => 5.96826078912344E-26,
- 'hh' => 5.96826078912344E-26,
- 'Wh' => 4.45053000026614E-23,
- 'wh' => 4.45053000026614E-23,
- 'flb' => 3.80206452103492E-18,
- 'BTU' => 1.51857982414846E-22,
- 'btu' => 1.51857982414846E-22,
- ],
- 'HPh' => [
- 'J' => 2.68451741316170E+06,
- 'e' => 2.68451612283024E+13,
- 'c' => 6.41616438565991E+05,
- 'cal' => 6.41186757845835E+05,
- 'eV' => 1.67553000000000E+25,
- 'ev' => 1.67553000000000E+25,
- 'HPh' => 1.0,
- 'hh' => 1.0,
- 'Wh' => 7.45699653134593E+02,
- 'wh' => 7.45699653134593E+02,
- 'flb' => 6.37047316692964E+07,
- 'BTU' => 2.54442605275546E+03,
- 'btu' => 2.54442605275546E+03,
- ],
- 'hh' => [
- 'J' => 2.68451741316170E+06,
- 'e' => 2.68451612283024E+13,
- 'c' => 6.41616438565991E+05,
- 'cal' => 6.41186757845835E+05,
- 'eV' => 1.67553000000000E+25,
- 'ev' => 1.67553000000000E+25,
- 'HPh' => 1.0,
- 'hh' => 1.0,
- 'Wh' => 7.45699653134593E+02,
- 'wh' => 7.45699653134593E+02,
- 'flb' => 6.37047316692964E+07,
- 'BTU' => 2.54442605275546E+03,
- 'btu' => 2.54442605275546E+03,
- ],
- 'Wh' => [
- 'J' => 3.59999820554720E+03,
- 'e' => 3.59999647518369E+10,
- 'c' => 8.60422069219046E+02,
- 'cal' => 8.59845857713046E+02,
- 'eV' => 2.24692340000000E+22,
- 'ev' => 2.24692340000000E+22,
- 'HPh' => 1.34102248243839E-03,
- 'hh' => 1.34102248243839E-03,
- 'Wh' => 1.0,
- 'wh' => 1.0,
- 'flb' => 8.54294774062316E+04,
- 'BTU' => 3.41213254164705E+00,
- 'btu' => 3.41213254164705E+00,
- ],
- 'wh' => [
- 'J' => 3.59999820554720E+03,
- 'e' => 3.59999647518369E+10,
- 'c' => 8.60422069219046E+02,
- 'cal' => 8.59845857713046E+02,
- 'eV' => 2.24692340000000E+22,
- 'ev' => 2.24692340000000E+22,
- 'HPh' => 1.34102248243839E-03,
- 'hh' => 1.34102248243839E-03,
- 'Wh' => 1.0,
- 'wh' => 1.0,
- 'flb' => 8.54294774062316E+04,
- 'BTU' => 3.41213254164705E+00,
- 'btu' => 3.41213254164705E+00,
- ],
- 'flb' => [
- 'J' => 4.21400003236424E-02,
- 'e' => 4.21399800687660E+05,
- 'c' => 1.00717234301644E-02,
- 'cal' => 1.00649785509554E-02,
- 'eV' => 2.63015000000000E+17,
- 'ev' => 2.63015000000000E+17,
- 'HPh' => 1.56974211145130E-08,
- 'hh' => 1.56974211145130E-08,
- 'Wh' => 1.17055614802000E-05,
- 'wh' => 1.17055614802000E-05,
- 'flb' => 1.0,
- 'BTU' => 3.99409272448406E-05,
- 'btu' => 3.99409272448406E-05,
- ],
- 'BTU' => [
- 'J' => 1.05505813786749E+03,
- 'e' => 1.05505763074665E+10,
- 'c' => 2.52165488508168E+02,
- 'cal' => 2.51996617135510E+02,
- 'eV' => 6.58510000000000E+21,
- 'ev' => 6.58510000000000E+21,
- 'HPh' => 3.93015941224568E-04,
- 'hh' => 3.93015941224568E-04,
- 'Wh' => 2.93071851047526E-01,
- 'wh' => 2.93071851047526E-01,
- 'flb' => 2.50369750774671E+04,
- 'BTU' => 1.0,
- 'btu' => 1.0,
- ],
- 'btu' => [
- 'J' => 1.05505813786749E+03,
- 'e' => 1.05505763074665E+10,
- 'c' => 2.52165488508168E+02,
- 'cal' => 2.51996617135510E+02,
- 'eV' => 6.58510000000000E+21,
- 'ev' => 6.58510000000000E+21,
- 'HPh' => 3.93015941224568E-04,
- 'hh' => 3.93015941224568E-04,
- 'Wh' => 2.93071851047526E-01,
- 'wh' => 2.93071851047526E-01,
- 'flb' => 2.50369750774671E+04,
- 'BTU' => 1.0,
- 'btu' => 1.0,
- ],
- ],
- 'Power' => [
- 'HP' => [
- 'HP' => 1.0,
- 'h' => 1.0,
- 'W' => 7.45701000000000E+02,
- 'w' => 7.45701000000000E+02,
- ],
- 'h' => [
- 'HP' => 1.0,
- 'h' => 1.0,
- 'W' => 7.45701000000000E+02,
- 'w' => 7.45701000000000E+02,
- ],
- 'W' => [
- 'HP' => 1.34102006031908E-03,
- 'h' => 1.34102006031908E-03,
- 'W' => 1.0,
- 'w' => 1.0,
- ],
- 'w' => [
- 'HP' => 1.34102006031908E-03,
- 'h' => 1.34102006031908E-03,
- 'W' => 1.0,
- 'w' => 1.0,
- ],
- ],
- 'Magnetism' => [
- 'T' => [
- 'T' => 1.0,
- 'ga' => 10000.0,
- ],
- 'ga' => [
- 'T' => 0.0001,
- 'ga' => 1.0,
- ],
- ],
- 'Liquid' => [
- 'tsp' => [
- 'tsp' => 1.0,
- 'tbs' => 3.33333333333333E-01,
- 'oz' => 1.66666666666667E-01,
- 'cup' => 2.08333333333333E-02,
- 'pt' => 1.04166666666667E-02,
- 'us_pt' => 1.04166666666667E-02,
- 'uk_pt' => 8.67558516821960E-03,
- 'qt' => 5.20833333333333E-03,
- 'gal' => 1.30208333333333E-03,
- 'l' => 4.92999408400710E-03,
- 'lt' => 4.92999408400710E-03,
- ],
- 'tbs' => [
- 'tsp' => 3.00000000000000E+00,
- 'tbs' => 1.0,
- 'oz' => 5.00000000000000E-01,
- 'cup' => 6.25000000000000E-02,
- 'pt' => 3.12500000000000E-02,
- 'us_pt' => 3.12500000000000E-02,
- 'uk_pt' => 2.60267555046588E-02,
- 'qt' => 1.56250000000000E-02,
- 'gal' => 3.90625000000000E-03,
- 'l' => 1.47899822520213E-02,
- 'lt' => 1.47899822520213E-02,
- ],
- 'oz' => [
- 'tsp' => 6.00000000000000E+00,
- 'tbs' => 2.00000000000000E+00,
- 'oz' => 1.0,
- 'cup' => 1.25000000000000E-01,
- 'pt' => 6.25000000000000E-02,
- 'us_pt' => 6.25000000000000E-02,
- 'uk_pt' => 5.20535110093176E-02,
- 'qt' => 3.12500000000000E-02,
- 'gal' => 7.81250000000000E-03,
- 'l' => 2.95799645040426E-02,
- 'lt' => 2.95799645040426E-02,
- ],
- 'cup' => [
- 'tsp' => 4.80000000000000E+01,
- 'tbs' => 1.60000000000000E+01,
- 'oz' => 8.00000000000000E+00,
- 'cup' => 1.0,
- 'pt' => 5.00000000000000E-01,
- 'us_pt' => 5.00000000000000E-01,
- 'uk_pt' => 4.16428088074541E-01,
- 'qt' => 2.50000000000000E-01,
- 'gal' => 6.25000000000000E-02,
- 'l' => 2.36639716032341E-01,
- 'lt' => 2.36639716032341E-01,
- ],
- 'pt' => [
- 'tsp' => 9.60000000000000E+01,
- 'tbs' => 3.20000000000000E+01,
- 'oz' => 1.60000000000000E+01,
- 'cup' => 2.00000000000000E+00,
- 'pt' => 1.0,
- 'us_pt' => 1.0,
- 'uk_pt' => 8.32856176149081E-01,
- 'qt' => 5.00000000000000E-01,
- 'gal' => 1.25000000000000E-01,
- 'l' => 4.73279432064682E-01,
- 'lt' => 4.73279432064682E-01,
- ],
- 'us_pt' => [
- 'tsp' => 9.60000000000000E+01,
- 'tbs' => 3.20000000000000E+01,
- 'oz' => 1.60000000000000E+01,
- 'cup' => 2.00000000000000E+00,
- 'pt' => 1.0,
- 'us_pt' => 1.0,
- 'uk_pt' => 8.32856176149081E-01,
- 'qt' => 5.00000000000000E-01,
- 'gal' => 1.25000000000000E-01,
- 'l' => 4.73279432064682E-01,
- 'lt' => 4.73279432064682E-01,
- ],
- 'uk_pt' => [
- 'tsp' => 1.15266000000000E+02,
- 'tbs' => 3.84220000000000E+01,
- 'oz' => 1.92110000000000E+01,
- 'cup' => 2.40137500000000E+00,
- 'pt' => 1.20068750000000E+00,
- 'us_pt' => 1.20068750000000E+00,
- 'uk_pt' => 1.0,
- 'qt' => 6.00343750000000E-01,
- 'gal' => 1.50085937500000E-01,
- 'l' => 5.68260698087162E-01,
- 'lt' => 5.68260698087162E-01,
- ],
- 'qt' => [
- 'tsp' => 1.92000000000000E+02,
- 'tbs' => 6.40000000000000E+01,
- 'oz' => 3.20000000000000E+01,
- 'cup' => 4.00000000000000E+00,
- 'pt' => 2.00000000000000E+00,
- 'us_pt' => 2.00000000000000E+00,
- 'uk_pt' => 1.66571235229816E+00,
- 'qt' => 1.0,
- 'gal' => 2.50000000000000E-01,
- 'l' => 9.46558864129363E-01,
- 'lt' => 9.46558864129363E-01,
- ],
- 'gal' => [
- 'tsp' => 7.68000000000000E+02,
- 'tbs' => 2.56000000000000E+02,
- 'oz' => 1.28000000000000E+02,
- 'cup' => 1.60000000000000E+01,
- 'pt' => 8.00000000000000E+00,
- 'us_pt' => 8.00000000000000E+00,
- 'uk_pt' => 6.66284940919265E+00,
- 'qt' => 4.00000000000000E+00,
- 'gal' => 1.0,
- 'l' => 3.78623545651745E+00,
- 'lt' => 3.78623545651745E+00,
- ],
- 'l' => [
- 'tsp' => 2.02840000000000E+02,
- 'tbs' => 6.76133333333333E+01,
- 'oz' => 3.38066666666667E+01,
- 'cup' => 4.22583333333333E+00,
- 'pt' => 2.11291666666667E+00,
- 'us_pt' => 2.11291666666667E+00,
- 'uk_pt' => 1.75975569552166E+00,
- 'qt' => 1.05645833333333E+00,
- 'gal' => 2.64114583333333E-01,
- 'l' => 1.0,
- 'lt' => 1.0,
- ],
- 'lt' => [
- 'tsp' => 2.02840000000000E+02,
- 'tbs' => 6.76133333333333E+01,
- 'oz' => 3.38066666666667E+01,
- 'cup' => 4.22583333333333E+00,
- 'pt' => 2.11291666666667E+00,
- 'us_pt' => 2.11291666666667E+00,
- 'uk_pt' => 1.75975569552166E+00,
- 'qt' => 1.05645833333333E+00,
- 'gal' => 2.64114583333333E-01,
- 'l' => 1.0,
- 'lt' => 1.0,
- ],
- ],
- ];
-
- /**
- * parseComplex.
- *
- * Parses a complex number into its real and imaginary parts, and an I or J suffix
- *
- * @deprecated 2.0.0 No longer used by internal code. Please use the Complex\Complex class instead
- *
- * @param string $complexNumber The complex number
- *
- * @return mixed[] Indexed on "real", "imaginary" and "suffix"
- */
- public static function parseComplex($complexNumber)
- {
- $complex = new Complex($complexNumber);
-
- return [
- 'real' => $complex->getReal(),
- 'imaginary' => $complex->getImaginary(),
- 'suffix' => $complex->getSuffix(),
- ];
- }
-
- /**
- * Formats a number base string value with leading zeroes.
- *
- * @param string $xVal The "number" to pad
- * @param int $places The length that we want to pad this value
- *
- * @return string The padded "number"
- */
- private static function nbrConversionFormat($xVal, $places)
- {
- if ($places !== null) {
- if (is_numeric($places)) {
- $places = (int) $places;
- } else {
- return Functions::VALUE();
- }
- if ($places < 0) {
- return Functions::NAN();
- }
- if (strlen($xVal) <= $places) {
- return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
- }
-
- return Functions::NAN();
- }
-
- return substr($xVal, -10);
- }
-
- /**
- * BESSELI.
- *
- * Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
- * for purely imaginary arguments
- *
- * Excel Function:
- * BESSELI(x,ord)
- *
- * @param float $x The value at which to evaluate the function.
- * If x is nonnumeric, BESSELI returns the #VALUE! error value.
- * @param int $ord The order of the Bessel function.
- * If ord is not an integer, it is truncated.
- * If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
- * If $ord < 0, BESSELI returns the #NUM! error value.
- *
- * @return float|string Result, or a string containing an error
- */
- public static function BESSELI($x, $ord)
- {
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- $ord = floor($ord);
- if ($ord < 0) {
- return Functions::NAN();
- }
-
- if (abs($x) <= 30) {
- $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord);
- $ordK = 1;
- $fSqrX = ($x * $x) / 4;
- do {
- $fTerm *= $fSqrX;
- $fTerm /= ($ordK * ($ordK + $ord));
- $fResult += $fTerm;
- } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
- } else {
- $f_2_PI = 2 * M_PI;
-
- $fXAbs = abs($x);
- $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
- if (($ord & 1) && ($x < 0)) {
- $fResult = -$fResult;
- }
- }
-
- return (is_nan($fResult)) ? Functions::NAN() : $fResult;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * BESSELJ.
- *
- * Returns the Bessel function
- *
- * Excel Function:
- * BESSELJ(x,ord)
- *
- * @param float $x The value at which to evaluate the function.
- * If x is nonnumeric, BESSELJ returns the #VALUE! error value.
- * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
- * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
- * If $ord < 0, BESSELJ returns the #NUM! error value.
- *
- * @return float|string Result, or a string containing an error
- */
- public static function BESSELJ($x, $ord)
- {
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- $ord = floor($ord);
- if ($ord < 0) {
- return Functions::NAN();
- }
-
- $fResult = 0;
- if (abs($x) <= 30) {
- $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord);
- $ordK = 1;
- $fSqrX = ($x * $x) / -4;
- do {
- $fTerm *= $fSqrX;
- $fTerm /= ($ordK * ($ordK + $ord));
- $fResult += $fTerm;
- } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
- } else {
- $f_PI_DIV_2 = M_PI / 2;
- $f_PI_DIV_4 = M_PI / 4;
-
- $fXAbs = abs($x);
- $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
- if (($ord & 1) && ($x < 0)) {
- $fResult = -$fResult;
- }
- }
-
- return (is_nan($fResult)) ? Functions::NAN() : $fResult;
- }
-
- return Functions::VALUE();
- }
-
- private static function besselK0($fNum)
- {
- if ($fNum <= 2) {
- $fNum2 = $fNum * 0.5;
- $y = ($fNum2 * $fNum2);
- $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
- (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
- (0.10750e-3 + $y * 0.74e-5))))));
- } else {
- $y = 2 / $fNum;
- $fRet = exp(-$fNum) / sqrt($fNum) *
- (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
- (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
- }
-
- return $fRet;
- }
-
- private static function besselK1($fNum)
- {
- if ($fNum <= 2) {
- $fNum2 = $fNum * 0.5;
- $y = ($fNum2 * $fNum2);
- $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
- (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
- (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
- } else {
- $y = 2 / $fNum;
- $fRet = exp(-$fNum) / sqrt($fNum) *
- (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
- (0.325614e-2 + $y * (-0.68245e-3)))))));
- }
-
- return $fRet;
- }
-
- /**
- * BESSELK.
- *
- * Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
- * for purely imaginary arguments.
- *
- * Excel Function:
- * BESSELK(x,ord)
- *
- * @param float $x The value at which to evaluate the function.
- * If x is nonnumeric, BESSELK returns the #VALUE! error value.
- * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
- * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
- * If $ord < 0, BESSELK returns the #NUM! error value.
- *
- * @return float|string Result, or a string containing an error
- */
- public static function BESSELK($x, $ord)
- {
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- if (($ord < 0) || ($x == 0.0)) {
- return Functions::NAN();
- }
-
- switch (floor($ord)) {
- case 0:
- $fBk = self::besselK0($x);
-
- break;
- case 1:
- $fBk = self::besselK1($x);
-
- break;
- default:
- $fTox = 2 / $x;
- $fBkm = self::besselK0($x);
- $fBk = self::besselK1($x);
- for ($n = 1; $n < $ord; ++$n) {
- $fBkp = $fBkm + $n * $fTox * $fBk;
- $fBkm = $fBk;
- $fBk = $fBkp;
- }
- }
-
- return (is_nan($fBk)) ? Functions::NAN() : $fBk;
- }
-
- return Functions::VALUE();
- }
-
- private static function besselY0($fNum)
- {
- if ($fNum < 8.0) {
- $y = ($fNum * $fNum);
- $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
- $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
- $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
- } else {
- $z = 8.0 / $fNum;
- $y = ($z * $z);
- $xx = $fNum - 0.785398164;
- $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
- $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
- $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
- }
-
- return $fRet;
- }
-
- private static function besselY1($fNum)
- {
- if ($fNum < 8.0) {
- $y = ($fNum * $fNum);
- $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
- (-0.4237922726e7 + $y * 0.8511937935e4)))));
- $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
- (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
- $fRet = $f1 / $f2 + 0.636619772 * (self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
- } else {
- $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
- }
-
- return $fRet;
- }
-
- /**
- * BESSELY.
- *
- * Returns the Bessel function, which is also called the Weber function or the Neumann function.
- *
- * Excel Function:
- * BESSELY(x,ord)
- *
- * @param float $x The value at which to evaluate the function.
- * If x is nonnumeric, BESSELK returns the #VALUE! error value.
- * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
- * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
- * If $ord < 0, BESSELK returns the #NUM! error value.
- *
- * @return float|string Result, or a string containing an error
- */
- public static function BESSELY($x, $ord)
- {
- $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
- $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
-
- if ((is_numeric($x)) && (is_numeric($ord))) {
- if (($ord < 0) || ($x == 0.0)) {
- return Functions::NAN();
- }
-
- switch (floor($ord)) {
- case 0:
- $fBy = self::besselY0($x);
-
- break;
- case 1:
- $fBy = self::besselY1($x);
-
- break;
- default:
- $fTox = 2 / $x;
- $fBym = self::besselY0($x);
- $fBy = self::besselY1($x);
- for ($n = 1; $n < $ord; ++$n) {
- $fByp = $n * $fTox * $fBy - $fBym;
- $fBym = $fBy;
- $fBy = $fByp;
- }
- }
-
- return (is_nan($fBy)) ? Functions::NAN() : $fBy;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * BINTODEC.
- *
- * Return a binary value as decimal.
- *
- * Excel Function:
- * BIN2DEC(x)
- *
- * @param string $x The binary number (as a string) that you want to convert. The number
- * cannot contain more than 10 characters (10 bits). The most significant
- * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
- * Negative numbers are represented using two's-complement notation.
- * If number is not a valid binary number, or if number contains more than
- * 10 characters (10 bits), BIN2DEC returns the #NUM! error value.
- *
- * @return string
- */
- public static function BINTODEC($x)
- {
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- $x = substr($x, -9);
-
- return '-' . (512 - bindec($x));
- }
-
- return bindec($x);
- }
-
- /**
- * BINTOHEX.
- *
- * Return a binary value as hex.
- *
- * Excel Function:
- * BIN2HEX(x[,places])
- *
- * @param string $x The binary number (as a string) that you want to convert. The number
- * cannot contain more than 10 characters (10 bits). The most significant
- * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
- * Negative numbers are represented using two's-complement notation.
- * If number is not a valid binary number, or if number contains more than
- * 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
- * minimum number of characters necessary. Places is useful for padding the
- * return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
- * If places is negative, BIN2HEX returns the #NUM! error value.
- *
- * @return string
- */
- public static function BINTOHEX($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- // Argument X
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- return str_repeat('F', 8) . substr(strtoupper(dechex(bindec(substr($x, -9)))), -2);
- }
- $hexVal = (string) strtoupper(dechex(bindec($x)));
-
- return self::nbrConversionFormat($hexVal, $places);
- }
-
- /**
- * BINTOOCT.
- *
- * Return a binary value as octal.
- *
- * Excel Function:
- * BIN2OCT(x[,places])
- *
- * @param string $x The binary number (as a string) that you want to convert. The number
- * cannot contain more than 10 characters (10 bits). The most significant
- * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
- * Negative numbers are represented using two's-complement notation.
- * If number is not a valid binary number, or if number contains more than
- * 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
- * minimum number of characters necessary. Places is useful for padding the
- * return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
- * If places is negative, BIN2OCT returns the #NUM! error value.
- *
- * @return string
- */
- public static function BINTOOCT($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- $x = floor($x);
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
- return Functions::NAN();
- }
- if (strlen($x) > 10) {
- return Functions::NAN();
- } elseif (strlen($x) == 10) {
- // Two's Complement
- return str_repeat('7', 7) . substr(strtoupper(decoct(bindec(substr($x, -9)))), -3);
- }
- $octVal = (string) decoct(bindec($x));
-
- return self::nbrConversionFormat($octVal, $places);
- }
-
- /**
- * DECTOBIN.
- *
- * Return a decimal value as binary.
- *
- * Excel Function:
- * DEC2BIN(x[,places])
- *
- * @param string $x The decimal integer you want to convert. If number is negative,
- * valid place values are ignored and DEC2BIN returns a 10-character
- * (10-bit) binary number in which the most significant bit is the sign
- * bit. The remaining 9 bits are magnitude bits. Negative numbers are
- * represented using two's-complement notation.
- * If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
- * value.
- * If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
- * If DEC2BIN requires more than places characters, it returns the #NUM!
- * error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
- * the minimum number of characters necessary. Places is useful for
- * padding the return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
- * If places is zero or negative, DEC2BIN returns the #NUM! error value.
- *
- * @return string
- */
- public static function DECTOBIN($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
-
- $x = (string) floor($x);
- if ($x < -512 || $x > 511) {
- return Functions::NAN();
- }
-
- $r = decbin($x);
- // Two's Complement
- $r = substr($r, -10);
- if (strlen($r) >= 11) {
- return Functions::NAN();
- }
-
- return self::nbrConversionFormat($r, $places);
- }
-
- /**
- * DECTOHEX.
- *
- * Return a decimal value as hex.
- *
- * Excel Function:
- * DEC2HEX(x[,places])
- *
- * @param string $x The decimal integer you want to convert. If number is negative,
- * places is ignored and DEC2HEX returns a 10-character (40-bit)
- * hexadecimal number in which the most significant bit is the sign
- * bit. The remaining 39 bits are magnitude bits. Negative numbers
- * are represented using two's-complement notation.
- * If number < -549,755,813,888 or if number > 549,755,813,887,
- * DEC2HEX returns the #NUM! error value.
- * If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
- * If DEC2HEX requires more than places characters, it returns the
- * #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
- * the minimum number of characters necessary. Places is useful for
- * padding the return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
- * If places is zero or negative, DEC2HEX returns the #NUM! error value.
- *
- * @return string
- */
- public static function DECTOHEX($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
- $x = (string) floor($x);
- $r = strtoupper(dechex($x));
- if (strlen($r) == 8) {
- // Two's Complement
- $r = 'FF' . $r;
- }
-
- return self::nbrConversionFormat($r, $places);
- }
-
- /**
- * DECTOOCT.
- *
- * Return an decimal value as octal.
- *
- * Excel Function:
- * DEC2OCT(x[,places])
- *
- * @param string $x The decimal integer you want to convert. If number is negative,
- * places is ignored and DEC2OCT returns a 10-character (30-bit)
- * octal number in which the most significant bit is the sign bit.
- * The remaining 29 bits are magnitude bits. Negative numbers are
- * represented using two's-complement notation.
- * If number < -536,870,912 or if number > 536,870,911, DEC2OCT
- * returns the #NUM! error value.
- * If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
- * If DEC2OCT requires more than places characters, it returns the
- * #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
- * the minimum number of characters necessary. Places is useful for
- * padding the return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
- * If places is zero or negative, DEC2OCT returns the #NUM! error value.
- *
- * @return string
- */
- public static function DECTOOCT($x, $places = null)
- {
- $xorig = $x;
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $x = (int) $x;
- } else {
- return Functions::VALUE();
- }
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
- return Functions::VALUE();
- }
- $x = (string) floor($x);
- $r = decoct($x);
- if (strlen($r) == 11) {
- // Two's Complement
- $r = substr($r, -10);
- }
-
- return self::nbrConversionFormat($r, $places);
- }
-
- /**
- * HEXTOBIN.
- *
- * Return a hex value as binary.
- *
- * Excel Function:
- * HEX2BIN(x[,places])
- *
- * @param string $x the hexadecimal number you want to convert.
- * Number cannot contain more than 10 characters.
- * The most significant bit of number is the sign bit (40th bit from the right).
- * The remaining 9 bits are magnitude bits.
- * Negative numbers are represented using two's-complement notation.
- * If number is negative, HEX2BIN ignores places and returns a 10-character binary number.
- * If number is negative, it cannot be less than FFFFFFFE00,
- * and if number is positive, it cannot be greater than 1FF.
- * If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
- * If HEX2BIN requires more than places characters, it returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted,
- * HEX2BIN uses the minimum number of characters necessary. Places
- * is useful for padding the return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
- * If places is negative, HEX2BIN returns the #NUM! error value.
- *
- * @return string
- */
- public static function HEXTOBIN($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- return self::DECTOBIN(self::HEXTODEC($x), $places);
- }
-
- /**
- * HEXTODEC.
- *
- * Return a hex value as decimal.
- *
- * Excel Function:
- * HEX2DEC(x)
- *
- * @param string $x The hexadecimal number you want to convert. This number cannot
- * contain more than 10 characters (40 bits). The most significant
- * bit of number is the sign bit. The remaining 39 bits are magnitude
- * bits. Negative numbers are represented using two's-complement
- * notation.
- * If number is not a valid hexadecimal number, HEX2DEC returns the
- * #NUM! error value.
- *
- * @return string
- */
- public static function HEXTODEC($x)
- {
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- if (strlen($x) > 10) {
- return Functions::NAN();
- }
-
- $binX = '';
- foreach (str_split($x) as $char) {
- $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
- }
- if (strlen($binX) == 40 && $binX[0] == '1') {
- for ($i = 0; $i < 40; ++$i) {
- $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
- }
-
- return (bindec($binX) + 1) * -1;
- }
-
- return bindec($binX);
- }
-
- /**
- * HEXTOOCT.
- *
- * Return a hex value as octal.
- *
- * Excel Function:
- * HEX2OCT(x[,places])
- *
- * @param string $x The hexadecimal number you want to convert. Number cannot
- * contain more than 10 characters. The most significant bit of
- * number is the sign bit. The remaining 39 bits are magnitude
- * bits. Negative numbers are represented using two's-complement
- * notation.
- * If number is negative, HEX2OCT ignores places and returns a
- * 10-character octal number.
- * If number is negative, it cannot be less than FFE0000000, and
- * if number is positive, it cannot be greater than 1FFFFFFF.
- * If number is not a valid hexadecimal number, HEX2OCT returns
- * the #NUM! error value.
- * If HEX2OCT requires more than places characters, it returns
- * the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, HEX2OCT
- * uses the minimum number of characters necessary. Places is
- * useful for padding the return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, HEX2OCT returns the #VALUE! error
- * value.
- * If places is negative, HEX2OCT returns the #NUM! error value.
- *
- * @return string
- */
- public static function HEXTOOCT($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
- return Functions::NAN();
- }
-
- $decimal = self::HEXTODEC($x);
- if ($decimal < -536870912 || $decimal > 536870911) {
- return Functions::NAN();
- }
-
- return self::DECTOOCT($decimal, $places);
- }
-
- /**
- * OCTTOBIN.
- *
- * Return an octal value as binary.
- *
- * Excel Function:
- * OCT2BIN(x[,places])
- *
- * @param string $x The octal number you want to convert. Number may not
- * contain more than 10 characters. The most significant
- * bit of number is the sign bit. The remaining 29 bits
- * are magnitude bits. Negative numbers are represented
- * using two's-complement notation.
- * If number is negative, OCT2BIN ignores places and returns
- * a 10-character binary number.
- * If number is negative, it cannot be less than 7777777000,
- * and if number is positive, it cannot be greater than 777.
- * If number is not a valid octal number, OCT2BIN returns
- * the #NUM! error value.
- * If OCT2BIN requires more than places characters, it
- * returns the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted,
- * OCT2BIN uses the minimum number of characters necessary.
- * Places is useful for padding the return value with
- * leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, OCT2BIN returns the #VALUE!
- * error value.
- * If places is negative, OCT2BIN returns the #NUM! error
- * value.
- *
- * @return string
- */
- public static function OCTTOBIN($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
-
- return self::DECTOBIN(self::OCTTODEC($x), $places);
- }
-
- /**
- * OCTTODEC.
- *
- * Return an octal value as decimal.
- *
- * Excel Function:
- * OCT2DEC(x)
- *
- * @param string $x The octal number you want to convert. Number may not contain
- * more than 10 octal characters (30 bits). The most significant
- * bit of number is the sign bit. The remaining 29 bits are
- * magnitude bits. Negative numbers are represented using
- * two's-complement notation.
- * If number is not a valid octal number, OCT2DEC returns the
- * #NUM! error value.
- *
- * @return string
- */
- public static function OCTTODEC($x)
- {
- $x = Functions::flattenSingleValue($x);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
- $binX = '';
- foreach (str_split($x) as $char) {
- $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
- }
- if (strlen($binX) == 30 && $binX[0] == '1') {
- for ($i = 0; $i < 30; ++$i) {
- $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
- }
-
- return (bindec($binX) + 1) * -1;
- }
-
- return bindec($binX);
- }
-
- /**
- * OCTTOHEX.
- *
- * Return an octal value as hex.
- *
- * Excel Function:
- * OCT2HEX(x[,places])
- *
- * @param string $x The octal number you want to convert. Number may not contain
- * more than 10 octal characters (30 bits). The most significant
- * bit of number is the sign bit. The remaining 29 bits are
- * magnitude bits. Negative numbers are represented using
- * two's-complement notation.
- * If number is negative, OCT2HEX ignores places and returns a
- * 10-character hexadecimal number.
- * If number is not a valid octal number, OCT2HEX returns the
- * #NUM! error value.
- * If OCT2HEX requires more than places characters, it returns
- * the #NUM! error value.
- * @param int $places The number of characters to use. If places is omitted, OCT2HEX
- * uses the minimum number of characters necessary. Places is useful
- * for padding the return value with leading 0s (zeros).
- * If places is not an integer, it is truncated.
- * If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
- * If places is negative, OCT2HEX returns the #NUM! error value.
- *
- * @return string
- */
- public static function OCTTOHEX($x, $places = null)
- {
- $x = Functions::flattenSingleValue($x);
- $places = Functions::flattenSingleValue($places);
-
- if (is_bool($x)) {
- return Functions::VALUE();
- }
- $x = (string) $x;
- if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
- return Functions::NAN();
- }
- $hexVal = strtoupper(dechex(self::OCTTODEC($x)));
-
- return self::nbrConversionFormat($hexVal, $places);
- }
-
- /**
- * COMPLEX.
- *
- * Converts real and imaginary coefficients into a complex number of the form x +/- yi or x +/- yj.
- *
- * Excel Function:
- * COMPLEX(realNumber,imaginary[,suffix])
- *
- * @param float $realNumber the real coefficient of the complex number
- * @param float $imaginary the imaginary coefficient of the complex number
- * @param string $suffix The suffix for the imaginary component of the complex number.
- * If omitted, the suffix is assumed to be "i".
- *
- * @return string
- */
- public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
- {
- $realNumber = ($realNumber === null) ? 0.0 : Functions::flattenSingleValue($realNumber);
- $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary);
- $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix);
-
- if (
- ((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
- (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))
- ) {
- $complex = new Complex($realNumber, $imaginary, $suffix);
-
- return (string) $complex;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * IMAGINARY.
- *
- * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMAGINARY(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the imaginary
- * coefficient
- *
- * @return float
- */
- public static function IMAGINARY($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->getImaginary();
- }
-
- /**
- * IMREAL.
- *
- * Returns the real coefficient of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMREAL(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the real coefficient
- *
- * @return float
- */
- public static function IMREAL($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->getReal();
- }
-
- /**
- * IMABS.
- *
- * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMABS(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the absolute value
- *
- * @return float
- */
- public static function IMABS($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (new Complex($complexNumber))->abs();
- }
-
- /**
- * IMARGUMENT.
- *
- * Returns the argument theta of a complex number, i.e. the angle in radians from the real
- * axis to the representation of the number in polar coordinates.
- *
- * Excel Function:
- * IMARGUMENT(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the argument theta
- *
- * @return float|string
- */
- public static function IMARGUMENT($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::DIV0();
- }
-
- return $complex->argument();
- }
-
- /**
- * IMCONJUGATE.
- *
- * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMCONJUGATE(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the conjugate
- *
- * @return string
- */
- public static function IMCONJUGATE($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->conjugate();
- }
-
- /**
- * IMCOS.
- *
- * Returns the cosine of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMCOS(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the cosine
- *
- * @return float|string
- */
- public static function IMCOS($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cos();
- }
-
- /**
- * IMCOSH.
- *
- * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMCOSH(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the hyperbolic cosine
- *
- * @return float|string
- */
- public static function IMCOSH($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cosh();
- }
-
- /**
- * IMCOT.
- *
- * Returns the cotangent of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMCOT(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the cotangent
- *
- * @return float|string
- */
- public static function IMCOT($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->cot();
- }
-
- /**
- * IMCSC.
- *
- * Returns the cosecant of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMCSC(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the cosecant
- *
- * @return float|string
- */
- public static function IMCSC($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->csc();
- }
-
- /**
- * IMCSCH.
- *
- * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMCSCH(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the hyperbolic cosecant
- *
- * @return float|string
- */
- public static function IMCSCH($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->csch();
- }
-
- /**
- * IMSIN.
- *
- * Returns the sine of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSIN(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the sine
- *
- * @return float|string
- */
- public static function IMSIN($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sin();
- }
-
- /**
- * IMSINH.
- *
- * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSINH(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the hyperbolic sine
- *
- * @return float|string
- */
- public static function IMSINH($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sinh();
- }
-
- /**
- * IMSEC.
- *
- * Returns the secant of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSEC(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the secant
- *
- * @return float|string
- */
- public static function IMSEC($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sec();
- }
-
- /**
- * IMSECH.
- *
- * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSECH(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the hyperbolic secant
- *
- * @return float|string
- */
- public static function IMSECH($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->sech();
- }
-
- /**
- * IMTAN.
- *
- * Returns the tangent of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMTAN(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the tangent
- *
- * @return float|string
- */
- public static function IMTAN($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->tan();
- }
-
- /**
- * IMSQRT.
- *
- * Returns the square root of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSQRT(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the square root
- *
- * @return string
- */
- public static function IMSQRT($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $theta = self::IMARGUMENT($complexNumber);
- if ($theta === Functions::DIV0()) {
- return '0';
- }
-
- return (string) (new Complex($complexNumber))->sqrt();
- }
-
- /**
- * IMLN.
- *
- * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMLN(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the natural logarithm
- *
- * @return string
- */
- public static function IMLN($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->ln();
- }
-
- /**
- * IMLOG10.
- *
- * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMLOG10(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the common logarithm
- *
- * @return string
- */
- public static function IMLOG10($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->log10();
- }
-
- /**
- * IMLOG2.
- *
- * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMLOG2(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the base-2 logarithm
- *
- * @return string
- */
- public static function IMLOG2($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- $complex = new Complex($complexNumber);
- if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
- return Functions::NAN();
- }
-
- return (string) (new Complex($complexNumber))->log2();
- }
-
- /**
- * IMEXP.
- *
- * Returns the exponential of a complex number in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMEXP(complexNumber)
- *
- * @param string $complexNumber the complex number for which you want the exponential
- *
- * @return string
- */
- public static function IMEXP($complexNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
-
- return (string) (new Complex($complexNumber))->exp();
- }
-
- /**
- * IMPOWER.
- *
- * Returns a complex number in x + yi or x + yj text format raised to a power.
- *
- * Excel Function:
- * IMPOWER(complexNumber,realNumber)
- *
- * @param string $complexNumber the complex number you want to raise to a power
- * @param float $realNumber the power to which you want to raise the complex number
- *
- * @return string
- */
- public static function IMPOWER($complexNumber, $realNumber)
- {
- $complexNumber = Functions::flattenSingleValue($complexNumber);
- $realNumber = Functions::flattenSingleValue($realNumber);
-
- if (!is_numeric($realNumber)) {
- return Functions::VALUE();
- }
-
- return (string) (new Complex($complexNumber))->pow($realNumber);
- }
-
- /**
- * IMDIV.
- *
- * Returns the quotient of two complex numbers in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMDIV(complexDividend,complexDivisor)
- *
- * @param string $complexDividend the complex numerator or dividend
- * @param string $complexDivisor the complex denominator or divisor
- *
- * @return string
- */
- public static function IMDIV($complexDividend, $complexDivisor)
- {
- $complexDividend = Functions::flattenSingleValue($complexDividend);
- $complexDivisor = Functions::flattenSingleValue($complexDivisor);
-
- try {
- return (string) (new Complex($complexDividend))->divideby(new Complex($complexDivisor));
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
- }
-
- /**
- * IMSUB.
- *
- * Returns the difference of two complex numbers in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSUB(complexNumber1,complexNumber2)
- *
- * @param string $complexNumber1 the complex number from which to subtract complexNumber2
- * @param string $complexNumber2 the complex number to subtract from complexNumber1
- *
- * @return string
- */
- public static function IMSUB($complexNumber1, $complexNumber2)
- {
- $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
- $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
-
- try {
- return (string) (new Complex($complexNumber1))->subtract(new Complex($complexNumber2));
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
- }
-
- /**
- * IMSUM.
- *
- * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMSUM(complexNumber[,complexNumber[,...]])
- *
- * @param string ...$complexNumbers Series of complex numbers to add
- *
- * @return string
- */
- public static function IMSUM(...$complexNumbers)
- {
- // Return value
- $returnValue = new Complex(0.0);
- $aArgs = Functions::flattenArray($complexNumbers);
-
- try {
- // Loop through the arguments
- foreach ($aArgs as $complex) {
- $returnValue = $returnValue->add(new Complex($complex));
- }
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
-
- return (string) $returnValue;
- }
-
- /**
- * IMPRODUCT.
- *
- * Returns the product of two or more complex numbers in x + yi or x + yj text format.
- *
- * Excel Function:
- * IMPRODUCT(complexNumber[,complexNumber[,...]])
- *
- * @param string ...$complexNumbers Series of complex numbers to multiply
- *
- * @return string
- */
- public static function IMPRODUCT(...$complexNumbers)
- {
- // Return value
- $returnValue = new Complex(1.0);
- $aArgs = Functions::flattenArray($complexNumbers);
-
- try {
- // Loop through the arguments
- foreach ($aArgs as $complex) {
- $returnValue = $returnValue->multiply(new Complex($complex));
- }
- } catch (ComplexException $e) {
- return Functions::NAN();
- }
-
- return (string) $returnValue;
- }
-
- /**
- * DELTA.
- *
- * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
- * Use this function to filter a set of values. For example, by summing several DELTA
- * functions you calculate the count of equal pairs. This function is also known as the
- * Kronecker Delta function.
- *
- * Excel Function:
- * DELTA(a[,b])
- *
- * @param float $a the first number
- * @param float $b The second number. If omitted, b is assumed to be zero.
- *
- * @return int
- */
- public static function DELTA($a, $b = 0)
- {
- $a = Functions::flattenSingleValue($a);
- $b = Functions::flattenSingleValue($b);
-
- return (int) ($a == $b);
- }
-
- /**
- * GESTEP.
- *
- * Excel Function:
- * GESTEP(number[,step])
- *
- * Returns 1 if number >= step; returns 0 (zero) otherwise
- * Use this function to filter a set of values. For example, by summing several GESTEP
- * functions you calculate the count of values that exceed a threshold.
- *
- * @param float $number the value to test against step
- * @param float $step The threshold value.
- * If you omit a value for step, GESTEP uses zero.
- *
- * @return int
- */
- public static function GESTEP($number, $step = 0)
- {
- $number = Functions::flattenSingleValue($number);
- $step = Functions::flattenSingleValue($step);
-
- return (int) ($number >= $step);
- }
-
- //
- // Private method to calculate the erf value
- //
- private static $twoSqrtPi = 1.128379167095512574;
-
- public static function erfVal($x)
- {
- if (abs($x) > 2.2) {
- return 1 - self::erfcVal($x);
- }
- $sum = $term = $x;
- $xsqr = ($x * $x);
- $j = 1;
- do {
- $term *= $xsqr / $j;
- $sum -= $term / (2 * $j + 1);
- ++$j;
- $term *= $xsqr / $j;
- $sum += $term / (2 * $j + 1);
- ++$j;
- if ($sum == 0.0) {
- break;
- }
- } while (abs($term / $sum) > Functions::PRECISION);
-
- return self::$twoSqrtPi * $sum;
- }
-
- /**
- * Validate arguments passed to the bitwise functions.
- *
- * @param mixed $value
- *
- * @return int
- */
- private static function validateBitwiseArgument($value)
- {
- $value = Functions::flattenSingleValue($value);
-
- if (is_int($value)) {
- return $value;
- } elseif (is_numeric($value)) {
- if ($value == (int) ($value)) {
- $value = (int) ($value);
- if (($value > 2 ** 48 - 1) || ($value < 0)) {
- throw new Exception(Functions::NAN());
- }
-
- return $value;
- }
-
- throw new Exception(Functions::NAN());
- }
-
- throw new Exception(Functions::VALUE());
- }
-
- /**
- * BITAND.
- *
- * Returns the bitwise AND of two integer values.
- *
- * Excel Function:
- * BITAND(number1, number2)
- *
- * @param int $number1
- * @param int $number2
- *
- * @return int|string
- */
- public static function BITAND($number1, $number2)
- {
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 & $number2;
- }
-
- /**
- * BITOR.
- *
- * Returns the bitwise OR of two integer values.
- *
- * Excel Function:
- * BITOR(number1, number2)
- *
- * @param int $number1
- * @param int $number2
- *
- * @return int|string
- */
- public static function BITOR($number1, $number2)
- {
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 | $number2;
- }
-
- /**
- * BITXOR.
- *
- * Returns the bitwise XOR of two integer values.
- *
- * Excel Function:
- * BITXOR(number1, number2)
- *
- * @param int $number1
- * @param int $number2
- *
- * @return int|string
- */
- public static function BITXOR($number1, $number2)
- {
- try {
- $number1 = self::validateBitwiseArgument($number1);
- $number2 = self::validateBitwiseArgument($number2);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- return $number1 ^ $number2;
- }
-
- /**
- * BITLSHIFT.
- *
- * Returns the number value shifted left by shift_amount bits.
- *
- * Excel Function:
- * BITLSHIFT(number, shift_amount)
- *
- * @param int $number
- * @param int $shiftAmount
- *
- * @return int|string
- */
- public static function BITLSHIFT($number, $shiftAmount)
- {
- try {
- $number = self::validateBitwiseArgument($number);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- $shiftAmount = Functions::flattenSingleValue($shiftAmount);
-
- $result = $number << $shiftAmount;
- if ($result > 2 ** 48 - 1) {
- return Functions::NAN();
- }
-
- return $result;
- }
-
- /**
- * BITRSHIFT.
- *
- * Returns the number value shifted right by shift_amount bits.
- *
- * Excel Function:
- * BITRSHIFT(number, shift_amount)
- *
- * @param int $number
- * @param int $shiftAmount
- *
- * @return int|string
- */
- public static function BITRSHIFT($number, $shiftAmount)
- {
- try {
- $number = self::validateBitwiseArgument($number);
- } catch (Exception $e) {
- return $e->getMessage();
- }
-
- $shiftAmount = Functions::flattenSingleValue($shiftAmount);
-
- return $number >> $shiftAmount;
- }
-
- /**
- * ERF.
- *
- * Returns the error function integrated between the lower and upper bound arguments.
- *
- * Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
- * the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
- * improved, so that it can now calculate the function for both positive and negative ranges.
- * PhpSpreadsheet follows Excel 2010 behaviour, and accepts negative arguments.
- *
- * Excel Function:
- * ERF(lower[,upper])
- *
- * @param float $lower lower bound for integrating ERF
- * @param float $upper upper bound for integrating ERF.
- * If omitted, ERF integrates between zero and lower_limit
- *
- * @return float|string
- */
- public static function ERF($lower, $upper = null)
- {
- $lower = Functions::flattenSingleValue($lower);
- $upper = Functions::flattenSingleValue($upper);
-
- if (is_numeric($lower)) {
- if ($upper === null) {
- return self::erfVal($lower);
- }
- if (is_numeric($upper)) {
- return self::erfVal($upper) - self::erfVal($lower);
- }
- }
-
- return Functions::VALUE();
- }
-
- /**
- * ERFPRECISE.
- *
- * Returns the error function integrated between the lower and upper bound arguments.
- *
- * Excel Function:
- * ERF.PRECISE(limit)
- *
- * @param float $limit bound for integrating ERF
- *
- * @return float|string
- */
- public static function ERFPRECISE($limit)
- {
- $limit = Functions::flattenSingleValue($limit);
-
- return self::ERF($limit);
- }
-
- //
- // Private method to calculate the erfc value
- //
- private static $oneSqrtPi = 0.564189583547756287;
-
- private static function erfcVal($x)
- {
- if (abs($x) < 2.2) {
- return 1 - self::erfVal($x);
- }
- if ($x < 0) {
- return 2 - self::ERFC(-$x);
- }
- $a = $n = 1;
- $b = $c = $x;
- $d = ($x * $x) + 0.5;
- $q1 = $q2 = $b / $d;
- $t = 0;
- do {
- $t = $a * $n + $b * $x;
- $a = $b;
- $b = $t;
- $t = $c * $n + $d * $x;
- $c = $d;
- $d = $t;
- $n += 0.5;
- $q1 = $q2;
- $q2 = $b / $d;
- } while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
-
- return self::$oneSqrtPi * exp(-$x * $x) * $q2;
- }
-
- /**
- * ERFC.
- *
- * Returns the complementary ERF function integrated between x and infinity
- *
- * Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
- * the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
- * improved, so that it can now calculate the function for both positive and negative x values.
- * PhpSpreadsheet follows Excel 2010 behaviour, and accepts nagative arguments.
- *
- * Excel Function:
- * ERFC(x)
- *
- * @param float $x The lower bound for integrating ERFC
- *
- * @return float|string
- */
- public static function ERFC($x)
- {
- $x = Functions::flattenSingleValue($x);
-
- if (is_numeric($x)) {
- return self::erfcVal($x);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * getConversionGroups
- * Returns a list of the different conversion groups for UOM conversions.
- *
- * @return array
- */
- public static function getConversionGroups()
- {
- $conversionGroups = [];
- foreach (self::$conversionUnits as $conversionUnit) {
- $conversionGroups[] = $conversionUnit['Group'];
- }
-
- return array_merge(array_unique($conversionGroups));
- }
-
- /**
- * getConversionGroupUnits
- * Returns an array of units of measure, for a specified conversion group, or for all groups.
- *
- * @param string $group The group whose units of measure you want to retrieve
- *
- * @return array
- */
- public static function getConversionGroupUnits($group = null)
- {
- $conversionGroups = [];
- foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
- if (($group === null) || ($conversionGroup['Group'] == $group)) {
- $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
- }
- }
-
- return $conversionGroups;
- }
-
- /**
- * getConversionGroupUnitDetails.
- *
- * @param string $group The group whose units of measure you want to retrieve
- *
- * @return array
- */
- public static function getConversionGroupUnitDetails($group = null)
- {
- $conversionGroups = [];
- foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
- if (($group === null) || ($conversionGroup['Group'] == $group)) {
- $conversionGroups[$conversionGroup['Group']][] = [
- 'unit' => $conversionUnit,
- 'description' => $conversionGroup['Unit Name'],
- ];
- }
- }
-
- return $conversionGroups;
- }
-
- /**
- * getConversionMultipliers
- * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
- *
- * @return array of mixed
- */
- public static function getConversionMultipliers()
- {
- return self::$conversionMultipliers;
- }
-
- /**
- * CONVERTUOM.
- *
- * Converts a number from one measurement system to another.
- * For example, CONVERT can translate a table of distances in miles to a table of distances
- * in kilometers.
- *
- * Excel Function:
- * CONVERT(value,fromUOM,toUOM)
- *
- * @param float $value the value in fromUOM to convert
- * @param string $fromUOM the units for value
- * @param string $toUOM the units for the result
- *
- * @return float|string
- */
- public static function CONVERTUOM($value, $fromUOM, $toUOM)
- {
- $value = Functions::flattenSingleValue($value);
- $fromUOM = Functions::flattenSingleValue($fromUOM);
- $toUOM = Functions::flattenSingleValue($toUOM);
-
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- $fromMultiplier = 1.0;
- if (isset(self::$conversionUnits[$fromUOM])) {
- $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
- } else {
- $fromMultiplier = substr($fromUOM, 0, 1);
- $fromUOM = substr($fromUOM, 1);
- if (isset(self::$conversionMultipliers[$fromMultiplier])) {
- $fromMultiplier = self::$conversionMultipliers[$fromMultiplier]['multiplier'];
- } else {
- return Functions::NA();
- }
- if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) {
- $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
- } else {
- return Functions::NA();
- }
- }
- $value *= $fromMultiplier;
-
- $toMultiplier = 1.0;
- if (isset(self::$conversionUnits[$toUOM])) {
- $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
- } else {
- $toMultiplier = substr($toUOM, 0, 1);
- $toUOM = substr($toUOM, 1);
- if (isset(self::$conversionMultipliers[$toMultiplier])) {
- $toMultiplier = self::$conversionMultipliers[$toMultiplier]['multiplier'];
- } else {
- return Functions::NA();
- }
- if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) {
- $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
- } else {
- return Functions::NA();
- }
- }
- if ($unitGroup1 != $unitGroup2) {
- return Functions::NA();
- }
-
- if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) {
- // We've already factored $fromMultiplier into the value, so we need
- // to reverse it again
- return $value / $fromMultiplier;
- } elseif ($unitGroup1 == 'Temperature') {
- if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
- if (($toUOM == 'F') || ($toUOM == 'fah')) {
- return $value;
- }
- $value = (($value - 32) / 1.8);
- if (($toUOM == 'K') || ($toUOM == 'kel')) {
- $value += 273.15;
- }
-
- return $value;
- } elseif (
- (($fromUOM == 'K') || ($fromUOM == 'kel')) &&
- (($toUOM == 'K') || ($toUOM == 'kel'))
- ) {
- return $value;
- } elseif (
- (($fromUOM == 'C') || ($fromUOM == 'cel')) &&
- (($toUOM == 'C') || ($toUOM == 'cel'))
- ) {
- return $value;
- }
- if (($toUOM == 'F') || ($toUOM == 'fah')) {
- if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
- $value -= 273.15;
- }
-
- return ($value * 1.8) + 32;
- }
- if (($toUOM == 'C') || ($toUOM == 'cel')) {
- return $value - 273.15;
- }
-
- return $value + 273.15;
- }
-
- return ($value * self::$unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Exception.php b/vendor/PhpSpreadsheet/Calculation/Exception.php
deleted file mode 100644
index 87c7d22..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Exception.php
+++ /dev/null
@@ -1,26 +0,0 @@
-line = $line;
- $e->file = $file;
-
- throw $e;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/ExceptionHandler.php b/vendor/PhpSpreadsheet/Calculation/ExceptionHandler.php
deleted file mode 100644
index 41e51d4..0000000
--- a/vendor/PhpSpreadsheet/Calculation/ExceptionHandler.php
+++ /dev/null
@@ -1,22 +0,0 @@
-format('d') == $testDate->format('t');
- }
-
- private static function couponFirstPeriodDate($settlement, $maturity, $frequency, $next)
- {
- $months = 12 / $frequency;
-
- $result = Date::excelToDateTimeObject($maturity);
- $eom = self::isLastDayOfMonth($result);
-
- while ($settlement < Date::PHPToExcel($result)) {
- $result->modify('-' . $months . ' months');
- }
- if ($next) {
- $result->modify('+' . $months . ' months');
- }
-
- if ($eom) {
- $result->modify('-1 day');
- }
-
- return Date::PHPToExcel($result);
- }
-
- private static function isValidFrequency($frequency)
- {
- if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * daysPerYear.
- *
- * Returns the number of days in a specified year, as defined by the "basis" value
- *
- * @param int|string $year The year against which we're testing
- * @param int|string $basis The type of day count:
- * 0 or omitted US (NASD) 360
- * 1 Actual (365 or 366 in a leap year)
- * 2 360
- * 3 365
- * 4 European 360
- *
- * @return int|string Result, or a string containing an error
- */
- private static function daysPerYear($year, $basis = 0)
- {
- switch ($basis) {
- case 0:
- case 2:
- case 4:
- $daysPerYear = 360;
-
- break;
- case 3:
- $daysPerYear = 365;
-
- break;
- case 1:
- $daysPerYear = (DateTime::isLeapYear($year)) ? 366 : 365;
-
- break;
- default:
- return Functions::NAN();
- }
-
- return $daysPerYear;
- }
-
- private static function interestAndPrincipal($rate = 0, $per = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
- {
- $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
- $capital = $pv;
- for ($i = 1; $i <= $per; ++$i) {
- $interest = ($type && $i == 1) ? 0 : -$capital * $rate;
- $principal = $pmt - $interest;
- $capital += $principal;
- }
-
- return [$interest, $principal];
- }
-
- /**
- * ACCRINT.
- *
- * Returns the accrued interest for a security that pays periodic interest.
- *
- * Excel Function:
- * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis])
- *
- * @param mixed $issue the security's issue date
- * @param mixed $firstinterest the security's first interest date
- * @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 float $rate the security's annual coupon rate
- * @param float $par The security's par value.
- * If you omit par, ACCRINT uses $1,000.
- * @param int $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
- {
- $issue = Functions::flattenSingleValue($issue);
- $firstinterest = Functions::flattenSingleValue($firstinterest);
- $settlement = Functions::flattenSingleValue($settlement);
- $rate = Functions::flattenSingleValue($rate);
- $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par);
- $frequency = ($frequency === null) ? 1 : Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($rate)) && (is_numeric($par))) {
- $rate = (float) $rate;
- $par = (float) $par;
- if (($rate <= 0) || ($par <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
-
- return $par * $rate * $daysBetweenIssueAndSettlement;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * ACCRINTM.
- *
- * Returns the accrued interest for a security that pays interest at maturity.
- *
- * Excel Function:
- * ACCRINTM(issue,settlement,rate[,par[,basis]])
- *
- * @param mixed $issue The security's issue date
- * @param mixed $settlement The security's settlement (or maturity) date
- * @param float $rate The security's annual coupon rate
- * @param float $par The security's par value.
- * If you omit par, ACCRINT uses $1,000.
- * @param int $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 ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
- {
- $issue = Functions::flattenSingleValue($issue);
- $settlement = Functions::flattenSingleValue($settlement);
- $rate = Functions::flattenSingleValue($rate);
- $par = ($par === null) ? 1000 : Functions::flattenSingleValue($par);
- $basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($rate)) && (is_numeric($par))) {
- $rate = (float) $rate;
- $par = (float) $par;
- if (($rate <= 0) || ($par <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
-
- return $par * $rate * $daysBetweenIssueAndSettlement;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * AMORDEGRC.
- *
- * Returns the depreciation for each accounting period.
- * This function is provided for the French accounting system. If an asset is purchased in
- * the middle of the accounting period, the prorated depreciation is taken into account.
- * The function is similar to AMORLINC, except that a depreciation coefficient is applied in
- * the calculation depending on the life of the assets.
- * This function will return the depreciation until the last period of the life of the assets
- * or until the cumulated value of depreciation is greater than the cost of the assets minus
- * the salvage value.
- *
- * Excel Function:
- * AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
- *
- * @param float $cost The cost of the asset
- * @param mixed $purchased Date of the purchase of the asset
- * @param mixed $firstPeriod Date of the end of the first period
- * @param mixed $salvage The salvage value at the end of the life of the asset
- * @param float $period The period
- * @param float $rate Rate of depreciation
- * @param int $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
- */
- public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
- {
- $cost = Functions::flattenSingleValue($cost);
- $purchased = Functions::flattenSingleValue($purchased);
- $firstPeriod = Functions::flattenSingleValue($firstPeriod);
- $salvage = Functions::flattenSingleValue($salvage);
- $period = floor(Functions::flattenSingleValue($period));
- $rate = Functions::flattenSingleValue($rate);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- // The depreciation coefficients are:
- // Life of assets (1/rate) Depreciation coefficient
- // Less than 3 years 1
- // Between 3 and 4 years 1.5
- // Between 5 and 6 years 2
- // More than 6 years 2.5
- $fUsePer = 1.0 / $rate;
- if ($fUsePer < 3.0) {
- $amortiseCoeff = 1.0;
- } elseif ($fUsePer < 5.0) {
- $amortiseCoeff = 1.5;
- } elseif ($fUsePer <= 6.0) {
- $amortiseCoeff = 2.0;
- } else {
- $amortiseCoeff = 2.5;
- }
-
- $rate *= $amortiseCoeff;
- $fNRate = round(DateTime::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost, 0);
- $cost -= $fNRate;
- $fRest = $cost - $salvage;
-
- for ($n = 0; $n < $period; ++$n) {
- $fNRate = round($rate * $cost, 0);
- $fRest -= $fNRate;
-
- if ($fRest < 0.0) {
- switch ($period - $n) {
- case 0:
- case 1:
- return round($cost * 0.5, 0);
- default:
- return 0.0;
- }
- }
- $cost -= $fNRate;
- }
-
- return $fNRate;
- }
-
- /**
- * AMORLINC.
- *
- * Returns the depreciation for each accounting period.
- * This function is provided for the French accounting system. If an asset is purchased in
- * the middle of the accounting period, the prorated depreciation is taken into account.
- *
- * Excel Function:
- * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
- *
- * @param float $cost The cost of the asset
- * @param mixed $purchased Date of the purchase of the asset
- * @param mixed $firstPeriod Date of the end of the first period
- * @param mixed $salvage The salvage value at the end of the life of the asset
- * @param float $period The period
- * @param float $rate Rate of depreciation
- * @param int $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
- */
- public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
- {
- $cost = Functions::flattenSingleValue($cost);
- $purchased = Functions::flattenSingleValue($purchased);
- $firstPeriod = Functions::flattenSingleValue($firstPeriod);
- $salvage = Functions::flattenSingleValue($salvage);
- $period = Functions::flattenSingleValue($period);
- $rate = Functions::flattenSingleValue($rate);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- $fOneRate = $cost * $rate;
- $fCostDelta = $cost - $salvage;
- // Note, quirky variation for leap years on the YEARFRAC for this function
- $purchasedYear = DateTime::YEAR($purchased);
- $yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
-
- if (($basis == 1) && ($yearFrac < 1) && (DateTime::isLeapYear($purchasedYear))) {
- $yearFrac *= 365 / 366;
- }
-
- $f0Rate = $yearFrac * $rate * $cost;
- $nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
-
- if ($period == 0) {
- return $f0Rate;
- } elseif ($period <= $nNumOfFullPeriods) {
- return $fOneRate;
- } elseif ($period == ($nNumOfFullPeriods + 1)) {
- return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
- }
-
- return 0.0;
- }
-
- /**
- * COUPDAYBS.
- *
- * Returns the number of days from the beginning of the coupon period to the settlement date.
- *
- * Excel Function:
- * COUPDAYBS(settlement,maturity,frequency[,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 int $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
-
- if ($basis == 1) {
- return abs(DateTime::DAYS($prev, $settlement));
- }
-
- return DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
- }
-
- /**
- * COUPDAYS.
- *
- * Returns the number of days in the coupon period that contains the settlement date.
- *
- * Excel Function:
- * COUPDAYS(settlement,maturity,frequency[,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 $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- switch ($basis) {
- case 3:
- // Actual/365
- return 365 / $frequency;
- case 1:
- // Actual/actual
- if ($frequency == 1) {
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
-
- return $daysPerYear / $frequency;
- }
- $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
- $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
-
- return $next - $prev;
- default:
- // US (NASD) 30/360, Actual/360 or European 30/360
- return 360 / $frequency;
- }
- }
-
- /**
- * COUPDAYSNC.
- *
- * Returns the number of days from the settlement date to the next coupon date.
- *
- * Excel Function:
- * COUPDAYSNC(settlement,maturity,frequency[,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 $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
-
- return DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
- }
-
- /**
- * COUPNCD.
- *
- * Returns the next coupon date after the settlement date.
- *
- * Excel Function:
- * COUPNCD(settlement,maturity,frequency[,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 $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
- * depending on the value of the ReturnDateType flag
- */
- public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- return self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
- }
-
- /**
- * COUPNUM.
- *
- * Returns the number of coupons payable between the settlement date and maturity date,
- * rounded up to the nearest whole coupon.
- *
- * Excel Function:
- * COUPNUM(settlement,maturity,frequency[,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 $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 int|string
- */
- public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $yearsBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, 0);
-
- return ceil($yearsBetweenSettlementAndMaturity * $frequency);
- }
-
- /**
- * COUPPCD.
- *
- * Returns the previous coupon date before the settlement date.
- *
- * Excel Function:
- * COUPPCD(settlement,maturity,frequency[,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 $frequency the number of coupon payments per year.
- * Valid frequency values are:
- * 1 Annual
- * 2 Semi-Annual
- * 4 Quarterly
- * @param int $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 mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
- * depending on the value of the ReturnDateType flag
- */
- public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $frequency = (int) Functions::flattenSingleValue($frequency);
- $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
-
- if (is_string($settlement = DateTime::getDateValue($settlement))) {
- return Functions::VALUE();
- }
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (
- ($settlement >= $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- return self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
- }
-
- /**
- * CUMIPMT.
- *
- * Returns the cumulative interest paid on a loan between the start and end periods.
- *
- * Excel Function:
- * CUMIPMT(rate,nper,pv,start,end[,type])
- *
- * @param float $rate The Interest rate
- * @param int $nper The total number of payment periods
- * @param float $pv Present Value
- * @param int $start The first period in the calculation.
- * Payment periods are numbered beginning with 1.
- * @param int $end the last period in the calculation
- * @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
- *
- * @return float|string
- */
- public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $start = (int) Functions::flattenSingleValue($start);
- $end = (int) Functions::flattenSingleValue($end);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($start < 1 || $start > $end) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interest = 0;
- for ($per = $start; $per <= $end; ++$per) {
- $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
- }
-
- return $interest;
- }
-
- /**
- * CUMPRINC.
- *
- * Returns the cumulative principal paid on a loan between the start and end periods.
- *
- * Excel Function:
- * CUMPRINC(rate,nper,pv,start,end[,type])
- *
- * @param float $rate The Interest rate
- * @param int $nper The total number of payment periods
- * @param float $pv Present Value
- * @param int $start The first period in the calculation.
- * Payment periods are numbered beginning with 1.
- * @param int $end the last period in the calculation
- * @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
- *
- * @return float|string
- */
- public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $start = (int) Functions::flattenSingleValue($start);
- $end = (int) Functions::flattenSingleValue($end);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($start < 1 || $start > $end) {
- return Functions::VALUE();
- }
-
- // Calculate
- $principal = 0;
- for ($per = $start; $per <= $end; ++$per) {
- $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
- }
-
- return $principal;
- }
-
- /**
- * DB.
- *
- * Returns the depreciation of an asset for a specified period using the
- * fixed-declining balance method.
- * This form of depreciation is used if you want to get a higher depreciation value
- * at the beginning of the depreciation (as opposed to linear depreciation). The
- * depreciation value is reduced with every depreciation period by the depreciation
- * already deducted from the initial cost.
- *
- * Excel Function:
- * DB(cost,salvage,life,period[,month])
- *
- * @param float $cost Initial cost of the asset
- * @param float $salvage Value at the end of the depreciation.
- * (Sometimes called the salvage value of the asset)
- * @param int $life Number of periods over which the asset is depreciated.
- * (Sometimes called the useful life of the asset)
- * @param int $period The period for which you want to calculate the
- * depreciation. Period must use the same units as life.
- * @param int $month Number of months in the first year. If month is omitted,
- * it defaults to 12.
- *
- * @return float|string
- */
- public static function DB($cost, $salvage, $life, $period, $month = 12)
- {
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
- $month = Functions::flattenSingleValue($month);
-
- // Validate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
- $cost = (float) $cost;
- $salvage = (float) $salvage;
- $life = (int) $life;
- $period = (int) $period;
- $month = (int) $month;
- if ($cost == 0) {
- return 0.0;
- } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
- return Functions::NAN();
- }
- // Set Fixed Depreciation Rate
- $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
- $fixedDepreciationRate = round($fixedDepreciationRate, 3);
-
- // Loop through each period calculating the depreciation
- $previousDepreciation = 0;
- $depreciation = 0;
- for ($per = 1; $per <= $period; ++$per) {
- if ($per == 1) {
- $depreciation = $cost * $fixedDepreciationRate * $month / 12;
- } elseif ($per == ($life + 1)) {
- $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
- } else {
- $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
- }
- $previousDepreciation += $depreciation;
- }
-
- return $depreciation;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * DDB.
- *
- * Returns the depreciation of an asset for a specified period using the
- * double-declining balance method or some other method you specify.
- *
- * Excel Function:
- * DDB(cost,salvage,life,period[,factor])
- *
- * @param float $cost Initial cost of the asset
- * @param float $salvage Value at the end of the depreciation.
- * (Sometimes called the salvage value of the asset)
- * @param int $life Number of periods over which the asset is depreciated.
- * (Sometimes called the useful life of the asset)
- * @param int $period The period for which you want to calculate the
- * depreciation. Period must use the same units as life.
- * @param float $factor The rate at which the balance declines.
- * If factor is omitted, it is assumed to be 2 (the
- * double-declining balance method).
- *
- * @return float|string
- */
- public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
- {
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
- $factor = Functions::flattenSingleValue($factor);
-
- // Validate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
- $cost = (float) $cost;
- $salvage = (float) $salvage;
- $life = (int) $life;
- $period = (int) $period;
- $factor = (float) $factor;
- if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
- return Functions::NAN();
- }
- // Set Fixed Depreciation Rate
- $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
- $fixedDepreciationRate = round($fixedDepreciationRate, 3);
-
- // Loop through each period calculating the depreciation
- $previousDepreciation = 0;
- $depreciation = 0;
- for ($per = 1; $per <= $period; ++$per) {
- $depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation));
- $previousDepreciation += $depreciation;
- }
-
- return $depreciation;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 int $price The security's price per $100 face value
- * @param int $redemption The security's redemption value per $100 face value
- * @param int $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 DISC($settlement, $maturity, $price, $redemption, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- $price = (float) $price;
- $redemption = (float) $redemption;
- $basis = (int) $basis;
- if (($price <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * DOLLARDE.
- *
- * Converts a dollar price expressed as an integer part and a fraction
- * part into a dollar price expressed as a decimal number.
- * Fractional dollar numbers are sometimes used for security prices.
- *
- * Excel Function:
- * DOLLARDE(fractional_dollar,fraction)
- *
- * @param float $fractional_dollar Fractional Dollar
- * @param int $fraction Fraction
- *
- * @return float|string
- */
- public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
- {
- $fractional_dollar = Functions::flattenSingleValue($fractional_dollar);
- $fraction = (int) Functions::flattenSingleValue($fraction);
-
- // Validate parameters
- if ($fractional_dollar === null || $fraction < 0) {
- return Functions::NAN();
- }
- if ($fraction == 0) {
- return Functions::DIV0();
- }
-
- $dollars = floor($fractional_dollar);
- $cents = fmod($fractional_dollar, 1);
- $cents /= $fraction;
- $cents *= 10 ** ceil(log10($fraction));
-
- return $dollars + $cents;
- }
-
- /**
- * DOLLARFR.
- *
- * Converts a dollar price expressed as a decimal number into a dollar price
- * expressed as a fraction.
- * Fractional dollar numbers are sometimes used for security prices.
- *
- * Excel Function:
- * DOLLARFR(decimal_dollar,fraction)
- *
- * @param float $decimal_dollar Decimal Dollar
- * @param int $fraction Fraction
- *
- * @return float|string
- */
- public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
- {
- $decimal_dollar = Functions::flattenSingleValue($decimal_dollar);
- $fraction = (int) Functions::flattenSingleValue($fraction);
-
- // Validate parameters
- if ($decimal_dollar === null || $fraction < 0) {
- return Functions::NAN();
- }
- if ($fraction == 0) {
- return Functions::DIV0();
- }
-
- $dollars = floor($decimal_dollar);
- $cents = fmod($decimal_dollar, 1);
- $cents *= $fraction;
- $cents *= 10 ** (-ceil(log10($fraction)));
-
- return $dollars + $cents;
- }
-
- /**
- * EFFECT.
- *
- * Returns the effective interest rate given the nominal rate and the number of
- * compounding payments per year.
- *
- * Excel Function:
- * EFFECT(nominal_rate,npery)
- *
- * @param float $nominal_rate Nominal interest rate
- * @param int $npery Number of compounding payments per year
- *
- * @return float|string
- */
- public static function EFFECT($nominal_rate = 0, $npery = 0)
- {
- $nominal_rate = Functions::flattenSingleValue($nominal_rate);
- $npery = (int) Functions::flattenSingleValue($npery);
-
- // Validate parameters
- if ($nominal_rate <= 0 || $npery < 1) {
- return Functions::NAN();
- }
-
- return (1 + $nominal_rate / $npery) ** $npery - 1;
- }
-
- /**
- * FV.
- *
- * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
- *
- * Excel Function:
- * FV(rate,nper,pmt[,pv[,type]])
- *
- * @param float $rate The interest rate per period
- * @param int $nper Total number of payment periods in an annuity
- * @param float $pmt The payment made each period: it cannot change over the
- * life of the annuity. Typically, pmt contains principal
- * and interest but no other fees or taxes.
- * @param float $pv present Value, or the lump-sum amount that a series of
- * future payments is worth right now
- * @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
- *
- * @return float|string
- */
- public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return -$pv * (1 + $rate) ** $nper - $pmt * (1 + $rate * $type) * ((1 + $rate) ** $nper - 1) / $rate;
- }
-
- return -$pv - $pmt * $nper;
- }
-
- /**
- * 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 float $principal the present value
- * @param float[] $schedule an array of interest rates to apply
- *
- * @return float
- */
- public static function FVSCHEDULE($principal, $schedule)
- {
- $principal = Functions::flattenSingleValue($principal);
- $schedule = Functions::flattenArray($schedule);
-
- foreach ($schedule as $rate) {
- $principal *= 1 + $rate;
- }
-
- return $principal;
- }
-
- /**
- * 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 int $investment the amount invested in the security
- * @param int $redemption the amount to be received at maturity
- * @param int $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 INTRATE($settlement, $maturity, $investment, $redemption, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $investment = Functions::flattenSingleValue($investment);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- $investment = (float) $investment;
- $redemption = (float) $redemption;
- $basis = (int) $basis;
- if (($investment <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * IPMT.
- *
- * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
- *
- * Excel Function:
- * IPMT(rate,per,nper,pv[,fv][,type])
- *
- * @param float $rate Interest rate per period
- * @param int $per Period for which we want to find the interest
- * @param int $nper Number of periods
- * @param float $pv Present Value
- * @param float $fv Future Value
- * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
- *
- * @return float|string
- */
- public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $per = (int) Functions::flattenSingleValue($per);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($per <= 0 || $per > $nper) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
-
- return $interestAndPrincipal[0];
- }
-
- /**
- * 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 float[] $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 float $guess A number that you guess is close to the result of IRR
- *
- * @return float|string
- */
- public static function IRR($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::NPV($x1, $values);
- $f2 = self::NPV($x2, $values);
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- if (($f1 * $f2) < 0.0) {
- break;
- }
- if (abs($f1) < abs($f2)) {
- $f1 = self::NPV($x1 += 1.6 * ($x1 - $x2), $values);
- } else {
- $f2 = self::NPV($x2 += 1.6 * ($x2 - $x1), $values);
- }
- }
- if (($f1 * $f2) > 0.0) {
- return Functions::VALUE();
- }
-
- $f = self::NPV($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::NPV($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();
- }
-
- /**
- * ISPMT.
- *
- * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
- *
- * Excel Function:
- * =ISPMT(interest_rate, period, number_payments, PV)
- *
- * interest_rate is the interest rate for the investment
- *
- * period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
- *
- * number_payments is the number of payments for the annuity
- *
- * PV is the loan amount or present value of the payments
- */
- public static function ISPMT(...$args)
- {
- // Return value
- $returnValue = 0;
-
- // Get the parameters
- $aArgs = Functions::flattenArray($args);
- $interestRate = array_shift($aArgs);
- $period = array_shift($aArgs);
- $numberPeriods = array_shift($aArgs);
- $principleRemaining = array_shift($aArgs);
-
- // Calculate
- $principlePayment = ($principleRemaining * 1.0) / ($numberPeriods * 1.0);
- for ($i = 0; $i <= $period; ++$i) {
- $returnValue = $interestRate * $principleRemaining * -1;
- $principleRemaining -= $principlePayment;
- // principle needs to be 0 after the last payment, don't let floating point screw it up
- if ($i == $numberPeriods) {
- $returnValue = 0;
- }
- }
-
- return $returnValue;
- }
-
- /**
- * 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 float[] $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 float $finance_rate The interest rate you pay on the money used in the cash flows
- * @param float $reinvestment_rate 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 MIRR($values, $finance_rate, $reinvestment_rate)
- {
- if (!is_array($values)) {
- return Functions::VALUE();
- }
- $values = Functions::flattenArray($values);
- $finance_rate = Functions::flattenSingleValue($finance_rate);
- $reinvestment_rate = Functions::flattenSingleValue($reinvestment_rate);
- $n = count($values);
-
- $rr = 1.0 + $reinvestment_rate;
- $fr = 1.0 + $finance_rate;
-
- $npv_pos = $npv_neg = 0.0;
- foreach ($values as $i => $v) {
- if ($v >= 0) {
- $npv_pos += $v / $rr ** $i;
- } else {
- $npv_neg += $v / $fr ** $i;
- }
- }
-
- if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvestment_rate <= -1)) {
- return Functions::VALUE();
- }
-
- $mirr = ((-$npv_pos * $rr ** $n)
- / ($npv_neg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
-
- return is_finite($mirr) ? $mirr : Functions::VALUE();
- }
-
- /**
- * NOMINAL.
- *
- * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
- *
- * @param float $effect_rate Effective interest rate
- * @param int $npery Number of compounding payments per year
- *
- * @return float|string Result, or a string containing an error
- */
- public static function NOMINAL($effect_rate = 0, $npery = 0)
- {
- $effect_rate = Functions::flattenSingleValue($effect_rate);
- $npery = (int) Functions::flattenSingleValue($npery);
-
- // Validate parameters
- if ($effect_rate <= 0 || $npery < 1) {
- return Functions::NAN();
- }
-
- // Calculate
- return $npery * (($effect_rate + 1) ** (1 / $npery) - 1);
- }
-
- /**
- * NPER.
- *
- * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
- *
- * @param float $rate Interest rate per period
- * @param int $pmt Periodic payment (annuity)
- * @param float $pv Present Value
- * @param float $fv Future Value
- * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
- *
- * @return float|string Result, or a string containing an error
- */
- public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- if ($pmt == 0 && $pv == 0) {
- return Functions::NAN();
- }
-
- return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
- }
- if ($pmt == 0) {
- return Functions::NAN();
- }
-
- return (-$pv - $fv) / $pmt;
- }
-
- /**
- * NPV.
- *
- * Returns the Net Present Value of a cash flow series given a discount rate.
- *
- * @return float
- */
- public static function NPV(...$args)
- {
- // Return value
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- // Calculate
- $rate = array_shift($aArgs);
- $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
- return $returnValue;
- }
-
- /**
- * PDURATION.
- *
- * Calculates the number of periods required for an investment to reach a specified value.
- *
- * @param float $rate Interest rate per period
- * @param float $pv Present Value
- * @param float $fv Future Value
- *
- * @return float|string Result, or a string containing an error
- */
- public static function PDURATION($rate = 0, $pv = 0, $fv = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
-
- // Validate parameters
- if (!is_numeric($rate) || !is_numeric($pv) || !is_numeric($fv)) {
- return Functions::VALUE();
- } elseif ($rate <= 0.0 || $pv <= 0.0 || $fv <= 0.0) {
- return Functions::NAN();
- }
-
- return (log($fv) - log($pv)) / log(1 + $rate);
- }
-
- /**
- * PMT.
- *
- * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
- *
- * @param float $rate Interest rate per period
- * @param int $nper Number of periods
- * @param float $pv Present Value
- * @param float $fv Future Value
- * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
- *
- * @return float|string Result, or a string containing an error
- */
- public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return (-$fv - $pv * (1 + $rate) ** $nper) / (1 + $rate * $type) / (((1 + $rate) ** $nper - 1) / $rate);
- }
-
- return (-$pv - $fv) / $nper;
- }
-
- /**
- * PPMT.
- *
- * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
- *
- * @param float $rate Interest rate per period
- * @param int $per Period for which we want to find the interest
- * @param int $nper Number of periods
- * @param float $pv Present Value
- * @param float $fv Future Value
- * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
- *
- * @return float|string Result, or a string containing an error
- */
- public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $per = (int) Functions::flattenSingleValue($per);
- $nper = (int) Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
- $type = (int) Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
- if ($per <= 0 || $per > $nper) {
- return Functions::VALUE();
- }
-
- // Calculate
- $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
-
- return $interestAndPrincipal[1];
- }
-
- private static function validatePrice($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis)
- {
- if (is_string($settlement)) {
- return Functions::VALUE();
- }
- if (is_string($maturity)) {
- return Functions::VALUE();
- }
- if (!is_numeric($rate)) {
- return Functions::VALUE();
- }
- if (!is_numeric($yield)) {
- return Functions::VALUE();
- }
- if (!is_numeric($redemption)) {
- return Functions::VALUE();
- }
- if (!is_numeric($frequency)) {
- return Functions::VALUE();
- }
- if (!is_numeric($basis)) {
- return Functions::VALUE();
- }
-
- return '';
- }
-
- public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
- {
- $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 = Functions::flattenSingleValue($basis);
-
- $settlement = DateTime::getDateValue($settlement);
- $maturity = DateTime::getDateValue($maturity);
- $rslt = self::validatePrice($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
- if ($rslt) {
- return $rslt;
- }
- $rate = (float) $rate;
- $yield = (float) $yield;
- $redemption = (float) $redemption;
- $frequency = (int) $frequency;
- $basis = (int) $basis;
-
- if (
- ($settlement > $maturity) ||
- (!self::isValidFrequency($frequency)) ||
- (($basis < 0) || ($basis > 4))
- ) {
- return Functions::NAN();
- }
-
- $dsc = self::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
- $e = self::COUPDAYS($settlement, $maturity, $frequency, $basis);
- $n = self::COUPNUM($settlement, $maturity, $frequency, $basis);
- $a = self::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 int $discount The security's discount rate
- * @param int $redemption The security's redemption value per $100 face value
- * @param int $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 PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = (float) Functions::flattenSingleValue($discount);
- $redemption = (float) Functions::flattenSingleValue($redemption);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
- if (($discount <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 int $rate The security's interest rate at date of issue
- * @param int $yield The security's annual yield
- * @param int $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 PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $issue = Functions::flattenSingleValue($issue);
- $rate = Functions::flattenSingleValue($rate);
- $yield = Functions::flattenSingleValue($yield);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($rate) && is_numeric($yield)) {
- if (($rate <= 0) || ($yield <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
- $daysBetweenIssueAndSettlement *= $daysPerYear;
- $daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
- if (!is_numeric($daysBetweenIssueAndMaturity)) {
- // return date error
- return $daysBetweenIssueAndMaturity;
- }
- $daysBetweenIssueAndMaturity *= $daysPerYear;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($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);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * PV.
- *
- * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
- *
- * @param float $rate Interest rate per period
- * @param int $nper Number of periods
- * @param float $pmt Periodic payment (annuity)
- * @param float $fv Future Value
- * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
- *
- * @return float|string Result, or a string containing an error
- */
- public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
- {
- $rate = Functions::flattenSingleValue($rate);
- $nper = Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $fv = Functions::flattenSingleValue($fv);
- $type = Functions::flattenSingleValue($type);
-
- // Validate parameters
- if ($type != 0 && $type != 1) {
- return Functions::NAN();
- }
-
- // Calculate
- if ($rate !== null && $rate != 0) {
- return (-$pmt * (1 + $rate * $type) * (((1 + $rate) ** $nper - 1) / $rate) - $fv) / (1 + $rate) ** $nper;
- }
-
- return -$fv - $pmt * $nper;
- }
-
- /**
- * RATE.
- *
- * Returns the interest rate per period of an annuity.
- * RATE is calculated by iteration and can have zero or more solutions.
- * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
- * RATE returns the #NUM! error value.
- *
- * Excel Function:
- * RATE(nper,pmt,pv[,fv[,type[,guess]]])
- *
- * @param float $nper The total number of payment periods in an annuity
- * @param float $pmt The payment made each period and cannot change over the life
- * of the annuity.
- * Typically, pmt includes principal and interest but no other
- * fees or taxes.
- * @param float $pv The present value - the total amount that a series of future
- * payments is worth now
- * @param float $fv The future value, or a cash balance you want to attain after
- * the last payment is made. If fv is omitted, it is assumed
- * to be 0 (the future value of a loan, for example, is 0).
- * @param int $type A number 0 or 1 and indicates when payments are due:
- * 0 or omitted At the end of the period.
- * 1 At the beginning of the period.
- * @param float $guess Your guess for what the rate will be.
- * If you omit guess, it is assumed to be 10 percent.
- *
- * @return float|string
- */
- public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
- {
- $nper = (int) Functions::flattenSingleValue($nper);
- $pmt = Functions::flattenSingleValue($pmt);
- $pv = Functions::flattenSingleValue($pv);
- $fv = ($fv === null) ? 0.0 : Functions::flattenSingleValue($fv);
- $type = ($type === null) ? 0 : (int) Functions::flattenSingleValue($type);
- $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
-
- $rate = $guess;
- // rest of code adapted from python/numpy
- $close = false;
- $iter = 0;
- while (!$close && $iter < self::FINANCIAL_MAX_ITERATIONS) {
- $nextdiff = self::rateNextGuess($rate, $nper, $pmt, $pv, $fv, $type);
- if (!is_numeric($nextdiff)) {
- break;
- }
- $rate1 = $rate - $nextdiff;
- $close = abs($rate1 - $rate) < self::FINANCIAL_PRECISION;
- ++$iter;
- $rate = $rate1;
- }
-
- return $close ? $rate : Functions::NAN();
- }
-
- private static function rateNextGuess($rate, $nper, $pmt, $pv, $fv, $type)
- {
- if ($rate == 0) {
- return Functions::NAN();
- }
- $tt1 = ($rate + 1) ** $nper;
- $tt2 = ($rate + 1) ** ($nper - 1);
- $numerator = $fv + $tt1 * $pv + $pmt * ($tt1 - 1) * ($rate * $type + 1) / $rate;
- $denominator = $nper * $tt2 * $pv - $pmt * ($tt1 - 1) * ($rate * $type + 1) / ($rate * $rate)
- + $nper * $pmt * $tt2 * ($rate * $type + 1) / $rate
- + $pmt * ($tt1 - 1) * $type / $rate;
- if ($denominator == 0) {
- return Functions::NAN();
- }
-
- return $numerator / $denominator;
- }
-
- /**
- * RECEIVED.
- *
- * 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 int $investment The amount invested in the security
- * @param int $discount The security's discount rate
- * @param int $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 = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $investment = (float) Functions::flattenSingleValue($investment);
- $discount = (float) Functions::flattenSingleValue($discount);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
- if (($investment <= 0) || ($discount <= 0)) {
- return Functions::NAN();
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
-
- return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
- }
-
- return Functions::VALUE();
- }
-
- /**
- * RRI.
- *
- * Calculates the interest rate required for an investment to grow to a specified future value .
- *
- * @param float $nper The number of periods over which the investment is made
- * @param float $pv Present Value
- * @param float $fv Future Value
- *
- * @return float|string Result, or a string containing an error
- */
- public static function RRI($nper = 0, $pv = 0, $fv = 0)
- {
- $nper = Functions::flattenSingleValue($nper);
- $pv = Functions::flattenSingleValue($pv);
- $fv = Functions::flattenSingleValue($fv);
-
- // Validate parameters
- if (!is_numeric($nper) || !is_numeric($pv) || !is_numeric($fv)) {
- return Functions::VALUE();
- } elseif ($nper <= 0.0 || $pv <= 0.0 || $fv < 0.0) {
- return Functions::NAN();
- }
-
- return ($fv / $pv) ** (1 / $nper) - 1;
- }
-
- /**
- * SLN.
- *
- * Returns the straight-line depreciation of an asset for one period
- *
- * @param mixed $cost Initial cost of the asset
- * @param mixed $salvage Value at the end of the depreciation
- * @param mixed $life Number of periods over which the asset is depreciated
- *
- * @return float|string Result, or a string containing an error
- */
- public static function SLN($cost, $salvage, $life)
- {
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
-
- // Calculate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
- if ($life < 0) {
- return Functions::NAN();
- }
-
- return ($cost - $salvage) / $life;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * SYD.
- *
- * Returns the sum-of-years' digits depreciation of an asset for a specified period.
- *
- * @param mixed $cost Initial cost of the asset
- * @param mixed $salvage Value at the end of the depreciation
- * @param mixed $life Number of periods over which the asset is depreciated
- * @param mixed $period Period
- *
- * @return float|string Result, or a string containing an error
- */
- public static function SYD($cost, $salvage, $life, $period)
- {
- $cost = Functions::flattenSingleValue($cost);
- $salvage = Functions::flattenSingleValue($salvage);
- $life = Functions::flattenSingleValue($life);
- $period = Functions::flattenSingleValue($period);
-
- // Calculate
- if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
- if (($life < 1) || ($period > $life)) {
- return Functions::NAN();
- }
-
- return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 int $discount The Treasury bill's discount rate
- *
- * @return float|string Result, or a string containing an error
- */
- public static function TBILLEQ($settlement, $maturity, $discount)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = Functions::flattenSingleValue($discount);
-
- // Use TBILLPRICE for validation
- $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
- if (is_string($testValue)) {
- return $testValue;
- }
-
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
- }
-
- /**
- * TBILLPRICE.
- *
- * 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 int $discount The Treasury bill's discount rate
- *
- * @return float|string Result, or a string containing an error
- */
- public static function TBILLPRICE($settlement, $maturity, $discount)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $discount = Functions::flattenSingleValue($discount);
-
- if (is_string($maturity = DateTime::getDateValue($maturity))) {
- return Functions::VALUE();
- }
-
- // Validate
- if (is_numeric($discount)) {
- if ($discount <= 0) {
- return Functions::NAN();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- if ($daysBetweenSettlementAndMaturity > 360) {
- return Functions::NAN();
- }
-
- $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
- if ($price <= 0) {
- return Functions::NAN();
- }
-
- return $price;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 int $price The Treasury bill's price per $100 face value
- *
- * @return float|mixed|string
- */
- public static function TBILLYIELD($settlement, $maturity, $price)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
-
- // Validate
- if (is_numeric($price)) {
- if ($price <= 0) {
- return Functions::NAN();
- }
-
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- ++$maturity;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- } else {
- $daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
- }
-
- if ($daysBetweenSettlementAndMaturity > 360) {
- return Functions::NAN();
- }
-
- return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
- }
-
- private static function bothNegAndPos($neg, $pos)
- {
- return $neg && $pos;
- }
-
- private static function xirrPart2(&$values)
- {
- $valCount = count($values);
- $foundpos = false;
- $foundneg = false;
- for ($i = 0; $i < $valCount; ++$i) {
- $fld = $values[$i];
- if (!is_numeric($fld)) {
- return Functions::VALUE();
- } elseif ($fld > 0) {
- $foundpos = true;
- } elseif ($fld < 0) {
- $foundneg = true;
- }
- }
- if (!self::bothNegAndPos($foundneg, $foundpos)) {
- return Functions::NAN();
- }
-
- return '';
- }
-
- private static function xirrPart1(&$values, &$dates)
- {
- if ((!is_array($values)) && (!is_array($dates))) {
- return Functions::NA();
- }
- $values = Functions::flattenArray($values);
- $dates = Functions::flattenArray($dates);
- if (count($values) != count($dates)) {
- return Functions::NAN();
- }
-
- $datesCount = count($dates);
- for ($i = 0; $i < $datesCount; ++$i) {
- $dates[$i] = DateTime::getDateValue($dates[$i]);
- if (!is_numeric($dates[$i])) {
- return Functions::VALUE();
- }
- }
-
- return self::xirrPart2($values);
- }
-
- private static function xirrPart3($values, $dates, $x1, $x2)
- {
- $f = self::xnpvOrdered($x1, $values, $dates, false);
- if ($f < 0.0) {
- $rtb = $x1;
- $dx = $x2 - $x1;
- } else {
- $rtb = $x2;
- $dx = $x1 - $x2;
- }
-
- $rslt = Functions::VALUE();
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- $dx *= 0.5;
- $x_mid = $rtb + $dx;
- $f_mid = self::xnpvOrdered($x_mid, $values, $dates, false);
- if ($f_mid <= 0.0) {
- $rtb = $x_mid;
- }
- if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
- $rslt = $x_mid;
-
- break;
- }
- }
-
- return $rslt;
- }
-
- /**
- * XIRR.
- *
- * Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic.
- *
- * Excel Function:
- * =XIRR(values,dates,guess)
- *
- * @param float[] $values A series of cash flow payments
- * The series of values must contain at least one positive value & one negative value
- * @param mixed[] $dates A series of payment dates
- * The first payment date indicates the beginning of the schedule of payments
- * All other dates must be later than this date, but they may occur in any order
- * @param float $guess An optional guess at the expected answer
- *
- * @return float|mixed|string
- */
- public static function XIRR($values, $dates, $guess = 0.1)
- {
- $rslt = self::xirrPart1($values, $dates);
- if ($rslt) {
- return $rslt;
- }
-
- // create an initial range, with a root somewhere between 0 and guess
- $guess = Functions::flattenSingleValue($guess);
- $x1 = 0.0;
- $x2 = $guess ? $guess : 0.1;
- $f1 = self::xnpvOrdered($x1, $values, $dates, false);
- $f2 = self::xnpvOrdered($x2, $values, $dates, false);
- $found = false;
- for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
- if (!is_numeric($f1) || !is_numeric($f2)) {
- break;
- }
- if (($f1 * $f2) < 0.0) {
- $found = true;
-
- break;
- } elseif (abs($f1) < abs($f2)) {
- $f1 = self::xnpvOrdered($x1 += 1.6 * ($x1 - $x2), $values, $dates, false);
- } else {
- $f2 = self::xnpvOrdered($x2 += 1.6 * ($x2 - $x1), $values, $dates, false);
- }
- }
- if (!$found) {
- return Functions::NAN();
- }
-
- return self::xirrPart3($values, $dates, $x1, $x2);
- }
-
- /**
- * XNPV.
- *
- * Returns the net present value for a schedule of cash flows that is not necessarily periodic.
- * To calculate the net present value for a series of cash flows that is periodic, use the NPV function.
- *
- * Excel Function:
- * =XNPV(rate,values,dates)
- *
- * @param float $rate the discount rate to apply to the cash flows
- * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
- * The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
- * If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
- * The series of values must contain at least one positive value and one negative value.
- * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
- * The first payment date indicates the beginning of the schedule of payments.
- * All other dates must be later than this date, but they may occur in any order.
- *
- * @return float|mixed|string
- */
- public static function XNPV($rate, $values, $dates)
- {
- return self::xnpvOrdered($rate, $values, $dates, true);
- }
-
- private static function validateXnpv($rate, $values, $dates)
- {
- if (!is_numeric($rate)) {
- return Functions::VALUE();
- }
- $valCount = count($values);
- if ($valCount != count($dates)) {
- return Functions::NAN();
- }
- if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
- return Functions::NAN();
- }
- $date0 = DateTime::getDateValue($dates[0]);
- if (is_string($date0)) {
- return Functions::VALUE();
- }
-
- return '';
- }
-
- private static function xnpvOrdered($rate, $values, $dates, $ordered = true)
- {
- $rate = Functions::flattenSingleValue($rate);
- $values = Functions::flattenArray($values);
- $dates = Functions::flattenArray($dates);
- $valCount = count($values);
- $date0 = DateTime::getDateValue($dates[0]);
- $rslt = self::validateXnpv($rate, $values, $dates);
- if ($rslt) {
- return $rslt;
- }
- $xnpv = 0.0;
- for ($i = 0; $i < $valCount; ++$i) {
- if (!is_numeric($values[$i])) {
- return Functions::VALUE();
- }
- $datei = DateTime::getDateValue($dates[$i]);
- if (is_string($datei)) {
- return Functions::VALUE();
- }
- if ($date0 > $datei) {
- $dif = $ordered ? Functions::NAN() : -DateTime::DATEDIF($datei, $date0, 'd');
- } else {
- $dif = DateTime::DATEDIF($date0, $datei, 'd');
- }
- if (!is_numeric($dif)) {
- return $dif;
- }
- $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
- }
-
- return is_finite($xnpv) ? $xnpv : Functions::VALUE();
- }
-
- /**
- * 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 int $price The security's price per $100 face value
- * @param int $redemption The security's redemption value per $100 face value
- * @param int $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 YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $price = Functions::flattenSingleValue($price);
- $redemption = Functions::flattenSingleValue($redemption);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($price) && is_numeric($redemption)) {
- if (($price <= 0) || ($redemption <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
- if (!is_numeric($daysBetweenSettlementAndMaturity)) {
- // return date error
- return $daysBetweenSettlementAndMaturity;
- }
- $daysBetweenSettlementAndMaturity *= $daysPerYear;
-
- return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 int $rate The security's interest rate at date of issue
- * @param int $price The security's price per $100 face value
- * @param int $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 YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
- {
- $settlement = Functions::flattenSingleValue($settlement);
- $maturity = Functions::flattenSingleValue($maturity);
- $issue = Functions::flattenSingleValue($issue);
- $rate = Functions::flattenSingleValue($rate);
- $price = Functions::flattenSingleValue($price);
- $basis = (int) Functions::flattenSingleValue($basis);
-
- // Validate
- if (is_numeric($rate) && is_numeric($price)) {
- if (($rate <= 0) || ($price <= 0)) {
- return Functions::NAN();
- }
- $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
- if (!is_numeric($daysPerYear)) {
- return $daysPerYear;
- }
- $daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
- if (!is_numeric($daysBetweenIssueAndSettlement)) {
- // return date error
- return $daysBetweenIssueAndSettlement;
- }
- $daysBetweenIssueAndSettlement *= $daysPerYear;
- $daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
- if (!is_numeric($daysBetweenIssueAndMaturity)) {
- // return date error
- return $daysBetweenIssueAndMaturity;
- }
- $daysBetweenIssueAndMaturity *= $daysPerYear;
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($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);
- }
-
- return Functions::VALUE();
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/FormulaParser.php b/vendor/PhpSpreadsheet/Calculation/FormulaParser.php
deleted file mode 100644
index c11af83..0000000
--- a/vendor/PhpSpreadsheet/Calculation/FormulaParser.php
+++ /dev/null
@@ -1,631 +0,0 @@
-<';
- const OPERATORS_POSTFIX = '%';
-
- /**
- * Formula.
- *
- * @var string
- */
- private $formula;
-
- /**
- * Tokens.
- *
- * @var FormulaToken[]
- */
- private $tokens = [];
-
- /**
- * Create a new FormulaParser.
- *
- * @param string $pFormula Formula to parse
- */
- public function __construct($pFormula = '')
- {
- // Check parameters
- if ($pFormula === null) {
- throw new Exception('Invalid parameter passed: formula');
- }
-
- // Initialise values
- $this->formula = trim($pFormula);
- // Parse!
- $this->parseToTokens();
- }
-
- /**
- * Get Formula.
- *
- * @return string
- */
- public function getFormula()
- {
- return $this->formula;
- }
-
- /**
- * Get Token.
- *
- * @param int $pId Token id
- *
- * @return string
- */
- public function getToken($pId = 0)
- {
- if (isset($this->tokens[$pId])) {
- return $this->tokens[$pId];
- }
-
- throw new Exception("Token with id $pId does not exist.");
- }
-
- /**
- * Get Token count.
- *
- * @return int
- */
- public function getTokenCount()
- {
- return count($this->tokens);
- }
-
- /**
- * Get Tokens.
- *
- * @return FormulaToken[]
- */
- public function getTokens()
- {
- return $this->tokens;
- }
-
- /**
- * Parse to tokens.
- */
- private function parseToTokens(): void
- {
- // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
- // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
-
- // Check if the formula has a valid starting =
- $formulaLength = strlen($this->formula);
- if ($formulaLength < 2 || $this->formula[0] != '=') {
- return;
- }
-
- // Helper variables
- $tokens1 = $tokens2 = $stack = [];
- $inString = $inPath = $inRange = $inError = false;
- $token = $previousToken = $nextToken = null;
-
- $index = 1;
- $value = '';
-
- $ERRORS = ['#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', '#N/A'];
- $COMPARATORS_MULTI = ['>=', '<=', '<>'];
-
- while ($index < $formulaLength) {
- // state-dependent character evaluation (order is important)
-
- // double-quoted strings
- // embeds are doubled
- // end marks token
- if ($inString) {
- if ($this->formula[$index] == self::QUOTE_DOUBLE) {
- if ((($index + 2) <= $formulaLength) && ($this->formula[$index + 1] == self::QUOTE_DOUBLE)) {
- $value .= self::QUOTE_DOUBLE;
- ++$index;
- } else {
- $inString = false;
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_TEXT);
- $value = '';
- }
- } else {
- $value .= $this->formula[$index];
- }
- ++$index;
-
- continue;
- }
-
- // single-quoted strings (links)
- // embeds are double
- // end does not mark a token
- if ($inPath) {
- if ($this->formula[$index] == self::QUOTE_SINGLE) {
- if ((($index + 2) <= $formulaLength) && ($this->formula[$index + 1] == self::QUOTE_SINGLE)) {
- $value .= self::QUOTE_SINGLE;
- ++$index;
- } else {
- $inPath = false;
- }
- } else {
- $value .= $this->formula[$index];
- }
- ++$index;
-
- continue;
- }
-
- // bracked strings (R1C1 range index or linked workbook name)
- // no embeds (changed to "()" by Excel)
- // end does not mark a token
- if ($inRange) {
- if ($this->formula[$index] == self::BRACKET_CLOSE) {
- $inRange = false;
- }
- $value .= $this->formula[$index];
- ++$index;
-
- continue;
- }
-
- // error values
- // end marks a token, determined from absolute list of values
- if ($inError) {
- $value .= $this->formula[$index];
- ++$index;
- if (in_array($value, $ERRORS)) {
- $inError = false;
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_ERROR);
- $value = '';
- }
-
- continue;
- }
-
- // scientific notation check
- if (strpos(self::OPERATORS_SN, $this->formula[$index]) !== false) {
- if (strlen($value) > 1) {
- if (preg_match('/^[1-9]{1}(\\.\\d+)?E{1}$/', $this->formula[$index]) != 0) {
- $value .= $this->formula[$index];
- ++$index;
-
- continue;
- }
- }
- }
-
- // independent character evaluation (order not important)
-
- // establish state-dependent character evaluations
- if ($this->formula[$index] == self::QUOTE_DOUBLE) {
- if (strlen($value) > 0) {
- // unexpected
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
- $value = '';
- }
- $inString = true;
- ++$index;
-
- continue;
- }
-
- if ($this->formula[$index] == self::QUOTE_SINGLE) {
- if (strlen($value) > 0) {
- // unexpected
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
- $value = '';
- }
- $inPath = true;
- ++$index;
-
- continue;
- }
-
- if ($this->formula[$index] == self::BRACKET_OPEN) {
- $inRange = true;
- $value .= self::BRACKET_OPEN;
- ++$index;
-
- continue;
- }
-
- if ($this->formula[$index] == self::ERROR_START) {
- if (strlen($value) > 0) {
- // unexpected
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
- $value = '';
- }
- $inError = true;
- $value .= self::ERROR_START;
- ++$index;
-
- continue;
- }
-
- // mark start and end of arrays and array rows
- if ($this->formula[$index] == self::BRACE_OPEN) {
- if (strlen($value) > 0) {
- // unexpected
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
- $value = '';
- }
-
- $tmp = new FormulaToken('ARRAY', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
- $tokens1[] = $tmp;
- $stack[] = clone $tmp;
-
- $tmp = new FormulaToken('ARRAYROW', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
- $tokens1[] = $tmp;
- $stack[] = clone $tmp;
-
- ++$index;
-
- continue;
- }
-
- if ($this->formula[$index] == self::SEMICOLON) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
-
- $tmp = array_pop($stack);
- $tmp->setValue('');
- $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
- $tokens1[] = $tmp;
-
- $tmp = new FormulaToken(',', FormulaToken::TOKEN_TYPE_ARGUMENT);
- $tokens1[] = $tmp;
-
- $tmp = new FormulaToken('ARRAYROW', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
- $tokens1[] = $tmp;
- $stack[] = clone $tmp;
-
- ++$index;
-
- continue;
- }
-
- if ($this->formula[$index] == self::BRACE_CLOSE) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
-
- $tmp = array_pop($stack);
- $tmp->setValue('');
- $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
- $tokens1[] = $tmp;
-
- $tmp = array_pop($stack);
- $tmp->setValue('');
- $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
- $tokens1[] = $tmp;
-
- ++$index;
-
- continue;
- }
-
- // trim white-space
- if ($this->formula[$index] == self::WHITESPACE) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
- $tokens1[] = new FormulaToken('', FormulaToken::TOKEN_TYPE_WHITESPACE);
- ++$index;
- while (($this->formula[$index] == self::WHITESPACE) && ($index < $formulaLength)) {
- ++$index;
- }
-
- continue;
- }
-
- // multi-character comparators
- if (($index + 2) <= $formulaLength) {
- if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
- $tokens1[] = new FormulaToken(substr($this->formula, $index, 2), FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_LOGICAL);
- $index += 2;
-
- continue;
- }
- }
-
- // standard infix operators
- if (strpos(self::OPERATORS_INFIX, $this->formula[$index]) !== false) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
- $tokens1[] = new FormulaToken($this->formula[$index], FormulaToken::TOKEN_TYPE_OPERATORINFIX);
- ++$index;
-
- continue;
- }
-
- // standard postfix operators (only one)
- if (strpos(self::OPERATORS_POSTFIX, $this->formula[$index]) !== false) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
- $tokens1[] = new FormulaToken($this->formula[$index], FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
- ++$index;
-
- continue;
- }
-
- // start subexpression or function
- if ($this->formula[$index] == self::PAREN_OPEN) {
- if (strlen($value) > 0) {
- $tmp = new FormulaToken($value, FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
- $tokens1[] = $tmp;
- $stack[] = clone $tmp;
- $value = '';
- } else {
- $tmp = new FormulaToken('', FormulaToken::TOKEN_TYPE_SUBEXPRESSION, FormulaToken::TOKEN_SUBTYPE_START);
- $tokens1[] = $tmp;
- $stack[] = clone $tmp;
- }
- ++$index;
-
- continue;
- }
-
- // function, subexpression, or array parameters, or operand unions
- if ($this->formula[$index] == self::COMMA) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
-
- $tmp = array_pop($stack);
- $tmp->setValue('');
- $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
- $stack[] = $tmp;
-
- if ($tmp->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
- $tokens1[] = new FormulaToken(',', FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_UNION);
- } else {
- $tokens1[] = new FormulaToken(',', FormulaToken::TOKEN_TYPE_ARGUMENT);
- }
- ++$index;
-
- continue;
- }
-
- // stop subexpression
- if ($this->formula[$index] == self::PAREN_CLOSE) {
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- $value = '';
- }
-
- $tmp = array_pop($stack);
- $tmp->setValue('');
- $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
- $tokens1[] = $tmp;
-
- ++$index;
-
- continue;
- }
-
- // token accumulation
- $value .= $this->formula[$index];
- ++$index;
- }
-
- // dump remaining accumulation
- if (strlen($value) > 0) {
- $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
- }
-
- // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
- $tokenCount = count($tokens1);
- for ($i = 0; $i < $tokenCount; ++$i) {
- $token = $tokens1[$i];
- if (isset($tokens1[$i - 1])) {
- $previousToken = $tokens1[$i - 1];
- } else {
- $previousToken = null;
- }
- if (isset($tokens1[$i + 1])) {
- $nextToken = $tokens1[$i + 1];
- } else {
- $nextToken = null;
- }
-
- if ($token === null) {
- continue;
- }
-
- if ($token->getTokenType() != FormulaToken::TOKEN_TYPE_WHITESPACE) {
- $tokens2[] = $token;
-
- continue;
- }
-
- if ($previousToken === null) {
- continue;
- }
-
- if (
- !(
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
- )
- ) {
- continue;
- }
-
- if ($nextToken === null) {
- continue;
- }
-
- if (
- !(
- (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
- (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
- ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
- )
- ) {
- continue;
- }
-
- $tokens2[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
- }
-
- // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
- // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
- $this->tokens = [];
-
- $tokenCount = count($tokens2);
- for ($i = 0; $i < $tokenCount; ++$i) {
- $token = $tokens2[$i];
- if (isset($tokens2[$i - 1])) {
- $previousToken = $tokens2[$i - 1];
- } else {
- $previousToken = null;
- }
- if (isset($tokens2[$i + 1])) {
- $nextToken = $tokens2[$i + 1];
- } else {
- $nextToken = null;
- }
-
- if ($token === null) {
- continue;
- }
-
- if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') {
- if ($i == 0) {
- $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
- } elseif (
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
- ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
- ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
- ) {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
- } else {
- $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
- }
-
- $this->tokens[] = $token;
-
- continue;
- }
-
- if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '+') {
- if ($i == 0) {
- continue;
- } elseif (
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
- ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
- (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
- ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
- ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
- ) {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
- } else {
- continue;
- }
-
- $this->tokens[] = $token;
-
- continue;
- }
-
- if (
- $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
- $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
- ) {
- if (strpos('<>=', substr($token->getValue(), 0, 1)) !== false) {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
- } elseif ($token->getValue() == '&') {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
- } else {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
- }
-
- $this->tokens[] = $token;
-
- continue;
- }
-
- if (
- $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
- $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
- ) {
- if (!is_numeric($token->getValue())) {
- if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
- } else {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_RANGE);
- }
- } else {
- $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_NUMBER);
- }
-
- $this->tokens[] = $token;
-
- continue;
- }
-
- if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
- if (strlen($token->getValue()) > 0) {
- if (substr($token->getValue(), 0, 1) == '@') {
- $token->setValue(substr($token->getValue(), 1));
- }
- }
- }
-
- $this->tokens[] = $token;
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/FormulaToken.php b/vendor/PhpSpreadsheet/Calculation/FormulaToken.php
deleted file mode 100644
index 4d225de..0000000
--- a/vendor/PhpSpreadsheet/Calculation/FormulaToken.php
+++ /dev/null
@@ -1,150 +0,0 @@
-value = $pValue;
- $this->tokenType = $pTokenType;
- $this->tokenSubType = $pTokenSubType;
- }
-
- /**
- * Get Value.
- *
- * @return string
- */
- public function getValue()
- {
- return $this->value;
- }
-
- /**
- * Set Value.
- *
- * @param string $value
- */
- public function setValue($value): void
- {
- $this->value = $value;
- }
-
- /**
- * Get Token Type (represented by TOKEN_TYPE_*).
- *
- * @return string
- */
- public function getTokenType()
- {
- return $this->tokenType;
- }
-
- /**
- * Set Token Type (represented by TOKEN_TYPE_*).
- *
- * @param string $value
- */
- public function setTokenType($value): void
- {
- $this->tokenType = $value;
- }
-
- /**
- * Get Token SubType (represented by TOKEN_SUBTYPE_*).
- *
- * @return string
- */
- public function getTokenSubType()
- {
- return $this->tokenSubType;
- }
-
- /**
- * Set Token SubType (represented by TOKEN_SUBTYPE_*).
- *
- * @param string $value
- */
- public function setTokenSubType($value): void
- {
- $this->tokenSubType = $value;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Functions.php b/vendor/PhpSpreadsheet/Calculation/Functions.php
deleted file mode 100644
index 2e8a7ec..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Functions.php
+++ /dev/null
@@ -1,658 +0,0 @@
- '#NULL!',
- 'divisionbyzero' => '#DIV/0!',
- 'value' => '#VALUE!',
- 'reference' => '#REF!',
- 'name' => '#NAME?',
- 'num' => '#NUM!',
- 'na' => '#N/A',
- 'gettingdata' => '#GETTING_DATA',
- ];
-
- /**
- * Set the Compatibility Mode.
- *
- * @param string $compatibilityMode Compatibility Mode
- * Permitted values are:
- * Functions::COMPATIBILITY_EXCEL 'Excel'
- * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
- * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
- *
- * @return bool (Success or Failure)
- */
- public static function setCompatibilityMode($compatibilityMode)
- {
- if (
- ($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
- ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
- ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
- ) {
- self::$compatibilityMode = $compatibilityMode;
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Return the current Compatibility Mode.
- *
- * @return string Compatibility Mode
- * Possible Return values are:
- * Functions::COMPATIBILITY_EXCEL 'Excel'
- * Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
- * Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
- */
- public static function getCompatibilityMode()
- {
- return self::$compatibilityMode;
- }
-
- /**
- * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
- *
- * @param string $returnDateType Return Date Format
- * Permitted values are:
- * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
- * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
- * Functions::RETURNDATE_EXCEL 'E'
- *
- * @return bool Success or failure
- */
- public static function setReturnDateType($returnDateType)
- {
- if (
- ($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
- ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
- ($returnDateType == self::RETURNDATE_EXCEL)
- ) {
- self::$returnDateType = $returnDateType;
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
- *
- * @return string Return Date Format
- * Possible Return values are:
- * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
- * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
- * Functions::RETURNDATE_EXCEL 'E'
- */
- public static function getReturnDateType()
- {
- return self::$returnDateType;
- }
-
- /**
- * DUMMY.
- *
- * @return string #Not Yet Implemented
- */
- public static function DUMMY()
- {
- return '#Not Yet Implemented';
- }
-
- /**
- * DIV0.
- *
- * @return string #Not Yet Implemented
- */
- public static function DIV0()
- {
- return self::$errorCodes['divisionbyzero'];
- }
-
- /**
- * NA.
- *
- * Excel Function:
- * =NA()
- *
- * Returns the error value #N/A
- * #N/A is the error value that means "no value is available."
- *
- * @return string #N/A!
- */
- public static function NA()
- {
- return self::$errorCodes['na'];
- }
-
- /**
- * NaN.
- *
- * Returns the error value #NUM!
- *
- * @return string #NUM!
- */
- public static function NAN()
- {
- return self::$errorCodes['num'];
- }
-
- /**
- * NAME.
- *
- * Returns the error value #NAME?
- *
- * @return string #NAME?
- */
- public static function NAME()
- {
- return self::$errorCodes['name'];
- }
-
- /**
- * REF.
- *
- * Returns the error value #REF!
- *
- * @return string #REF!
- */
- public static function REF()
- {
- return self::$errorCodes['reference'];
- }
-
- /**
- * NULL.
- *
- * Returns the error value #NULL!
- *
- * @return string #NULL!
- */
- public static function null()
- {
- return self::$errorCodes['null'];
- }
-
- /**
- * VALUE.
- *
- * Returns the error value #VALUE!
- *
- * @return string #VALUE!
- */
- public static function VALUE()
- {
- return self::$errorCodes['value'];
- }
-
- public static function isMatrixValue($idx)
- {
- return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
- }
-
- public static function isValue($idx)
- {
- return substr_count($idx, '.') == 0;
- }
-
- public static function isCellValue($idx)
- {
- return substr_count($idx, '.') > 1;
- }
-
- public static function ifCondition($condition)
- {
- $condition = self::flattenSingleValue($condition);
-
- if ($condition === '') {
- $condition = '=""';
- }
-
- if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
- if (!is_numeric($condition)) {
- $condition = Calculation::wrapResult(strtoupper($condition));
- }
-
- return str_replace('""""', '""', '=' . $condition);
- }
- preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
- [, $operator, $operand] = $matches;
-
- if (is_numeric(trim($operand, '"'))) {
- $operand = trim($operand, '"');
- } elseif (!is_numeric($operand)) {
- $operand = str_replace('"', '""', $operand);
- $operand = Calculation::wrapResult(strtoupper($operand));
- }
-
- return str_replace('""""', '""', $operator . $operand);
- }
-
- /**
- * ERROR_TYPE.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function errorType($value = '')
- {
- $value = self::flattenSingleValue($value);
-
- $i = 1;
- foreach (self::$errorCodes as $errorCode) {
- if ($value === $errorCode) {
- return $i;
- }
- ++$i;
- }
-
- return self::NA();
- }
-
- /**
- * IS_BLANK.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isBlank($value = null)
- {
- if ($value !== null) {
- $value = self::flattenSingleValue($value);
- }
-
- return $value === null;
- }
-
- /**
- * IS_ERR.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isErr($value = '')
- {
- $value = self::flattenSingleValue($value);
-
- return self::isError($value) && (!self::isNa(($value)));
- }
-
- /**
- * IS_ERROR.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isError($value = '')
- {
- $value = self::flattenSingleValue($value);
-
- if (!is_string($value)) {
- return false;
- }
-
- return in_array($value, self::$errorCodes);
- }
-
- /**
- * IS_NA.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isNa($value = '')
- {
- $value = self::flattenSingleValue($value);
-
- return $value === self::NA();
- }
-
- /**
- * IS_EVEN.
- *
- * @param mixed $value Value to check
- *
- * @return bool|string
- */
- public static function isEven($value = null)
- {
- $value = self::flattenSingleValue($value);
-
- if ($value === null) {
- return self::NAME();
- } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
- return self::VALUE();
- }
-
- return $value % 2 == 0;
- }
-
- /**
- * IS_ODD.
- *
- * @param mixed $value Value to check
- *
- * @return bool|string
- */
- public static function isOdd($value = null)
- {
- $value = self::flattenSingleValue($value);
-
- if ($value === null) {
- return self::NAME();
- } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
- return self::VALUE();
- }
-
- return abs($value) % 2 == 1;
- }
-
- /**
- * IS_NUMBER.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isNumber($value = null)
- {
- $value = self::flattenSingleValue($value);
-
- if (is_string($value)) {
- return false;
- }
-
- return is_numeric($value);
- }
-
- /**
- * IS_LOGICAL.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isLogical($value = null)
- {
- $value = self::flattenSingleValue($value);
-
- return is_bool($value);
- }
-
- /**
- * IS_TEXT.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isText($value = null)
- {
- $value = self::flattenSingleValue($value);
-
- return is_string($value) && !self::isError($value);
- }
-
- /**
- * IS_NONTEXT.
- *
- * @param mixed $value Value to check
- *
- * @return bool
- */
- public static function isNonText($value = null)
- {
- return !self::isText($value);
- }
-
- /**
- * N.
- *
- * Returns a value converted to a number
- *
- * @param null|mixed $value The value you want converted
- *
- * @return number N converts values listed in the following table
- * If value is or refers to N returns
- * A number That number
- * A date The serial number of that date
- * TRUE 1
- * FALSE 0
- * An error value The error value
- * Anything else 0
- */
- public static function n($value = null)
- {
- while (is_array($value)) {
- $value = array_shift($value);
- }
-
- switch (gettype($value)) {
- case 'double':
- case 'float':
- case 'integer':
- return $value;
- case 'boolean':
- return (int) $value;
- case 'string':
- // Errors
- if ((strlen($value) > 0) && ($value[0] == '#')) {
- return $value;
- }
-
- break;
- }
-
- return 0;
- }
-
- /**
- * TYPE.
- *
- * Returns a number that identifies the type of a value
- *
- * @param null|mixed $value The value you want tested
- *
- * @return number N converts values listed in the following table
- * If value is or refers to N returns
- * A number 1
- * Text 2
- * Logical Value 4
- * An error value 16
- * Array or Matrix 64
- */
- public static function TYPE($value = null)
- {
- $value = self::flattenArrayIndexed($value);
- if (is_array($value) && (count($value) > 1)) {
- end($value);
- $a = key($value);
- // Range of cells is an error
- if (self::isCellValue($a)) {
- return 16;
- // Test for Matrix
- } elseif (self::isMatrixValue($a)) {
- return 64;
- }
- } elseif (empty($value)) {
- // Empty Cell
- return 1;
- }
- $value = self::flattenSingleValue($value);
-
- if (($value === null) || (is_float($value)) || (is_int($value))) {
- return 1;
- } elseif (is_bool($value)) {
- return 4;
- } elseif (is_array($value)) {
- return 64;
- } elseif (is_string($value)) {
- // Errors
- if ((strlen($value) > 0) && ($value[0] == '#')) {
- return 16;
- }
-
- return 2;
- }
-
- return 0;
- }
-
- /**
- * Convert a multi-dimensional array to a simple 1-dimensional array.
- *
- * @param array $array Array to be flattened
- *
- * @return array Flattened array
- */
- public static function flattenArray($array)
- {
- if (!is_array($array)) {
- return (array) $array;
- }
-
- $arrayValues = [];
- foreach ($array as $value) {
- if (is_array($value)) {
- foreach ($value as $val) {
- if (is_array($val)) {
- foreach ($val as $v) {
- $arrayValues[] = $v;
- }
- } else {
- $arrayValues[] = $val;
- }
- }
- } else {
- $arrayValues[] = $value;
- }
- }
-
- return $arrayValues;
- }
-
- /**
- * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
- *
- * @param array $array Array to be flattened
- *
- * @return array Flattened array
- */
- public static function flattenArrayIndexed($array)
- {
- if (!is_array($array)) {
- return (array) $array;
- }
-
- $arrayValues = [];
- foreach ($array as $k1 => $value) {
- if (is_array($value)) {
- foreach ($value as $k2 => $val) {
- if (is_array($val)) {
- foreach ($val as $k3 => $v) {
- $arrayValues[$k1 . '.' . $k2 . '.' . $k3] = $v;
- }
- } else {
- $arrayValues[$k1 . '.' . $k2] = $val;
- }
- }
- } else {
- $arrayValues[$k1] = $value;
- }
- }
-
- return $arrayValues;
- }
-
- /**
- * Convert an array to a single scalar value by extracting the first element.
- *
- * @param mixed $value Array or scalar value
- *
- * @return mixed
- */
- public static function flattenSingleValue($value = '')
- {
- while (is_array($value)) {
- $value = array_shift($value);
- }
-
- return $value;
- }
-
- /**
- * ISFORMULA.
- *
- * @param mixed $cellReference The cell to check
- * @param Cell $pCell The current cell (containing this formula)
- *
- * @return bool|string
- */
- public static function isFormula($cellReference = '', ?Cell $pCell = null)
- {
- if ($pCell === null) {
- return self::REF();
- }
-
- preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
-
- $cellReference = $matches[6] . $matches[7];
- $worksheetName = str_replace("''", "'", trim($matches[2], "'"));
-
- $worksheet = (!empty($worksheetName))
- ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
- : $pCell->getWorksheet();
-
- return $worksheet->getCell($cellReference)->isFormula();
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Logical.php b/vendor/PhpSpreadsheet/Calculation/Logical.php
deleted file mode 100644
index 69c543c..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Logical.php
+++ /dev/null
@@ -1,390 +0,0 @@
- 0) && ($returnValue == $argCount);
- }
-
- /**
- * LOGICAL_OR.
- *
- * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
- *
- * Excel Function:
- * =OR(logical1[,logical2[, ...]])
- *
- * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
- * or references that contain logical values.
- *
- * Boolean arguments are treated as True or False as appropriate
- * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
- * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
- * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
- *
- * @param mixed $args Data values
- *
- * @return bool|string the logical OR of the arguments
- */
- public static function logicalOr(...$args)
- {
- $args = Functions::flattenArray($args);
-
- if (count($args) == 0) {
- return Functions::VALUE();
- }
-
- $args = array_filter($args, function ($value) {
- return $value !== null || (is_string($value) && trim($value) == '');
- });
-
- $returnValue = self::countTrueValues($args);
- if (is_string($returnValue)) {
- return $returnValue;
- }
-
- return $returnValue > 0;
- }
-
- /**
- * LOGICAL_XOR.
- *
- * Returns the Exclusive Or logical operation for one or more supplied conditions.
- * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE, and FALSE otherwise.
- *
- * Excel Function:
- * =XOR(logical1[,logical2[, ...]])
- *
- * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
- * or references that contain logical values.
- *
- * Boolean arguments are treated as True or False as appropriate
- * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
- * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
- * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
- *
- * @param mixed $args Data values
- *
- * @return bool|string the logical XOR of the arguments
- */
- public static function logicalXor(...$args)
- {
- $args = Functions::flattenArray($args);
-
- if (count($args) == 0) {
- return Functions::VALUE();
- }
-
- $args = array_filter($args, function ($value) {
- return $value !== null || (is_string($value) && trim($value) == '');
- });
-
- $returnValue = self::countTrueValues($args);
- if (is_string($returnValue)) {
- return $returnValue;
- }
-
- return $returnValue % 2 == 1;
- }
-
- /**
- * NOT.
- *
- * Returns the boolean inverse of the argument.
- *
- * Excel Function:
- * =NOT(logical)
- *
- * The argument must evaluate to a logical value such as TRUE or FALSE
- *
- * Boolean arguments are treated as True or False as appropriate
- * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
- * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
- * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
- *
- * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
- *
- * @return bool|string the boolean inverse of the argument
- */
- public static function NOT($logical = false)
- {
- $logical = Functions::flattenSingleValue($logical);
-
- if (is_string($logical)) {
- $logical = strtoupper($logical);
- if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
- return false;
- } elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
- return true;
- }
-
- return Functions::VALUE();
- }
-
- return !$logical;
- }
-
- /**
- * STATEMENT_IF.
- *
- * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
- *
- * Excel Function:
- * =IF(condition[,returnIfTrue[,returnIfFalse]])
- *
- * Condition is any value or expression that can be evaluated to TRUE or FALSE.
- * For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
- * the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
- * This argument can use any comparison calculation operator.
- * ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
- * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
- * then the IF function returns the text "Within budget"
- * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
- * the logical value TRUE for this argument.
- * ReturnIfTrue can be another formula.
- * ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
- * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
- * then the IF function returns the text "Over budget".
- * If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
- * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
- * ReturnIfFalse can be another formula.
- *
- * @param mixed $condition Condition to evaluate
- * @param mixed $returnIfTrue Value to return when condition is true
- * @param mixed $returnIfFalse Optional value to return when condition is false
- *
- * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
- */
- public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
- {
- if (Functions::isError($condition)) {
- return $condition;
- }
-
- $condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition);
- $returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue);
- $returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse);
-
- return ($condition) ? $returnIfTrue : $returnIfFalse;
- }
-
- /**
- * STATEMENT_SWITCH.
- *
- * Returns corresponding with first match (any data type such as a string, numeric, date, etc).
- *
- * Excel Function:
- * =SWITCH (expression, value1, result1, value2, result2, ... value_n, result_n [, default])
- *
- * Expression
- * The expression to compare to a list of values.
- * value1, value2, ... value_n
- * A list of values that are compared to expression. The SWITCH function is looking for the first value that matches the expression.
- * result1, result2, ... result_n
- * A list of results. The SWITCH function returns the corresponding result when a value matches expression.
- * default
- * Optional. It is the default to return if expression does not match any of the values (value1, value2, ... value_n).
- *
- * @param mixed $arguments Statement arguments
- *
- * @return mixed The value of matched expression
- */
- public static function statementSwitch(...$arguments)
- {
- $result = Functions::VALUE();
-
- if (count($arguments) > 0) {
- $targetValue = Functions::flattenSingleValue($arguments[0]);
- $argc = count($arguments) - 1;
- $switchCount = floor($argc / 2);
- $switchSatisfied = false;
- $hasDefaultClause = $argc % 2 !== 0;
- $defaultClause = $argc % 2 === 0 ? null : $arguments[count($arguments) - 1];
-
- if ($switchCount) {
- for ($index = 0; $index < $switchCount; ++$index) {
- if ($targetValue == $arguments[$index * 2 + 1]) {
- $result = $arguments[$index * 2 + 2];
- $switchSatisfied = true;
-
- break;
- }
- }
- }
-
- if (!$switchSatisfied) {
- $result = $hasDefaultClause ? $defaultClause : Functions::NA();
- }
- }
-
- return $result;
- }
-
- /**
- * IFERROR.
- *
- * Excel Function:
- * =IFERROR(testValue,errorpart)
- *
- * @param mixed $testValue Value to check, is also the value returned when no error
- * @param mixed $errorpart Value to return when testValue is an error condition
- *
- * @return mixed The value of errorpart or testValue determined by error condition
- */
- public static function IFERROR($testValue = '', $errorpart = '')
- {
- $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
- $errorpart = ($errorpart === null) ? '' : Functions::flattenSingleValue($errorpart);
-
- return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
- }
-
- /**
- * IFNA.
- *
- * Excel Function:
- * =IFNA(testValue,napart)
- *
- * @param mixed $testValue Value to check, is also the value returned when not an NA
- * @param mixed $napart Value to return when testValue is an NA condition
- *
- * @return mixed The value of errorpart or testValue determined by error condition
- */
- public static function IFNA($testValue = '', $napart = '')
- {
- $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
- $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
-
- return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
- }
-
- /**
- * IFS.
- *
- * Excel Function:
- * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
- *
- * testValue1 ... testValue_n
- * Conditions to Evaluate
- * returnIfTrue1 ... returnIfTrue_n
- * Value returned if corresponding testValue (nth) was true
- *
- * @param mixed ...$arguments Statement arguments
- *
- * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
- */
- public static function IFS(...$arguments)
- {
- if (count($arguments) % 2 != 0) {
- return Functions::NA();
- }
- // We use instance of Exception as a falseValue in order to prevent string collision with value in cell
- $falseValueException = new Exception();
- for ($i = 0; $i < count($arguments); $i += 2) {
- $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
- $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]);
- $result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
-
- if ($result !== $falseValueException) {
- return $result;
- }
- }
-
- return Functions::NA();
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/LookupRef.php b/vendor/PhpSpreadsheet/Calculation/LookupRef.php
deleted file mode 100644
index 45aa923..0000000
--- a/vendor/PhpSpreadsheet/Calculation/LookupRef.php
+++ /dev/null
@@ -1,968 +0,0 @@
- '') {
- if (strpos($sheetText, ' ') !== false) {
- $sheetText = "'" . $sheetText . "'";
- }
- $sheetText .= '!';
- }
- if ((!is_bool($referenceStyle)) || $referenceStyle) {
- $rowRelative = $columnRelative = '$';
- $column = Coordinate::stringFromColumnIndex($column);
- if (($relativity == 2) || ($relativity == 4)) {
- $columnRelative = '';
- }
- if (($relativity == 3) || ($relativity == 4)) {
- $rowRelative = '';
- }
-
- return $sheetText . $columnRelative . $column . $rowRelative . $row;
- }
- if (($relativity == 2) || ($relativity == 4)) {
- $column = '[' . $column . ']';
- }
- if (($relativity == 3) || ($relativity == 4)) {
- $row = '[' . $row . ']';
- }
-
- return $sheetText . 'R' . $row . 'C' . $column;
- }
-
- /**
- * 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 0.
- *
- * 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)
- {
- if ($cellAddress === null || trim($cellAddress) === '') {
- return 0;
- }
-
- if (is_array($cellAddress)) {
- foreach ($cellAddress as $columnKey => $value) {
- $columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
-
- return (int) Coordinate::columnIndexFromString($columnKey);
- }
- } else {
- [$sheet, $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);
- $returnValue = [];
- do {
- $returnValue[] = (int) Coordinate::columnIndexFromString($startAddress);
- } while ($startAddress++ != $endAddress);
-
- return $returnValue;
- }
- $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 ($cellAddress === null || $cellAddress === '') {
- return 1;
- } elseif (!is_array($cellAddress)) {
- return Functions::VALUE();
- }
-
- reset($cellAddress);
- $isMatrix = (is_numeric(key($cellAddress)));
- [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
-
- if ($isMatrix) {
- return $rows;
- }
-
- return $columns;
- }
-
- /**
- * 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 0.
- *
- * 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)
- {
- if ($cellAddress === null || trim($cellAddress) === '') {
- return 0;
- }
-
- if (is_array($cellAddress)) {
- foreach ($cellAddress as $columnKey => $rowValue) {
- foreach ($rowValue as $rowKey => $cellValue) {
- return (int) preg_replace('/\D/', '', $rowKey);
- }
- }
- } else {
- [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- if (strpos($cellAddress, ':') !== false) {
- [$startAddress, $endAddress] = explode(':', $cellAddress);
- $startAddress = preg_replace('/\D/', '', $startAddress);
- $endAddress = preg_replace('/\D/', '', $endAddress);
- $returnValue = [];
- do {
- $returnValue[][] = (int) $startAddress;
- } while ($startAddress++ != $endAddress);
-
- return $returnValue;
- }
- [$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 ($cellAddress === null || $cellAddress === '') {
- return 1;
- } elseif (!is_array($cellAddress)) {
- return Functions::VALUE();
- }
-
- reset($cellAddress);
- $isMatrix = (is_numeric(key($cellAddress)));
- [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
-
- if ($isMatrix) {
- return $columns;
- }
-
- return $rows;
- }
-
- /**
- * HYPERLINK.
- *
- * Excel Function:
- * =HYPERLINK(linkURL,displayName)
- *
- * @param string $linkURL Value to check, is also the value returned when no error
- * @param string $displayName Value to return when testValue is an error condition
- * @param Cell $pCell The cell to set the hyperlink in
- *
- * @return mixed The value of $displayName (or $linkURL if $displayName was blank)
- */
- public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $pCell = null)
- {
- $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
- $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
-
- if ((!is_object($pCell)) || (trim($linkURL) == '')) {
- return Functions::REF();
- }
-
- if ((is_object($displayName)) || trim($displayName) == '') {
- $displayName = $linkURL;
- }
-
- $pCell->getHyperlink()->setUrl($linkURL);
- $pCell->getHyperlink()->setTooltip($displayName);
-
- return $displayName;
- }
-
- /**
- * INDIRECT.
- *
- * Returns the reference specified by a text string.
- * References are immediately evaluated to display their contents.
- *
- * Excel Function:
- * =INDIRECT(cellAddress)
- *
- * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
- *
- * @param null|array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
- * @param Cell $pCell The current cell (containing this formula)
- *
- * @return mixed The cells referenced by cellAddress
- *
- * @TODO Support for the optional a1 parameter introduced in Excel 2010
- */
- public static function INDIRECT($cellAddress = null, ?Cell $pCell = null)
- {
- $cellAddress = Functions::flattenSingleValue($cellAddress);
- if ($cellAddress === null || $cellAddress === '') {
- return Functions::REF();
- }
-
- $cellAddress1 = $cellAddress;
- $cellAddress2 = null;
- if (strpos($cellAddress, ':') !== false) {
- [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
- }
-
- if (
- (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
- (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))
- ) {
- if (!preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $cellAddress1, $matches)) {
- return Functions::REF();
- }
-
- if (strpos($cellAddress, '!') !== false) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractNamedRange($cellAddress, $pSheet, false);
- }
-
- if (strpos($cellAddress, '!') !== false) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
- }
-
- /**
- * OFFSET.
- *
- * Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
- * The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and
- * the number of columns to be returned.
- *
- * Excel Function:
- * =OFFSET(cellAddress, rows, cols, [height], [width])
- *
- * @param null|string $cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
- * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
- * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
- * Using 5 as the rows argument specifies that the upper-left cell in the reference is
- * five rows below reference. Rows can be positive (which means below the starting reference)
- * or negative (which means above the starting reference).
- * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell of the result
- * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
- * reference is five columns to the right of reference. Cols can be positive (which means
- * to the right of the starting reference) or negative (which means to the left of the
- * starting reference).
- * @param mixed $height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
- * @param mixed $width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
- *
- * @return string A reference to a cell or range of cells
- */
- public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $pCell = null)
- {
- $rows = Functions::flattenSingleValue($rows);
- $columns = Functions::flattenSingleValue($columns);
- $height = Functions::flattenSingleValue($height);
- $width = Functions::flattenSingleValue($width);
- if ($cellAddress === null) {
- return 0;
- }
-
- if (!is_object($pCell)) {
- return Functions::REF();
- }
-
- $sheetName = null;
- if (strpos($cellAddress, '!')) {
- [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
- $sheetName = trim($sheetName, "'");
- }
- if (strpos($cellAddress, ':')) {
- [$startCell, $endCell] = explode(':', $cellAddress);
- } else {
- $startCell = $endCell = $cellAddress;
- }
- [$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
- [$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
-
- $startCellRow += $rows;
- $startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
- $startCellColumn += $columns;
-
- if (($startCellRow <= 0) || ($startCellColumn < 0)) {
- return Functions::REF();
- }
- $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
- if (($width != null) && (!is_object($width))) {
- $endCellColumn = $startCellColumn + $width - 1;
- } else {
- $endCellColumn += $columns;
- }
- $startCellColumn = Coordinate::stringFromColumnIndex($startCellColumn + 1);
-
- if (($height != null) && (!is_object($height))) {
- $endCellRow = $startCellRow + $height - 1;
- } else {
- $endCellRow += $rows;
- }
-
- if (($endCellRow <= 0) || ($endCellColumn < 0)) {
- return Functions::REF();
- }
- $endCellColumn = Coordinate::stringFromColumnIndex($endCellColumn + 1);
-
- $cellAddress = $startCellColumn . $startCellRow;
- if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
- $cellAddress .= ':' . $endCellColumn . $endCellRow;
- }
-
- if ($sheetName !== null) {
- $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
- } else {
- $pSheet = $pCell->getWorksheet();
- }
-
- return Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
- }
-
- /**
- * 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], ...)
- *
- * @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)) && (!is_bool($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];
- }
-
- /**
- * MATCH.
- *
- * The MATCH function searches for a specified item in a range of cells
- *
- * Excel Function:
- * =MATCH(lookup_value, lookup_array, [match_type])
- *
- * @param mixed $lookupValue The value that you want to match in lookup_array
- * @param mixed $lookupArray The range of cells being searched
- * @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
- * If match_type is 1 or -1, the list has to be ordered.
- *
- * @return int|string The relative position of the found item
- */
- public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
- {
- $lookupArray = Functions::flattenArray($lookupArray);
- $lookupValue = Functions::flattenSingleValue($lookupValue);
- $matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
-
- // MATCH is not case sensitive, so we convert lookup value to be lower cased in case it's string type.
- if (is_string($lookupValue)) {
- $lookupValue = StringHelper::strToLower($lookupValue);
- }
-
- // Lookup_value type has to be number, text, or logical values
- if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
- return Functions::NA();
- }
-
- // Match_type is 0, 1 or -1
- if (($matchType !== 0) && ($matchType !== -1) && ($matchType !== 1)) {
- return Functions::NA();
- }
-
- // Lookup_array should not be empty
- $lookupArraySize = count($lookupArray);
- if ($lookupArraySize <= 0) {
- return Functions::NA();
- }
-
- if ($matchType == 1) {
- // If match_type is 1 the list has to be processed from last to first
-
- $lookupArray = array_reverse($lookupArray);
- $keySet = array_reverse(array_keys($lookupArray));
- }
-
- // Lookup_array should contain only number, text, or logical values, or empty (null) cells
- foreach ($lookupArray as $i => $lookupArrayValue) {
- // check the type of the value
- if (
- (!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) &&
- (!is_bool($lookupArrayValue)) && ($lookupArrayValue !== null)
- ) {
- return Functions::NA();
- }
- // Convert strings to lowercase for case-insensitive testing
- if (is_string($lookupArrayValue)) {
- $lookupArray[$i] = StringHelper::strToLower($lookupArrayValue);
- }
- if (($lookupArrayValue === null) && (($matchType == 1) || ($matchType == -1))) {
- unset($lookupArray[$i]);
- }
- }
-
- // **
- // find the match
- // **
-
- if ($matchType === 0 || $matchType === 1) {
- foreach ($lookupArray as $i => $lookupArrayValue) {
- $typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) || (is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
- $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
- $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
- $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
-
- if ($matchType === 0) {
- if ($typeMatch && is_string($lookupValue) && (bool) preg_match('/([\?\*])/', $lookupValue)) {
- $splitString = $lookupValue;
- $chars = array_map(function ($i) use ($splitString) {
- return mb_substr($splitString, $i, 1);
- }, range(0, mb_strlen($splitString) - 1));
-
- $length = count($chars);
- $pattern = '/^';
- for ($j = 0; $j < $length; ++$j) {
- if ($chars[$j] === '~') {
- if (isset($chars[$j + 1])) {
- if ($chars[$j + 1] === '*') {
- $pattern .= preg_quote($chars[$j + 1], '/');
- ++$j;
- } elseif ($chars[$j + 1] === '?') {
- $pattern .= preg_quote($chars[$j + 1], '/');
- ++$j;
- }
- } else {
- $pattern .= preg_quote($chars[$j], '/');
- }
- } elseif ($chars[$j] === '*') {
- $pattern .= '.*';
- } elseif ($chars[$j] === '?') {
- $pattern .= '.{1}';
- } else {
- $pattern .= preg_quote($chars[$j], '/');
- }
- }
-
- $pattern .= '$/';
- if ((bool) preg_match($pattern, $lookupArrayValue)) {
- // exact match
- return $i + 1;
- }
- } elseif ($exactMatch) {
- // exact match
- return $i + 1;
- }
- } elseif (($matchType === 1) && $typeMatch && ($lookupArrayValue <= $lookupValue)) {
- $i = array_search($i, $keySet);
-
- // The current value is the (first) match
- return $i + 1;
- }
- }
- } else {
- $maxValueKey = null;
-
- // The basic algorithm is:
- // Iterate and keep the highest match until the next element is smaller than the searched value.
- // Return immediately if perfect match is found
- foreach ($lookupArray as $i => $lookupArrayValue) {
- $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
- $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
- $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
- $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
-
- if ($exactMatch) {
- // Another "special" case. If a perfect match is found,
- // the algorithm gives up immediately
- return $i + 1;
- } elseif ($typeMatch & $lookupArrayValue >= $lookupValue) {
- $maxValueKey = $i + 1;
- } elseif ($typeMatch & $lookupArrayValue < $lookupValue) {
- //Excel algorithm gives up immediately if the first element is smaller than the searched value
- break;
- }
- }
-
- if ($maxValueKey !== null) {
- return $maxValueKey;
- }
- }
-
- // Unsuccessful in finding a match, return #N/A error value
- return Functions::NA();
- }
-
- /**
- * INDEX.
- *
- * Uses an index to choose a value from a reference or array
- *
- * Excel Function:
- * =INDEX(range_array, row_num, [column_num])
- *
- * @param mixed $arrayValues A range of cells or an array constant
- * @param mixed $rowNum The row in array from which to return a value. If row_num is omitted, column_num is required.
- * @param mixed $columnNum The column in array from which to return a value. If column_num is omitted, row_num is required.
- *
- * @return mixed the value of a specified cell or array of cells
- */
- public static function INDEX($arrayValues, $rowNum = 0, $columnNum = 0)
- {
- $rowNum = Functions::flattenSingleValue($rowNum);
- $columnNum = Functions::flattenSingleValue($columnNum);
-
- if (($rowNum < 0) || ($columnNum < 0)) {
- return Functions::VALUE();
- }
-
- if (!is_array($arrayValues) || ($rowNum > count($arrayValues))) {
- return Functions::REF();
- }
-
- $rowKeys = array_keys($arrayValues);
- $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
-
- if ($columnNum > count($columnKeys)) {
- return Functions::VALUE();
- } elseif ($columnNum == 0) {
- if ($rowNum == 0) {
- return $arrayValues;
- }
- $rowNum = $rowKeys[--$rowNum];
- $returnArray = [];
- foreach ($arrayValues as $arrayColumn) {
- if (is_array($arrayColumn)) {
- if (isset($arrayColumn[$rowNum])) {
- $returnArray[] = $arrayColumn[$rowNum];
- } else {
- return [$rowNum => $arrayValues[$rowNum]];
- }
- } else {
- return $arrayValues[$rowNum];
- }
- }
-
- return $returnArray;
- }
- $columnNum = $columnKeys[--$columnNum];
- if ($rowNum > count($rowKeys)) {
- return Functions::VALUE();
- } elseif ($rowNum == 0) {
- return $arrayValues[$columnNum];
- }
- $rowNum = $rowKeys[--$rowNum];
-
- return $arrayValues[$rowNum][$columnNum];
- }
-
- /**
- * TRANSPOSE.
- *
- * @param array $matrixData A matrix of values
- *
- * @return array
- *
- * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix
- */
- public static function TRANSPOSE($matrixData)
- {
- $returnMatrix = [];
- if (!is_array($matrixData)) {
- $matrixData = [[$matrixData]];
- }
-
- $column = 0;
- foreach ($matrixData as $matrixRow) {
- $row = 0;
- foreach ($matrixRow as $matrixCell) {
- $returnMatrix[$row][$column] = $matrixCell;
- ++$row;
- }
- ++$column;
- }
-
- return $returnMatrix;
- }
-
- 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;
- }
-
- /**
- * 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 $lookup_value The value that you want to match in lookup_array
- * @param mixed $lookup_array The range of cells being searched
- * @param mixed $index_number The column number in table_array from which the matching value must be returned. The first column is 1.
- * @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
- *
- * @return mixed The value of the found cell
- */
- public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
- {
- $lookup_value = Functions::flattenSingleValue($lookup_value);
- $index_number = Functions::flattenSingleValue($index_number);
- $not_exact_match = Functions::flattenSingleValue($not_exact_match);
-
- // index_number must be greater than or equal to 1
- if ($index_number < 1) {
- return Functions::VALUE();
- }
-
- // index_number must be less than or equal to the number of columns in lookup_array
- if ((!is_array($lookup_array)) || (empty($lookup_array))) {
- return Functions::REF();
- }
- $f = array_keys($lookup_array);
- $firstRow = array_pop($f);
- if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
- return Functions::REF();
- }
- $columnKeys = array_keys($lookup_array[$firstRow]);
- $returnColumn = $columnKeys[--$index_number];
- $firstColumn = array_shift($columnKeys);
-
- if (!$not_exact_match) {
- uasort($lookup_array, ['self', 'vlookupSort']);
- }
-
- $lookupLower = StringHelper::strToLower($lookup_value);
- $rowNumber = $rowValue = false;
- foreach ($lookup_array as $rowKey => $rowData) {
- $firstLower = StringHelper::strToLower($rowData[$firstColumn]);
-
- // break if we have passed possible keys
- if (
- (is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) ||
- (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))
- ) {
- break;
- }
- // remember the last key, but only if datatypes match
- if (
- (is_numeric($lookup_value) && is_numeric($rowData[$firstColumn])) ||
- (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))
- ) {
- if ($not_exact_match) {
- $rowNumber = $rowKey;
-
- continue;
- } elseif (
- ($firstLower == $lookupLower)
- // Spreadsheets software returns first exact match,
- // we have sorted and we might have broken key orders
- // we want the first one (by its initial index)
- && (($rowNumber == false) || ($rowKey < $rowNumber))
- ) {
- $rowNumber = $rowKey;
- }
- }
- }
-
- if ($rowNumber !== false) {
- // return the appropriate value
- return $lookup_array[$rowNumber][$returnColumn];
- }
-
- return Functions::NA();
- }
-
- /**
- * HLOOKUP
- * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value in the same column based on the index_number.
- *
- * @param mixed $lookup_value The value that you want to match in lookup_array
- * @param mixed $lookup_array The range of cells being searched
- * @param mixed $index_number The row number in table_array from which the matching value must be returned. The first row is 1.
- * @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
- *
- * @return mixed The value of the found cell
- */
- public static function HLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
- {
- $lookup_value = Functions::flattenSingleValue($lookup_value);
- $index_number = Functions::flattenSingleValue($index_number);
- $not_exact_match = Functions::flattenSingleValue($not_exact_match);
-
- // index_number must be greater than or equal to 1
- if ($index_number < 1) {
- return Functions::VALUE();
- }
-
- // index_number must be less than or equal to the number of columns in lookup_array
- if ((!is_array($lookup_array)) || (empty($lookup_array))) {
- return Functions::REF();
- }
- $f = array_keys($lookup_array);
- $firstRow = reset($f);
- if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array))) {
- return Functions::REF();
- }
-
- $firstkey = $f[0] - 1;
- $returnColumn = $firstkey + $index_number;
- $firstColumn = array_shift($f);
- $rowNumber = null;
- foreach ($lookup_array[$firstColumn] as $rowKey => $rowData) {
- // break if we have passed possible keys
- $bothNumeric = is_numeric($lookup_value) && is_numeric($rowData);
- $bothNotNumeric = !is_numeric($lookup_value) && !is_numeric($rowData);
- $lookupLower = StringHelper::strToLower($lookup_value);
- $rowDataLower = StringHelper::strToLower($rowData);
-
- if (
- $not_exact_match && (
- ($bothNumeric && $rowData > $lookup_value) ||
- ($bothNotNumeric && $rowDataLower > $lookupLower)
- )
- ) {
- break;
- }
-
- // Remember the last key, but only if datatypes match (as in VLOOKUP)
- if ($bothNumeric || $bothNotNumeric) {
- if ($not_exact_match) {
- $rowNumber = $rowKey;
-
- continue;
- } elseif (
- $rowDataLower === $lookupLower
- && ($rowNumber === null || $rowKey < $rowNumber)
- ) {
- $rowNumber = $rowKey;
- }
- }
- }
-
- if ($rowNumber !== null) {
- // otherwise return the appropriate value
- return $lookup_array[$returnColumn][$rowNumber];
- }
-
- return Functions::NA();
- }
-
- /**
- * LOOKUP
- * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
- *
- * @param mixed $lookup_value The value that you want to match in lookup_array
- * @param mixed $lookup_vector The range of cells being searched
- * @param null|mixed $result_vector The column from which the matching value must be returned
- *
- * @return mixed The value of the found cell
- */
- public static function LOOKUP($lookup_value, $lookup_vector, $result_vector = null)
- {
- $lookup_value = Functions::flattenSingleValue($lookup_value);
-
- if (!is_array($lookup_vector)) {
- return Functions::NA();
- }
- $hasResultVector = isset($result_vector);
- $lookupRows = count($lookup_vector);
- $l = array_keys($lookup_vector);
- $l = array_shift($l);
- $lookupColumns = count($lookup_vector[$l]);
- // we correctly orient our results
- if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
- $lookup_vector = self::TRANSPOSE($lookup_vector);
- $lookupRows = count($lookup_vector);
- $l = array_keys($lookup_vector);
- $lookupColumns = count($lookup_vector[array_shift($l)]);
- }
-
- if ($result_vector === null) {
- $result_vector = $lookup_vector;
- }
- $resultRows = count($result_vector);
- $l = array_keys($result_vector);
- $l = array_shift($l);
- $resultColumns = count($result_vector[$l]);
- // we correctly orient our results
- if ($resultRows === 1 && $resultColumns > 1) {
- $result_vector = self::TRANSPOSE($result_vector);
- $resultRows = count($result_vector);
- $r = array_keys($result_vector);
- $resultColumns = count($result_vector[array_shift($r)]);
- }
-
- if ($lookupRows === 2 && !$hasResultVector) {
- $result_vector = array_pop($lookup_vector);
- $lookup_vector = array_shift($lookup_vector);
- }
-
- if ($lookupColumns !== 2) {
- foreach ($lookup_vector as &$value) {
- if (is_array($value)) {
- $k = array_keys($value);
- $key1 = $key2 = array_shift($k);
- ++$key2;
- $dataValue1 = $value[$key1];
- } else {
- $key1 = 0;
- $key2 = 1;
- $dataValue1 = $value;
- }
- $dataValue2 = array_shift($result_vector);
- if (is_array($dataValue2)) {
- $dataValue2 = array_shift($dataValue2);
- }
- $value = [$key1 => $dataValue1, $key2 => $dataValue2];
- }
- unset($value);
- }
-
- return self::VLOOKUP($lookup_value, $lookup_vector, 2);
- }
-
- /**
- * FORMULATEXT.
- *
- * @param mixed $cellReference The cell to check
- * @param Cell $pCell The current cell (containing this formula)
- *
- * @return string
- */
- public static function FORMULATEXT($cellReference = '', ?Cell $pCell = null)
- {
- if ($pCell === null) {
- return Functions::REF();
- }
-
- preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
-
- $cellReference = $matches[6] . $matches[7];
- $worksheetName = trim($matches[3], "'");
- $worksheet = (!empty($worksheetName))
- ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
- : $pCell->getWorksheet();
-
- if (!$worksheet->getCell($cellReference)->isFormula()) {
- return Functions::NA();
- }
-
- return $worksheet->getCell($cellReference)->getValue();
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/MathTrig.php b/vendor/PhpSpreadsheet/Calculation/MathTrig.php
deleted file mode 100644
index 7539659..0000000
--- a/vendor/PhpSpreadsheet/Calculation/MathTrig.php
+++ /dev/null
@@ -1,1815 +0,0 @@
- 1; --$i) {
- if (($value % $i) == 0) {
- $factorArray = array_merge($factorArray, self::factors($value / $i));
- $factorArray = array_merge($factorArray, self::factors($i));
- if ($i <= sqrt($value)) {
- break;
- }
- }
- }
- if (!empty($factorArray)) {
- rsort($factorArray);
-
- return $factorArray;
- }
-
- return [(int) $value];
- }
-
- private static function romanCut($num, $n)
- {
- return ($num - ($num % $n)) / $n;
- }
-
- /**
- * ARABIC.
- *
- * Converts a Roman numeral to an Arabic numeral.
- *
- * Excel Function:
- * ARABIC(text)
- *
- * @param string $roman
- *
- * @return int|string the arabic numberal contrived from the roman numeral
- */
- public static function ARABIC($roman)
- {
- // An empty string should return 0
- $roman = substr(trim(strtoupper((string) Functions::flattenSingleValue($roman))), 0, 255);
- if ($roman === '') {
- return 0;
- }
-
- // Convert the roman numeral to an arabic number
- $negativeNumber = $roman[0] === '-';
- if ($negativeNumber) {
- $roman = substr($roman, 1);
- }
-
- try {
- $arabic = self::calculateArabic(str_split($roman));
- } catch (Exception $e) {
- return Functions::VALUE(); // Invalid character detected
- }
-
- if ($negativeNumber) {
- $arabic *= -1; // The number should be negative
- }
-
- return $arabic;
- }
-
- /**
- * Recursively calculate the arabic value of a roman numeral.
- *
- * @param int $sum
- * @param int $subtract
- *
- * @return int
- */
- protected static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
- {
- $lookup = [
- 'M' => 1000,
- 'D' => 500,
- 'C' => 100,
- 'L' => 50,
- 'X' => 10,
- 'V' => 5,
- 'I' => 1,
- ];
-
- $numeral = array_shift($roman);
- if (!isset($lookup[$numeral])) {
- throw new Exception('Invalid character detected');
- }
-
- $arabic = $lookup[$numeral];
- if (count($roman) > 0 && isset($lookup[$roman[0]]) && $arabic < $lookup[$roman[0]]) {
- $subtract += $arabic;
- } else {
- $sum += ($arabic - $subtract);
- $subtract = 0;
- }
-
- if (count($roman) > 0) {
- self::calculateArabic($roman, $sum, $subtract);
- }
-
- return $sum;
- }
-
- /**
- * 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 float $xCoordinate the x-coordinate of the point
- * @param float $yCoordinate the y-coordinate of the point
- *
- * @return float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
- */
- public static function ATAN2($xCoordinate = null, $yCoordinate = null)
- {
- $xCoordinate = Functions::flattenSingleValue($xCoordinate);
- $yCoordinate = Functions::flattenSingleValue($yCoordinate);
-
- $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
- $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
-
- if (
- ((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
- ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))
- ) {
- $xCoordinate = (float) $xCoordinate;
- $yCoordinate = (float) $yCoordinate;
-
- if (($xCoordinate == 0) && ($yCoordinate == 0)) {
- return Functions::DIV0();
- }
-
- return atan2($yCoordinate, $xCoordinate);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * BASE.
- *
- * Converts a number into a text representation with the given radix (base).
- *
- * Excel Function:
- * BASE(Number, Radix [Min_length])
- *
- * @param float $number
- * @param float $radix
- * @param int $minLength
- *
- * @return string the text representation with the given radix (base)
- */
- public static function BASE($number, $radix, $minLength = null)
- {
- $number = Functions::flattenSingleValue($number);
- $radix = Functions::flattenSingleValue($radix);
- $minLength = Functions::flattenSingleValue($minLength);
-
- if (is_numeric($number) && is_numeric($radix) && ($minLength === null || is_numeric($minLength))) {
- // Truncate to an integer
- $number = (int) $number;
- $radix = (int) $radix;
- $minLength = (int) $minLength;
-
- if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
- return Functions::NAN(); // Numeric range constraints
- }
-
- $outcome = strtoupper((string) base_convert($number, 10, $radix));
- if ($minLength !== null) {
- $outcome = str_pad($outcome, $minLength, '0', STR_PAD_LEFT); // String padding
- }
-
- return $outcome;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * CEILING.
- *
- * Returns number rounded up, away from zero, to the nearest multiple of significance.
- * For example, if you want to avoid using pennies in your prices and your product is
- * priced at $4.42, use the formula =CEILING(4.42,0.05) to round prices up to the
- * nearest nickel.
- *
- * Excel Function:
- * CEILING(number[,significance])
- *
- * @param float $number the number you want to round
- * @param float $significance the multiple to which you want to round
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function CEILING($number, $significance = null)
- {
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if (
- ($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)
- ) {
- $significance = $number / abs($number);
- }
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if (($number == 0.0) || ($significance == 0.0)) {
- return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
- return ceil($number / $significance) * $significance;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
- }
-
- /**
- * COMBIN.
- *
- * Returns the number of combinations for a given number of items. Use COMBIN to
- * determine the total possible number of groups for a given number of items.
- *
- * Excel Function:
- * COMBIN(numObjs,numInSet)
- *
- * @param int $numObjs Number of different objects
- * @param int $numInSet Number of objects in each combination
- *
- * @return int|string Number of combinations, or a string containing an error
- */
- public static function COMBIN($numObjs, $numInSet)
- {
- $numObjs = Functions::flattenSingleValue($numObjs);
- $numInSet = Functions::flattenSingleValue($numInSet);
-
- if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
- if ($numObjs < $numInSet) {
- return Functions::NAN();
- } elseif ($numInSet < 0) {
- return Functions::NAN();
- }
-
- return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 float $number Number to round
- *
- * @return int|string Rounded Number, or a string containing an error
- */
- public static function EVEN($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- $number = (int) $number;
- }
-
- if (is_numeric($number)) {
- $significance = 2 * self::SIGN($number);
-
- return (int) self::CEILING($number, $significance);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * FACT.
- *
- * Returns the factorial of a number.
- * The factorial of a number is equal to 1*2*3*...* number.
- *
- * Excel Function:
- * FACT(factVal)
- *
- * @param float $factVal Factorial Value
- *
- * @return int|string Factorial, or a string containing an error
- */
- public static function FACT($factVal)
- {
- $factVal = Functions::flattenSingleValue($factVal);
-
- if (is_numeric($factVal)) {
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factLoop = floor($factVal);
- if (
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) &&
- ($factVal > $factLoop)
- ) {
- return Functions::NAN();
- }
-
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- }
-
- return $factorial;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * FACTDOUBLE.
- *
- * Returns the double factorial of a number.
- *
- * Excel Function:
- * FACTDOUBLE(factVal)
- *
- * @param float $factVal Factorial Value
- *
- * @return int|string Double Factorial, or a string containing an error
- */
- public static function FACTDOUBLE($factVal)
- {
- $factLoop = Functions::flattenSingleValue($factVal);
-
- if (is_numeric($factLoop)) {
- $factLoop = floor($factLoop);
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- --$factLoop;
- }
-
- return $factorial;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * FLOOR.
- *
- * Rounds number down, toward zero, to the nearest multiple of significance.
- *
- * Excel Function:
- * FLOOR(number[,significance])
- *
- * @param float $number Number to round
- * @param float $significance Significance
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function FLOOR($number, $significance = null)
- {
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if (
- ($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)
- ) {
- $significance = $number / abs($number);
- }
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- } elseif (self::SIGN($significance) == 1) {
- return floor($number / $significance) * $significance;
- } elseif (self::SIGN($number) == -1 && self::SIGN($significance) == -1) {
- return floor($number / $significance) * $significance;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
- }
-
- /**
- * FLOOR.MATH.
- *
- * Round a number down to the nearest integer or to the nearest multiple of significance.
- *
- * Excel Function:
- * FLOOR.MATH(number[,significance[,mode]])
- *
- * @param float $number Number to round
- * @param float $significance Significance
- * @param int $mode direction to round negative numbers
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function FLOORMATH($number, $significance = null, $mode = 0)
- {
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
- $mode = Functions::flattenSingleValue($mode);
-
- if (is_numeric($number) && $significance === null) {
- $significance = $number / abs($number);
- }
-
- if (is_numeric($number) && is_numeric($significance) && is_numeric($mode)) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- } elseif (self::SIGN($significance) == -1 || (self::SIGN($number) == -1 && !empty($mode))) {
- return ceil($number / $significance) * $significance;
- }
-
- return floor($number / $significance) * $significance;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * FLOOR.PRECISE.
- *
- * Rounds number down, toward zero, to the nearest multiple of significance.
- *
- * Excel Function:
- * FLOOR.PRECISE(number[,significance])
- *
- * @param float $number Number to round
- * @param float $significance Significance
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function FLOORPRECISE($number, $significance = 1)
- {
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
-
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- }
-
- return floor($number / abs($significance)) * abs($significance);
- }
-
- return Functions::VALUE();
- }
-
- private static function evaluateGCD($a, $b)
- {
- return $b ? self::evaluateGCD($b, $a % $b) : $a;
- }
-
- /**
- * GCD.
- *
- * Returns the greatest common divisor of a series of numbers.
- * The greatest common divisor is the largest integer that divides both
- * number1 and number2 without a remainder.
- *
- * Excel Function:
- * GCD(number1[,number2[, ...]])
- *
- * @param mixed ...$args Data values
- *
- * @return int|mixed|string Greatest Common Divisor, or a string containing an error
- */
- public static function GCD(...$args)
- {
- $args = Functions::flattenArray($args);
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- }
-
- $gcd = (int) array_pop($args);
- do {
- $gcd = self::evaluateGCD($gcd, (int) array_pop($args));
- } while (!empty($args));
-
- return $gcd;
- }
-
- /**
- * INT.
- *
- * Casts a floating point value to an integer
- *
- * Excel Function:
- * INT(number)
- *
- * @param float $number Number to cast to an integer
- *
- * @return int|string Integer value, or a string containing an error
- */
- public static function INT($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- return (int) floor($number);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * LCM.
- *
- * Returns the lowest common multiplier of a series of numbers
- * The least common multiple is the smallest positive integer that is a multiple
- * of all integer arguments number1, number2, and so on. Use LCM to add fractions
- * with different denominators.
- *
- * Excel Function:
- * LCM(number1[,number2[, ...]])
- *
- * @param mixed ...$args Data values
- *
- * @return int|string Lowest Common Multiplier, or a string containing an error
- */
- public static function LCM(...$args)
- {
- $returnValue = 1;
- $allPoweredFactors = [];
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- if ($value == 0) {
- return 0;
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- $myFactors = self::factors(floor($value));
- $myCountedFactors = array_count_values($myFactors);
- $myPoweredFactors = [];
- foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
- $myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
- }
- foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
- if (isset($allPoweredFactors[$myPoweredValue])) {
- if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- } else {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- }
- }
- foreach ($allPoweredFactors as $allPoweredFactor) {
- $returnValue *= (int) $allPoweredFactor;
- }
-
- return $returnValue;
- }
-
- /**
- * LOG_BASE.
- *
- * Returns the logarithm of a number to a specified base. The default base is 10.
- *
- * Excel Function:
- * LOG(number[,base])
- *
- * @param float $number The positive real number for which you want the logarithm
- * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
- *
- * @return float|string The result, or a string containing an error
- */
- public static function logBase($number = null, $base = 10)
- {
- $number = Functions::flattenSingleValue($number);
- $base = ($base === null) ? 10 : (float) Functions::flattenSingleValue($base);
-
- if ((!is_numeric($base)) || (!is_numeric($number))) {
- return Functions::VALUE();
- }
- if (($base <= 0) || ($number <= 0)) {
- return Functions::NAN();
- }
-
- return log($number, $base);
- }
-
- /**
- * MDETERM.
- *
- * Returns the matrix determinant of an array.
- *
- * Excel Function:
- * MDETERM(array)
- *
- * @param array $matrixValues A matrix of values
- *
- * @return float|string The result, or a string containing an error
- */
- public static function MDETERM($matrixValues)
- {
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
-
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
-
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
-
- try {
- return $matrix->determinant();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
- }
-
- /**
- * MINVERSE.
- *
- * Returns the inverse matrix for the matrix stored in an array.
- *
- * Excel Function:
- * MINVERSE(array)
- *
- * @param array $matrixValues A matrix of values
- *
- * @return array|string The result, or a string containing an error
- */
- public static function MINVERSE($matrixValues)
- {
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
-
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
-
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
-
- if ($matrix->determinant() == 0.0) {
- return Functions::NAN();
- }
-
- try {
- return $matrix->inverse()->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
- }
-
- /**
- * MMULT.
- *
- * @param array $matrixData1 A matrix of values
- * @param array $matrixData2 A matrix of values
- *
- * @return array|string The result, or a string containing an error
- */
- public static function MMULT($matrixData1, $matrixData2)
- {
- $matrixAData = $matrixBData = [];
- if (!is_array($matrixData1)) {
- $matrixData1 = [[$matrixData1]];
- }
- if (!is_array($matrixData2)) {
- $matrixData2 = [[$matrixData2]];
- }
-
- try {
- $rowA = 0;
- foreach ($matrixData1 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnA = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixAData[$rowA][$columnA] = $matrixCell;
- ++$columnA;
- }
- ++$rowA;
- }
- $matrixA = new Matrix($matrixAData);
- $rowB = 0;
- foreach ($matrixData2 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnB = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixBData[$rowB][$columnB] = $matrixCell;
- ++$columnB;
- }
- ++$rowB;
- }
- $matrixB = new Matrix($matrixBData);
-
- if ($columnA != $rowB) {
- return Functions::VALUE();
- }
-
- return $matrixA->multiply($matrixB)->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
- }
-
- /**
- * MOD.
- *
- * @param int $a Dividend
- * @param int $b Divisor
- *
- * @return int|string Remainder, or a string containing an error
- */
- public static function MOD($a = 1, $b = 1)
- {
- $a = (float) Functions::flattenSingleValue($a);
- $b = (float) Functions::flattenSingleValue($b);
-
- if ($b == 0.0) {
- return Functions::DIV0();
- } elseif (($a < 0.0) && ($b > 0.0)) {
- return $b - fmod(abs($a), $b);
- } elseif (($a > 0.0) && ($b < 0.0)) {
- return $b + fmod($a, abs($b));
- }
-
- return fmod($a, $b);
- }
-
- /**
- * MROUND.
- *
- * Rounds a number to the nearest multiple of a specified value
- *
- * @param float $number Number to round
- * @param int $multiple Multiple to which you want to round $number
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function MROUND($number, $multiple)
- {
- $number = Functions::flattenSingleValue($number);
- $multiple = Functions::flattenSingleValue($multiple);
-
- if ((is_numeric($number)) && (is_numeric($multiple))) {
- if ($multiple == 0) {
- return 0;
- }
- if ((self::SIGN($number)) == (self::SIGN($multiple))) {
- $multiplier = 1 / $multiple;
-
- return round($number * $multiplier) / $multiplier;
- }
-
- return Functions::NAN();
- }
-
- return Functions::VALUE();
- }
-
- /**
- * MULTINOMIAL.
- *
- * Returns the ratio of the factorial of a sum of values to the product of factorials.
- *
- * @param mixed[] $args An array of mixed values for the Data Series
- *
- * @return float|string The result, or a string containing an error
- */
- public static function MULTINOMIAL(...$args)
- {
- $summer = 0;
- $divisor = 1;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if (is_numeric($arg)) {
- if ($arg < 1) {
- return Functions::NAN();
- }
- $summer += floor($arg);
- $divisor *= self::FACT($arg);
- } else {
- return Functions::VALUE();
- }
- }
-
- // Return
- if ($summer > 0) {
- $summer = self::FACT($summer);
-
- return $summer / $divisor;
- }
-
- return 0;
- }
-
- /**
- * ODD.
- *
- * Returns number rounded up to the nearest odd integer.
- *
- * @param float $number Number to round
- *
- * @return int|string Rounded Number, or a string containing an error
- */
- public static function ODD($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if ($number === null) {
- return 1;
- } elseif (is_bool($number)) {
- return 1;
- } elseif (is_numeric($number)) {
- $significance = self::SIGN($number);
- if ($significance == 0) {
- return 1;
- }
-
- $result = self::CEILING($number, $significance);
- if ($result == self::EVEN($result)) {
- $result += $significance;
- }
-
- return (int) $result;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * POWER.
- *
- * Computes x raised to the power y.
- *
- * @param float $x
- * @param float $y
- *
- * @return float|string The result, or a string containing an error
- */
- public static function POWER($x = 0, $y = 2)
- {
- $x = Functions::flattenSingleValue($x);
- $y = Functions::flattenSingleValue($y);
-
- // Validate parameters
- if ($x == 0.0 && $y == 0.0) {
- return Functions::NAN();
- } elseif ($x == 0.0 && $y < 0.0) {
- return Functions::DIV0();
- }
-
- // Return
- $result = $x ** $y;
-
- return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN();
- }
-
- /**
- * PRODUCT.
- *
- * PRODUCT returns the product of all the values and cells referenced in the argument list.
- *
- * Excel Function:
- * PRODUCT(value1[,value2[, ...]])
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function PRODUCT(...$args)
- {
- // Return value
- $returnValue = null;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = $arg;
- } else {
- $returnValue *= $arg;
- }
- }
- }
-
- // Return
- if ($returnValue === null) {
- return 0;
- }
-
- return $returnValue;
- }
-
- /**
- * QUOTIENT.
- *
- * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
- * and denominator is the divisor.
- *
- * Excel Function:
- * QUOTIENT(value1[,value2[, ...]])
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function QUOTIENT(...$args)
- {
- // Return value
- $returnValue = null;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg == 0) ? 0 : $arg;
- } else {
- if (($returnValue == 0) || ($arg == 0)) {
- $returnValue = 0;
- } else {
- $returnValue /= $arg;
- }
- }
- }
- }
-
- // Return
- return (int) $returnValue;
- }
-
- /**
- * RAND.
- *
- * @param int $min Minimal value
- * @param int $max Maximal value
- *
- * @return int Random number
- */
- public static function RAND($min = 0, $max = 0)
- {
- $min = Functions::flattenSingleValue($min);
- $max = Functions::flattenSingleValue($max);
-
- if ($min == 0 && $max == 0) {
- return (mt_rand(0, 10000000)) / 10000000;
- }
-
- return mt_rand($min, $max);
- }
-
- public static function ROMAN($aValue, $style = 0)
- {
- $aValue = Functions::flattenSingleValue($aValue);
- $style = ($style === null) ? 0 : (int) Functions::flattenSingleValue($style);
- if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
- return Functions::VALUE();
- }
- $aValue = (int) $aValue;
- if ($aValue == 0) {
- return '';
- }
-
- $mill = ['', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM'];
- $cent = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
- $tens = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
- $ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
-
- $roman = '';
- while ($aValue > 5999) {
- $roman .= 'M';
- $aValue -= 1000;
- }
- $m = self::romanCut($aValue, 1000);
- $aValue %= 1000;
- $c = self::romanCut($aValue, 100);
- $aValue %= 100;
- $t = self::romanCut($aValue, 10);
- $aValue %= 10;
-
- return $roman . $mill[$m] . $cent[$c] . $tens[$t] . $ones[$aValue];
- }
-
- /**
- * ROUNDUP.
- *
- * Rounds a number up to a specified number of decimal places
- *
- * @param float $number Number to round
- * @param int $digits Number of digits to which you want to round $number
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function ROUNDUP($number, $digits)
- {
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
-
- if ((is_numeric($number)) && (is_numeric($digits))) {
- 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);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * ROUNDDOWN.
- *
- * Rounds a number down to a specified number of decimal places
- *
- * @param float $number Number to round
- * @param int $digits Number of digits to which you want to round $number
- *
- * @return float|string Rounded Number, or a string containing an error
- */
- public static function ROUNDDOWN($number, $digits)
- {
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
-
- if ((is_numeric($number)) && (is_numeric($digits))) {
- 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);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * SERIESSUM.
- *
- * Returns the sum of a power series
- *
- * @param mixed[] $args An array of mixed values for the Data Series
- *
- * @return float|string The result, or a string containing an error
- */
- public static function SERIESSUM(...$args)
- {
- $returnValue = 0;
-
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
-
- $x = array_shift($aArgs);
- $n = array_shift($aArgs);
- $m = array_shift($aArgs);
-
- if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
- // Calculate
- $i = 0;
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg * $x ** ($n + ($m * $i++));
- } else {
- return Functions::VALUE();
- }
- }
-
- return $returnValue;
- }
-
- return Functions::VALUE();
- }
-
- /**
- * 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 float $number Number to round
- *
- * @return int|string sign value, or a string containing an error
- */
- public static function SIGN($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- if ($number == 0.0) {
- return 0;
- }
-
- return $number / abs($number);
- }
-
- return Functions::VALUE();
- }
-
- /**
- * SQRTPI.
- *
- * Returns the square root of (number * pi).
- *
- * @param float $number Number
- *
- * @return float|string Square Root of Number * Pi, or a string containing an error
- */
- public static function SQRTPI($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if (is_numeric($number)) {
- if ($number < 0) {
- return Functions::NAN();
- }
-
- return sqrt($number * M_PI);
- }
-
- return Functions::VALUE();
- }
-
- protected static function filterHiddenArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- [, $row, $column] = explode('.', $index);
-
- return $cellReference->getWorksheet()->getRowDimension($row)->getVisible() &&
- $cellReference->getWorksheet()->getColumnDimension($column)->getVisible();
- },
- ARRAY_FILTER_USE_KEY
- );
- }
-
- protected static function filterFormulaArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- [, $row, $column] = explode('.', $index);
- 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());
-
- return !$isFormula || $cellFormula;
- }
-
- return true;
- },
- ARRAY_FILTER_USE_KEY
- );
- }
-
- /**
- * SUBTOTAL.
- *
- * Returns a subtotal in a list or database.
- *
- * @param int $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 or columns
- * @param mixed[] $args A mixed data series of values
- *
- * @return float|string
- */
- public static function SUBTOTAL($functionType, ...$args)
- {
- $cellReference = array_pop($args);
- $aArgs = Functions::flattenArrayIndexed($args);
- $subtotal = Functions::flattenSingleValue($functionType);
-
- // Calculate
- if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
- if ($subtotal > 100) {
- $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
- $subtotal -= 100;
- }
-
- $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
- switch ($subtotal) {
- case 1:
- return Statistical::AVERAGE($aArgs);
- case 2:
- return Statistical::COUNT($aArgs);
- case 3:
- return Statistical::COUNTA($aArgs);
- case 4:
- return Statistical::MAX($aArgs);
- case 5:
- return Statistical::MIN($aArgs);
- case 6:
- return self::PRODUCT($aArgs);
- case 7:
- return Statistical::STDEV($aArgs);
- case 8:
- return Statistical::STDEVP($aArgs);
- case 9:
- return self::SUM($aArgs);
- case 10:
- return Statistical::VARFunc($aArgs);
- case 11:
- return Statistical::VARP($aArgs);
- }
- }
-
- return Functions::VALUE();
- }
-
- /**
- * SUM.
- *
- * 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
- */
- public static function SUM(...$args)
- {
- $returnValue = 0;
-
- // Loop through the arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg;
- } elseif (Functions::isError($arg)) {
- return $arg;
- }
- }
-
- return $returnValue;
- }
-
- /**
- * SUMIF.
- *
- * Counts the number of cells that contain numbers within the list of arguments
- *
- * Excel Function:
- * SUMIF(value1[,value2[, ...]],condition)
- *
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be summed
- * @param mixed $sumArgs
- *
- * @return float
- */
- public static function SUMIF($aArgs, $condition, $sumArgs = [])
- {
- $returnValue = 0;
-
- $aArgs = Functions::flattenArray($aArgs);
- $sumArgs = Functions::flattenArray($sumArgs);
- if (empty($sumArgs)) {
- $sumArgs = $aArgs;
- }
- $condition = Functions::ifCondition($condition);
- // Loop through arguments
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- $arg = str_replace('"', '""', $arg);
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
-
- $testCondition = '=' . $arg . $condition;
- $sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0;
-
- if (
- is_numeric($sumValue) &&
- Calculation::getInstance()->_calculateFormulaValue($testCondition)
- ) {
- // Is it a value within our criteria and only numeric can be added to the result
- $returnValue += $sumValue;
- }
- }
-
- return $returnValue;
- }
-
- /**
- * SUMIFS.
- *
- * Counts the number of cells that contain numbers within the list of arguments
- *
- * Excel Function:
- * SUMIFS(value1[,value2[, ...]],condition)
- *
- * @param mixed $args Data values
- *
- * @return float
- */
- public static function SUMIFS(...$args)
- {
- $arrayList = $args;
-
- // Return value
- $returnValue = 0;
-
- $sumArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
-
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
-
- // Loop through each sum and see if arguments and conditions are true
- foreach ($sumArgs as $index => $value) {
- $valid = true;
-
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
-
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
-
- break; // if false found, don't need to check other conditions
- }
- }
-
- if ($valid) {
- $returnValue += $value;
- }
- }
-
- // Return
- 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 SUMPRODUCT(...$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);
- }
-
- /**
- * SUMSQ.
- *
- * SUMSQ returns the sum of the squares of the arguments
- *
- * Excel Function:
- * SUMSQ(value1[,value2[, ...]])
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function SUMSQ(...$args)
- {
- $returnValue = 0;
-
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += ($arg * $arg);
- }
- }
-
- return $returnValue;
- }
-
- /**
- * SUMX2MY2.
- *
- * @param mixed[] $matrixData1 Matrix #1
- * @param mixed[] $matrixData2 Matrix #2
- *
- * @return float
- */
- public static function SUMX2MY2($matrixData1, $matrixData2)
- {
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (
- ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))
- ) {
- $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
- }
- }
-
- return $result;
- }
-
- /**
- * SUMX2PY2.
- *
- * @param mixed[] $matrixData1 Matrix #1
- * @param mixed[] $matrixData2 Matrix #2
- *
- * @return float
- */
- public static function SUMX2PY2($matrixData1, $matrixData2)
- {
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (
- ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))
- ) {
- $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
- }
- }
-
- return $result;
- }
-
- /**
- * SUMXMY2.
- *
- * @param mixed[] $matrixData1 Matrix #1
- * @param mixed[] $matrixData2 Matrix #2
- *
- * @return float
- */
- public static function SUMXMY2($matrixData1, $matrixData2)
- {
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
-
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (
- ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))
- ) {
- $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
- }
- }
-
- return $result;
- }
-
- /**
- * TRUNC.
- *
- * Truncates value to the number of fractional digits by number_digits.
- *
- * @param float $value
- * @param int $digits
- *
- * @return float|string Truncated value, or a string containing an error
- */
- public static function TRUNC($value = 0, $digits = 0)
- {
- $value = Functions::flattenSingleValue($value);
- $digits = Functions::flattenSingleValue($digits);
-
- // Validate parameters
- if ((!is_numeric($value)) || (!is_numeric($digits))) {
- return Functions::VALUE();
- }
- $digits = floor($digits);
-
- // Truncate
- $adjust = 10 ** $digits;
-
- if (($digits > 0) && (rtrim((int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
- return $value;
- }
-
- return ((int) ($value * $adjust)) / $adjust;
- }
-
- /**
- * SEC.
- *
- * Returns the secant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The secant of the angle
- */
- public static function SEC($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = cos($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
-
- /**
- * SECH.
- *
- * Returns the hyperbolic secant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The hyperbolic secant of the angle
- */
- public static function SECH($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = cosh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
-
- /**
- * CSC.
- *
- * Returns the cosecant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The cosecant of the angle
- */
- public static function CSC($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = sin($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
-
- /**
- * CSCH.
- *
- * Returns the hyperbolic cosecant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The hyperbolic cosecant of the angle
- */
- public static function CSCH($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = sinh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
-
- /**
- * COT.
- *
- * Returns the cotangent of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The cotangent of the angle
- */
- public static function COT($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = tan($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
-
- /**
- * COTH.
- *
- * Returns the hyperbolic cotangent of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The hyperbolic cotangent of the angle
- */
- public static function COTH($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
-
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
-
- $result = tanh($angle);
-
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
-
- /**
- * ACOT.
- *
- * Returns the arccotangent of a number.
- *
- * @param float $number Number
- *
- * @return float|string The arccotangent of the number
- */
- public static function ACOT($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if (!is_numeric($number)) {
- return Functions::VALUE();
- }
-
- return (M_PI / 2) - atan($number);
- }
-
- /**
- * ACOTH.
- *
- * Returns the hyperbolic arccotangent of a number.
- *
- * @param float $number Number
- *
- * @return float|string The hyperbolic arccotangent of the number
- */
- public static function ACOTH($number)
- {
- $number = Functions::flattenSingleValue($number);
-
- if (!is_numeric($number)) {
- return Functions::VALUE();
- }
-
- $result = log(($number + 1) / ($number - 1)) / 2;
-
- return is_nan($result) ? Functions::NAN() : $result;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Calculation/Statistical.php b/vendor/PhpSpreadsheet/Calculation/Statistical.php
deleted file mode 100644
index 19f40f2..0000000
--- a/vendor/PhpSpreadsheet/Calculation/Statistical.php
+++ /dev/null
@@ -1,3906 +0,0 @@
- $value) {
- if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
- unset($array1[$key], $array2[$key]);
- }
- }
- foreach ($array2 as $key => $value) {
- if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
- unset($array1[$key], $array2[$key]);
- }
- }
- $array1 = array_merge($array1);
- $array2 = array_merge($array2);
-
- return true;
- }
-
- /**
- * Incomplete beta function.
- *
- * @author Jaco van Kooten
- * @author Paul Meagher
- *
- * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
- *
- * @param mixed $x require 0<=x<=1
- * @param mixed $p require p>0
- * @param mixed $q require q>0
- *
- * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
- */
- private static function incompleteBeta($x, $p, $q)
- {
- if ($x <= 0.0) {
- return 0.0;
- } elseif ($x >= 1.0) {
- return 1.0;
- } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
- return 0.0;
- }
- $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
- if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
- return $beta_gam * self::betaFraction($x, $p, $q) / $p;
- }
-
- return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
- }
-
- // Function cache for logBeta function
- private static $logBetaCacheP = 0.0;
-
- private static $logBetaCacheQ = 0.0;
-
- private static $logBetaCacheResult = 0.0;
-
- /**
- * The natural logarithm of the beta function.
- *
- * @param mixed $p require p>0
- * @param mixed $q require q>0
- *
- * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
- *
- * @author Jaco van Kooten
- */
- private static function logBeta($p, $q)
- {
- if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
- self::$logBetaCacheP = $p;
- self::$logBetaCacheQ = $q;
- if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
- self::$logBetaCacheResult = 0.0;
- } else {
- self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
- }
- }
-
- return self::$logBetaCacheResult;
- }
-
- /**
- * Evaluates of continued fraction part of incomplete beta function.
- * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
- *
- * @author Jaco van Kooten
- *
- * @param mixed $x
- * @param mixed $p
- * @param mixed $q
- *
- * @return float
- */
- private static function betaFraction($x, $p, $q)
- {
- $c = 1.0;
- $sum_pq = $p + $q;
- $p_plus = $p + 1.0;
- $p_minus = $p - 1.0;
- $h = 1.0 - $sum_pq * $x / $p_plus;
- if (abs($h) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $frac = $h;
- $m = 1;
- $delta = 0.0;
- while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
- $m2 = 2 * $m;
- // even index for d
- $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
- $h = 1.0 + $d * $h;
- if (abs($h) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $c = 1.0 + $d / $c;
- if (abs($c) < self::XMININ) {
- $c = self::XMININ;
- }
- $frac *= $h * $c;
- // odd index for d
- $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
- $h = 1.0 + $d * $h;
- if (abs($h) < self::XMININ) {
- $h = self::XMININ;
- }
- $h = 1.0 / $h;
- $c = 1.0 + $d / $c;
- if (abs($c) < self::XMININ) {
- $c = self::XMININ;
- }
- $delta = $h * $c;
- $frac *= $delta;
- ++$m;
- }
-
- return $frac;
- }
-
- /**
- * logGamma function.
- *
- * @version 1.1
- *
- * @author Jaco van Kooten
- *
- * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
- *
- * The natural logarithm of the gamma function.
- * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
- * Applied Mathematics Division
- * Argonne National Laboratory
- * Argonne, IL 60439
- *
- * References: - *
- * From the original documentation: - *
- *- * This routine calculates the LOG(GAMMA) function for a positive real argument X. - * Computation is based on an algorithm outlined in references 1 and 2. - * The program uses rational functions that theoretically approximate LOG(GAMMA) - * to at least 18 significant decimal digits. The approximation for X > 12 is from - * reference 3, while approximations for X < 12.0 are similar to those in reference - * 1, but are unpublished. The accuracy achieved depends on the arithmetic system, - * the compiler, the intrinsic functions, and proper selection of the - * machine-dependent constants. - *
- *
- * Error returns:
- * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
- * The computation is believed to be free of underflow and overflow.
- *
{$path}
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 {$path}
in " . sprintf('%.4f', $callTime) . ' seconds';
-
- $this->log($message);
- }
-}
diff --git a/vendor/PhpSpreadsheet/IComparable.php b/vendor/PhpSpreadsheet/IComparable.php
deleted file mode 100644
index c215847..0000000
--- a/vendor/PhpSpreadsheet/IComparable.php
+++ /dev/null
@@ -1,13 +0,0 @@
- Reader\Xlsx::class,
- 'Xls' => Reader\Xls::class,
- 'Xml' => Reader\Xml::class,
- 'Ods' => Reader\Ods::class,
- 'Slk' => Reader\Slk::class,
- 'Gnumeric' => Reader\Gnumeric::class,
- 'Html' => Reader\Html::class,
- 'Csv' => Reader\Csv::class,
- ];
-
- private static $writers = [
- 'Xls' => Writer\Xls::class,
- 'Xlsx' => Writer\Xlsx::class,
- 'Ods' => Writer\Ods::class,
- 'Csv' => Writer\Csv::class,
- 'Html' => Writer\Html::class,
- 'Tcpdf' => Writer\Pdf\Tcpdf::class,
- 'Dompdf' => Writer\Pdf\Dompdf::class,
- 'Mpdf' => Writer\Pdf\Mpdf::class,
- ];
-
- /**
- * Create Writer\IWriter.
- *
- * @param string $writerType Example: Xlsx
- *
- * @return Writer\IWriter
- */
- public static function createWriter(Spreadsheet $spreadsheet, $writerType)
- {
- if (!isset(self::$writers[$writerType])) {
- throw new Writer\Exception("No writer found for type $writerType");
- }
-
- // Instantiate writer
- $className = self::$writers[$writerType];
-
- return new $className($spreadsheet);
- }
-
- /**
- * Create Reader\IReader.
- *
- * @param string $readerType Example: Xlsx
- *
- * @return Reader\IReader
- */
- public static function createReader($readerType)
- {
- if (!isset(self::$readers[$readerType])) {
- throw new Reader\Exception("No reader found for type $readerType");
- }
-
- // Instantiate reader
- $className = self::$readers[$readerType];
-
- return new $className();
- }
-
- /**
- * Loads Spreadsheet from file using automatic Reader\IReader resolution.
- *
- * @param string $pFilename The name of the spreadsheet file
- *
- * @return Spreadsheet
- */
- public static function load($pFilename)
- {
- $reader = self::createReaderForFile($pFilename);
-
- return $reader->load($pFilename);
- }
-
- /**
- * Identify file type using automatic Reader\IReader resolution.
- *
- * @param string $pFilename The name of the spreadsheet file to identify
- *
- * @return string
- */
- public static function identify($pFilename)
- {
- $reader = self::createReaderForFile($pFilename);
- $className = get_class($reader);
- $classType = explode('\\', $className);
- unset($reader);
-
- return array_pop($classType);
- }
-
- /**
- * Create Reader\IReader for file using automatic Reader\IReader resolution.
- *
- * @param string $filename The name of the spreadsheet file
- *
- * @return Reader\IReader
- */
- public static function createReaderForFile($filename)
- {
- File::assertFile($filename);
-
- // First, lucky guess by inspecting file extension
- $guessedReader = self::getReaderTypeFromExtension($filename);
- if ($guessedReader !== null) {
- $reader = self::createReader($guessedReader);
-
- // Let's see if we are lucky
- if (isset($reader) && $reader->canRead($filename)) {
- return $reader;
- }
- }
-
- // If we reach here then "lucky guess" didn't give any result
- // Try walking through all the options in self::$autoResolveClasses
- foreach (self::$readers as $type => $class) {
- // Ignore our original guess, we know that won't work
- if ($type !== $guessedReader) {
- $reader = self::createReader($type);
- if ($reader->canRead($filename)) {
- return $reader;
- }
- }
- }
-
- throw new Reader\Exception('Unable to identify a reader for this file');
- }
-
- /**
- * Guess a reader type from the file extension, if any.
- *
- * @param string $filename
- *
- * @return null|string
- */
- private static function getReaderTypeFromExtension($filename)
- {
- $pathinfo = pathinfo($filename);
- if (!isset($pathinfo['extension'])) {
- return null;
- }
-
- switch (strtolower($pathinfo['extension'])) {
- case 'xlsx': // Excel (OfficeOpenXML) Spreadsheet
- case 'xlsm': // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
- case 'xltx': // Excel (OfficeOpenXML) Template
- case 'xltm': // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
- return 'Xlsx';
- case 'xls': // Excel (BIFF) Spreadsheet
- case 'xlt': // Excel (BIFF) Template
- return 'Xls';
- case 'ods': // Open/Libre Offic Calc
- case 'ots': // Open/Libre Offic Calc Template
- return 'Ods';
- case 'slk':
- return 'Slk';
- case 'xml': // Excel 2003 SpreadSheetML
- return 'Xml';
- case 'gnumeric':
- return 'Gnumeric';
- case 'htm':
- case 'html':
- return 'Html';
- case 'csv':
- // Do nothing
- // We must not try to use CSV reader since it loads
- // all files including Excel files etc.
- return null;
- default:
- return null;
- }
- }
-
- /**
- * Register a writer with its type and class name.
- *
- * @param string $writerType
- * @param string $writerClass
- */
- public static function registerWriter($writerType, $writerClass): void
- {
- if (!is_a($writerClass, Writer\IWriter::class, true)) {
- throw new Writer\Exception('Registered writers must implement ' . Writer\IWriter::class);
- }
-
- self::$writers[$writerType] = $writerClass;
- }
-
- /**
- * Register a reader with its type and class name.
- *
- * @param string $readerType
- * @param string $readerClass
- */
- public static function registerReader($readerType, $readerClass): void
- {
- if (!is_a($readerClass, Reader\IReader::class, true)) {
- throw new Reader\Exception('Registered readers must implement ' . Reader\IReader::class);
- }
-
- self::$readers[$readerType] = $readerClass;
- }
-}
diff --git a/vendor/PhpSpreadsheet/NamedFormula.php b/vendor/PhpSpreadsheet/NamedFormula.php
deleted file mode 100644
index ffb1c9b..0000000
--- a/vendor/PhpSpreadsheet/NamedFormula.php
+++ /dev/null
@@ -1,45 +0,0 @@
-value;
- }
-
- /**
- * Set the formula value.
- */
- public function setFormula(string $formula): self
- {
- if (!empty($formula)) {
- $this->value = $formula;
- }
-
- return $this;
- }
-}
diff --git a/vendor/PhpSpreadsheet/NamedRange.php b/vendor/PhpSpreadsheet/NamedRange.php
deleted file mode 100644
index db9c5f1..0000000
--- a/vendor/PhpSpreadsheet/NamedRange.php
+++ /dev/null
@@ -1,55 +0,0 @@
-value;
- }
-
- /**
- * Set the range value.
- */
- public function setRange(string $range): self
- {
- if (!empty($range)) {
- $this->value = $range;
- }
-
- return $this;
- }
-
- public function getCellsInRange(): array
- {
- $range = $this->value;
- if (substr($range, 0, 1) === '=') {
- $range = substr($range, 1);
- }
-
- return Coordinate::extractAllCellReferencesInRange($range);
- }
-}
diff --git a/vendor/PhpSpreadsheet/Reader/BaseReader.php b/vendor/PhpSpreadsheet/Reader/BaseReader.php
deleted file mode 100644
index eb0e3ba..0000000
--- a/vendor/PhpSpreadsheet/Reader/BaseReader.php
+++ /dev/null
@@ -1,161 +0,0 @@
-readFilter = new DefaultReadFilter();
- }
-
- public function getReadDataOnly()
- {
- return $this->readDataOnly;
- }
-
- public function setReadDataOnly($pValue)
- {
- $this->readDataOnly = (bool) $pValue;
-
- return $this;
- }
-
- public function getReadEmptyCells()
- {
- return $this->readEmptyCells;
- }
-
- public function setReadEmptyCells($pValue)
- {
- $this->readEmptyCells = (bool) $pValue;
-
- return $this;
- }
-
- public function getIncludeCharts()
- {
- return $this->includeCharts;
- }
-
- public function setIncludeCharts($pValue)
- {
- $this->includeCharts = (bool) $pValue;
-
- return $this;
- }
-
- public function getLoadSheetsOnly()
- {
- return $this->loadSheetsOnly;
- }
-
- public function setLoadSheetsOnly($value)
- {
- if ($value === null) {
- return $this->setLoadAllSheets();
- }
-
- $this->loadSheetsOnly = is_array($value) ? $value : [$value];
-
- return $this;
- }
-
- public function setLoadAllSheets()
- {
- $this->loadSheetsOnly = null;
-
- return $this;
- }
-
- public function getReadFilter()
- {
- return $this->readFilter;
- }
-
- public function setReadFilter(IReadFilter $pValue)
- {
- $this->readFilter = $pValue;
-
- return $this;
- }
-
- public function getSecurityScanner()
- {
- return $this->securityScanner;
- }
-
- /**
- * Open file for reading.
- *
- * @param string $pFilename
- */
- protected function openFile($pFilename): void
- {
- if ($pFilename) {
- File::assertFile($pFilename);
-
- // Open file
- $fileHandle = fopen($pFilename, 'rb');
- } else {
- $fileHandle = false;
- }
- if ($fileHandle !== false) {
- $this->fileHandle = $fileHandle;
- } else {
- throw new ReaderException('Could not open file ' . $pFilename . ' for reading.');
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Reader/Csv.php b/vendor/PhpSpreadsheet/Reader/Csv.php
deleted file mode 100644
index db2b50b..0000000
--- a/vendor/PhpSpreadsheet/Reader/Csv.php
+++ /dev/null
@@ -1,533 +0,0 @@
-inputEncoding = $pValue;
-
- return $this;
- }
-
- /**
- * Get input encoding.
- *
- * @return string
- */
- public function getInputEncoding()
- {
- return $this->inputEncoding;
- }
-
- /**
- * Move filepointer past any BOM marker.
- */
- protected function skipBOM(): void
- {
- rewind($this->fileHandle);
-
- switch ($this->inputEncoding) {
- case 'UTF-8':
- fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ?
- fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0);
-
- break;
- }
- }
-
- /**
- * Identify any separator that is explicitly set in the file.
- */
- protected function checkSeparator(): void
- {
- $line = fgets($this->fileHandle);
- if ($line === false) {
- return;
- }
-
- if ((strlen(trim($line, "\r\n")) == 5) && (stripos($line, 'sep=') === 0)) {
- $this->delimiter = substr($line, 4, 1);
-
- return;
- }
-
- $this->skipBOM();
- }
-
- /**
- * Infer the separator if it isn't explicitly set in the file or specified by the user.
- */
- protected function inferSeparator(): void
- {
- if ($this->delimiter !== null) {
- return;
- }
-
- $potentialDelimiters = [',', ';', "\t", '|', ':', ' ', '~'];
- $counts = [];
- foreach ($potentialDelimiters as $delimiter) {
- $counts[$delimiter] = [];
- }
-
- // Count how many times each of the potential delimiters appears in each line
- $numberLines = 0;
- while (($line = $this->getNextLine()) !== false && (++$numberLines < 1000)) {
- $countLine = [];
- for ($i = strlen($line) - 1; $i >= 0; --$i) {
- $char = $line[$i];
- if (isset($counts[$char])) {
- if (!isset($countLine[$char])) {
- $countLine[$char] = 0;
- }
- ++$countLine[$char];
- }
- }
- foreach ($potentialDelimiters as $delimiter) {
- $counts[$delimiter][] = $countLine[$delimiter]
- ?? 0;
- }
- }
-
- // If number of lines is 0, nothing to infer : fall back to the default
- if ($numberLines === 0) {
- $this->delimiter = reset($potentialDelimiters);
- $this->skipBOM();
-
- return;
- }
-
- // Calculate the mean square deviations for each delimiter (ignoring delimiters that haven't been found consistently)
- $meanSquareDeviations = [];
- $middleIdx = floor(($numberLines - 1) / 2);
-
- foreach ($potentialDelimiters as $delimiter) {
- $series = $counts[$delimiter];
- sort($series);
-
- $median = ($numberLines % 2)
- ? $series[$middleIdx]
- : ($series[$middleIdx] + $series[$middleIdx + 1]) / 2;
-
- if ($median === 0) {
- continue;
- }
-
- $meanSquareDeviations[$delimiter] = array_reduce(
- $series,
- function ($sum, $value) use ($median) {
- return $sum + ($value - $median) ** 2;
- }
- ) / count($series);
- }
-
- // ... and pick the delimiter with the smallest mean square deviation (in case of ties, the order in potentialDelimiters is respected)
- $min = INF;
- foreach ($potentialDelimiters as $delimiter) {
- if (!isset($meanSquareDeviations[$delimiter])) {
- continue;
- }
-
- if ($meanSquareDeviations[$delimiter] < $min) {
- $min = $meanSquareDeviations[$delimiter];
- $this->delimiter = $delimiter;
- }
- }
-
- // If no delimiter could be detected, fall back to the default
- if ($this->delimiter === null) {
- $this->delimiter = reset($potentialDelimiters);
- }
-
- $this->skipBOM();
- }
-
- /**
- * Get the next full line from the file.
- *
- * @return false|string
- */
- private function getNextLine()
- {
- $line = '';
- $enclosure = '(?escapeCharacter, '/') . ')' . preg_quote($this->enclosure, '/');
-
- do {
- // Get the next line in the file
- $newLine = fgets($this->fileHandle);
-
- // Return false if there is no next line
- if ($newLine === false) {
- return false;
- }
-
- // Add the new line to the line passed in
- $line = $line . $newLine;
-
- // Drop everything that is enclosed to avoid counting false positives in enclosures
- $line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
-
- // See if we have any enclosures left in the line
- // if we still have an enclosure then we need to read the next line as well
- } while (preg_match('/(' . $enclosure . ')/', $line) > 0);
-
- return $line;
- }
-
- /**
- * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
- *
- * @param string $pFilename
- *
- * @return array
- */
- public function listWorksheetInfo($pFilename)
- {
- // Open file
- $this->openFileOrMemory($pFilename);
- $fileHandle = $this->fileHandle;
-
- // Skip BOM, if any
- $this->skipBOM();
- $this->checkSeparator();
- $this->inferSeparator();
-
- $worksheetInfo = [];
- $worksheetInfo[0]['worksheetName'] = 'Worksheet';
- $worksheetInfo[0]['lastColumnLetter'] = 'A';
- $worksheetInfo[0]['lastColumnIndex'] = 0;
- $worksheetInfo[0]['totalRows'] = 0;
- $worksheetInfo[0]['totalColumns'] = 0;
-
- // Loop through each line of the file in turn
- while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) {
- ++$worksheetInfo[0]['totalRows'];
- $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
- }
-
- $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
- $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
-
- // Close file
- fclose($fileHandle);
-
- return $worksheetInfo;
- }
-
- /**
- * Loads Spreadsheet from file.
- *
- * @param string $pFilename
- *
- * @return Spreadsheet
- */
- public function load($pFilename)
- {
- // Create new Spreadsheet
- $spreadsheet = new Spreadsheet();
-
- // Load into this instance
- return $this->loadIntoExisting($pFilename, $spreadsheet);
- }
-
- private function openFileOrMemory($pFilename): void
- {
- // Open file
- $fhandle = $this->canRead($pFilename);
- if (!$fhandle) {
- throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
- }
- $this->openFile($pFilename);
- if ($this->inputEncoding !== 'UTF-8') {
- fclose($this->fileHandle);
- $entireFile = file_get_contents($pFilename);
- $this->fileHandle = fopen('php://memory', 'r+b');
- $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
- fwrite($this->fileHandle, $data);
- rewind($this->fileHandle);
- }
- }
-
- /**
- * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
- *
- * @param string $pFilename
- *
- * @return Spreadsheet
- */
- public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
- {
- $lineEnding = ini_get('auto_detect_line_endings');
- ini_set('auto_detect_line_endings', true);
-
- // Open file
- $this->openFileOrMemory($pFilename);
- $fileHandle = $this->fileHandle;
-
- // Skip BOM, if any
- $this->skipBOM();
- $this->checkSeparator();
- $this->inferSeparator();
-
- // Create new PhpSpreadsheet object
- while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
- $spreadsheet->createSheet();
- }
- $sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex);
-
- // Set our starting row based on whether we're in contiguous mode or not
- $currentRow = 1;
- $outRow = 0;
-
- // Loop through each line of the file in turn
- while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) {
- $noOutputYet = true;
- $columnLetter = 'A';
- foreach ($rowData as $rowDatum) {
- if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) {
- if ($this->contiguous) {
- if ($noOutputYet) {
- $noOutputYet = false;
- ++$outRow;
- }
- } else {
- $outRow = $currentRow;
- }
- // Set cell value
- $sheet->getCell($columnLetter . $outRow)->setValue($rowDatum);
- }
- ++$columnLetter;
- }
- ++$currentRow;
- }
-
- // Close file
- fclose($fileHandle);
-
- ini_set('auto_detect_line_endings', $lineEnding);
-
- // Return
- return $spreadsheet;
- }
-
- /**
- * Get delimiter.
- *
- * @return string
- */
- public function getDelimiter()
- {
- return $this->delimiter;
- }
-
- /**
- * Set delimiter.
- *
- * @param string $delimiter Delimiter, eg: ','
- *
- * @return $this
- */
- public function setDelimiter($delimiter)
- {
- $this->delimiter = $delimiter;
-
- return $this;
- }
-
- /**
- * Get enclosure.
- *
- * @return string
- */
- public function getEnclosure()
- {
- return $this->enclosure;
- }
-
- /**
- * Set enclosure.
- *
- * @param string $enclosure Enclosure, defaults to "
- *
- * @return $this
- */
- public function setEnclosure($enclosure)
- {
- if ($enclosure == '') {
- $enclosure = '"';
- }
- $this->enclosure = $enclosure;
-
- return $this;
- }
-
- /**
- * Get sheet index.
- *
- * @return int
- */
- public function getSheetIndex()
- {
- return $this->sheetIndex;
- }
-
- /**
- * Set sheet index.
- *
- * @param int $pValue Sheet index
- *
- * @return $this
- */
- public function setSheetIndex($pValue)
- {
- $this->sheetIndex = $pValue;
-
- return $this;
- }
-
- /**
- * Set Contiguous.
- *
- * @param bool $contiguous
- *
- * @return $this
- */
- public function setContiguous($contiguous)
- {
- $this->contiguous = (bool) $contiguous;
-
- return $this;
- }
-
- /**
- * Get Contiguous.
- *
- * @return bool
- */
- public function getContiguous()
- {
- return $this->contiguous;
- }
-
- /**
- * Set escape backslashes.
- *
- * @param string $escapeCharacter
- *
- * @return $this
- */
- public function setEscapeCharacter($escapeCharacter)
- {
- $this->escapeCharacter = $escapeCharacter;
-
- return $this;
- }
-
- /**
- * Get escape backslashes.
- *
- * @return string
- */
- public function getEscapeCharacter()
- {
- return $this->escapeCharacter;
- }
-
- /**
- * Can the current IReader read the file?
- *
- * @param string $pFilename
- *
- * @return bool
- */
- public function canRead($pFilename)
- {
- // Check if file exists
- try {
- $this->openFile($pFilename);
- } catch (InvalidArgumentException $e) {
- return false;
- }
-
- fclose($this->fileHandle);
-
- // Trust file extension if any
- $extension = strtolower(pathinfo($pFilename, PATHINFO_EXTENSION));
- if (in_array($extension, ['csv', 'tsv'])) {
- return true;
- }
-
- // Attempt to guess mimetype
- $type = mime_content_type($pFilename);
- $supportedTypes = [
- 'text/csv',
- 'text/plain',
- 'inode/x-empty',
- ];
-
- return in_array($type, $supportedTypes, true);
- }
-}
diff --git a/vendor/PhpSpreadsheet/Reader/DefaultReadFilter.php b/vendor/PhpSpreadsheet/Reader/DefaultReadFilter.php
deleted file mode 100644
index e104186..0000000
--- a/vendor/PhpSpreadsheet/Reader/DefaultReadFilter.php
+++ /dev/null
@@ -1,20 +0,0 @@
-referenceHelper = ReferenceHelper::getInstance();
- $this->securityScanner = XmlScanner::getInstance($this);
- }
-
- /**
- * Can the current IReader read the file?
- *
- * @param string $pFilename
- *
- * @return bool
- */
- public function canRead($pFilename)
- {
- File::assertFile($pFilename);
-
- // Check if gzlib functions are available
- $data = '';
- if (function_exists('gzread')) {
- // Read signature data (first 3 bytes)
- $fh = fopen($pFilename, 'rb');
- $data = fread($fh, 2);
- fclose($fh);
- }
-
- return $data == chr(0x1F) . chr(0x8B);
- }
-
- private static function matchXml(string $name, string $field): bool
- {
- return 1 === preg_match("/^(gnm|gmr):$field$/", $name);
- }
-
- /**
- * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
- *
- * @param string $pFilename
- *
- * @return array
- */
- public function listWorksheetNames($pFilename)
- {
- File::assertFile($pFilename);
-
- $xml = new XMLReader();
- $xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
- $xml->setParserProperty(2, true);
-
- $worksheetNames = [];
- while ($xml->read()) {
- if (self::matchXml($xml->name, 'SheetName') && $xml->nodeType == XMLReader::ELEMENT) {
- $xml->read(); // Move onto the value node
- $worksheetNames[] = (string) $xml->value;
- } elseif (self::matchXml($xml->name, 'Sheets')) {
- // break out of the loop once we've got our sheet names rather than parse the entire file
- break;
- }
- }
-
- return $worksheetNames;
- }
-
- /**
- * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
- *
- * @param string $pFilename
- *
- * @return array
- */
- public function listWorksheetInfo($pFilename)
- {
- File::assertFile($pFilename);
-
- $xml = new XMLReader();
- $xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
- $xml->setParserProperty(2, true);
-
- $worksheetInfo = [];
- while ($xml->read()) {
- if (self::matchXml($xml->name, 'Sheet') && $xml->nodeType == XMLReader::ELEMENT) {
- $tmpInfo = [
- 'worksheetName' => '',
- 'lastColumnLetter' => 'A',
- 'lastColumnIndex' => 0,
- 'totalRows' => 0,
- 'totalColumns' => 0,
- ];
-
- while ($xml->read()) {
- if ($xml->nodeType == XMLReader::ELEMENT) {
- if (self::matchXml($xml->name, 'Name')) {
- $xml->read(); // Move onto the value node
- $tmpInfo['worksheetName'] = (string) $xml->value;
- } elseif (self::matchXml($xml->name, 'MaxCol')) {
- $xml->read(); // Move onto the value node
- $tmpInfo['lastColumnIndex'] = (int) $xml->value;
- $tmpInfo['totalColumns'] = (int) $xml->value + 1;
- } elseif (self::matchXml($xml->name, 'MaxRow')) {
- $xml->read(); // Move onto the value node
- $tmpInfo['totalRows'] = (int) $xml->value + 1;
-
- break;
- }
- }
- }
- $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
- $worksheetInfo[] = $tmpInfo;
- }
- }
-
- return $worksheetInfo;
- }
-
- /**
- * @param string $filename
- *
- * @return string
- */
- private function gzfileGetContents($filename)
- {
- $file = @gzopen($filename, 'rb');
- $data = '';
- if ($file !== false) {
- while (!gzeof($file)) {
- $data .= gzread($file, 1024);
- }
- gzclose($file);
- }
-
- return $data;
- }
-
- private 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,
- ],
- 'dataType' => [
- '10' => DataType::TYPE_NULL,
- '20' => DataType::TYPE_BOOL,
- '30' => DataType::TYPE_NUMERIC, // Integer doesn't exist in Excel
- '40' => DataType::TYPE_NUMERIC, // Float
- '50' => DataType::TYPE_ERROR,
- '60' => DataType::TYPE_STRING,
- //'70': // Cell Range
- //'80': // Array
- ],
- '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 static function gnumericMappings(): array
- {
- return self::$mappings;
- }
-
- 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 = strtotime($propertyValue);
- $docProps->setCreated($creationDate);
- $docProps->setModified($creationDate);
-
- break;
- case 'description':
- $docProps->setDescription($propertyValue);
-
- break;
- }
- }
- }
-
- private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta, array $namespacesMeta): void
- {
- $docProps = $this->spreadsheet->getProperties();
- foreach ($officePropertyMeta as $propertyName => $propertyValue) {
- $attributes = $propertyValue->attributes($namespacesMeta['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 = strtotime($propertyValue);
- $docProps->setCreated($creationDate);
- $docProps->setModified($creationDate);
-
- break;
- case 'user-defined':
- [, $attrName] = explode(':', $attributes['name']);
- switch ($attrName) {
- case 'publisher':
- $docProps->setCompany($propertyValue);
-
- break;
- case 'category':
- $docProps->setCategory($propertyValue);
-
- break;
- case 'manager':
- $docProps->setManager($propertyValue);
-
- break;
- }
-
- break;
- }
- }
- }
-
- private function docProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML, array $namespacesMeta): void
- {
- if (isset($namespacesMeta['office'])) {
- $officeXML = $xml->children($namespacesMeta['office']);
- $officeDocXML = $officeXML->{'document-meta'};
- $officeDocMetaXML = $officeDocXML->meta;
-
- foreach ($officeDocMetaXML as $officePropertyData) {
- $officePropertyDC = [];
- if (isset($namespacesMeta['dc'])) {
- $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']);
- }
- $this->docPropertiesDC($officePropertyDC);
-
- $officePropertyMeta = [];
- if (isset($namespacesMeta['meta'])) {
- $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
- }
- $this->docPropertiesMeta($officePropertyMeta, $namespacesMeta);
- }
- } elseif (isset($gnmXML->Summary)) {
- $this->docPropertiesOld($gnmXML);
- }
- }
-
- private function processComments(SimpleXMLElement $sheet): void
- {
- if ((!$this->readDataOnly) && (isset($sheet->Objects))) {
- foreach ($sheet->Objects->children($this->gnm, true) as $key => $comment) {
- $commentAttributes = $comment->attributes();
- // Only comment objects are handled at the moment
- if ($commentAttributes->Text) {
- $this->spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text));
- }
- }
- }
- }
-
- /**
- * Loads Spreadsheet from file.
- *
- * @param string $pFilename
- *
- * @return Spreadsheet
- */
- public function load($pFilename)
- {
- // Create new Spreadsheet
- $spreadsheet = new Spreadsheet();
- $spreadsheet->removeSheetByIndex(0);
-
- // Load into this instance
- return $this->loadIntoExisting($pFilename, $spreadsheet);
- }
-
- /**
- * Loads from file into Spreadsheet instance.
- */
- public function loadIntoExisting(string $pFilename, Spreadsheet $spreadsheet): Spreadsheet
- {
- $this->spreadsheet = $spreadsheet;
- File::assertFile($pFilename);
-
- $gFileData = $this->gzfileGetContents($pFilename);
-
- $xml2 = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
- $xml = ($xml2 !== false) ? $xml2 : new SimpleXMLElement('= -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()
- {
- 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;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Shared/JAMA/utils/Maths.php b/vendor/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
deleted file mode 100644
index 49877b2..0000000
--- a/vendor/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
+++ /dev/null
@@ -1,31 +0,0 @@
- abs($b)) {
- $r = $b / $a;
- $r = abs($a) * sqrt(1 + $r * $r);
- } elseif ($b != 0) {
- $r = $a / $b;
- $r = abs($b) * sqrt(1 + $r * $r);
- } else {
- $r = 0.0;
- }
-
- return $r;
-}
diff --git a/vendor/PhpSpreadsheet/Shared/OLE.php b/vendor/PhpSpreadsheet/Shared/OLE.php
deleted file mode 100644
index d380995..0000000
--- a/vendor/PhpSpreadsheet/Shared/OLE.php
+++ /dev/null
@@ -1,566 +0,0 @@
- |
-// | Based on OLE::Storage_Lite by Kawai, Takanori |
-// +----------------------------------------------------------------------+
-//
-
-use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
-use PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream;
-use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root;
-
-/*
- * Array for storing OLE instances that are accessed from
- * OLE_ChainedBlockStream::stream_open().
- *
- * @var array
- */
-$GLOBALS['_OLE_INSTANCES'] = [];
-
-/**
- * OLE package base class.
- *
- * @author Xavier Noguer
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getAlignment()->applyFromArray(
- * [
- * 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
- * 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
- * 'textRotation' => 0,
- * 'wrapText' => TRUE
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())
- ->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['horizontal'])) {
- $this->setHorizontal($pStyles['horizontal']);
- }
- if (isset($pStyles['vertical'])) {
- $this->setVertical($pStyles['vertical']);
- }
- if (isset($pStyles['textRotation'])) {
- $this->setTextRotation($pStyles['textRotation']);
- }
- if (isset($pStyles['wrapText'])) {
- $this->setWrapText($pStyles['wrapText']);
- }
- if (isset($pStyles['shrinkToFit'])) {
- $this->setShrinkToFit($pStyles['shrinkToFit']);
- }
- if (isset($pStyles['indent'])) {
- $this->setIndent($pStyles['indent']);
- }
- if (isset($pStyles['readOrder'])) {
- $this->setReadOrder($pStyles['readOrder']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get Horizontal.
- *
- * @return string
- */
- public function getHorizontal()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHorizontal();
- }
-
- return $this->horizontal;
- }
-
- /**
- * Set Horizontal.
- *
- * @param string $pValue see self::HORIZONTAL_*
- *
- * @return $this
- */
- public function setHorizontal($pValue)
- {
- if ($pValue == '') {
- $pValue = self::HORIZONTAL_GENERAL;
- }
-
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['horizontal' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->horizontal = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Vertical.
- *
- * @return string
- */
- public function getVertical()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getVertical();
- }
-
- return $this->vertical;
- }
-
- /**
- * Set Vertical.
- *
- * @param string $pValue see self::VERTICAL_*
- *
- * @return $this
- */
- public function setVertical($pValue)
- {
- if ($pValue == '') {
- $pValue = self::VERTICAL_BOTTOM;
- }
-
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['vertical' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->vertical = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get TextRotation.
- *
- * @return int
- */
- public function getTextRotation()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getTextRotation();
- }
-
- return $this->textRotation;
- }
-
- /**
- * Set TextRotation.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setTextRotation($pValue)
- {
- // Excel2007 value 255 => PhpSpreadsheet value -165
- if ($pValue == 255) {
- $pValue = -165;
- }
-
- // Set rotation
- if (($pValue >= -90 && $pValue <= 90) || $pValue == -165) {
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['textRotation' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->textRotation = $pValue;
- }
- } else {
- throw new PhpSpreadsheetException('Text rotation should be a value between -90 and 90.');
- }
-
- return $this;
- }
-
- /**
- * Get Wrap Text.
- *
- * @return bool
- */
- public function getWrapText()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getWrapText();
- }
-
- return $this->wrapText;
- }
-
- /**
- * Set Wrap Text.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setWrapText($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['wrapText' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->wrapText = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Shrink to fit.
- *
- * @return bool
- */
- public function getShrinkToFit()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getShrinkToFit();
- }
-
- return $this->shrinkToFit;
- }
-
- /**
- * Set Shrink to fit.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setShrinkToFit($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['shrinkToFit' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->shrinkToFit = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get indent.
- *
- * @return int
- */
- public function getIndent()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getIndent();
- }
-
- return $this->indent;
- }
-
- /**
- * Set indent.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setIndent($pValue)
- {
- if ($pValue > 0) {
- if (
- $this->getHorizontal() != self::HORIZONTAL_GENERAL &&
- $this->getHorizontal() != self::HORIZONTAL_LEFT &&
- $this->getHorizontal() != self::HORIZONTAL_RIGHT
- ) {
- $pValue = 0; // indent not supported
- }
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['indent' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->indent = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get read order.
- *
- * @return int
- */
- public function getReadOrder()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getReadOrder();
- }
-
- return $this->readOrder;
- }
-
- /**
- * Set read order.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setReadOrder($pValue)
- {
- if ($pValue < 0 || $pValue > 2) {
- $pValue = 0;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['readOrder' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->readOrder = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
-
- return md5(
- $this->horizontal .
- $this->vertical .
- $this->textRotation .
- ($this->wrapText ? 't' : 'f') .
- ($this->shrinkToFit ? 't' : 'f') .
- $this->indent .
- $this->readOrder .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Border.php b/vendor/PhpSpreadsheet/Style/Border.php
deleted file mode 100644
index 78ad8b2..0000000
--- a/vendor/PhpSpreadsheet/Style/Border.php
+++ /dev/null
@@ -1,231 +0,0 @@
-color = new Color(Color::COLOR_BLACK, $isSupervisor);
-
- // bind parent if we are a supervisor
- if ($isSupervisor) {
- $this->color->bindParent($this, 'color');
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return Border
- */
- public function getSharedComponent()
- {
- switch ($this->parentPropertyName) {
- case 'allBorders':
- case 'horizontal':
- case 'inside':
- case 'outline':
- case 'vertical':
- throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.');
-
- break;
- case 'bottom':
- return $this->parent->getSharedComponent()->getBottom();
- case 'diagonal':
- return $this->parent->getSharedComponent()->getDiagonal();
- case 'left':
- return $this->parent->getSharedComponent()->getLeft();
- case 'right':
- return $this->parent->getSharedComponent()->getRight();
- case 'top':
- return $this->parent->getSharedComponent()->getTop();
- }
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->getTop()->applyFromArray(
- * [
- * 'borderStyle' => Border::BORDER_DASHDOT,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['borderStyle'])) {
- $this->setBorderStyle($pStyles['borderStyle']);
- }
- if (isset($pStyles['color'])) {
- $this->getColor()->applyFromArray($pStyles['color']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get Border style.
- *
- * @return string
- */
- public function getBorderStyle()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getBorderStyle();
- }
-
- return $this->borderStyle;
- }
-
- /**
- * Set Border style.
- *
- * @param bool|string $pValue
- * When passing a boolean, FALSE equates Border::BORDER_NONE
- * and TRUE to Border::BORDER_MEDIUM
- *
- * @return $this
- */
- public function setBorderStyle($pValue)
- {
- if (empty($pValue)) {
- $pValue = self::BORDER_NONE;
- } elseif (is_bool($pValue) && $pValue) {
- $pValue = self::BORDER_MEDIUM;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['borderStyle' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->borderStyle = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Border Color.
- *
- * @return Color
- */
- public function getColor()
- {
- return $this->color;
- }
-
- /**
- * Set Border Color.
- *
- * @return $this
- */
- public function setColor(Color $pValue)
- {
- // make sure parameter is a real color and not a supervisor
- $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
-
- if ($this->isSupervisor) {
- $styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->color = $color;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
-
- return md5(
- $this->borderStyle .
- $this->color->getHashCode() .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Borders.php b/vendor/PhpSpreadsheet/Style/Borders.php
deleted file mode 100644
index e75d7ee..0000000
--- a/vendor/PhpSpreadsheet/Style/Borders.php
+++ /dev/null
@@ -1,411 +0,0 @@
-left = new Border($isSupervisor, $isConditional);
- $this->right = new Border($isSupervisor, $isConditional);
- $this->top = new Border($isSupervisor, $isConditional);
- $this->bottom = new Border($isSupervisor, $isConditional);
- $this->diagonal = new Border($isSupervisor, $isConditional);
- $this->diagonalDirection = self::DIAGONAL_NONE;
-
- // Specially for supervisor
- if ($isSupervisor) {
- // Initialize pseudo-borders
- $this->allBorders = new Border(true);
- $this->outline = new Border(true);
- $this->inside = new Border(true);
- $this->vertical = new Border(true);
- $this->horizontal = new Border(true);
-
- // bind parent if we are a supervisor
- $this->left->bindParent($this, 'left');
- $this->right->bindParent($this, 'right');
- $this->top->bindParent($this, 'top');
- $this->bottom->bindParent($this, 'bottom');
- $this->diagonal->bindParent($this, 'diagonal');
- $this->allBorders->bindParent($this, 'allBorders');
- $this->outline->bindParent($this, 'outline');
- $this->inside->bindParent($this, 'inside');
- $this->vertical->bindParent($this, 'vertical');
- $this->horizontal->bindParent($this, 'horizontal');
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return Borders
- */
- public function getSharedComponent()
- {
- return $this->parent->getSharedComponent()->getBorders();
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return ['borders' => $array];
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
- * [
- * 'bottom' => [
- * 'borderStyle' => Border::BORDER_DASHDOT,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ],
- * 'top' => [
- * 'borderStyle' => Border::BORDER_DASHDOT,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ]
- * ]
- * );
- *
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
- * [
- * 'allBorders' => [
- * 'borderStyle' => Border::BORDER_DASHDOT,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ]
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['left'])) {
- $this->getLeft()->applyFromArray($pStyles['left']);
- }
- if (isset($pStyles['right'])) {
- $this->getRight()->applyFromArray($pStyles['right']);
- }
- if (isset($pStyles['top'])) {
- $this->getTop()->applyFromArray($pStyles['top']);
- }
- if (isset($pStyles['bottom'])) {
- $this->getBottom()->applyFromArray($pStyles['bottom']);
- }
- if (isset($pStyles['diagonal'])) {
- $this->getDiagonal()->applyFromArray($pStyles['diagonal']);
- }
- if (isset($pStyles['diagonalDirection'])) {
- $this->setDiagonalDirection($pStyles['diagonalDirection']);
- }
- if (isset($pStyles['allBorders'])) {
- $this->getLeft()->applyFromArray($pStyles['allBorders']);
- $this->getRight()->applyFromArray($pStyles['allBorders']);
- $this->getTop()->applyFromArray($pStyles['allBorders']);
- $this->getBottom()->applyFromArray($pStyles['allBorders']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get Left.
- *
- * @return Border
- */
- public function getLeft()
- {
- return $this->left;
- }
-
- /**
- * Get Right.
- *
- * @return Border
- */
- public function getRight()
- {
- return $this->right;
- }
-
- /**
- * Get Top.
- *
- * @return Border
- */
- public function getTop()
- {
- return $this->top;
- }
-
- /**
- * Get Bottom.
- *
- * @return Border
- */
- public function getBottom()
- {
- return $this->bottom;
- }
-
- /**
- * Get Diagonal.
- *
- * @return Border
- */
- public function getDiagonal()
- {
- return $this->diagonal;
- }
-
- /**
- * Get AllBorders (pseudo-border). Only applies to supervisor.
- *
- * @return Border
- */
- public function getAllBorders()
- {
- if (!$this->isSupervisor) {
- throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
- }
-
- return $this->allBorders;
- }
-
- /**
- * Get Outline (pseudo-border). Only applies to supervisor.
- *
- * @return Border
- */
- public function getOutline()
- {
- if (!$this->isSupervisor) {
- throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
- }
-
- return $this->outline;
- }
-
- /**
- * Get Inside (pseudo-border). Only applies to supervisor.
- *
- * @return Border
- */
- public function getInside()
- {
- if (!$this->isSupervisor) {
- throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
- }
-
- return $this->inside;
- }
-
- /**
- * Get Vertical (pseudo-border). Only applies to supervisor.
- *
- * @return Border
- */
- public function getVertical()
- {
- if (!$this->isSupervisor) {
- throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
- }
-
- return $this->vertical;
- }
-
- /**
- * Get Horizontal (pseudo-border). Only applies to supervisor.
- *
- * @return Border
- */
- public function getHorizontal()
- {
- if (!$this->isSupervisor) {
- throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
- }
-
- return $this->horizontal;
- }
-
- /**
- * Get DiagonalDirection.
- *
- * @return int
- */
- public function getDiagonalDirection()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getDiagonalDirection();
- }
-
- return $this->diagonalDirection;
- }
-
- /**
- * Set DiagonalDirection.
- *
- * @param int $pValue see self::DIAGONAL_*
- *
- * @return $this
- */
- public function setDiagonalDirection($pValue)
- {
- if ($pValue == '') {
- $pValue = self::DIAGONAL_NONE;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['diagonalDirection' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->diagonalDirection = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashcode();
- }
-
- return md5(
- $this->getLeft()->getHashCode() .
- $this->getRight()->getHashCode() .
- $this->getTop()->getHashCode() .
- $this->getBottom()->getHashCode() .
- $this->getDiagonal()->getHashCode() .
- $this->getDiagonalDirection() .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Color.php b/vendor/PhpSpreadsheet/Style/Color.php
deleted file mode 100644
index d8ba08b..0000000
--- a/vendor/PhpSpreadsheet/Style/Color.php
+++ /dev/null
@@ -1,407 +0,0 @@
-argb = $pARGB;
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return Color
- */
- public function getSharedComponent()
- {
- switch ($this->parentPropertyName) {
- case 'endColor':
- return $this->parent->getSharedComponent()->getEndColor();
- case 'color':
- return $this->parent->getSharedComponent()->getColor();
- case 'startColor':
- return $this->parent->getSharedComponent()->getStartColor();
- }
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['rgb'])) {
- $this->setRGB($pStyles['rgb']);
- }
- if (isset($pStyles['argb'])) {
- $this->setARGB($pStyles['argb']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get ARGB.
- *
- * @return string
- */
- public function getARGB()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getARGB();
- }
-
- return $this->argb;
- }
-
- /**
- * Set ARGB.
- *
- * @param string $pValue see self::COLOR_*
- *
- * @return $this
- */
- public function setARGB($pValue)
- {
- if ($pValue == '') {
- $pValue = self::COLOR_BLACK;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['argb' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->argb = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get RGB.
- *
- * @return string
- */
- public function getRGB()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getRGB();
- }
-
- return substr($this->argb, 2);
- }
-
- /**
- * Set RGB.
- *
- * @param string $pValue RGB value
- *
- * @return $this
- */
- public function setRGB($pValue)
- {
- if ($pValue == '') {
- $pValue = '000000';
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['argb' => 'FF' . $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->argb = 'FF' . $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get a specified colour component of an RGB value.
- *
- * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
- * @param int $offset Position within the RGB value to extract
- * @param bool $hex Flag indicating whether the component should be returned as a hex or a
- * decimal value
- *
- * @return string The extracted colour component
- */
- private static function getColourComponent($RGB, $offset, $hex = true)
- {
- $colour = substr($RGB, $offset, 2);
-
- return ($hex) ? $colour : hexdec($colour);
- }
-
- /**
- * Get the red colour component of an RGB value.
- *
- * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
- * @param bool $hex Flag indicating whether the component should be returned as a hex or a
- * decimal value
- *
- * @return string The red colour component
- */
- public static function getRed($RGB, $hex = true)
- {
- return self::getColourComponent($RGB, strlen($RGB) - 6, $hex);
- }
-
- /**
- * Get the green colour component of an RGB value.
- *
- * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
- * @param bool $hex Flag indicating whether the component should be returned as a hex or a
- * decimal value
- *
- * @return string The green colour component
- */
- public static function getGreen($RGB, $hex = true)
- {
- return self::getColourComponent($RGB, strlen($RGB) - 4, $hex);
- }
-
- /**
- * Get the blue colour component of an RGB value.
- *
- * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
- * @param bool $hex Flag indicating whether the component should be returned as a hex or a
- * decimal value
- *
- * @return string The blue colour component
- */
- public static function getBlue($RGB, $hex = true)
- {
- return self::getColourComponent($RGB, strlen($RGB) - 2, $hex);
- }
-
- /**
- * Adjust the brightness of a color.
- *
- * @param string $hex The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
- * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
- *
- * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
- */
- public static function changeBrightness($hex, $adjustPercentage)
- {
- $rgba = (strlen($hex) === 8);
-
- $red = self::getRed($hex, false);
- $green = self::getGreen($hex, false);
- $blue = self::getBlue($hex, false);
- if ($adjustPercentage > 0) {
- $red += (255 - $red) * $adjustPercentage;
- $green += (255 - $green) * $adjustPercentage;
- $blue += (255 - $blue) * $adjustPercentage;
- } else {
- $red += $red * $adjustPercentage;
- $green += $green * $adjustPercentage;
- $blue += $blue * $adjustPercentage;
- }
-
- if ($red < 0) {
- $red = 0;
- } elseif ($red > 255) {
- $red = 255;
- }
- if ($green < 0) {
- $green = 0;
- } elseif ($green > 255) {
- $green = 255;
- }
- if ($blue < 0) {
- $blue = 0;
- } elseif ($blue > 255) {
- $blue = 255;
- }
-
- $rgb = strtoupper(
- str_pad(dechex((int) $red), 2, '0', 0) .
- str_pad(dechex((int) $green), 2, '0', 0) .
- str_pad(dechex((int) $blue), 2, '0', 0)
- );
-
- return (($rgba) ? 'FF' : '') . $rgb;
- }
-
- /**
- * Get indexed color.
- *
- * @param int $pIndex Index entry point into the colour array
- * @param bool $background Flag to indicate whether default background or foreground colour
- * should be returned if the indexed colour doesn't exist
- *
- * @return self
- */
- public static function indexedColor($pIndex, $background = false)
- {
- // Clean parameter
- $pIndex = (int) $pIndex;
-
- // Indexed colors
- if (self::$indexedColors === null) {
- self::$indexedColors = [
- 1 => 'FF000000', // System Colour #1 - Black
- 2 => 'FFFFFFFF', // System Colour #2 - White
- 3 => 'FFFF0000', // System Colour #3 - Red
- 4 => 'FF00FF00', // System Colour #4 - Green
- 5 => 'FF0000FF', // System Colour #5 - Blue
- 6 => 'FFFFFF00', // System Colour #6 - Yellow
- 7 => 'FFFF00FF', // System Colour #7- Magenta
- 8 => 'FF00FFFF', // System Colour #8- Cyan
- 9 => 'FF800000', // Standard Colour #9
- 10 => 'FF008000', // Standard Colour #10
- 11 => 'FF000080', // Standard Colour #11
- 12 => 'FF808000', // Standard Colour #12
- 13 => 'FF800080', // Standard Colour #13
- 14 => 'FF008080', // Standard Colour #14
- 15 => 'FFC0C0C0', // Standard Colour #15
- 16 => 'FF808080', // Standard Colour #16
- 17 => 'FF9999FF', // Chart Fill Colour #17
- 18 => 'FF993366', // Chart Fill Colour #18
- 19 => 'FFFFFFCC', // Chart Fill Colour #19
- 20 => 'FFCCFFFF', // Chart Fill Colour #20
- 21 => 'FF660066', // Chart Fill Colour #21
- 22 => 'FFFF8080', // Chart Fill Colour #22
- 23 => 'FF0066CC', // Chart Fill Colour #23
- 24 => 'FFCCCCFF', // Chart Fill Colour #24
- 25 => 'FF000080', // Chart Line Colour #25
- 26 => 'FFFF00FF', // Chart Line Colour #26
- 27 => 'FFFFFF00', // Chart Line Colour #27
- 28 => 'FF00FFFF', // Chart Line Colour #28
- 29 => 'FF800080', // Chart Line Colour #29
- 30 => 'FF800000', // Chart Line Colour #30
- 31 => 'FF008080', // Chart Line Colour #31
- 32 => 'FF0000FF', // Chart Line Colour #32
- 33 => 'FF00CCFF', // Standard Colour #33
- 34 => 'FFCCFFFF', // Standard Colour #34
- 35 => 'FFCCFFCC', // Standard Colour #35
- 36 => 'FFFFFF99', // Standard Colour #36
- 37 => 'FF99CCFF', // Standard Colour #37
- 38 => 'FFFF99CC', // Standard Colour #38
- 39 => 'FFCC99FF', // Standard Colour #39
- 40 => 'FFFFCC99', // Standard Colour #40
- 41 => 'FF3366FF', // Standard Colour #41
- 42 => 'FF33CCCC', // Standard Colour #42
- 43 => 'FF99CC00', // Standard Colour #43
- 44 => 'FFFFCC00', // Standard Colour #44
- 45 => 'FFFF9900', // Standard Colour #45
- 46 => 'FFFF6600', // Standard Colour #46
- 47 => 'FF666699', // Standard Colour #47
- 48 => 'FF969696', // Standard Colour #48
- 49 => 'FF003366', // Standard Colour #49
- 50 => 'FF339966', // Standard Colour #50
- 51 => 'FF003300', // Standard Colour #51
- 52 => 'FF333300', // Standard Colour #52
- 53 => 'FF993300', // Standard Colour #53
- 54 => 'FF993366', // Standard Colour #54
- 55 => 'FF333399', // Standard Colour #55
- 56 => 'FF333333', // Standard Colour #56
- ];
- }
-
- if (isset(self::$indexedColors[$pIndex])) {
- return new self(self::$indexedColors[$pIndex]);
- }
-
- if ($background) {
- return new self(self::COLOR_WHITE);
- }
-
- return new self(self::COLOR_BLACK);
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
-
- return md5(
- $this->argb .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Conditional.php b/vendor/PhpSpreadsheet/Style/Conditional.php
deleted file mode 100644
index 35ec479..0000000
--- a/vendor/PhpSpreadsheet/Style/Conditional.php
+++ /dev/null
@@ -1,274 +0,0 @@
-style = new Style(false, true);
- }
-
- /**
- * Get Condition type.
- *
- * @return string
- */
- public function getConditionType()
- {
- return $this->conditionType;
- }
-
- /**
- * Set Condition type.
- *
- * @param string $pValue Condition type, see self::CONDITION_*
- *
- * @return $this
- */
- public function setConditionType($pValue)
- {
- $this->conditionType = $pValue;
-
- return $this;
- }
-
- /**
- * Get Operator type.
- *
- * @return string
- */
- public function getOperatorType()
- {
- return $this->operatorType;
- }
-
- /**
- * Set Operator type.
- *
- * @param string $pValue Conditional operator type, see self::OPERATOR_*
- *
- * @return $this
- */
- public function setOperatorType($pValue)
- {
- $this->operatorType = $pValue;
-
- return $this;
- }
-
- /**
- * Get text.
- *
- * @return string
- */
- public function getText()
- {
- return $this->text;
- }
-
- /**
- * Set text.
- *
- * @param string $value
- *
- * @return $this
- */
- public function setText($value)
- {
- $this->text = $value;
-
- return $this;
- }
-
- /**
- * Get StopIfTrue.
- *
- * @return bool
- */
- public function getStopIfTrue()
- {
- return $this->stopIfTrue;
- }
-
- /**
- * Set StopIfTrue.
- *
- * @param bool $value
- *
- * @return $this
- */
- public function setStopIfTrue($value)
- {
- $this->stopIfTrue = $value;
-
- return $this;
- }
-
- /**
- * Get Conditions.
- *
- * @return string[]
- */
- public function getConditions()
- {
- return $this->condition;
- }
-
- /**
- * Set Conditions.
- *
- * @param string[] $pValue Condition
- *
- * @return $this
- */
- public function setConditions($pValue)
- {
- if (!is_array($pValue)) {
- $pValue = [$pValue];
- }
- $this->condition = $pValue;
-
- return $this;
- }
-
- /**
- * Add Condition.
- *
- * @param string $pValue Condition
- *
- * @return $this
- */
- public function addCondition($pValue)
- {
- $this->condition[] = $pValue;
-
- return $this;
- }
-
- /**
- * Get Style.
- *
- * @return Style
- */
- public function getStyle()
- {
- return $this->style;
- }
-
- /**
- * Set Style.
- *
- * @param Style $pValue
- *
- * @return $this
- */
- public function setStyle(?Style $pValue = null)
- {
- $this->style = $pValue;
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- return md5(
- $this->conditionType .
- $this->operatorType .
- implode(';', $this->condition) .
- $this->style->getHashCode() .
- __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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Fill.php b/vendor/PhpSpreadsheet/Style/Fill.php
deleted file mode 100644
index c6baeed..0000000
--- a/vendor/PhpSpreadsheet/Style/Fill.php
+++ /dev/null
@@ -1,314 +0,0 @@
-fillType = null;
- }
- $this->startColor = new Color(Color::COLOR_WHITE, $isSupervisor, $isConditional);
- $this->endColor = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
-
- // bind parent if we are a supervisor
- if ($isSupervisor) {
- $this->startColor->bindParent($this, 'startColor');
- $this->endColor->bindParent($this, 'endColor');
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return Fill
- */
- public function getSharedComponent()
- {
- return $this->parent->getSharedComponent()->getFill();
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return ['fill' => $array];
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getFill()->applyFromArray(
- * [
- * 'fillType' => Fill::FILL_GRADIENT_LINEAR,
- * 'rotation' => 0,
- * 'startColor' => [
- * 'rgb' => '000000'
- * ],
- * 'endColor' => [
- * 'argb' => 'FFFFFFFF'
- * ]
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['fillType'])) {
- $this->setFillType($pStyles['fillType']);
- }
- if (isset($pStyles['rotation'])) {
- $this->setRotation($pStyles['rotation']);
- }
- if (isset($pStyles['startColor'])) {
- $this->getStartColor()->applyFromArray($pStyles['startColor']);
- }
- if (isset($pStyles['endColor'])) {
- $this->getEndColor()->applyFromArray($pStyles['endColor']);
- }
- if (isset($pStyles['color'])) {
- $this->getStartColor()->applyFromArray($pStyles['color']);
- $this->getEndColor()->applyFromArray($pStyles['color']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get Fill Type.
- *
- * @return string
- */
- public function getFillType()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getFillType();
- }
-
- return $this->fillType;
- }
-
- /**
- * Set Fill Type.
- *
- * @param string $pValue Fill type, see self::FILL_*
- *
- * @return $this
- */
- public function setFillType($pValue)
- {
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['fillType' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->fillType = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Rotation.
- *
- * @return float
- */
- public function getRotation()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getRotation();
- }
-
- return $this->rotation;
- }
-
- /**
- * Set Rotation.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setRotation($pValue)
- {
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['rotation' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->rotation = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Start Color.
- *
- * @return Color
- */
- public function getStartColor()
- {
- return $this->startColor;
- }
-
- /**
- * Set Start Color.
- *
- * @return $this
- */
- public function setStartColor(Color $pValue)
- {
- // make sure parameter is a real color and not a supervisor
- $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
-
- if ($this->isSupervisor) {
- $styleArray = $this->getStartColor()->getStyleArray(['argb' => $color->getARGB()]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->startColor = $color;
- }
-
- return $this;
- }
-
- /**
- * Get End Color.
- *
- * @return Color
- */
- public function getEndColor()
- {
- return $this->endColor;
- }
-
- /**
- * Set End Color.
- *
- * @return $this
- */
- public function setEndColor(Color $pValue)
- {
- // make sure parameter is a real color and not a supervisor
- $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
-
- if ($this->isSupervisor) {
- $styleArray = $this->getEndColor()->getStyleArray(['argb' => $color->getARGB()]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->endColor = $color;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
- // Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
- // different hashes if we don't explicitly prevent this
- return md5(
- $this->getFillType() .
- $this->getRotation() .
- ($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
- ($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Font.php b/vendor/PhpSpreadsheet/Style/Font.php
deleted file mode 100644
index eee7df0..0000000
--- a/vendor/PhpSpreadsheet/Style/Font.php
+++ /dev/null
@@ -1,548 +0,0 @@
-name = null;
- $this->size = null;
- $this->bold = null;
- $this->italic = null;
- $this->superscript = null;
- $this->subscript = null;
- $this->underline = null;
- $this->strikethrough = null;
- $this->color = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
- } else {
- $this->color = new Color(Color::COLOR_BLACK, $isSupervisor);
- }
- // bind parent if we are a supervisor
- if ($isSupervisor) {
- $this->color->bindParent($this, 'color');
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return Font
- */
- public function getSharedComponent()
- {
- return $this->parent->getSharedComponent()->getFont();
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return ['font' => $array];
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->applyFromArray(
- * [
- * 'name' => 'Arial',
- * 'bold' => TRUE,
- * 'italic' => FALSE,
- * 'underline' => \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE,
- * 'strikethrough' => FALSE,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['name'])) {
- $this->setName($pStyles['name']);
- }
- if (isset($pStyles['bold'])) {
- $this->setBold($pStyles['bold']);
- }
- if (isset($pStyles['italic'])) {
- $this->setItalic($pStyles['italic']);
- }
- if (isset($pStyles['superscript'])) {
- $this->setSuperscript($pStyles['superscript']);
- }
- if (isset($pStyles['subscript'])) {
- $this->setSubscript($pStyles['subscript']);
- }
- if (isset($pStyles['underline'])) {
- $this->setUnderline($pStyles['underline']);
- }
- if (isset($pStyles['strikethrough'])) {
- $this->setStrikethrough($pStyles['strikethrough']);
- }
- if (isset($pStyles['color'])) {
- $this->getColor()->applyFromArray($pStyles['color']);
- }
- if (isset($pStyles['size'])) {
- $this->setSize($pStyles['size']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get Name.
- *
- * @return string
- */
- public function getName()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getName();
- }
-
- return $this->name;
- }
-
- /**
- * Set Name.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setName($pValue)
- {
- if ($pValue == '') {
- $pValue = 'Calibri';
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['name' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->name = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Size.
- *
- * @return float
- */
- public function getSize()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getSize();
- }
-
- return $this->size;
- }
-
- /**
- * Set Size.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setSize($pValue)
- {
- if ($pValue == '') {
- $pValue = 10;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['size' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->size = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Bold.
- *
- * @return bool
- */
- public function getBold()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getBold();
- }
-
- return $this->bold;
- }
-
- /**
- * Set Bold.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setBold($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['bold' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->bold = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Italic.
- *
- * @return bool
- */
- public function getItalic()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getItalic();
- }
-
- return $this->italic;
- }
-
- /**
- * Set Italic.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setItalic($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['italic' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->italic = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Superscript.
- *
- * @return bool
- */
- public function getSuperscript()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getSuperscript();
- }
-
- return $this->superscript;
- }
-
- /**
- * Set Superscript.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setSuperscript($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['superscript' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->superscript = $pValue;
- $this->subscript = !$pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Subscript.
- *
- * @return bool
- */
- public function getSubscript()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getSubscript();
- }
-
- return $this->subscript;
- }
-
- /**
- * Set Subscript.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setSubscript($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['subscript' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->subscript = $pValue;
- $this->superscript = !$pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Underline.
- *
- * @return string
- */
- public function getUnderline()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getUnderline();
- }
-
- return $this->underline;
- }
-
- /**
- * Set Underline.
- *
- * @param bool|string $pValue \PhpOffice\PhpSpreadsheet\Style\Font underline type
- * If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
- * false equates to UNDERLINE_NONE
- *
- * @return $this
- */
- public function setUnderline($pValue)
- {
- if (is_bool($pValue)) {
- $pValue = ($pValue) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE;
- } elseif ($pValue == '') {
- $pValue = self::UNDERLINE_NONE;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['underline' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->underline = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Strikethrough.
- *
- * @return bool
- */
- public function getStrikethrough()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getStrikethrough();
- }
-
- return $this->strikethrough;
- }
-
- /**
- * Set Strikethrough.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setStrikethrough($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
-
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['strikethrough' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->strikethrough = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get Color.
- *
- * @return Color
- */
- public function getColor()
- {
- return $this->color;
- }
-
- /**
- * Set Color.
- *
- * @return $this
- */
- public function setColor(Color $pValue)
- {
- // make sure parameter is a real color and not a supervisor
- $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
-
- if ($this->isSupervisor) {
- $styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->color = $color;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
-
- return md5(
- $this->name .
- $this->size .
- ($this->bold ? 't' : 'f') .
- ($this->italic ? 't' : 'f') .
- ($this->superscript ? 't' : 'f') .
- ($this->subscript ? 't' : 'f') .
- $this->underline .
- ($this->strikethrough ? 't' : 'f') .
- $this->color->getHashCode() .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/NumberFormat.php b/vendor/PhpSpreadsheet/Style/NumberFormat.php
deleted file mode 100644
index 259acab..0000000
--- a/vendor/PhpSpreadsheet/Style/NumberFormat.php
+++ /dev/null
@@ -1,873 +0,0 @@
-formatCode = null;
- $this->builtInFormatCode = false;
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return NumberFormat
- */
- public function getSharedComponent()
- {
- return $this->parent->getSharedComponent()->getNumberFormat();
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return ['numberFormat' => $array];
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getNumberFormat()->applyFromArray(
- * [
- * 'formatCode' => NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['formatCode'])) {
- $this->setFormatCode($pStyles['formatCode']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get Format Code.
- *
- * @return string
- */
- public function getFormatCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getFormatCode();
- }
- if ($this->builtInFormatCode !== false) {
- return self::builtInFormatCode($this->builtInFormatCode);
- }
-
- return $this->formatCode;
- }
-
- /**
- * Set Format Code.
- *
- * @param string $pValue see self::FORMAT_*
- *
- * @return $this
- */
- public function setFormatCode($pValue)
- {
- if ($pValue == '') {
- $pValue = self::FORMAT_GENERAL;
- }
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['formatCode' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->formatCode = $pValue;
- $this->builtInFormatCode = self::builtInFormatCodeIndex($pValue);
- }
-
- return $this;
- }
-
- /**
- * Get Built-In Format Code.
- *
- * @return int
- */
- public function getBuiltInFormatCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getBuiltInFormatCode();
- }
-
- return $this->builtInFormatCode;
- }
-
- /**
- * Set Built-In Format Code.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setBuiltInFormatCode($pValue)
- {
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['formatCode' => self::builtInFormatCode($pValue)]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->builtInFormatCode = $pValue;
- $this->formatCode = self::builtInFormatCode($pValue);
- }
-
- return $this;
- }
-
- /**
- * Fill built-in format codes.
- */
- private static function fillBuiltInFormatCodes(): void
- {
- // [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance]
- // 18.8.30. numFmt (Number Format)
- //
- // The ECMA standard defines built-in format IDs
- // 14: "mm-dd-yy"
- // 22: "m/d/yy h:mm"
- // 37: "#,##0 ;(#,##0)"
- // 38: "#,##0 ;[Red](#,##0)"
- // 39: "#,##0.00;(#,##0.00)"
- // 40: "#,##0.00;[Red](#,##0.00)"
- // 47: "mmss.0"
- // KOR fmt 55: "yyyy-mm-dd"
- // Excel defines built-in format IDs
- // 14: "m/d/yyyy"
- // 22: "m/d/yyyy h:mm"
- // 37: "#,##0_);(#,##0)"
- // 38: "#,##0_);[Red](#,##0)"
- // 39: "#,##0.00_);(#,##0.00)"
- // 40: "#,##0.00_);[Red](#,##0.00)"
- // 47: "mm:ss.0"
- // KOR fmt 55: "yyyy/mm/dd"
-
- // Built-in format codes
- if (self::$builtInFormats === null) {
- self::$builtInFormats = [];
-
- // General
- self::$builtInFormats[0] = self::FORMAT_GENERAL;
- self::$builtInFormats[1] = '0';
- self::$builtInFormats[2] = '0.00';
- self::$builtInFormats[3] = '#,##0';
- self::$builtInFormats[4] = '#,##0.00';
-
- self::$builtInFormats[9] = '0%';
- self::$builtInFormats[10] = '0.00%';
- self::$builtInFormats[11] = '0.00E+00';
- self::$builtInFormats[12] = '# ?/?';
- self::$builtInFormats[13] = '# ??/??';
- self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
- self::$builtInFormats[15] = 'd-mmm-yy';
- self::$builtInFormats[16] = 'd-mmm';
- self::$builtInFormats[17] = 'mmm-yy';
- self::$builtInFormats[18] = 'h:mm AM/PM';
- self::$builtInFormats[19] = 'h:mm:ss AM/PM';
- self::$builtInFormats[20] = 'h:mm';
- self::$builtInFormats[21] = 'h:mm:ss';
- self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
-
- self::$builtInFormats[37] = '#,##0_);(#,##0)'; // Despite ECMA '#,##0 ;(#,##0)';
- self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; // Despite ECMA '#,##0 ;[Red](#,##0)';
- self::$builtInFormats[39] = '#,##0.00_);(#,##0.00)'; // Despite ECMA '#,##0.00;(#,##0.00)';
- self::$builtInFormats[40] = '#,##0.00_);[Red](#,##0.00)'; // Despite ECMA '#,##0.00;[Red](#,##0.00)';
-
- self::$builtInFormats[44] = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
- self::$builtInFormats[45] = 'mm:ss';
- self::$builtInFormats[46] = '[h]:mm:ss';
- self::$builtInFormats[47] = 'mm:ss.0'; // Despite ECMA 'mmss.0';
- self::$builtInFormats[48] = '##0.0E+0';
- self::$builtInFormats[49] = '@';
-
- // CHT
- self::$builtInFormats[27] = '[$-404]e/m/d';
- self::$builtInFormats[30] = 'm/d/yy';
- self::$builtInFormats[36] = '[$-404]e/m/d';
- self::$builtInFormats[50] = '[$-404]e/m/d';
- self::$builtInFormats[57] = '[$-404]e/m/d';
-
- // THA
- self::$builtInFormats[59] = 't0';
- self::$builtInFormats[60] = 't0.00';
- self::$builtInFormats[61] = 't#,##0';
- self::$builtInFormats[62] = 't#,##0.00';
- self::$builtInFormats[67] = 't0%';
- self::$builtInFormats[68] = 't0.00%';
- self::$builtInFormats[69] = 't# ?/?';
- self::$builtInFormats[70] = 't# ??/??';
-
- // JPN
- self::$builtInFormats[28] = '[$-411]ggge"年"m"月"d"日"';
- self::$builtInFormats[29] = '[$-411]ggge"年"m"月"d"日"';
- self::$builtInFormats[31] = 'yyyy"年"m"月"d"日"';
- self::$builtInFormats[32] = 'h"時"mm"分"';
- self::$builtInFormats[33] = 'h"時"mm"分"ss"秒"';
- self::$builtInFormats[34] = 'yyyy"年"m"月"';
- self::$builtInFormats[35] = 'm"月"d"日"';
- self::$builtInFormats[51] = '[$-411]ggge"年"m"月"d"日"';
- self::$builtInFormats[52] = 'yyyy"年"m"月"';
- self::$builtInFormats[53] = 'm"月"d"日"';
- self::$builtInFormats[54] = '[$-411]ggge"年"m"月"d"日"';
- self::$builtInFormats[55] = 'yyyy"年"m"月"';
- self::$builtInFormats[56] = 'm"月"d"日"';
- self::$builtInFormats[58] = '[$-411]ggge"年"m"月"d"日"';
-
- // Flip array (for faster lookups)
- self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
- }
- }
-
- /**
- * Get built-in format code.
- *
- * @param int $pIndex
- *
- * @return string
- */
- public static function builtInFormatCode($pIndex)
- {
- // Clean parameter
- $pIndex = (int) $pIndex;
-
- // Ensure built-in format codes are available
- self::fillBuiltInFormatCodes();
-
- // Lookup format code
- if (isset(self::$builtInFormats[$pIndex])) {
- return self::$builtInFormats[$pIndex];
- }
-
- return '';
- }
-
- /**
- * Get built-in format code index.
- *
- * @param string $formatCode
- *
- * @return bool|int
- */
- public static function builtInFormatCodeIndex($formatCode)
- {
- // Ensure built-in format codes are available
- self::fillBuiltInFormatCodes();
-
- // Lookup format code
- if (array_key_exists($formatCode, self::$flippedBuiltInFormats)) {
- return self::$flippedBuiltInFormats[$formatCode];
- }
-
- return false;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
-
- return md5(
- $this->formatCode .
- $this->builtInFormatCode .
- __CLASS__
- );
- }
-
- /**
- * Search/replace values to convert Excel date/time format masks to PHP format masks.
- *
- * @var array
- */
- private static $dateFormatReplacements = [
- // first remove escapes related to non-format characters
- '\\' => '',
- // 12-hour suffix
- 'am/pm' => 'A',
- // 4-digit year
- 'e' => 'Y',
- 'yyyy' => 'Y',
- // 2-digit year
- 'yy' => 'y',
- // first letter of month - no php equivalent
- 'mmmmm' => 'M',
- // full month name
- 'mmmm' => 'F',
- // short month name
- 'mmm' => 'M',
- // mm is minutes if time, but can also be month w/leading zero
- // so we try to identify times be the inclusion of a : separator in the mask
- // It isn't perfect, but the best way I know how
- ':mm' => ':i',
- 'mm:' => 'i:',
- // month leading zero
- 'mm' => 'm',
- // month no leading zero
- 'm' => 'n',
- // full day of week name
- 'dddd' => 'l',
- // short day of week name
- 'ddd' => 'D',
- // days leading zero
- 'dd' => 'd',
- // days no leading zero
- 'd' => 'j',
- // seconds
- 'ss' => 's',
- // fractional seconds - no php equivalent
- '.s' => '',
- ];
-
- /**
- * Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock).
- *
- * @var array
- */
- private static $dateFormatReplacements24 = [
- 'hh' => 'H',
- 'h' => 'G',
- ];
-
- /**
- * Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock).
- *
- * @var array
- */
- private static $dateFormatReplacements12 = [
- 'hh' => 'h',
- 'h' => 'g',
- ];
-
- private static function setLowercaseCallback($matches)
- {
- return mb_strtolower($matches[0]);
- }
-
- private static function escapeQuotesCallback($matches)
- {
- return '\\' . implode('\\', str_split($matches[1]));
- }
-
- private static function formatAsDate(&$value, &$format): void
- {
- // strip off first part containing e.g. [$-F800] or [$USD-409]
- // general syntax: [$
- * $spreadsheet->getActiveSheet()->getStyle('B2')->getLocked()->applyFromArray(
- * [
- * 'locked' => TRUE,
- * 'hidden' => FALSE
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles)
- {
- if ($this->isSupervisor) {
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
- } else {
- if (isset($pStyles['locked'])) {
- $this->setLocked($pStyles['locked']);
- }
- if (isset($pStyles['hidden'])) {
- $this->setHidden($pStyles['hidden']);
- }
- }
-
- return $this;
- }
-
- /**
- * Get locked.
- *
- * @return string
- */
- public function getLocked()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getLocked();
- }
-
- return $this->locked;
- }
-
- /**
- * Set locked.
- *
- * @param string $pValue see self::PROTECTION_*
- *
- * @return $this
- */
- public function setLocked($pValue)
- {
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['locked' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->locked = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get hidden.
- *
- * @return string
- */
- public function getHidden()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHidden();
- }
-
- return $this->hidden;
- }
-
- /**
- * Set hidden.
- *
- * @param string $pValue see self::PROTECTION_*
- *
- * @return $this
- */
- public function setHidden($pValue)
- {
- if ($this->isSupervisor) {
- $styleArray = $this->getStyleArray(['hidden' => $pValue]);
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->hidden = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getHashCode();
- }
-
- return md5(
- $this->locked .
- $this->hidden .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Style.php b/vendor/PhpSpreadsheet/Style/Style.php
deleted file mode 100644
index 533a7c3..0000000
--- a/vendor/PhpSpreadsheet/Style/Style.php
+++ /dev/null
@@ -1,639 +0,0 @@
-conditionalStyles = [];
- $this->font = new Font($isSupervisor, $isConditional);
- $this->fill = new Fill($isSupervisor, $isConditional);
- $this->borders = new Borders($isSupervisor, $isConditional);
- $this->alignment = new Alignment($isSupervisor, $isConditional);
- $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
- $this->protection = new Protection($isSupervisor, $isConditional);
-
- // bind parent if we are a supervisor
- if ($isSupervisor) {
- $this->font->bindParent($this);
- $this->fill->bindParent($this);
- $this->borders->bindParent($this);
- $this->alignment->bindParent($this);
- $this->numberFormat->bindParent($this);
- $this->protection->bindParent($this);
- }
- }
-
- /**
- * Get the shared style component for the currently active cell in currently active sheet.
- * Only used for style supervisor.
- *
- * @return Style
- */
- public function getSharedComponent()
- {
- $activeSheet = $this->getActiveSheet();
- $selectedCell = $this->getActiveCell(); // e.g. 'A1'
-
- if ($activeSheet->cellExists($selectedCell)) {
- $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
- } else {
- $xfIndex = 0;
- }
-
- return $this->parent->getCellXfByIndex($xfIndex);
- }
-
- /**
- * Get parent. Only used for style supervisor.
- *
- * @return Spreadsheet
- */
- public function getParent()
- {
- return $this->parent;
- }
-
- /**
- * Build style array from subcomponents.
- *
- * @param array $array
- *
- * @return array
- */
- public function getStyleArray($array)
- {
- return ['quotePrefix' => $array];
- }
-
- /**
- * Apply styles from array.
- *
- *
- * $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray(
- * [
- * 'font' => [
- * 'name' => 'Arial',
- * 'bold' => true,
- * 'italic' => false,
- * 'underline' => Font::UNDERLINE_DOUBLE,
- * 'strikethrough' => false,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ],
- * 'borders' => [
- * 'bottom' => [
- * 'borderStyle' => Border::BORDER_DASHDOT,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ],
- * 'top' => [
- * 'borderStyle' => Border::BORDER_DASHDOT,
- * 'color' => [
- * 'rgb' => '808080'
- * ]
- * ]
- * ],
- * 'alignment' => [
- * 'horizontal' => Alignment::HORIZONTAL_CENTER,
- * 'vertical' => Alignment::VERTICAL_CENTER,
- * 'wrapText' => true,
- * ],
- * 'quotePrefix' => true
- * ]
- * );
- *
- *
- * @param array $pStyles Array containing style information
- * @param bool $pAdvanced advanced mode for setting borders
- *
- * @return $this
- */
- public function applyFromArray(array $pStyles, $pAdvanced = true)
- {
- if ($this->isSupervisor) {
- $pRange = $this->getSelectedCells();
-
- // Uppercase coordinate
- $pRange = strtoupper($pRange);
-
- // Is it a cell range or a single cell?
- if (strpos($pRange, ':') === false) {
- $rangeA = $pRange;
- $rangeB = $pRange;
- } else {
- [$rangeA, $rangeB] = explode(':', $pRange);
- }
-
- // Calculate range outer borders
- $rangeStart = Coordinate::coordinateFromString($rangeA);
- $rangeEnd = Coordinate::coordinateFromString($rangeB);
-
- // Translate column into index
- $rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]);
- $rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]);
-
- // Make sure we can loop upwards on rows and columns
- if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
- $tmp = $rangeStart;
- $rangeStart = $rangeEnd;
- $rangeEnd = $tmp;
- }
-
- // ADVANCED MODE:
- if ($pAdvanced && isset($pStyles['borders'])) {
- // 'allBorders' is a shorthand property for 'outline' and 'inside' and
- // it applies to components that have not been set explicitly
- if (isset($pStyles['borders']['allBorders'])) {
- foreach (['outline', 'inside'] as $component) {
- if (!isset($pStyles['borders'][$component])) {
- $pStyles['borders'][$component] = $pStyles['borders']['allBorders'];
- }
- }
- unset($pStyles['borders']['allBorders']); // not needed any more
- }
- // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
- // it applies to components that have not been set explicitly
- if (isset($pStyles['borders']['outline'])) {
- foreach (['top', 'right', 'bottom', 'left'] as $component) {
- if (!isset($pStyles['borders'][$component])) {
- $pStyles['borders'][$component] = $pStyles['borders']['outline'];
- }
- }
- unset($pStyles['borders']['outline']); // not needed any more
- }
- // 'inside' is a shorthand property for 'vertical' and 'horizontal'
- // it applies to components that have not been set explicitly
- if (isset($pStyles['borders']['inside'])) {
- foreach (['vertical', 'horizontal'] as $component) {
- if (!isset($pStyles['borders'][$component])) {
- $pStyles['borders'][$component] = $pStyles['borders']['inside'];
- }
- }
- unset($pStyles['borders']['inside']); // not needed any more
- }
- // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
- $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
- $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);
-
- // loop through up to 3 x 3 = 9 regions
- for ($x = 1; $x <= $xMax; ++$x) {
- // start column index for region
- $colStart = ($x == 3) ?
- Coordinate::stringFromColumnIndex($rangeEnd[0])
- : Coordinate::stringFromColumnIndex($rangeStart[0] + $x - 1);
- // end column index for region
- $colEnd = ($x == 1) ?
- Coordinate::stringFromColumnIndex($rangeStart[0])
- : Coordinate::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
-
- for ($y = 1; $y <= $yMax; ++$y) {
- // which edges are touching the region
- $edges = [];
- if ($x == 1) {
- // are we at left edge
- $edges[] = 'left';
- }
- if ($x == $xMax) {
- // are we at right edge
- $edges[] = 'right';
- }
- if ($y == 1) {
- // are we at top edge?
- $edges[] = 'top';
- }
- if ($y == $yMax) {
- // are we at bottom edge?
- $edges[] = 'bottom';
- }
-
- // start row index for region
- $rowStart = ($y == 3) ?
- $rangeEnd[1] : $rangeStart[1] + $y - 1;
-
- // end row index for region
- $rowEnd = ($y == 1) ?
- $rangeStart[1] : $rangeEnd[1] - $yMax + $y;
-
- // build range for region
- $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
-
- // retrieve relevant style array for region
- $regionStyles = $pStyles;
- unset($regionStyles['borders']['inside']);
-
- // what are the inner edges of the region when looking at the selection
- $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);
-
- // inner edges that are not touching the region should take the 'inside' border properties if they have been set
- foreach ($innerEdges as $innerEdge) {
- switch ($innerEdge) {
- case 'top':
- case 'bottom':
- // should pick up 'horizontal' border property if set
- if (isset($pStyles['borders']['horizontal'])) {
- $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
- } else {
- unset($regionStyles['borders'][$innerEdge]);
- }
-
- break;
- case 'left':
- case 'right':
- // should pick up 'vertical' border property if set
- if (isset($pStyles['borders']['vertical'])) {
- $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
- } else {
- unset($regionStyles['borders'][$innerEdge]);
- }
-
- break;
- }
- }
-
- // apply region style to region by calling applyFromArray() in simple mode
- $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
- }
- }
-
- // restore initial cell selection range
- $this->getActiveSheet()->getStyle($pRange);
-
- return $this;
- }
-
- // SIMPLE MODE:
- // Selection type, inspect
- if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
- $selectionType = 'COLUMN';
- } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
- $selectionType = 'ROW';
- } else {
- $selectionType = 'CELL';
- }
-
- // First loop through columns, rows, or cells to find out which styles are affected by this operation
- switch ($selectionType) {
- case 'COLUMN':
- $oldXfIndexes = [];
- for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
- $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
- }
-
- break;
- case 'ROW':
- $oldXfIndexes = [];
- for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
- if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
- $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
- } else {
- $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
- }
- }
-
- break;
- case 'CELL':
- $oldXfIndexes = [];
- for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
- for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
- $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
- }
- }
-
- break;
- }
-
- // clone each of the affected styles, apply the style array, and add the new styles to the workbook
- $workbook = $this->getActiveSheet()->getParent();
- foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
- $style = $workbook->getCellXfByIndex($oldXfIndex);
- $newStyle = clone $style;
- $newStyle->applyFromArray($pStyles);
-
- if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
- // there is already such cell Xf in our collection
- $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
- } else {
- // we don't have such a cell Xf, need to add
- $workbook->addCellXf($newStyle);
- $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
- }
- }
-
- // Loop through columns, rows, or cells again and update the XF index
- switch ($selectionType) {
- case 'COLUMN':
- for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
- $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
- $oldXfIndex = $columnDimension->getXfIndex();
- $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
- }
-
- break;
- case 'ROW':
- for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
- $rowDimension = $this->getActiveSheet()->getRowDimension($row);
- $oldXfIndex = $rowDimension->getXfIndex() === null ?
- 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
- $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
- }
-
- break;
- case 'CELL':
- for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
- for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
- $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
- $oldXfIndex = $cell->getXfIndex();
- $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
- }
- }
-
- break;
- }
- } else {
- // not a supervisor, just apply the style array directly on style object
- if (isset($pStyles['fill'])) {
- $this->getFill()->applyFromArray($pStyles['fill']);
- }
- if (isset($pStyles['font'])) {
- $this->getFont()->applyFromArray($pStyles['font']);
- }
- if (isset($pStyles['borders'])) {
- $this->getBorders()->applyFromArray($pStyles['borders']);
- }
- if (isset($pStyles['alignment'])) {
- $this->getAlignment()->applyFromArray($pStyles['alignment']);
- }
- if (isset($pStyles['numberFormat'])) {
- $this->getNumberFormat()->applyFromArray($pStyles['numberFormat']);
- }
- if (isset($pStyles['protection'])) {
- $this->getProtection()->applyFromArray($pStyles['protection']);
- }
- if (isset($pStyles['quotePrefix'])) {
- $this->quotePrefix = $pStyles['quotePrefix'];
- }
- }
-
- return $this;
- }
-
- /**
- * Get Fill.
- *
- * @return Fill
- */
- public function getFill()
- {
- return $this->fill;
- }
-
- /**
- * Get Font.
- *
- * @return Font
- */
- public function getFont()
- {
- return $this->font;
- }
-
- /**
- * Set font.
- *
- * @return $this
- */
- public function setFont(Font $font)
- {
- $this->font = $font;
-
- return $this;
- }
-
- /**
- * Get Borders.
- *
- * @return Borders
- */
- public function getBorders()
- {
- return $this->borders;
- }
-
- /**
- * Get Alignment.
- *
- * @return Alignment
- */
- public function getAlignment()
- {
- return $this->alignment;
- }
-
- /**
- * Get Number Format.
- *
- * @return NumberFormat
- */
- public function getNumberFormat()
- {
- return $this->numberFormat;
- }
-
- /**
- * Get Conditional Styles. Only used on supervisor.
- *
- * @return Conditional[]
- */
- public function getConditionalStyles()
- {
- return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
- }
-
- /**
- * Set Conditional Styles. Only used on supervisor.
- *
- * @param Conditional[] $pValue Array of conditional styles
- *
- * @return $this
- */
- public function setConditionalStyles(array $pValue)
- {
- $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
-
- return $this;
- }
-
- /**
- * Get Protection.
- *
- * @return Protection
- */
- public function getProtection()
- {
- return $this->protection;
- }
-
- /**
- * Get quote prefix.
- *
- * @return bool
- */
- public function getQuotePrefix()
- {
- if ($this->isSupervisor) {
- return $this->getSharedComponent()->getQuotePrefix();
- }
-
- return $this->quotePrefix;
- }
-
- /**
- * Set quote prefix.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setQuotePrefix($pValue)
- {
- if ($pValue == '') {
- $pValue = false;
- }
- if ($this->isSupervisor) {
- $styleArray = ['quotePrefix' => $pValue];
- $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
- } else {
- $this->quotePrefix = (bool) $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- $hashConditionals = '';
- foreach ($this->conditionalStyles as $conditional) {
- $hashConditionals .= $conditional->getHashCode();
- }
-
- return md5(
- $this->fill->getHashCode() .
- $this->font->getHashCode() .
- $this->borders->getHashCode() .
- $this->alignment->getHashCode() .
- $this->numberFormat->getHashCode() .
- $hashConditionals .
- $this->protection->getHashCode() .
- ($this->quotePrefix ? 't' : 'f') .
- __CLASS__
- );
- }
-
- /**
- * Get own index in style collection.
- *
- * @return int
- */
- public function getIndex()
- {
- return $this->index;
- }
-
- /**
- * Set own index in style collection.
- *
- * @param int $pValue
- */
- public function setIndex($pValue): void
- {
- $this->index = $pValue;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Style/Supervisor.php b/vendor/PhpSpreadsheet/Style/Supervisor.php
deleted file mode 100644
index 1a70097..0000000
--- a/vendor/PhpSpreadsheet/Style/Supervisor.php
+++ /dev/null
@@ -1,117 +0,0 @@
-isSupervisor = $isSupervisor;
- }
-
- /**
- * Bind parent. Only used for supervisor.
- *
- * @param Spreadsheet|Style $parent
- * @param null|string $parentPropertyName
- *
- * @return $this
- */
- public function bindParent($parent, $parentPropertyName = null)
- {
- $this->parent = $parent;
- $this->parentPropertyName = $parentPropertyName;
-
- return $this;
- }
-
- /**
- * Is this a supervisor or a cell style component?
- *
- * @return bool
- */
- public function getIsSupervisor()
- {
- return $this->isSupervisor;
- }
-
- /**
- * Get the currently active sheet. Only used for supervisor.
- *
- * @return Worksheet
- */
- public function getActiveSheet()
- {
- return $this->parent->getActiveSheet();
- }
-
- /**
- * Get the currently active cell coordinate in currently active sheet.
- * Only used for supervisor.
- *
- * @return string E.g. 'A1'
- */
- public function getSelectedCells()
- {
- return $this->getActiveSheet()->getSelectedCells();
- }
-
- /**
- * Get the currently active cell coordinate in currently active sheet.
- * Only used for supervisor.
- *
- * @return string E.g. 'A1'
- */
- public function getActiveCell()
- {
- return $this->getActiveSheet()->getActiveCell();
- }
-
- /**
- * 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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/AutoFilter.php b/vendor/PhpSpreadsheet/Worksheet/AutoFilter.php
deleted file mode 100644
index c2ded19..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/AutoFilter.php
+++ /dev/null
@@ -1,871 +0,0 @@
-range = $pRange;
- $this->workSheet = $pSheet;
- }
-
- /**
- * Get AutoFilter Parent Worksheet.
- *
- * @return Worksheet
- */
- public function getParent()
- {
- return $this->workSheet;
- }
-
- /**
- * Set AutoFilter Parent Worksheet.
- *
- * @param Worksheet $pSheet
- *
- * @return $this
- */
- public function setParent(?Worksheet $pSheet = null)
- {
- $this->workSheet = $pSheet;
-
- return $this;
- }
-
- /**
- * Get AutoFilter Range.
- *
- * @return string
- */
- public function getRange()
- {
- return $this->range;
- }
-
- /**
- * Set AutoFilter Range.
- *
- * @param string $pRange Cell range (i.e. A1:E10)
- *
- * @return $this
- */
- public function setRange($pRange)
- {
- // extract coordinate
- [$worksheet, $pRange] = Worksheet::extractSheetTitle($pRange, true);
-
- if (strpos($pRange, ':') !== false) {
- $this->range = $pRange;
- } elseif (empty($pRange)) {
- $this->range = '';
- } else {
- throw new PhpSpreadsheetException('Autofilter must be set on a range of cells.');
- }
-
- if (empty($pRange)) {
- // Discard all column rules
- $this->columns = [];
- } else {
- // Discard any column rules that are no longer valid within this range
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
- foreach ($this->columns as $key => $value) {
- $colIndex = Coordinate::columnIndexFromString($key);
- if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
- unset($this->columns[$key]);
- }
- }
- }
-
- return $this;
- }
-
- /**
- * Get all AutoFilter Columns.
- *
- * @return AutoFilter\Column[]
- */
- public function getColumns()
- {
- return $this->columns;
- }
-
- /**
- * Validate that the specified column is in the AutoFilter range.
- *
- * @param string $column Column name (e.g. A)
- *
- * @return int The column offset within the autofilter range
- */
- public function testColumnInRange($column)
- {
- if (empty($this->range)) {
- throw new PhpSpreadsheetException('No autofilter range is defined.');
- }
-
- $columnIndex = Coordinate::columnIndexFromString($column);
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
- if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
- throw new PhpSpreadsheetException('Column is outside of current autofilter range.');
- }
-
- return $columnIndex - $rangeStart[0];
- }
-
- /**
- * Get a specified AutoFilter Column Offset within the defined AutoFilter range.
- *
- * @param string $pColumn Column name (e.g. A)
- *
- * @return int The offset of the specified column within the autofilter range
- */
- public function getColumnOffset($pColumn)
- {
- return $this->testColumnInRange($pColumn);
- }
-
- /**
- * Get a specified AutoFilter Column.
- *
- * @param string $pColumn Column name (e.g. A)
- *
- * @return AutoFilter\Column
- */
- public function getColumn($pColumn)
- {
- $this->testColumnInRange($pColumn);
-
- if (!isset($this->columns[$pColumn])) {
- $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this);
- }
-
- return $this->columns[$pColumn];
- }
-
- /**
- * Get a specified AutoFilter Column by it's offset.
- *
- * @param int $pColumnOffset Column offset within range (starting from 0)
- *
- * @return AutoFilter\Column
- */
- public function getColumnByOffset($pColumnOffset)
- {
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
- $pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $pColumnOffset);
-
- return $this->getColumn($pColumn);
- }
-
- /**
- * Set AutoFilter.
- *
- * @param AutoFilter\Column|string $pColumn
- * A simple string containing a Column ID like 'A' is permitted
- *
- * @return $this
- */
- public function setColumn($pColumn)
- {
- if ((is_string($pColumn)) && (!empty($pColumn))) {
- $column = $pColumn;
- } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) {
- $column = $pColumn->getColumnIndex();
- } else {
- throw new PhpSpreadsheetException('Column is not within the autofilter range.');
- }
- $this->testColumnInRange($column);
-
- if (is_string($pColumn)) {
- $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this);
- } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) {
- $pColumn->setParent($this);
- $this->columns[$column] = $pColumn;
- }
- ksort($this->columns);
-
- return $this;
- }
-
- /**
- * Clear a specified AutoFilter Column.
- *
- * @param string $pColumn Column name (e.g. A)
- *
- * @return $this
- */
- public function clearColumn($pColumn)
- {
- $this->testColumnInRange($pColumn);
-
- if (isset($this->columns[$pColumn])) {
- unset($this->columns[$pColumn]);
- }
-
- return $this;
- }
-
- /**
- * Shift an AutoFilter Column Rule to a different column.
- *
- * Note: This method bypasses validation of the destination column to ensure it is within this AutoFilter range.
- * Nor does it verify whether any column rule already exists at $toColumn, but will simply override any existing value.
- * Use with caution.
- *
- * @param string $fromColumn Column name (e.g. A)
- * @param string $toColumn Column name (e.g. B)
- *
- * @return $this
- */
- public function shiftColumn($fromColumn, $toColumn)
- {
- $fromColumn = strtoupper($fromColumn);
- $toColumn = strtoupper($toColumn);
-
- if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) {
- $this->columns[$fromColumn]->setParent();
- $this->columns[$fromColumn]->setColumnIndex($toColumn);
- $this->columns[$toColumn] = $this->columns[$fromColumn];
- $this->columns[$toColumn]->setParent($this);
- unset($this->columns[$fromColumn]);
-
- ksort($this->columns);
- }
-
- return $this;
- }
-
- /**
- * Test if cell value is in the defined set of values.
- *
- * @param mixed $cellValue
- * @param mixed[] $dataSet
- *
- * @return bool
- */
- private static function filterTestInSimpleDataSet($cellValue, $dataSet)
- {
- $dataSetValues = $dataSet['filterValues'];
- $blanks = $dataSet['blanks'];
- if (($cellValue == '') || ($cellValue === null)) {
- return $blanks;
- }
-
- return in_array($cellValue, $dataSetValues);
- }
-
- /**
- * Test if cell value is in the defined set of Excel date values.
- *
- * @param mixed $cellValue
- * @param mixed[] $dataSet
- *
- * @return bool
- */
- private static function filterTestInDateGroupSet($cellValue, $dataSet)
- {
- $dateSet = $dataSet['filterValues'];
- $blanks = $dataSet['blanks'];
- if (($cellValue == '') || ($cellValue === null)) {
- return $blanks;
- }
-
- if (is_numeric($cellValue)) {
- $dateValue = Date::excelToTimestamp($cellValue);
- if ($cellValue < 1) {
- // Just the time part
- $dtVal = date('His', $dateValue);
- $dateSet = $dateSet['time'];
- } elseif ($cellValue == floor($cellValue)) {
- // Just the date part
- $dtVal = date('Ymd', $dateValue);
- $dateSet = $dateSet['date'];
- } else {
- // date and time parts
- $dtVal = date('YmdHis', $dateValue);
- $dateSet = $dateSet['dateTime'];
- }
- foreach ($dateSet as $dateValue) {
- // Use of substr to extract value at the appropriate group level
- if (substr($dtVal, 0, strlen($dateValue)) == $dateValue) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Test if cell value is within a set of values defined by a ruleset.
- *
- * @param mixed $cellValue
- * @param mixed[] $ruleSet
- *
- * @return bool
- */
- private static function filterTestInCustomDataSet($cellValue, $ruleSet)
- {
- $dataSet = $ruleSet['filterRules'];
- $join = $ruleSet['join'];
- $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
-
- if (!$customRuleForBlanks) {
- // Blank cells are always ignored, so return a FALSE
- if (($cellValue == '') || ($cellValue === null)) {
- return false;
- }
- }
- $returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND);
- foreach ($dataSet as $rule) {
- $retVal = false;
-
- if (is_numeric($rule['value'])) {
- // Numeric values are tested using the appropriate operator
- switch ($rule['operator']) {
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
- $retVal = ($cellValue == $rule['value']);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
- $retVal = ($cellValue != $rule['value']);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
- $retVal = ($cellValue > $rule['value']);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
- $retVal = ($cellValue >= $rule['value']);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
- $retVal = ($cellValue < $rule['value']);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
- $retVal = ($cellValue <= $rule['value']);
-
- break;
- }
- } elseif ($rule['value'] == '') {
- switch ($rule['operator']) {
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
- $retVal = (($cellValue == '') || ($cellValue === null));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
- $retVal = (($cellValue != '') && ($cellValue !== null));
-
- break;
- default:
- $retVal = true;
-
- break;
- }
- } else {
- // String values are always tested for equality, factoring in for wildcards (hence a regexp test)
- $retVal = preg_match('/^' . $rule['value'] . '$/i', $cellValue);
- }
- // If there are multiple conditions, then we need to test both using the appropriate join operator
- switch ($join) {
- case AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR:
- $returnVal = $returnVal || $retVal;
- // Break as soon as we have a TRUE match for OR joins,
- // to avoid unnecessary additional code execution
- if ($returnVal) {
- return $returnVal;
- }
-
- break;
- case AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND:
- $returnVal = $returnVal && $retVal;
-
- break;
- }
- }
-
- return $returnVal;
- }
-
- /**
- * Test if cell date value is matches a set of values defined by a set of months.
- *
- * @param mixed $cellValue
- * @param mixed[] $monthSet
- *
- * @return bool
- */
- private static function filterTestInPeriodDateSet($cellValue, $monthSet)
- {
- // Blank cells are always ignored, so return a FALSE
- if (($cellValue == '') || ($cellValue === null)) {
- return false;
- }
-
- if (is_numeric($cellValue)) {
- $dateValue = date('m', Date::excelToTimestamp($cellValue));
- if (in_array($dateValue, $monthSet)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Search/Replace arrays to convert Excel wildcard syntax to a regexp syntax for preg_matching.
- *
- * @var array
- */
- private static $fromReplace = ['\*', '\?', '~~', '~.*', '~.?'];
-
- private static $toReplace = ['.*', '.', '~', '\*', '\?'];
-
- /**
- * Convert a dynamic rule daterange to a custom filter range expression for ease of calculation.
- *
- * @param string $dynamicRuleType
- * @param AutoFilter\Column $filterColumn
- *
- * @return mixed[]
- */
- private function dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
- {
- $rDateType = Functions::getReturnDateType();
- Functions::setReturnDateType(Functions::RETURNDATE_PHP_NUMERIC);
- $val = $maxVal = null;
-
- $ruleValues = [];
- $baseDate = DateTime::DATENOW();
- // Calculate start/end dates for the required date range based on current date
- switch ($dynamicRuleType) {
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
- $baseDate = strtotime('-7 days', $baseDate);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
- $baseDate = strtotime('-7 days', $baseDate);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
- $baseDate = strtotime('-1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
- $baseDate = strtotime('+1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
- $baseDate = strtotime('-3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
- $baseDate = strtotime('+3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
- $baseDate = strtotime('-1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
- $baseDate = strtotime('+1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- }
-
- switch ($dynamicRuleType) {
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
- $maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
- $val = (int) Date::PHPToExcel($baseDate);
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE:
- $maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
- $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
- $maxVal = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 31, 12, date('Y', $baseDate)));
- ++$maxVal;
- $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
- $thisMonth = date('m', $baseDate);
- $thisQuarter = floor(--$thisMonth / 3);
- $maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), (1 + $thisQuarter) * 3, date('Y', $baseDate)));
- ++$maxVal;
- $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1 + $thisQuarter * 3, date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
- $maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), date('m', $baseDate), date('Y', $baseDate)));
- ++$maxVal;
- $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
- $dayOfWeek = date('w', $baseDate);
- $val = (int) Date::PHPToExcel($baseDate) - $dayOfWeek;
- $maxVal = $val + 7;
-
- break;
- }
-
- switch ($dynamicRuleType) {
- // Adjust Today dates for Yesterday and Tomorrow
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
- --$maxVal;
- --$val;
-
- break;
- case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
- ++$maxVal;
- ++$val;
-
- break;
- }
-
- // Set the filter column rule attributes ready for writing
- $filterColumn->setAttributes(['val' => $val, 'maxVal' => $maxVal]);
-
- // Set the rules for identifying rows for hide/show
- $ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'value' => $val];
- $ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'value' => $maxVal];
- Functions::setReturnDateType($rDateType);
-
- return ['method' => 'filterTestInCustomDataSet', 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND]];
- }
-
- private function calculateTopTenValue($columnID, $startRow, $endRow, $ruleType, $ruleValue)
- {
- $range = $columnID . $startRow . ':' . $columnID . $endRow;
- $dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false));
-
- $dataValues = array_filter($dataValues);
- if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
- rsort($dataValues);
- } else {
- sort($dataValues);
- }
-
- return array_pop(array_slice($dataValues, 0, $ruleValue));
- }
-
- /**
- * Apply the AutoFilter rules to the AutoFilter Range.
- *
- * @return $this
- */
- public function showHideRows()
- {
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
-
- // The heading row should always be visible
- $this->workSheet->getRowDimension($rangeStart[1])->setVisible(true);
-
- $columnFilterTests = [];
- foreach ($this->columns as $columnID => $filterColumn) {
- $rules = $filterColumn->getRules();
- switch ($filterColumn->getFilterType()) {
- case AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER:
- $ruleType = null;
- $ruleValues = [];
- // Build a list of the filter value selections
- foreach ($rules as $rule) {
- $ruleType = $rule->getRuleType();
- $ruleValues[] = $rule->getValue();
- }
- // Test if we want to include blanks in our filter criteria
- $blanks = false;
- $ruleDataSet = array_filter($ruleValues);
- if (count($ruleValues) != count($ruleDataSet)) {
- $blanks = true;
- }
- if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_FILTER) {
- // Filter on absolute values
- $columnFilterTests[$columnID] = [
- 'method' => 'filterTestInSimpleDataSet',
- 'arguments' => ['filterValues' => $ruleDataSet, 'blanks' => $blanks],
- ];
- } else {
- // Filter on date group values
- $arguments = [
- 'date' => [],
- 'time' => [],
- 'dateTime' => [],
- ];
- foreach ($ruleDataSet as $ruleValue) {
- $date = $time = '';
- if (
- (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
- ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')
- ) {
- $date .= sprintf('%04d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
- }
- if (
- (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
- ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')
- ) {
- $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
- }
- if (
- (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
- ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')
- ) {
- $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
- }
- if (
- (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
- ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')
- ) {
- $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
- }
- if (
- (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
- ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')
- ) {
- $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
- }
- if (
- (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
- ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')
- ) {
- $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
- }
- $dateTime = $date . $time;
- $arguments['date'][] = $date;
- $arguments['time'][] = $time;
- $arguments['dateTime'][] = $dateTime;
- }
- // Remove empty elements
- $arguments['date'] = array_filter($arguments['date']);
- $arguments['time'] = array_filter($arguments['time']);
- $arguments['dateTime'] = array_filter($arguments['dateTime']);
- $columnFilterTests[$columnID] = [
- 'method' => 'filterTestInDateGroupSet',
- 'arguments' => ['filterValues' => $arguments, 'blanks' => $blanks],
- ];
- }
-
- break;
- case AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER:
- $customRuleForBlanks = false;
- $ruleValues = [];
- // Build a list of the filter value selections
- foreach ($rules as $rule) {
- $ruleValue = $rule->getValue();
- if (!is_numeric($ruleValue)) {
- // Convert to a regexp allowing for regexp reserved characters, wildcards and escaped wildcards
- $ruleValue = preg_quote($ruleValue);
- $ruleValue = str_replace(self::$fromReplace, self::$toReplace, $ruleValue);
- if (trim($ruleValue) == '') {
- $customRuleForBlanks = true;
- $ruleValue = trim($ruleValue);
- }
- }
- $ruleValues[] = ['operator' => $rule->getOperator(), 'value' => $ruleValue];
- }
- $join = $filterColumn->getJoin();
- $columnFilterTests[$columnID] = [
- 'method' => 'filterTestInCustomDataSet',
- 'arguments' => ['filterRules' => $ruleValues, 'join' => $join, 'customRuleForBlanks' => $customRuleForBlanks],
- ];
-
- break;
- case AutoFilter\Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER:
- $ruleValues = [];
- foreach ($rules as $rule) {
- // We should only ever have one Dynamic Filter Rule anyway
- $dynamicRuleType = $rule->getGrouping();
- if (
- ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
- ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)
- ) {
- // Number (Average) based
- // Calculate the average
- $averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')';
- $average = Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
- // Set above/below rule based on greaterThan or LessTan
- $operator = ($dynamicRuleType === AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
- ? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
- : AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
- $ruleValues[] = [
- 'operator' => $operator,
- 'value' => $average,
- ];
- $columnFilterTests[$columnID] = [
- 'method' => 'filterTestInCustomDataSet',
- 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR],
- ];
- } else {
- // Date based
- if ($dynamicRuleType[0] == 'M' || $dynamicRuleType[0] == 'Q') {
- $periodType = '';
- $period = 0;
- // Month or Quarter
- sscanf($dynamicRuleType, '%[A-Z]%d', $periodType, $period);
- if ($periodType == 'M') {
- $ruleValues = [$period];
- } else {
- --$period;
- $periodEnd = (1 + $period) * 3;
- $periodStart = 1 + $period * 3;
- $ruleValues = range($periodStart, $periodEnd);
- }
- $columnFilterTests[$columnID] = [
- 'method' => 'filterTestInPeriodDateSet',
- 'arguments' => $ruleValues,
- ];
- $filterColumn->setAttributes([]);
- } else {
- // Date Range
- $columnFilterTests[$columnID] = $this->dynamicFilterDateRange($dynamicRuleType, $filterColumn);
-
- break;
- }
- }
- }
-
- break;
- case AutoFilter\Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER:
- $ruleValues = [];
- $dataRowCount = $rangeEnd[1] - $rangeStart[1];
- foreach ($rules as $rule) {
- // We should only ever have one Dynamic Filter Rule anyway
- $toptenRuleType = $rule->getGrouping();
- $ruleValue = $rule->getValue();
- $ruleOperator = $rule->getOperator();
- }
- if ($ruleOperator === AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
- $ruleValue = floor($ruleValue * ($dataRowCount / 100));
- }
- if ($ruleValue < 1) {
- $ruleValue = 1;
- }
- if ($ruleValue > 500) {
- $ruleValue = 500;
- }
-
- $maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1] + 1, $rangeEnd[1], $toptenRuleType, $ruleValue);
-
- $operator = ($toptenRuleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
- ? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
- : AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
- $ruleValues[] = ['operator' => $operator, 'value' => $maxVal];
- $columnFilterTests[$columnID] = [
- 'method' => 'filterTestInCustomDataSet',
- 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR],
- ];
- $filterColumn->setAttributes(['maxVal' => $maxVal]);
-
- break;
- }
- }
-
- // Execute the column tests for each row in the autoFilter range to determine show/hide,
- for ($row = $rangeStart[1] + 1; $row <= $rangeEnd[1]; ++$row) {
- $result = true;
- foreach ($columnFilterTests as $columnID => $columnFilterTest) {
- $cellValue = $this->workSheet->getCell($columnID . $row)->getCalculatedValue();
- // Execute the filter test
- $result = $result &&
- call_user_func_array(
- [self::class, $columnFilterTest['method']],
- [$cellValue, $columnFilterTest['arguments']]
- );
- // If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests
- if (!$result) {
- break;
- }
- }
- // Set show/hide for the row based on the result of the autoFilter result
- $this->workSheet->getRowDimension($row)->setVisible($result);
- }
-
- return $this;
- }
-
- /**
- * 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)) {
- if ($key === 'workSheet') {
- // Detach from worksheet
- $this->{$key} = null;
- } else {
- $this->{$key} = clone $value;
- }
- } elseif ((is_array($value)) && ($key == 'columns')) {
- // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
- $this->{$key} = [];
- foreach ($value as $k => $v) {
- $this->{$key}[$k] = clone $v;
- // attach the new cloned Column to this new cloned Autofilter object
- $this->{$key}[$k]->setParent($this);
- }
- } else {
- $this->{$key} = $value;
- }
- }
- }
-
- /**
- * toString method replicates previous behavior by returning the range if object is
- * referenced as a property of its parent.
- */
- public function __toString()
- {
- return (string) $this->range;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/vendor/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
deleted file mode 100644
index 09584a7..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
+++ /dev/null
@@ -1,380 +0,0 @@
-columnIndex = $pColumn;
- $this->parent = $pParent;
- }
-
- /**
- * Get AutoFilter column index as string eg: 'A'.
- *
- * @return string
- */
- public function getColumnIndex()
- {
- return $this->columnIndex;
- }
-
- /**
- * Set AutoFilter column index as string eg: 'A'.
- *
- * @param string $pColumn Column (e.g. A)
- *
- * @return $this
- */
- public function setColumnIndex($pColumn)
- {
- // Uppercase coordinate
- $pColumn = strtoupper($pColumn);
- if ($this->parent !== null) {
- $this->parent->testColumnInRange($pColumn);
- }
-
- $this->columnIndex = $pColumn;
-
- return $this;
- }
-
- /**
- * Get this Column's AutoFilter Parent.
- *
- * @return AutoFilter
- */
- public function getParent()
- {
- return $this->parent;
- }
-
- /**
- * Set this Column's AutoFilter Parent.
- *
- * @param AutoFilter $pParent
- *
- * @return $this
- */
- public function setParent(?AutoFilter $pParent = null)
- {
- $this->parent = $pParent;
-
- return $this;
- }
-
- /**
- * Get AutoFilter Type.
- *
- * @return string
- */
- public function getFilterType()
- {
- return $this->filterType;
- }
-
- /**
- * Set AutoFilter Type.
- *
- * @param string $pFilterType
- *
- * @return $this
- */
- public function setFilterType($pFilterType)
- {
- if (!in_array($pFilterType, self::$filterTypes)) {
- throw new PhpSpreadsheetException('Invalid filter type for column AutoFilter.');
- }
-
- $this->filterType = $pFilterType;
-
- return $this;
- }
-
- /**
- * Get AutoFilter Multiple Rules And/Or Join.
- *
- * @return string
- */
- public function getJoin()
- {
- return $this->join;
- }
-
- /**
- * Set AutoFilter Multiple Rules And/Or.
- *
- * @param string $pJoin And/Or
- *
- * @return $this
- */
- public function setJoin($pJoin)
- {
- // Lowercase And/Or
- $pJoin = strtolower($pJoin);
- if (!in_array($pJoin, self::$ruleJoins)) {
- throw new PhpSpreadsheetException('Invalid rule connection for column AutoFilter.');
- }
-
- $this->join = $pJoin;
-
- return $this;
- }
-
- /**
- * Set AutoFilter Attributes.
- *
- * @param string[] $attributes
- *
- * @return $this
- */
- public function setAttributes(array $attributes)
- {
- $this->attributes = $attributes;
-
- return $this;
- }
-
- /**
- * Set An AutoFilter Attribute.
- *
- * @param string $pName Attribute Name
- * @param string $pValue Attribute Value
- *
- * @return $this
- */
- public function setAttribute($pName, $pValue)
- {
- $this->attributes[$pName] = $pValue;
-
- return $this;
- }
-
- /**
- * Get AutoFilter Column Attributes.
- *
- * @return string[]
- */
- public function getAttributes()
- {
- return $this->attributes;
- }
-
- /**
- * Get specific AutoFilter Column Attribute.
- *
- * @param string $pName Attribute Name
- *
- * @return string
- */
- public function getAttribute($pName)
- {
- if (isset($this->attributes[$pName])) {
- return $this->attributes[$pName];
- }
-
- return null;
- }
-
- /**
- * Get all AutoFilter Column Rules.
- *
- * @return Column\Rule[]
- */
- public function getRules()
- {
- return $this->ruleset;
- }
-
- /**
- * Get a specified AutoFilter Column Rule.
- *
- * @param int $pIndex Rule index in the ruleset array
- *
- * @return Column\Rule
- */
- public function getRule($pIndex)
- {
- if (!isset($this->ruleset[$pIndex])) {
- $this->ruleset[$pIndex] = new Column\Rule($this);
- }
-
- return $this->ruleset[$pIndex];
- }
-
- /**
- * Create a new AutoFilter Column Rule in the ruleset.
- *
- * @return Column\Rule
- */
- public function createRule()
- {
- $this->ruleset[] = new Column\Rule($this);
-
- return end($this->ruleset);
- }
-
- /**
- * Add a new AutoFilter Column Rule to the ruleset.
- *
- * @return $this
- */
- public function addRule(Column\Rule $pRule)
- {
- $pRule->setParent($this);
- $this->ruleset[] = $pRule;
-
- return $this;
- }
-
- /**
- * Delete a specified AutoFilter Column Rule
- * If the number of rules is reduced to 1, then we reset And/Or logic to Or.
- *
- * @param int $pIndex Rule index in the ruleset array
- *
- * @return $this
- */
- public function deleteRule($pIndex)
- {
- if (isset($this->ruleset[$pIndex])) {
- unset($this->ruleset[$pIndex]);
- // If we've just deleted down to a single rule, then reset And/Or joining to Or
- if (count($this->ruleset) <= 1) {
- $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
- }
- }
-
- return $this;
- }
-
- /**
- * Delete all AutoFilter Column Rules.
- *
- * @return $this
- */
- public function clearRules()
- {
- $this->ruleset = [];
- $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
-
- return $this;
- }
-
- /**
- * 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 ($key === 'parent') {
- // Detach from autofilter parent
- $this->parent = null;
- } elseif ($key === 'ruleset') {
- // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
- $this->ruleset = [];
- foreach ($value as $k => $v) {
- $cloned = clone $v;
- $cloned->setParent($this); // attach the new cloned Rule to this new cloned Autofilter Cloned object
- $this->ruleset[$k] = $cloned;
- }
- } elseif (is_object($value)) {
- $this->$key = clone $value;
- } else {
- $this->$key = $value;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/vendor/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
deleted file mode 100644
index 1aacb0c..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
+++ /dev/null
@@ -1,449 +0,0 @@
-
- *
- * $objDrawing->setResizeProportional(true);
- * $objDrawing->setWidthAndHeight(160,120);
- *
- *
- * @author Vincent@luo MSN:kele_100@hotmail.com
- *
- * @param int $width
- * @param int $height
- *
- * @return $this
- */
- public function setWidthAndHeight($width, $height)
- {
- $xratio = $width / ($this->width != 0 ? $this->width : 1);
- $yratio = $height / ($this->height != 0 ? $this->height : 1);
- if ($this->resizeProportional && !($width == 0 || $height == 0)) {
- if (($xratio * $this->height) < $height) {
- $this->height = ceil($xratio * $this->height);
- $this->width = $width;
- } else {
- $this->width = ceil($yratio * $this->width);
- $this->height = $height;
- }
- } else {
- $this->width = $width;
- $this->height = $height;
- }
-
- return $this;
- }
-
- /**
- * Get ResizeProportional.
- *
- * @return bool
- */
- public function getResizeProportional()
- {
- return $this->resizeProportional;
- }
-
- /**
- * Set ResizeProportional.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setResizeProportional($pValue)
- {
- $this->resizeProportional = $pValue;
-
- return $this;
- }
-
- /**
- * Get Rotation.
- *
- * @return int
- */
- public function getRotation()
- {
- return $this->rotation;
- }
-
- /**
- * Set Rotation.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setRotation($pValue)
- {
- $this->rotation = $pValue;
-
- return $this;
- }
-
- /**
- * Get Shadow.
- *
- * @return Drawing\Shadow
- */
- public function getShadow()
- {
- return $this->shadow;
- }
-
- /**
- * Set Shadow.
- *
- * @param Drawing\Shadow $pValue
- *
- * @return $this
- */
- public function setShadow(?Drawing\Shadow $pValue = null)
- {
- $this->shadow = $pValue;
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- return md5(
- $this->name .
- $this->description .
- $this->worksheet->getHashCode() .
- $this->coordinates .
- $this->offsetX .
- $this->offsetY .
- $this->width .
- $this->height .
- $this->rotation .
- $this->shadow->getHashCode() .
- __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 ($key == 'worksheet') {
- $this->worksheet = null;
- } elseif (is_object($value)) {
- $this->$key = clone $value;
- } else {
- $this->$key = $value;
- }
- }
- }
-
- public function setHyperlink(?Hyperlink $pHyperlink = null): void
- {
- $this->hyperlink = $pHyperlink;
- }
-
- /**
- * @return null|Hyperlink
- */
- public function getHyperlink()
- {
- return $this->hyperlink;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/CellIterator.php b/vendor/PhpSpreadsheet/Worksheet/CellIterator.php
deleted file mode 100644
index 45f76ca..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/CellIterator.php
+++ /dev/null
@@ -1,57 +0,0 @@
-worksheet = null;
- }
-
- /**
- * Get loop only existing cells.
- *
- * @return bool
- */
- public function getIterateOnlyExistingCells()
- {
- return $this->onlyExistingCells;
- }
-
- /**
- * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
- */
- abstract protected function adjustForExistingOnlyRange();
-
- /**
- * Set the iterator to loop only existing cells.
- *
- * @param bool $value
- */
- public function setIterateOnlyExistingCells($value): void
- {
- $this->onlyExistingCells = (bool) $value;
-
- $this->adjustForExistingOnlyRange();
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Column.php b/vendor/PhpSpreadsheet/Worksheet/Column.php
deleted file mode 100644
index 410e807..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Column.php
+++ /dev/null
@@ -1,64 +0,0 @@
-parent = $parent;
- $this->columnIndex = $columnIndex;
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- $this->parent = null;
- }
-
- /**
- * Get column index as string eg: 'A'.
- *
- * @return string
- */
- public function getColumnIndex()
- {
- return $this->columnIndex;
- }
-
- /**
- * Get cell iterator.
- *
- * @param int $startRow The row number at which to start iterating
- * @param int $endRow Optionally, the row number at which to stop iterating
- *
- * @return ColumnCellIterator
- */
- public function getCellIterator($startRow = 1, $endRow = null)
- {
- return new ColumnCellIterator($this->parent, $this->columnIndex, $startRow, $endRow);
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/ColumnCellIterator.php b/vendor/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
deleted file mode 100644
index 12420d7..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
+++ /dev/null
@@ -1,197 +0,0 @@
-worksheet = $subject;
- $this->columnIndex = Coordinate::columnIndexFromString($columnIndex);
- $this->resetEnd($endRow);
- $this->resetStart($startRow);
- }
-
- /**
- * (Re)Set the start row and the current row pointer.
- *
- * @param int $startRow The row number at which to start iterating
- *
- * @return $this
- */
- public function resetStart($startRow = 1)
- {
- $this->startRow = $startRow;
- $this->adjustForExistingOnlyRange();
- $this->seek($startRow);
-
- return $this;
- }
-
- /**
- * (Re)Set the end row.
- *
- * @param int $endRow The row number at which to stop iterating
- *
- * @return $this
- */
- public function resetEnd($endRow = null)
- {
- $this->endRow = ($endRow) ? $endRow : $this->worksheet->getHighestRow();
- $this->adjustForExistingOnlyRange();
-
- return $this;
- }
-
- /**
- * Set the row pointer to the selected row.
- *
- * @param int $row The row number to set the current pointer at
- *
- * @return $this
- */
- public function seek($row = 1)
- {
- if (($row < $this->startRow) || ($row > $this->endRow)) {
- throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
- } elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) {
- throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
- }
- $this->currentRow = $row;
-
- return $this;
- }
-
- /**
- * Rewind the iterator to the starting row.
- */
- public function rewind(): void
- {
- $this->currentRow = $this->startRow;
- }
-
- /**
- * Return the current cell in this worksheet column.
- *
- * @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell
- */
- public function current()
- {
- return $this->worksheet->getCellByColumnAndRow($this->columnIndex, $this->currentRow);
- }
-
- /**
- * Return the current iterator key.
- *
- * @return int
- */
- public function key()
- {
- return $this->currentRow;
- }
-
- /**
- * Set the iterator to its next value.
- */
- public function next(): void
- {
- do {
- ++$this->currentRow;
- } while (
- ($this->onlyExistingCells) &&
- (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->currentRow)) &&
- ($this->currentRow <= $this->endRow)
- );
- }
-
- /**
- * Set the iterator to its previous value.
- */
- public function prev(): void
- {
- do {
- --$this->currentRow;
- } while (
- ($this->onlyExistingCells) &&
- (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->currentRow)) &&
- ($this->currentRow >= $this->startRow)
- );
- }
-
- /**
- * Indicate if more rows exist in the worksheet range of rows that we're iterating.
- *
- * @return bool
- */
- public function valid()
- {
- return $this->currentRow <= $this->endRow && $this->currentRow >= $this->startRow;
- }
-
- /**
- * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
- */
- protected function adjustForExistingOnlyRange(): void
- {
- if ($this->onlyExistingCells) {
- while (
- (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->startRow)) &&
- ($this->startRow <= $this->endRow)
- ) {
- ++$this->startRow;
- }
- if ($this->startRow > $this->endRow) {
- throw new PhpSpreadsheetException('No cells exist within the specified range');
- }
- while (
- (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) &&
- ($this->endRow >= $this->startRow)
- ) {
- --$this->endRow;
- }
- if ($this->endRow < $this->startRow) {
- throw new PhpSpreadsheetException('No cells exist within the specified range');
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/ColumnDimension.php b/vendor/PhpSpreadsheet/Worksheet/ColumnDimension.php
deleted file mode 100644
index 4e87a34..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/ColumnDimension.php
+++ /dev/null
@@ -1,115 +0,0 @@
-columnIndex = $pIndex;
-
- // set dimension as unformatted by default
- parent::__construct(0);
- }
-
- /**
- * Get column index as string eg: 'A'.
- *
- * @return string
- */
- public function getColumnIndex()
- {
- return $this->columnIndex;
- }
-
- /**
- * Set column index as string eg: 'A'.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setColumnIndex($pValue)
- {
- $this->columnIndex = $pValue;
-
- return $this;
- }
-
- /**
- * Get Width.
- *
- * @return float
- */
- public function getWidth()
- {
- return $this->width;
- }
-
- /**
- * Set Width.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setWidth($pValue)
- {
- $this->width = $pValue;
-
- return $this;
- }
-
- /**
- * Get Auto Size.
- *
- * @return bool
- */
- public function getAutoSize()
- {
- return $this->autoSize;
- }
-
- /**
- * Set Auto Size.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setAutoSize($pValue)
- {
- $this->autoSize = $pValue;
-
- return $this;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/ColumnIterator.php b/vendor/PhpSpreadsheet/Worksheet/ColumnIterator.php
deleted file mode 100644
index d0bb20c..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/ColumnIterator.php
+++ /dev/null
@@ -1,172 +0,0 @@
-worksheet = $worksheet;
- $this->resetEnd($endColumn);
- $this->resetStart($startColumn);
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- $this->worksheet = null;
- }
-
- /**
- * (Re)Set the start column and the current column pointer.
- *
- * @param string $startColumn The column address at which to start iterating
- *
- * @return $this
- */
- public function resetStart($startColumn = 'A')
- {
- $startColumnIndex = Coordinate::columnIndexFromString($startColumn);
- if ($startColumnIndex > Coordinate::columnIndexFromString($this->worksheet->getHighestColumn())) {
- throw new Exception("Start column ({$startColumn}) is beyond highest column ({$this->worksheet->getHighestColumn()})");
- }
-
- $this->startColumnIndex = $startColumnIndex;
- if ($this->endColumnIndex < $this->startColumnIndex) {
- $this->endColumnIndex = $this->startColumnIndex;
- }
- $this->seek($startColumn);
-
- return $this;
- }
-
- /**
- * (Re)Set the end column.
- *
- * @param string $endColumn The column address at which to stop iterating
- *
- * @return $this
- */
- public function resetEnd($endColumn = null)
- {
- $endColumn = $endColumn ? $endColumn : $this->worksheet->getHighestColumn();
- $this->endColumnIndex = Coordinate::columnIndexFromString($endColumn);
-
- return $this;
- }
-
- /**
- * Set the column pointer to the selected column.
- *
- * @param string $column The column address to set the current pointer at
- *
- * @return $this
- */
- public function seek($column = 'A')
- {
- $column = Coordinate::columnIndexFromString($column);
- if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
- throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
- }
- $this->currentColumnIndex = $column;
-
- return $this;
- }
-
- /**
- * Rewind the iterator to the starting column.
- */
- public function rewind(): void
- {
- $this->currentColumnIndex = $this->startColumnIndex;
- }
-
- /**
- * Return the current column in this worksheet.
- *
- * @return Column
- */
- public function current()
- {
- return new Column($this->worksheet, Coordinate::stringFromColumnIndex($this->currentColumnIndex));
- }
-
- /**
- * Return the current iterator key.
- *
- * @return string
- */
- public function key()
- {
- return Coordinate::stringFromColumnIndex($this->currentColumnIndex);
- }
-
- /**
- * Set the iterator to its next value.
- */
- public function next(): void
- {
- ++$this->currentColumnIndex;
- }
-
- /**
- * Set the iterator to its previous value.
- */
- public function prev(): void
- {
- --$this->currentColumnIndex;
- }
-
- /**
- * Indicate if more columns exist in the worksheet range of columns that we're iterating.
- *
- * @return bool
- */
- public function valid()
- {
- return $this->currentColumnIndex <= $this->endColumnIndex && $this->currentColumnIndex >= $this->startColumnIndex;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Dimension.php b/vendor/PhpSpreadsheet/Worksheet/Dimension.php
deleted file mode 100644
index a27daf0..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Dimension.php
+++ /dev/null
@@ -1,163 +0,0 @@
-xfIndex = $initialValue;
- }
-
- /**
- * Get Visible.
- *
- * @return bool
- */
- public function getVisible()
- {
- return $this->visible;
- }
-
- /**
- * Set Visible.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setVisible($pValue)
- {
- $this->visible = (bool) $pValue;
-
- return $this;
- }
-
- /**
- * Get Outline Level.
- *
- * @return int
- */
- public function getOutlineLevel()
- {
- return $this->outlineLevel;
- }
-
- /**
- * Set Outline Level.
- * Value must be between 0 and 7.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setOutlineLevel($pValue)
- {
- if ($pValue < 0 || $pValue > 7) {
- throw new PhpSpreadsheetException('Outline level must range between 0 and 7.');
- }
-
- $this->outlineLevel = $pValue;
-
- return $this;
- }
-
- /**
- * Get Collapsed.
- *
- * @return bool
- */
- public function getCollapsed()
- {
- return $this->collapsed;
- }
-
- /**
- * Set Collapsed.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setCollapsed($pValue)
- {
- $this->collapsed = (bool) $pValue;
-
- return $this;
- }
-
- /**
- * Get index to cellXf.
- *
- * @return int
- */
- public function getXfIndex()
- {
- return $this->xfIndex;
- }
-
- /**
- * Set index to cellXf.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setXfIndex($pValue)
- {
- $this->xfIndex = $pValue;
-
- return $this;
- }
-
- /**
- * 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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Drawing.php b/vendor/PhpSpreadsheet/Worksheet/Drawing.php
deleted file mode 100644
index 1f1dae9..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Drawing.php
+++ /dev/null
@@ -1,114 +0,0 @@
-path = '';
-
- // Initialize parent
- parent::__construct();
- }
-
- /**
- * Get Filename.
- *
- * @return string
- */
- public function getFilename()
- {
- return basename($this->path);
- }
-
- /**
- * Get indexed filename (using image index).
- *
- * @return string
- */
- public function getIndexedFilename()
- {
- $fileName = $this->getFilename();
- $fileName = str_replace(' ', '_', $fileName);
-
- return str_replace('.' . $this->getExtension(), '', $fileName) . $this->getImageIndex() . '.' . $this->getExtension();
- }
-
- /**
- * Get Extension.
- *
- * @return string
- */
- public function getExtension()
- {
- $exploded = explode('.', basename($this->path));
-
- return $exploded[count($exploded) - 1];
- }
-
- /**
- * Get Path.
- *
- * @return string
- */
- public function getPath()
- {
- return $this->path;
- }
-
- /**
- * Set Path.
- *
- * @param string $pValue File path
- * @param bool $pVerifyFile Verify file
- *
- * @return $this
- */
- public function setPath($pValue, $pVerifyFile = true)
- {
- if ($pVerifyFile) {
- if (file_exists($pValue)) {
- $this->path = $pValue;
-
- if ($this->width == 0 && $this->height == 0) {
- // Get width/height
- [$this->width, $this->height] = getimagesize($pValue);
- }
- } else {
- throw new PhpSpreadsheetException("File $pValue not found!");
- }
- } else {
- $this->path = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- return md5(
- $this->path .
- parent::getHashCode() .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Drawing/Shadow.php b/vendor/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
deleted file mode 100644
index 01ffed9..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
+++ /dev/null
@@ -1,289 +0,0 @@
-visible = false;
- $this->blurRadius = 6;
- $this->distance = 2;
- $this->direction = 0;
- $this->alignment = self::SHADOW_BOTTOM_RIGHT;
- $this->color = new Color(Color::COLOR_BLACK);
- $this->alpha = 50;
- }
-
- /**
- * Get Visible.
- *
- * @return bool
- */
- public function getVisible()
- {
- return $this->visible;
- }
-
- /**
- * Set Visible.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setVisible($pValue)
- {
- $this->visible = $pValue;
-
- return $this;
- }
-
- /**
- * Get Blur radius.
- *
- * @return int
- */
- public function getBlurRadius()
- {
- return $this->blurRadius;
- }
-
- /**
- * Set Blur radius.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setBlurRadius($pValue)
- {
- $this->blurRadius = $pValue;
-
- return $this;
- }
-
- /**
- * Get Shadow distance.
- *
- * @return int
- */
- public function getDistance()
- {
- return $this->distance;
- }
-
- /**
- * Set Shadow distance.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setDistance($pValue)
- {
- $this->distance = $pValue;
-
- return $this;
- }
-
- /**
- * Get Shadow direction (in degrees).
- *
- * @return int
- */
- public function getDirection()
- {
- return $this->direction;
- }
-
- /**
- * Set Shadow direction (in degrees).
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setDirection($pValue)
- {
- $this->direction = $pValue;
-
- return $this;
- }
-
- /**
- * Get Shadow alignment.
- *
- * @return int
- */
- public function getAlignment()
- {
- return $this->alignment;
- }
-
- /**
- * Set Shadow alignment.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setAlignment($pValue)
- {
- $this->alignment = $pValue;
-
- return $this;
- }
-
- /**
- * Get Color.
- *
- * @return Color
- */
- public function getColor()
- {
- return $this->color;
- }
-
- /**
- * Set Color.
- *
- * @param Color $pValue
- *
- * @return $this
- */
- public function setColor(?Color $pValue = null)
- {
- $this->color = $pValue;
-
- return $this;
- }
-
- /**
- * Get Alpha.
- *
- * @return int
- */
- public function getAlpha()
- {
- return $this->alpha;
- }
-
- /**
- * Set Alpha.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setAlpha($pValue)
- {
- $this->alpha = $pValue;
-
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- return md5(
- ($this->visible ? 't' : 'f') .
- $this->blurRadius .
- $this->distance .
- $this->direction .
- $this->alignment .
- $this->color->getHashCode() .
- $this->alpha .
- __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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/HeaderFooter.php b/vendor/PhpSpreadsheet/Worksheet/HeaderFooter.php
deleted file mode 100644
index cc37e7f..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/HeaderFooter.php
+++ /dev/null
@@ -1,490 +0,0 @@
-
- * Header/Footer Formatting Syntax taken from Office Open XML Part 4 - Markup Language Reference, page 1970:.
- *
- * There are a number of formatting codes that can be written inline with the actual header / footer text, which
- * affect the formatting in the header or footer.
- *
- * Example: This example shows the text "Center Bold Header" on the first line (center section), and the date on
- * the second line (center section).
- * &CCenter &"-,Bold"Bold&"-,Regular"Header_x000A_&D
- *
- * General Rules:
- * There is no required order in which these codes must appear.
- *
- * The first occurrence of the following codes turns the formatting ON, the second occurrence turns it OFF again:
- * - strikethrough
- * - superscript
- * - subscript
- * Superscript and subscript cannot both be ON at same time. Whichever comes first wins and the other is ignored,
- * while the first is ON.
- * &L - code for "left section" (there are three header / footer locations, "left", "center", and "right"). When
- * two or more occurrences of this section marker exist, the contents from all markers are concatenated, in the
- * order of appearance, and placed into the left section.
- * &P - code for "current page #"
- * &N - code for "total pages"
- * &font size - code for "text font size", where font size is a font size in points.
- * &K - code for "text font color"
- * RGB Color is specified as RRGGBB
- * Theme Color is specifed as TTSNN where TT is the theme color Id, S is either "+" or "-" of the tint/shade
- * value, NN is the tint/shade value.
- * &S - code for "text strikethrough" on / off
- * &X - code for "text super script" on / off
- * &Y - code for "text subscript" on / off
- * &C - code for "center section". When two or more occurrences of this section marker exist, the contents
- * from all markers are concatenated, in the order of appearance, and placed into the center section.
- *
- * &D - code for "date"
- * &T - code for "time"
- * &G - code for "picture as background"
- * &U - code for "text single underline"
- * &E - code for "double underline"
- * &R - code for "right section". When two or more occurrences of this section marker exist, the contents
- * from all markers are concatenated, in the order of appearance, and placed into the right section.
- * &Z - code for "this workbook's file path"
- * &F - code for "this workbook's file name"
- * &A - code for "sheet tab name"
- * &+ - code for add to page #.
- * &- - code for subtract from page #.
- * &"font name,font type" - code for "text font name" and "text font type", where font name and font type
- * are strings specifying the name and type of the font, separated by a comma. When a hyphen appears in font
- * name, it means "none specified". Both of font name and font type can be localized values.
- * &"-,Bold" - code for "bold font style"
- * &B - also means "bold font style".
- * &"-,Regular" - code for "regular font style"
- * &"-,Italic" - code for "italic font style"
- * &I - also means "italic font style"
- * &"-,Bold Italic" code for "bold italic font style"
- * &O - code for "outline style"
- * &H - code for "shadow style"
- *
- */
-class HeaderFooter
-{
- // Header/footer image location
- const IMAGE_HEADER_LEFT = 'LH';
- const IMAGE_HEADER_CENTER = 'CH';
- const IMAGE_HEADER_RIGHT = 'RH';
- const IMAGE_FOOTER_LEFT = 'LF';
- const IMAGE_FOOTER_CENTER = 'CF';
- const IMAGE_FOOTER_RIGHT = 'RF';
-
- /**
- * OddHeader.
- *
- * @var string
- */
- private $oddHeader = '';
-
- /**
- * OddFooter.
- *
- * @var string
- */
- private $oddFooter = '';
-
- /**
- * EvenHeader.
- *
- * @var string
- */
- private $evenHeader = '';
-
- /**
- * EvenFooter.
- *
- * @var string
- */
- private $evenFooter = '';
-
- /**
- * FirstHeader.
- *
- * @var string
- */
- private $firstHeader = '';
-
- /**
- * FirstFooter.
- *
- * @var string
- */
- private $firstFooter = '';
-
- /**
- * Different header for Odd/Even, defaults to false.
- *
- * @var bool
- */
- private $differentOddEven = false;
-
- /**
- * Different header for first page, defaults to false.
- *
- * @var bool
- */
- private $differentFirst = false;
-
- /**
- * Scale with document, defaults to true.
- *
- * @var bool
- */
- private $scaleWithDocument = true;
-
- /**
- * Align with margins, defaults to true.
- *
- * @var bool
- */
- private $alignWithMargins = true;
-
- /**
- * Header/footer images.
- *
- * @var HeaderFooterDrawing[]
- */
- private $headerFooterImages = [];
-
- /**
- * Create a new HeaderFooter.
- */
- public function __construct()
- {
- }
-
- /**
- * Get OddHeader.
- *
- * @return string
- */
- public function getOddHeader()
- {
- return $this->oddHeader;
- }
-
- /**
- * Set OddHeader.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setOddHeader($pValue)
- {
- $this->oddHeader = $pValue;
-
- return $this;
- }
-
- /**
- * Get OddFooter.
- *
- * @return string
- */
- public function getOddFooter()
- {
- return $this->oddFooter;
- }
-
- /**
- * Set OddFooter.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setOddFooter($pValue)
- {
- $this->oddFooter = $pValue;
-
- return $this;
- }
-
- /**
- * Get EvenHeader.
- *
- * @return string
- */
- public function getEvenHeader()
- {
- return $this->evenHeader;
- }
-
- /**
- * Set EvenHeader.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setEvenHeader($pValue)
- {
- $this->evenHeader = $pValue;
-
- return $this;
- }
-
- /**
- * Get EvenFooter.
- *
- * @return string
- */
- public function getEvenFooter()
- {
- return $this->evenFooter;
- }
-
- /**
- * Set EvenFooter.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setEvenFooter($pValue)
- {
- $this->evenFooter = $pValue;
-
- return $this;
- }
-
- /**
- * Get FirstHeader.
- *
- * @return string
- */
- public function getFirstHeader()
- {
- return $this->firstHeader;
- }
-
- /**
- * Set FirstHeader.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setFirstHeader($pValue)
- {
- $this->firstHeader = $pValue;
-
- return $this;
- }
-
- /**
- * Get FirstFooter.
- *
- * @return string
- */
- public function getFirstFooter()
- {
- return $this->firstFooter;
- }
-
- /**
- * Set FirstFooter.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setFirstFooter($pValue)
- {
- $this->firstFooter = $pValue;
-
- return $this;
- }
-
- /**
- * Get DifferentOddEven.
- *
- * @return bool
- */
- public function getDifferentOddEven()
- {
- return $this->differentOddEven;
- }
-
- /**
- * Set DifferentOddEven.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setDifferentOddEven($pValue)
- {
- $this->differentOddEven = $pValue;
-
- return $this;
- }
-
- /**
- * Get DifferentFirst.
- *
- * @return bool
- */
- public function getDifferentFirst()
- {
- return $this->differentFirst;
- }
-
- /**
- * Set DifferentFirst.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setDifferentFirst($pValue)
- {
- $this->differentFirst = $pValue;
-
- return $this;
- }
-
- /**
- * Get ScaleWithDocument.
- *
- * @return bool
- */
- public function getScaleWithDocument()
- {
- return $this->scaleWithDocument;
- }
-
- /**
- * Set ScaleWithDocument.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setScaleWithDocument($pValue)
- {
- $this->scaleWithDocument = $pValue;
-
- return $this;
- }
-
- /**
- * Get AlignWithMargins.
- *
- * @return bool
- */
- public function getAlignWithMargins()
- {
- return $this->alignWithMargins;
- }
-
- /**
- * Set AlignWithMargins.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setAlignWithMargins($pValue)
- {
- $this->alignWithMargins = $pValue;
-
- return $this;
- }
-
- /**
- * Add header/footer image.
- *
- * @param string $location
- *
- * @return $this
- */
- public function addImage(HeaderFooterDrawing $image, $location = self::IMAGE_HEADER_LEFT)
- {
- $this->headerFooterImages[$location] = $image;
-
- return $this;
- }
-
- /**
- * Remove header/footer image.
- *
- * @param string $location
- *
- * @return $this
- */
- public function removeImage($location = self::IMAGE_HEADER_LEFT)
- {
- if (isset($this->headerFooterImages[$location])) {
- unset($this->headerFooterImages[$location]);
- }
-
- return $this;
- }
-
- /**
- * Set header/footer images.
- *
- * @param HeaderFooterDrawing[] $images
- *
- * @return $this
- */
- public function setImages(array $images)
- {
- $this->headerFooterImages = $images;
-
- return $this;
- }
-
- /**
- * Get header/footer images.
- *
- * @return HeaderFooterDrawing[]
- */
- public function getImages()
- {
- // Sort array
- $images = [];
- if (isset($this->headerFooterImages[self::IMAGE_HEADER_LEFT])) {
- $images[self::IMAGE_HEADER_LEFT] = $this->headerFooterImages[self::IMAGE_HEADER_LEFT];
- }
- if (isset($this->headerFooterImages[self::IMAGE_HEADER_CENTER])) {
- $images[self::IMAGE_HEADER_CENTER] = $this->headerFooterImages[self::IMAGE_HEADER_CENTER];
- }
- if (isset($this->headerFooterImages[self::IMAGE_HEADER_RIGHT])) {
- $images[self::IMAGE_HEADER_RIGHT] = $this->headerFooterImages[self::IMAGE_HEADER_RIGHT];
- }
- if (isset($this->headerFooterImages[self::IMAGE_FOOTER_LEFT])) {
- $images[self::IMAGE_FOOTER_LEFT] = $this->headerFooterImages[self::IMAGE_FOOTER_LEFT];
- }
- if (isset($this->headerFooterImages[self::IMAGE_FOOTER_CENTER])) {
- $images[self::IMAGE_FOOTER_CENTER] = $this->headerFooterImages[self::IMAGE_FOOTER_CENTER];
- }
- if (isset($this->headerFooterImages[self::IMAGE_FOOTER_RIGHT])) {
- $images[self::IMAGE_FOOTER_RIGHT] = $this->headerFooterImages[self::IMAGE_FOOTER_RIGHT];
- }
- $this->headerFooterImages = $images;
-
- return $this->headerFooterImages;
- }
-
- /**
- * 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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php b/vendor/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
deleted file mode 100644
index b42c732..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
+++ /dev/null
@@ -1,24 +0,0 @@
-getPath() .
- $this->name .
- $this->offsetX .
- $this->offsetY .
- $this->width .
- $this->height .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Iterator.php b/vendor/PhpSpreadsheet/Worksheet/Iterator.php
deleted file mode 100644
index 6cfed37..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Iterator.php
+++ /dev/null
@@ -1,85 +0,0 @@
-subject = $subject;
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- $this->subject = null;
- }
-
- /**
- * Rewind iterator.
- */
- public function rewind(): void
- {
- $this->position = 0;
- }
-
- /**
- * Current Worksheet.
- *
- * @return Worksheet
- */
- public function current()
- {
- return $this->subject->getSheet($this->position);
- }
-
- /**
- * Current key.
- *
- * @return int
- */
- public function key()
- {
- return $this->position;
- }
-
- /**
- * Next value.
- */
- public function next(): void
- {
- ++$this->position;
- }
-
- /**
- * Are there more Worksheet instances available?
- *
- * @return bool
- */
- public function valid()
- {
- return $this->position < $this->subject->getSheetCount() && $this->position >= 0;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/vendor/PhpSpreadsheet/Worksheet/MemoryDrawing.php
deleted file mode 100644
index 22e0909..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/MemoryDrawing.php
+++ /dev/null
@@ -1,169 +0,0 @@
-imageResource = null;
- $this->renderingFunction = self::RENDERING_DEFAULT;
- $this->mimeType = self::MIMETYPE_DEFAULT;
- $this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
-
- // Initialize parent
- parent::__construct();
- }
-
- /**
- * Get image resource.
- *
- * @return resource
- */
- public function getImageResource()
- {
- return $this->imageResource;
- }
-
- /**
- * Set image resource.
- *
- * @param resource $value
- *
- * @return $this
- */
- public function setImageResource($value)
- {
- $this->imageResource = $value;
-
- if ($this->imageResource !== null) {
- // Get width/height
- $this->width = imagesx($this->imageResource);
- $this->height = imagesy($this->imageResource);
- }
-
- return $this;
- }
-
- /**
- * Get rendering function.
- *
- * @return string
- */
- public function getRenderingFunction()
- {
- return $this->renderingFunction;
- }
-
- /**
- * Set rendering function.
- *
- * @param string $value see self::RENDERING_*
- *
- * @return $this
- */
- public function setRenderingFunction($value)
- {
- $this->renderingFunction = $value;
-
- return $this;
- }
-
- /**
- * Get mime type.
- *
- * @return string
- */
- public function getMimeType()
- {
- return $this->mimeType;
- }
-
- /**
- * Set mime type.
- *
- * @param string $value see self::MIMETYPE_*
- *
- * @return $this
- */
- public function setMimeType($value)
- {
- $this->mimeType = $value;
-
- return $this;
- }
-
- /**
- * Get indexed filename (using image index).
- *
- * @return string
- */
- public function getIndexedFilename()
- {
- $extension = strtolower($this->getMimeType());
- $extension = explode('/', $extension);
- $extension = $extension[1];
-
- return $this->uniqueName . $this->getImageIndex() . '.' . $extension;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- return md5(
- $this->renderingFunction .
- $this->mimeType .
- $this->uniqueName .
- parent::getHashCode() .
- __CLASS__
- );
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/PageMargins.php b/vendor/PhpSpreadsheet/Worksheet/PageMargins.php
deleted file mode 100644
index a829793..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/PageMargins.php
+++ /dev/null
@@ -1,244 +0,0 @@
-left;
- }
-
- /**
- * Set Left.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setLeft($pValue)
- {
- $this->left = $pValue;
-
- return $this;
- }
-
- /**
- * Get Right.
- *
- * @return float
- */
- public function getRight()
- {
- return $this->right;
- }
-
- /**
- * Set Right.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setRight($pValue)
- {
- $this->right = $pValue;
-
- return $this;
- }
-
- /**
- * Get Top.
- *
- * @return float
- */
- public function getTop()
- {
- return $this->top;
- }
-
- /**
- * Set Top.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setTop($pValue)
- {
- $this->top = $pValue;
-
- return $this;
- }
-
- /**
- * Get Bottom.
- *
- * @return float
- */
- public function getBottom()
- {
- return $this->bottom;
- }
-
- /**
- * Set Bottom.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setBottom($pValue)
- {
- $this->bottom = $pValue;
-
- return $this;
- }
-
- /**
- * Get Header.
- *
- * @return float
- */
- public function getHeader()
- {
- return $this->header;
- }
-
- /**
- * Set Header.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setHeader($pValue)
- {
- $this->header = $pValue;
-
- return $this;
- }
-
- /**
- * Get Footer.
- *
- * @return float
- */
- public function getFooter()
- {
- return $this->footer;
- }
-
- /**
- * Set Footer.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setFooter($pValue)
- {
- $this->footer = $pValue;
-
- return $this;
- }
-
- /**
- * 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;
- }
- }
- }
-
- public static function fromCentimeters(float $value): float
- {
- return $value / 2.54;
- }
-
- public static function toCentimeters(float $value): float
- {
- return $value * 2.54;
- }
-
- public static function fromMillimeters(float $value): float
- {
- return $value / 25.4;
- }
-
- public static function toMillimeters(float $value): float
- {
- return $value * 25.4;
- }
-
- public static function fromPoints(float $value): float
- {
- return $value / 72;
- }
-
- public static function toPoints(float $value): float
- {
- return $value * 72;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/PageSetup.php b/vendor/PhpSpreadsheet/Worksheet/PageSetup.php
deleted file mode 100644
index d1a22a7..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/PageSetup.php
+++ /dev/null
@@ -1,857 +0,0 @@
-
- * Paper size taken from Office Open XML Part 4 - Markup Language Reference, page 1988:.
- *
- * 1 = Letter paper (8.5 in. by 11 in.)
- * 2 = Letter small paper (8.5 in. by 11 in.)
- * 3 = Tabloid paper (11 in. by 17 in.)
- * 4 = Ledger paper (17 in. by 11 in.)
- * 5 = Legal paper (8.5 in. by 14 in.)
- * 6 = Statement paper (5.5 in. by 8.5 in.)
- * 7 = Executive paper (7.25 in. by 10.5 in.)
- * 8 = A3 paper (297 mm by 420 mm)
- * 9 = A4 paper (210 mm by 297 mm)
- * 10 = A4 small paper (210 mm by 297 mm)
- * 11 = A5 paper (148 mm by 210 mm)
- * 12 = B4 paper (250 mm by 353 mm)
- * 13 = B5 paper (176 mm by 250 mm)
- * 14 = Folio paper (8.5 in. by 13 in.)
- * 15 = Quarto paper (215 mm by 275 mm)
- * 16 = Standard paper (10 in. by 14 in.)
- * 17 = Standard paper (11 in. by 17 in.)
- * 18 = Note paper (8.5 in. by 11 in.)
- * 19 = #9 envelope (3.875 in. by 8.875 in.)
- * 20 = #10 envelope (4.125 in. by 9.5 in.)
- * 21 = #11 envelope (4.5 in. by 10.375 in.)
- * 22 = #12 envelope (4.75 in. by 11 in.)
- * 23 = #14 envelope (5 in. by 11.5 in.)
- * 24 = C paper (17 in. by 22 in.)
- * 25 = D paper (22 in. by 34 in.)
- * 26 = E paper (34 in. by 44 in.)
- * 27 = DL envelope (110 mm by 220 mm)
- * 28 = C5 envelope (162 mm by 229 mm)
- * 29 = C3 envelope (324 mm by 458 mm)
- * 30 = C4 envelope (229 mm by 324 mm)
- * 31 = C6 envelope (114 mm by 162 mm)
- * 32 = C65 envelope (114 mm by 229 mm)
- * 33 = B4 envelope (250 mm by 353 mm)
- * 34 = B5 envelope (176 mm by 250 mm)
- * 35 = B6 envelope (176 mm by 125 mm)
- * 36 = Italy envelope (110 mm by 230 mm)
- * 37 = Monarch envelope (3.875 in. by 7.5 in.).
- * 38 = 6 3/4 envelope (3.625 in. by 6.5 in.)
- * 39 = US standard fanfold (14.875 in. by 11 in.)
- * 40 = German standard fanfold (8.5 in. by 12 in.)
- * 41 = German legal fanfold (8.5 in. by 13 in.)
- * 42 = ISO B4 (250 mm by 353 mm)
- * 43 = Japanese double postcard (200 mm by 148 mm)
- * 44 = Standard paper (9 in. by 11 in.)
- * 45 = Standard paper (10 in. by 11 in.)
- * 46 = Standard paper (15 in. by 11 in.)
- * 47 = Invite envelope (220 mm by 220 mm)
- * 50 = Letter extra paper (9.275 in. by 12 in.)
- * 51 = Legal extra paper (9.275 in. by 15 in.)
- * 52 = Tabloid extra paper (11.69 in. by 18 in.)
- * 53 = A4 extra paper (236 mm by 322 mm)
- * 54 = Letter transverse paper (8.275 in. by 11 in.)
- * 55 = A4 transverse paper (210 mm by 297 mm)
- * 56 = Letter extra transverse paper (9.275 in. by 12 in.)
- * 57 = SuperA/SuperA/A4 paper (227 mm by 356 mm)
- * 58 = SuperB/SuperB/A3 paper (305 mm by 487 mm)
- * 59 = Letter plus paper (8.5 in. by 12.69 in.)
- * 60 = A4 plus paper (210 mm by 330 mm)
- * 61 = A5 transverse paper (148 mm by 210 mm)
- * 62 = JIS B5 transverse paper (182 mm by 257 mm)
- * 63 = A3 extra paper (322 mm by 445 mm)
- * 64 = A5 extra paper (174 mm by 235 mm)
- * 65 = ISO B5 extra paper (201 mm by 276 mm)
- * 66 = A2 paper (420 mm by 594 mm)
- * 67 = A3 transverse paper (297 mm by 420 mm)
- * 68 = A3 extra transverse paper (322 mm by 445 mm)
- *
- */
-class PageSetup
-{
- // Paper size
- const PAPERSIZE_LETTER = 1;
- const PAPERSIZE_LETTER_SMALL = 2;
- const PAPERSIZE_TABLOID = 3;
- const PAPERSIZE_LEDGER = 4;
- const PAPERSIZE_LEGAL = 5;
- const PAPERSIZE_STATEMENT = 6;
- const PAPERSIZE_EXECUTIVE = 7;
- const PAPERSIZE_A3 = 8;
- const PAPERSIZE_A4 = 9;
- const PAPERSIZE_A4_SMALL = 10;
- const PAPERSIZE_A5 = 11;
- const PAPERSIZE_B4 = 12;
- const PAPERSIZE_B5 = 13;
- const PAPERSIZE_FOLIO = 14;
- const PAPERSIZE_QUARTO = 15;
- const PAPERSIZE_STANDARD_1 = 16;
- const PAPERSIZE_STANDARD_2 = 17;
- const PAPERSIZE_NOTE = 18;
- const PAPERSIZE_NO9_ENVELOPE = 19;
- const PAPERSIZE_NO10_ENVELOPE = 20;
- const PAPERSIZE_NO11_ENVELOPE = 21;
- const PAPERSIZE_NO12_ENVELOPE = 22;
- const PAPERSIZE_NO14_ENVELOPE = 23;
- const PAPERSIZE_C = 24;
- const PAPERSIZE_D = 25;
- const PAPERSIZE_E = 26;
- const PAPERSIZE_DL_ENVELOPE = 27;
- const PAPERSIZE_C5_ENVELOPE = 28;
- const PAPERSIZE_C3_ENVELOPE = 29;
- const PAPERSIZE_C4_ENVELOPE = 30;
- const PAPERSIZE_C6_ENVELOPE = 31;
- const PAPERSIZE_C65_ENVELOPE = 32;
- const PAPERSIZE_B4_ENVELOPE = 33;
- const PAPERSIZE_B5_ENVELOPE = 34;
- const PAPERSIZE_B6_ENVELOPE = 35;
- const PAPERSIZE_ITALY_ENVELOPE = 36;
- const PAPERSIZE_MONARCH_ENVELOPE = 37;
- const PAPERSIZE_6_3_4_ENVELOPE = 38;
- const PAPERSIZE_US_STANDARD_FANFOLD = 39;
- const PAPERSIZE_GERMAN_STANDARD_FANFOLD = 40;
- const PAPERSIZE_GERMAN_LEGAL_FANFOLD = 41;
- const PAPERSIZE_ISO_B4 = 42;
- const PAPERSIZE_JAPANESE_DOUBLE_POSTCARD = 43;
- const PAPERSIZE_STANDARD_PAPER_1 = 44;
- const PAPERSIZE_STANDARD_PAPER_2 = 45;
- const PAPERSIZE_STANDARD_PAPER_3 = 46;
- const PAPERSIZE_INVITE_ENVELOPE = 47;
- const PAPERSIZE_LETTER_EXTRA_PAPER = 48;
- const PAPERSIZE_LEGAL_EXTRA_PAPER = 49;
- const PAPERSIZE_TABLOID_EXTRA_PAPER = 50;
- const PAPERSIZE_A4_EXTRA_PAPER = 51;
- const PAPERSIZE_LETTER_TRANSVERSE_PAPER = 52;
- const PAPERSIZE_A4_TRANSVERSE_PAPER = 53;
- const PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER = 54;
- const PAPERSIZE_SUPERA_SUPERA_A4_PAPER = 55;
- const PAPERSIZE_SUPERB_SUPERB_A3_PAPER = 56;
- const PAPERSIZE_LETTER_PLUS_PAPER = 57;
- const PAPERSIZE_A4_PLUS_PAPER = 58;
- const PAPERSIZE_A5_TRANSVERSE_PAPER = 59;
- const PAPERSIZE_JIS_B5_TRANSVERSE_PAPER = 60;
- const PAPERSIZE_A3_EXTRA_PAPER = 61;
- const PAPERSIZE_A5_EXTRA_PAPER = 62;
- const PAPERSIZE_ISO_B5_EXTRA_PAPER = 63;
- const PAPERSIZE_A2_PAPER = 64;
- const PAPERSIZE_A3_TRANSVERSE_PAPER = 65;
- const PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER = 66;
-
- // Page orientation
- const ORIENTATION_DEFAULT = 'default';
- const ORIENTATION_LANDSCAPE = 'landscape';
- const ORIENTATION_PORTRAIT = 'portrait';
-
- // Print Range Set Method
- const SETPRINTRANGE_OVERWRITE = 'O';
- const SETPRINTRANGE_INSERT = 'I';
-
- const PAGEORDER_OVER_THEN_DOWN = 'overThenDown';
- const PAGEORDER_DOWN_THEN_OVER = 'downThenOver';
-
- /**
- * Paper size.
- *
- * @var int
- */
- private $paperSize = self::PAPERSIZE_LETTER;
-
- /**
- * Orientation.
- *
- * @var string
- */
- private $orientation = self::ORIENTATION_DEFAULT;
-
- /**
- * Scale (Print Scale).
- *
- * Print scaling. Valid values range from 10 to 400
- * This setting is overridden when fitToWidth and/or fitToHeight are in use
- *
- * @var null|int
- */
- private $scale = 100;
-
- /**
- * Fit To Page
- * Whether scale or fitToWith / fitToHeight applies.
- *
- * @var bool
- */
- private $fitToPage = false;
-
- /**
- * Fit To Height
- * Number of vertical pages to fit on.
- *
- * @var null|int
- */
- private $fitToHeight = 1;
-
- /**
- * Fit To Width
- * Number of horizontal pages to fit on.
- *
- * @var null|int
- */
- private $fitToWidth = 1;
-
- /**
- * Columns to repeat at left.
- *
- * @var array Containing start column and end column, empty array if option unset
- */
- private $columnsToRepeatAtLeft = ['', ''];
-
- /**
- * Rows to repeat at top.
- *
- * @var array Containing start row number and end row number, empty array if option unset
- */
- private $rowsToRepeatAtTop = [0, 0];
-
- /**
- * Center page horizontally.
- *
- * @var bool
- */
- private $horizontalCentered = false;
-
- /**
- * Center page vertically.
- *
- * @var bool
- */
- private $verticalCentered = false;
-
- /**
- * Print area.
- *
- * @var string
- */
- private $printArea;
-
- /**
- * First page number.
- *
- * @var int
- */
- private $firstPageNumber;
-
- private $pageOrder = self::PAGEORDER_DOWN_THEN_OVER;
-
- /**
- * Create a new PageSetup.
- */
- public function __construct()
- {
- }
-
- /**
- * Get Paper Size.
- *
- * @return int
- */
- public function getPaperSize()
- {
- return $this->paperSize;
- }
-
- /**
- * Set Paper Size.
- *
- * @param int $pValue see self::PAPERSIZE_*
- *
- * @return $this
- */
- public function setPaperSize($pValue)
- {
- $this->paperSize = $pValue;
-
- return $this;
- }
-
- /**
- * Get Orientation.
- *
- * @return string
- */
- public function getOrientation()
- {
- return $this->orientation;
- }
-
- /**
- * Set Orientation.
- *
- * @param string $pValue see self::ORIENTATION_*
- *
- * @return $this
- */
- public function setOrientation($pValue)
- {
- $this->orientation = $pValue;
-
- return $this;
- }
-
- /**
- * Get Scale.
- *
- * @return null|int
- */
- public function getScale()
- {
- return $this->scale;
- }
-
- /**
- * Set Scale.
- * Print scaling. Valid values range from 10 to 400
- * This setting is overridden when fitToWidth and/or fitToHeight are in use.
- *
- * @param null|int $pValue
- * @param bool $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
- *
- * @return $this
- */
- public function setScale($pValue, $pUpdate = true)
- {
- // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
- // but it is apparently still able to handle any scale >= 0, where 0 results in 100
- if (($pValue >= 0) || $pValue === null) {
- $this->scale = $pValue;
- if ($pUpdate) {
- $this->fitToPage = false;
- }
- } else {
- throw new PhpSpreadsheetException('Scale must not be negative');
- }
-
- return $this;
- }
-
- /**
- * Get Fit To Page.
- *
- * @return bool
- */
- public function getFitToPage()
- {
- return $this->fitToPage;
- }
-
- /**
- * Set Fit To Page.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setFitToPage($pValue)
- {
- $this->fitToPage = $pValue;
-
- return $this;
- }
-
- /**
- * Get Fit To Height.
- *
- * @return null|int
- */
- public function getFitToHeight()
- {
- return $this->fitToHeight;
- }
-
- /**
- * Set Fit To Height.
- *
- * @param null|int $pValue
- * @param bool $pUpdate Update fitToPage so it applies rather than scaling
- *
- * @return $this
- */
- public function setFitToHeight($pValue, $pUpdate = true)
- {
- $this->fitToHeight = $pValue;
- if ($pUpdate) {
- $this->fitToPage = true;
- }
-
- return $this;
- }
-
- /**
- * Get Fit To Width.
- *
- * @return null|int
- */
- public function getFitToWidth()
- {
- return $this->fitToWidth;
- }
-
- /**
- * Set Fit To Width.
- *
- * @param null|int $pValue
- * @param bool $pUpdate Update fitToPage so it applies rather than scaling
- *
- * @return $this
- */
- public function setFitToWidth($pValue, $pUpdate = true)
- {
- $this->fitToWidth = $pValue;
- if ($pUpdate) {
- $this->fitToPage = true;
- }
-
- return $this;
- }
-
- /**
- * Is Columns to repeat at left set?
- *
- * @return bool
- */
- public function isColumnsToRepeatAtLeftSet()
- {
- if (is_array($this->columnsToRepeatAtLeft)) {
- if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Get Columns to repeat at left.
- *
- * @return array Containing start column and end column, empty array if option unset
- */
- public function getColumnsToRepeatAtLeft()
- {
- return $this->columnsToRepeatAtLeft;
- }
-
- /**
- * Set Columns to repeat at left.
- *
- * @param array $pValue Containing start column and end column, empty array if option unset
- *
- * @return $this
- */
- public function setColumnsToRepeatAtLeft(array $pValue)
- {
- $this->columnsToRepeatAtLeft = $pValue;
-
- return $this;
- }
-
- /**
- * Set Columns to repeat at left by start and end.
- *
- * @param string $pStart eg: 'A'
- * @param string $pEnd eg: 'B'
- *
- * @return $this
- */
- public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd)
- {
- $this->columnsToRepeatAtLeft = [$pStart, $pEnd];
-
- return $this;
- }
-
- /**
- * Is Rows to repeat at top set?
- *
- * @return bool
- */
- public function isRowsToRepeatAtTopSet()
- {
- if (is_array($this->rowsToRepeatAtTop)) {
- if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Get Rows to repeat at top.
- *
- * @return array Containing start column and end column, empty array if option unset
- */
- public function getRowsToRepeatAtTop()
- {
- return $this->rowsToRepeatAtTop;
- }
-
- /**
- * Set Rows to repeat at top.
- *
- * @param array $pValue Containing start column and end column, empty array if option unset
- *
- * @return $this
- */
- public function setRowsToRepeatAtTop(array $pValue)
- {
- $this->rowsToRepeatAtTop = $pValue;
-
- return $this;
- }
-
- /**
- * Set Rows to repeat at top by start and end.
- *
- * @param int $pStart eg: 1
- * @param int $pEnd eg: 1
- *
- * @return $this
- */
- public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd)
- {
- $this->rowsToRepeatAtTop = [$pStart, $pEnd];
-
- return $this;
- }
-
- /**
- * Get center page horizontally.
- *
- * @return bool
- */
- public function getHorizontalCentered()
- {
- return $this->horizontalCentered;
- }
-
- /**
- * Set center page horizontally.
- *
- * @param bool $value
- *
- * @return $this
- */
- public function setHorizontalCentered($value)
- {
- $this->horizontalCentered = $value;
-
- return $this;
- }
-
- /**
- * Get center page vertically.
- *
- * @return bool
- */
- public function getVerticalCentered()
- {
- return $this->verticalCentered;
- }
-
- /**
- * Set center page vertically.
- *
- * @param bool $value
- *
- * @return $this
- */
- public function setVerticalCentered($value)
- {
- $this->verticalCentered = $value;
-
- return $this;
- }
-
- /**
- * Get print area.
- *
- * @param int $index Identifier for a specific print area range if several ranges have been set
- * Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
- * Otherwise, the specific range identified by the value of $index will be returned
- * Print areas are numbered from 1
- *
- * @return string
- */
- public function getPrintArea($index = 0)
- {
- if ($index == 0) {
- return $this->printArea;
- }
- $printAreas = explode(',', $this->printArea);
- if (isset($printAreas[$index - 1])) {
- return $printAreas[$index - 1];
- }
-
- throw new PhpSpreadsheetException('Requested Print Area does not exist');
- }
-
- /**
- * Is print area set?
- *
- * @param int $index Identifier for a specific print area range if several ranges have been set
- * Default behaviour, or an index value of 0, will identify whether any print range is set
- * Otherwise, existence of the range identified by the value of $index will be returned
- * Print areas are numbered from 1
- *
- * @return bool
- */
- public function isPrintAreaSet($index = 0)
- {
- if ($index == 0) {
- return $this->printArea !== null;
- }
- $printAreas = explode(',', $this->printArea);
-
- return isset($printAreas[$index - 1]);
- }
-
- /**
- * Clear a print area.
- *
- * @param int $index Identifier for a specific print area range if several ranges have been set
- * Default behaviour, or an index value of 0, will clear all print ranges that are set
- * Otherwise, the range identified by the value of $index will be removed from the series
- * Print areas are numbered from 1
- *
- * @return $this
- */
- public function clearPrintArea($index = 0)
- {
- if ($index == 0) {
- $this->printArea = null;
- } else {
- $printAreas = explode(',', $this->printArea);
- if (isset($printAreas[$index - 1])) {
- unset($printAreas[$index - 1]);
- $this->printArea = implode(',', $printAreas);
- }
- }
-
- return $this;
- }
-
- /**
- * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
- *
- * @param string $value
- * @param int $index Identifier for a specific print area range allowing several ranges to be set
- * When the method is "O"verwrite, then a positive integer index will overwrite that indexed
- * entry in the print areas list; a negative index value will identify which entry to
- * overwrite working bacward through the print area to the list, with the last entry as -1.
- * Specifying an index value of 0, will overwrite all existing print ranges.
- * When the method is "I"nsert, then a positive index will insert after that indexed entry in
- * the print areas list, while a negative index will insert before the indexed entry.
- * Specifying an index value of 0, will always append the new print range at the end of the
- * list.
- * Print areas are numbered from 1
- * @param string $method Determines the method used when setting multiple print areas
- * Default behaviour, or the "O" method, overwrites existing print area
- * The "I" method, inserts the new print area before any specified index, or at the end of the list
- *
- * @return $this
- */
- public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
- {
- if (strpos($value, '!') !== false) {
- throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
- } elseif (strpos($value, ':') === false) {
- throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
- } elseif (strpos($value, '$') !== false) {
- throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
- }
- $value = strtoupper($value);
- if (!$this->printArea) {
- $index = 0;
- }
-
- if ($method == self::SETPRINTRANGE_OVERWRITE) {
- if ($index == 0) {
- $this->printArea = $value;
- } else {
- $printAreas = explode(',', $this->printArea);
- if ($index < 0) {
- $index = count($printAreas) - abs($index) + 1;
- }
- if (($index <= 0) || ($index > count($printAreas))) {
- throw new PhpSpreadsheetException('Invalid index for setting print range.');
- }
- $printAreas[$index - 1] = $value;
- $this->printArea = implode(',', $printAreas);
- }
- } elseif ($method == self::SETPRINTRANGE_INSERT) {
- if ($index == 0) {
- $this->printArea = $this->printArea ? ($this->printArea . ',' . $value) : $value;
- } else {
- $printAreas = explode(',', $this->printArea);
- if ($index < 0) {
- $index = abs($index) - 1;
- }
- if ($index > count($printAreas)) {
- throw new PhpSpreadsheetException('Invalid index for setting print range.');
- }
- $printAreas = array_merge(array_slice($printAreas, 0, $index), [$value], array_slice($printAreas, $index));
- $this->printArea = implode(',', $printAreas);
- }
- } else {
- throw new PhpSpreadsheetException('Invalid method for setting print range.');
- }
-
- return $this;
- }
-
- /**
- * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
- *
- * @param string $value
- * @param int $index Identifier for a specific print area range allowing several ranges to be set
- * A positive index will insert after that indexed entry in the print areas list, while a
- * negative index will insert before the indexed entry.
- * Specifying an index value of 0, will always append the new print range at the end of the
- * list.
- * Print areas are numbered from 1
- *
- * @return $this
- */
- public function addPrintArea($value, $index = -1)
- {
- return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
- }
-
- /**
- * Set print area.
- *
- * @param int $column1 Column 1
- * @param int $row1 Row 1
- * @param int $column2 Column 2
- * @param int $row2 Row 2
- * @param int $index Identifier for a specific print area range allowing several ranges to be set
- * When the method is "O"verwrite, then a positive integer index will overwrite that indexed
- * entry in the print areas list; a negative index value will identify which entry to
- * overwrite working backward through the print area to the list, with the last entry as -1.
- * Specifying an index value of 0, will overwrite all existing print ranges.
- * When the method is "I"nsert, then a positive index will insert after that indexed entry in
- * the print areas list, while a negative index will insert before the indexed entry.
- * Specifying an index value of 0, will always append the new print range at the end of the
- * list.
- * Print areas are numbered from 1
- * @param string $method Determines the method used when setting multiple print areas
- * Default behaviour, or the "O" method, overwrites existing print area
- * The "I" method, inserts the new print area before any specified index, or at the end of the list
- *
- * @return $this
- */
- public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
- {
- return $this->setPrintArea(
- Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
- $index,
- $method
- );
- }
-
- /**
- * Add a new print area to the list of print areas.
- *
- * @param int $column1 Start Column for the print area
- * @param int $row1 Start Row for the print area
- * @param int $column2 End Column for the print area
- * @param int $row2 End Row for the print area
- * @param int $index Identifier for a specific print area range allowing several ranges to be set
- * A positive index will insert after that indexed entry in the print areas list, while a
- * negative index will insert before the indexed entry.
- * Specifying an index value of 0, will always append the new print range at the end of the
- * list.
- * Print areas are numbered from 1
- *
- * @return $this
- */
- public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
- {
- return $this->setPrintArea(
- Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
- $index,
- self::SETPRINTRANGE_INSERT
- );
- }
-
- /**
- * Get first page number.
- *
- * @return int
- */
- public function getFirstPageNumber()
- {
- return $this->firstPageNumber;
- }
-
- /**
- * Set first page number.
- *
- * @param int $value
- *
- * @return $this
- */
- public function setFirstPageNumber($value)
- {
- $this->firstPageNumber = $value;
-
- return $this;
- }
-
- /**
- * Reset first page number.
- *
- * @return $this
- */
- public function resetFirstPageNumber()
- {
- return $this->setFirstPageNumber(null);
- }
-
- public function getPageOrder(): string
- {
- return $this->pageOrder;
- }
-
- public function setPageOrder(?string $pageOrder): self
- {
- if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) {
- $this->pageOrder = $pageOrder ?? self::PAGEORDER_DOWN_THEN_OVER;
- }
-
- return $this;
- }
-
- /**
- * 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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Protection.php b/vendor/PhpSpreadsheet/Worksheet/Protection.php
deleted file mode 100644
index ba3af0a..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Protection.php
+++ /dev/null
@@ -1,691 +0,0 @@
-sheet ||
- $this->objects ||
- $this->scenarios ||
- $this->formatCells ||
- $this->formatColumns ||
- $this->formatRows ||
- $this->insertColumns ||
- $this->insertRows ||
- $this->insertHyperlinks ||
- $this->deleteColumns ||
- $this->deleteRows ||
- $this->selectLockedCells ||
- $this->sort ||
- $this->autoFilter ||
- $this->pivotTables ||
- $this->selectUnlockedCells;
- }
-
- /**
- * Get Sheet.
- *
- * @return bool
- */
- public function getSheet()
- {
- return $this->sheet;
- }
-
- /**
- * Set Sheet.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setSheet($pValue)
- {
- $this->sheet = $pValue;
-
- return $this;
- }
-
- /**
- * Get Objects.
- *
- * @return bool
- */
- public function getObjects()
- {
- return $this->objects;
- }
-
- /**
- * Set Objects.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setObjects($pValue)
- {
- $this->objects = $pValue;
-
- return $this;
- }
-
- /**
- * Get Scenarios.
- *
- * @return bool
- */
- public function getScenarios()
- {
- return $this->scenarios;
- }
-
- /**
- * Set Scenarios.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setScenarios($pValue)
- {
- $this->scenarios = $pValue;
-
- return $this;
- }
-
- /**
- * Get FormatCells.
- *
- * @return bool
- */
- public function getFormatCells()
- {
- return $this->formatCells;
- }
-
- /**
- * Set FormatCells.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setFormatCells($pValue)
- {
- $this->formatCells = $pValue;
-
- return $this;
- }
-
- /**
- * Get FormatColumns.
- *
- * @return bool
- */
- public function getFormatColumns()
- {
- return $this->formatColumns;
- }
-
- /**
- * Set FormatColumns.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setFormatColumns($pValue)
- {
- $this->formatColumns = $pValue;
-
- return $this;
- }
-
- /**
- * Get FormatRows.
- *
- * @return bool
- */
- public function getFormatRows()
- {
- return $this->formatRows;
- }
-
- /**
- * Set FormatRows.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setFormatRows($pValue)
- {
- $this->formatRows = $pValue;
-
- return $this;
- }
-
- /**
- * Get InsertColumns.
- *
- * @return bool
- */
- public function getInsertColumns()
- {
- return $this->insertColumns;
- }
-
- /**
- * Set InsertColumns.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setInsertColumns($pValue)
- {
- $this->insertColumns = $pValue;
-
- return $this;
- }
-
- /**
- * Get InsertRows.
- *
- * @return bool
- */
- public function getInsertRows()
- {
- return $this->insertRows;
- }
-
- /**
- * Set InsertRows.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setInsertRows($pValue)
- {
- $this->insertRows = $pValue;
-
- return $this;
- }
-
- /**
- * Get InsertHyperlinks.
- *
- * @return bool
- */
- public function getInsertHyperlinks()
- {
- return $this->insertHyperlinks;
- }
-
- /**
- * Set InsertHyperlinks.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setInsertHyperlinks($pValue)
- {
- $this->insertHyperlinks = $pValue;
-
- return $this;
- }
-
- /**
- * Get DeleteColumns.
- *
- * @return bool
- */
- public function getDeleteColumns()
- {
- return $this->deleteColumns;
- }
-
- /**
- * Set DeleteColumns.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setDeleteColumns($pValue)
- {
- $this->deleteColumns = $pValue;
-
- return $this;
- }
-
- /**
- * Get DeleteRows.
- *
- * @return bool
- */
- public function getDeleteRows()
- {
- return $this->deleteRows;
- }
-
- /**
- * Set DeleteRows.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setDeleteRows($pValue)
- {
- $this->deleteRows = $pValue;
-
- return $this;
- }
-
- /**
- * Get SelectLockedCells.
- *
- * @return bool
- */
- public function getSelectLockedCells()
- {
- return $this->selectLockedCells;
- }
-
- /**
- * Set SelectLockedCells.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setSelectLockedCells($pValue)
- {
- $this->selectLockedCells = $pValue;
-
- return $this;
- }
-
- /**
- * Get Sort.
- *
- * @return bool
- */
- public function getSort()
- {
- return $this->sort;
- }
-
- /**
- * Set Sort.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setSort($pValue)
- {
- $this->sort = $pValue;
-
- return $this;
- }
-
- /**
- * Get AutoFilter.
- *
- * @return bool
- */
- public function getAutoFilter()
- {
- return $this->autoFilter;
- }
-
- /**
- * Set AutoFilter.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setAutoFilter($pValue)
- {
- $this->autoFilter = $pValue;
-
- return $this;
- }
-
- /**
- * Get PivotTables.
- *
- * @return bool
- */
- public function getPivotTables()
- {
- return $this->pivotTables;
- }
-
- /**
- * Set PivotTables.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setPivotTables($pValue)
- {
- $this->pivotTables = $pValue;
-
- return $this;
- }
-
- /**
- * Get SelectUnlockedCells.
- *
- * @return bool
- */
- public function getSelectUnlockedCells()
- {
- return $this->selectUnlockedCells;
- }
-
- /**
- * Set SelectUnlockedCells.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setSelectUnlockedCells($pValue)
- {
- $this->selectUnlockedCells = $pValue;
-
- return $this;
- }
-
- /**
- * Get hashed password.
- *
- * @return string
- */
- public function getPassword()
- {
- return $this->password;
- }
-
- /**
- * Set Password.
- *
- * @param string $pValue
- * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
- *
- * @return $this
- */
- public function setPassword($pValue, $pAlreadyHashed = false)
- {
- if (!$pAlreadyHashed) {
- $salt = $this->generateSalt();
- $this->setSalt($salt);
- $pValue = PasswordHasher::hashPassword($pValue, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
- }
-
- $this->password = $pValue;
-
- return $this;
- }
-
- /**
- * Create a pseudorandom string.
- */
- private function generateSalt(): string
- {
- return base64_encode(random_bytes(16));
- }
-
- /**
- * Get algorithm name.
- */
- public function getAlgorithm(): string
- {
- return $this->algorithm;
- }
-
- /**
- * Set algorithm name.
- */
- public function setAlgorithm(string $algorithm): void
- {
- $this->algorithm = $algorithm;
- }
-
- /**
- * Get salt value.
- */
- public function getSalt(): string
- {
- return $this->salt;
- }
-
- /**
- * Set salt value.
- */
- public function setSalt(string $salt): void
- {
- $this->salt = $salt;
- }
-
- /**
- * Get spin count.
- */
- public function getSpinCount(): int
- {
- return $this->spinCount;
- }
-
- /**
- * Set spin count.
- */
- public function setSpinCount(int $spinCount): void
- {
- $this->spinCount = $spinCount;
- }
-
- /**
- * Verify that the given non-hashed password can "unlock" the protection.
- */
- public function verify(string $password): bool
- {
- if (!$this->isProtectionEnabled()) {
- return true;
- }
-
- $hash = PasswordHasher::hashPassword($password, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
-
- return $this->getPassword() === $hash;
- }
-
- /**
- * 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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Row.php b/vendor/PhpSpreadsheet/Worksheet/Row.php
deleted file mode 100644
index 4f48a34..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Row.php
+++ /dev/null
@@ -1,74 +0,0 @@
-worksheet = $worksheet;
- $this->rowIndex = $rowIndex;
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- $this->worksheet = null;
- }
-
- /**
- * Get row index.
- *
- * @return int
- */
- public function getRowIndex()
- {
- return $this->rowIndex;
- }
-
- /**
- * Get cell iterator.
- *
- * @param string $startColumn The column address at which to start iterating
- * @param string $endColumn Optionally, the column address at which to stop iterating
- *
- * @return RowCellIterator
- */
- public function getCellIterator($startColumn = 'A', $endColumn = null)
- {
- return new RowCellIterator($this->worksheet, $this->rowIndex, $startColumn, $endColumn);
- }
-
- /**
- * Returns bound worksheet.
- *
- * @return Worksheet
- */
- public function getWorksheet()
- {
- return $this->worksheet;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/RowCellIterator.php b/vendor/PhpSpreadsheet/Worksheet/RowCellIterator.php
deleted file mode 100644
index f5576dc..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/RowCellIterator.php
+++ /dev/null
@@ -1,195 +0,0 @@
-worksheet = $worksheet;
- $this->rowIndex = $rowIndex;
- $this->resetEnd($endColumn);
- $this->resetStart($startColumn);
- }
-
- /**
- * (Re)Set the start column and the current column pointer.
- *
- * @param string $startColumn The column address at which to start iterating
- *
- * @return $this
- */
- public function resetStart($startColumn = 'A')
- {
- $this->startColumnIndex = Coordinate::columnIndexFromString($startColumn);
- $this->adjustForExistingOnlyRange();
- $this->seek(Coordinate::stringFromColumnIndex($this->startColumnIndex));
-
- return $this;
- }
-
- /**
- * (Re)Set the end column.
- *
- * @param string $endColumn The column address at which to stop iterating
- *
- * @return $this
- */
- public function resetEnd($endColumn = null)
- {
- $endColumn = $endColumn ? $endColumn : $this->worksheet->getHighestColumn();
- $this->endColumnIndex = Coordinate::columnIndexFromString($endColumn);
- $this->adjustForExistingOnlyRange();
-
- return $this;
- }
-
- /**
- * Set the column pointer to the selected column.
- *
- * @param string $column The column address to set the current pointer at
- *
- * @return $this
- */
- public function seek($column = 'A')
- {
- $column = Coordinate::columnIndexFromString($column);
- if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
- throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
- } elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) {
- throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
- }
- $this->currentColumnIndex = $column;
-
- return $this;
- }
-
- /**
- * Rewind the iterator to the starting column.
- */
- public function rewind(): void
- {
- $this->currentColumnIndex = $this->startColumnIndex;
- }
-
- /**
- * Return the current cell in this worksheet row.
- *
- * @return \PhpOffice\PhpSpreadsheet\Cell\Cell
- */
- public function current()
- {
- return $this->worksheet->getCellByColumnAndRow($this->currentColumnIndex, $this->rowIndex);
- }
-
- /**
- * Return the current iterator key.
- *
- * @return string
- */
- public function key()
- {
- return Coordinate::stringFromColumnIndex($this->currentColumnIndex);
- }
-
- /**
- * Set the iterator to its next value.
- */
- public function next(): void
- {
- do {
- ++$this->currentColumnIndex;
- } while (($this->onlyExistingCells) && (!$this->worksheet->cellExistsByColumnAndRow($this->currentColumnIndex, $this->rowIndex)) && ($this->currentColumnIndex <= $this->endColumnIndex));
- }
-
- /**
- * Set the iterator to its previous value.
- */
- public function prev(): void
- {
- do {
- --$this->currentColumnIndex;
- } while (($this->onlyExistingCells) && (!$this->worksheet->cellExistsByColumnAndRow($this->currentColumnIndex, $this->rowIndex)) && ($this->currentColumnIndex >= $this->startColumnIndex));
- }
-
- /**
- * Indicate if more columns exist in the worksheet range of columns that we're iterating.
- *
- * @return bool
- */
- public function valid()
- {
- return $this->currentColumnIndex <= $this->endColumnIndex && $this->currentColumnIndex >= $this->startColumnIndex;
- }
-
- /**
- * Return the current iterator position.
- *
- * @return int
- */
- public function getCurrentColumnIndex()
- {
- return $this->currentColumnIndex;
- }
-
- /**
- * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
- */
- protected function adjustForExistingOnlyRange(): void
- {
- if ($this->onlyExistingCells) {
- while ((!$this->worksheet->cellExistsByColumnAndRow($this->startColumnIndex, $this->rowIndex)) && ($this->startColumnIndex <= $this->endColumnIndex)) {
- ++$this->startColumnIndex;
- }
- if ($this->startColumnIndex > $this->endColumnIndex) {
- throw new PhpSpreadsheetException('No cells exist within the specified range');
- }
- while ((!$this->worksheet->cellExistsByColumnAndRow($this->endColumnIndex, $this->rowIndex)) && ($this->endColumnIndex >= $this->startColumnIndex)) {
- --$this->endColumnIndex;
- }
- if ($this->endColumnIndex < $this->startColumnIndex) {
- throw new PhpSpreadsheetException('No cells exist within the specified range');
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/RowDimension.php b/vendor/PhpSpreadsheet/Worksheet/RowDimension.php
deleted file mode 100644
index c4a87bd..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/RowDimension.php
+++ /dev/null
@@ -1,115 +0,0 @@
-rowIndex = $pIndex;
-
- // set dimension as unformatted by default
- parent::__construct(null);
- }
-
- /**
- * Get Row Index.
- *
- * @return int
- */
- public function getRowIndex()
- {
- return $this->rowIndex;
- }
-
- /**
- * Set Row Index.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setRowIndex($pValue)
- {
- $this->rowIndex = $pValue;
-
- return $this;
- }
-
- /**
- * Get Row Height.
- *
- * @return float
- */
- public function getRowHeight()
- {
- return $this->height;
- }
-
- /**
- * Set Row Height.
- *
- * @param float $pValue
- *
- * @return $this
- */
- public function setRowHeight($pValue)
- {
- $this->height = $pValue;
-
- return $this;
- }
-
- /**
- * Get ZeroHeight.
- *
- * @return bool
- */
- public function getZeroHeight()
- {
- return $this->zeroHeight;
- }
-
- /**
- * Set ZeroHeight.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setZeroHeight($pValue)
- {
- $this->zeroHeight = $pValue;
-
- return $this;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/RowIterator.php b/vendor/PhpSpreadsheet/Worksheet/RowIterator.php
deleted file mode 100644
index 4254253..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/RowIterator.php
+++ /dev/null
@@ -1,167 +0,0 @@
-subject = $subject;
- $this->resetEnd($endRow);
- $this->resetStart($startRow);
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- $this->subject = null;
- }
-
- /**
- * (Re)Set the start row and the current row pointer.
- *
- * @param int $startRow The row number at which to start iterating
- *
- * @return $this
- */
- public function resetStart($startRow = 1)
- {
- if ($startRow > $this->subject->getHighestRow()) {
- throw new PhpSpreadsheetException("Start row ({$startRow}) is beyond highest row ({$this->subject->getHighestRow()})");
- }
-
- $this->startRow = $startRow;
- if ($this->endRow < $this->startRow) {
- $this->endRow = $this->startRow;
- }
- $this->seek($startRow);
-
- return $this;
- }
-
- /**
- * (Re)Set the end row.
- *
- * @param int $endRow The row number at which to stop iterating
- *
- * @return $this
- */
- public function resetEnd($endRow = null)
- {
- $this->endRow = ($endRow) ? $endRow : $this->subject->getHighestRow();
-
- return $this;
- }
-
- /**
- * Set the row pointer to the selected row.
- *
- * @param int $row The row number to set the current pointer at
- *
- * @return $this
- */
- public function seek($row = 1)
- {
- if (($row < $this->startRow) || ($row > $this->endRow)) {
- throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
- }
- $this->position = $row;
-
- return $this;
- }
-
- /**
- * Rewind the iterator to the starting row.
- */
- public function rewind(): void
- {
- $this->position = $this->startRow;
- }
-
- /**
- * Return the current row in this worksheet.
- *
- * @return Row
- */
- public function current()
- {
- return new Row($this->subject, $this->position);
- }
-
- /**
- * Return the current iterator key.
- *
- * @return int
- */
- public function key()
- {
- return $this->position;
- }
-
- /**
- * Set the iterator to its next value.
- */
- public function next(): void
- {
- ++$this->position;
- }
-
- /**
- * Set the iterator to its previous value.
- */
- public function prev(): void
- {
- --$this->position;
- }
-
- /**
- * Indicate if more rows exist in the worksheet range of rows that we're iterating.
- *
- * @return bool
- */
- public function valid()
- {
- return $this->position <= $this->endRow && $this->position >= $this->startRow;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/SheetView.php b/vendor/PhpSpreadsheet/Worksheet/SheetView.php
deleted file mode 100644
index 2f7d381..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/SheetView.php
+++ /dev/null
@@ -1,193 +0,0 @@
-zoomScale;
- }
-
- /**
- * Set ZoomScale.
- * Valid values range from 10 to 400.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setZoomScale($pValue)
- {
- // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
- // but it is apparently still able to handle any scale >= 1
- if (($pValue >= 1) || $pValue === null) {
- $this->zoomScale = $pValue;
- } else {
- throw new PhpSpreadsheetException('Scale must be greater than or equal to 1.');
- }
-
- return $this;
- }
-
- /**
- * Get ZoomScaleNormal.
- *
- * @return int
- */
- public function getZoomScaleNormal()
- {
- return $this->zoomScaleNormal;
- }
-
- /**
- * Set ZoomScale.
- * Valid values range from 10 to 400.
- *
- * @param int $pValue
- *
- * @return $this
- */
- public function setZoomScaleNormal($pValue)
- {
- if (($pValue >= 1) || $pValue === null) {
- $this->zoomScaleNormal = $pValue;
- } else {
- throw new PhpSpreadsheetException('Scale must be greater than or equal to 1.');
- }
-
- return $this;
- }
-
- /**
- * Set ShowZeroes setting.
- *
- * @param bool $pValue
- */
- public function setShowZeros($pValue): void
- {
- $this->showZeros = $pValue;
- }
-
- /**
- * @return bool
- */
- public function getShowZeros()
- {
- return $this->showZeros;
- }
-
- /**
- * Get View.
- *
- * @return string
- */
- public function getView()
- {
- return $this->sheetviewType;
- }
-
- /**
- * Set View.
- *
- * Valid values are
- * 'normal' self::SHEETVIEW_NORMAL
- * 'pageLayout' self::SHEETVIEW_PAGE_LAYOUT
- * 'pageBreakPreview' self::SHEETVIEW_PAGE_BREAK_PREVIEW
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setView($pValue)
- {
- // MS Excel 2007 allows setting the view to 'normal', 'pageLayout' or 'pageBreakPreview' via the user interface
- if ($pValue === null) {
- $pValue = self::SHEETVIEW_NORMAL;
- }
- if (in_array($pValue, self::$sheetViewTypes)) {
- $this->sheetviewType = $pValue;
- } else {
- throw new PhpSpreadsheetException('Invalid sheetview layout type.');
- }
-
- return $this;
- }
-
- /**
- * 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;
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Worksheet/Worksheet.php b/vendor/PhpSpreadsheet/Worksheet/Worksheet.php
deleted file mode 100644
index 19833b7..0000000
--- a/vendor/PhpSpreadsheet/Worksheet/Worksheet.php
+++ /dev/null
@@ -1,3019 +0,0 @@
-parent = $parent;
- $this->setTitle($pTitle, false);
- // setTitle can change $pTitle
- $this->setCodeName($this->getTitle());
- $this->setSheetState(self::SHEETSTATE_VISIBLE);
-
- $this->cellCollection = CellsFactory::getInstance($this);
- // Set page setup
- $this->pageSetup = new PageSetup();
- // Set page margins
- $this->pageMargins = new PageMargins();
- // Set page header/footer
- $this->headerFooter = new HeaderFooter();
- // Set sheet view
- $this->sheetView = new SheetView();
- // Drawing collection
- $this->drawingCollection = new ArrayObject();
- // Chart collection
- $this->chartCollection = new ArrayObject();
- // Protection
- $this->protection = new Protection();
- // Default row dimension
- $this->defaultRowDimension = new RowDimension(null);
- // Default column dimension
- $this->defaultColumnDimension = new ColumnDimension(null);
- $this->autoFilter = new AutoFilter(null, $this);
- }
-
- /**
- * Disconnect all cells from this Worksheet object,
- * typically so that the worksheet object can be unset.
- */
- public function disconnectCells(): void
- {
- if ($this->cellCollection !== null) {
- $this->cellCollection->unsetWorksheetCells();
- $this->cellCollection = null;
- }
- // detach ourself from the workbook, so that it can then delete this worksheet successfully
- $this->parent = null;
- }
-
- /**
- * Code to execute when this worksheet is unset().
- */
- public function __destruct()
- {
- Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
-
- $this->disconnectCells();
- }
-
- /**
- * Return the cell collection.
- *
- * @return Cells
- */
- public function getCellCollection()
- {
- return $this->cellCollection;
- }
-
- /**
- * Get array of invalid characters for sheet title.
- *
- * @return array
- */
- public static function getInvalidCharacters()
- {
- return self::$invalidCharacters;
- }
-
- /**
- * Check sheet code name for valid Excel syntax.
- *
- * @param string $pValue The string to check
- *
- * @return string The valid string
- */
- private static function checkSheetCodeName($pValue)
- {
- $CharCount = Shared\StringHelper::countCharacters($pValue);
- if ($CharCount == 0) {
- throw new Exception('Sheet code name cannot be empty.');
- }
- // Some of the printable ASCII characters are invalid: * : / \ ? [ ] and first and last characters cannot be a "'"
- if (
- (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
- (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
- (Shared\StringHelper::substring($pValue, 0, 1) == '\'')
- ) {
- throw new Exception('Invalid character found in sheet code name');
- }
-
- // Enforce maximum characters allowed for sheet title
- if ($CharCount > self::SHEET_TITLE_MAXIMUM_LENGTH) {
- throw new Exception('Maximum ' . self::SHEET_TITLE_MAXIMUM_LENGTH . ' characters allowed in sheet code name.');
- }
-
- return $pValue;
- }
-
- /**
- * Check sheet title for valid Excel syntax.
- *
- * @param string $pValue The string to check
- *
- * @return string The valid string
- */
- private static function checkSheetTitle($pValue)
- {
- // Some of the printable ASCII characters are invalid: * : / \ ? [ ]
- if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
- throw new Exception('Invalid character found in sheet title');
- }
-
- // Enforce maximum characters allowed for sheet title
- if (Shared\StringHelper::countCharacters($pValue) > self::SHEET_TITLE_MAXIMUM_LENGTH) {
- throw new Exception('Maximum ' . self::SHEET_TITLE_MAXIMUM_LENGTH . ' characters allowed in sheet title.');
- }
-
- return $pValue;
- }
-
- /**
- * Get a sorted list of all cell coordinates currently held in the collection by row and column.
- *
- * @param bool $sorted Also sort the cell collection?
- *
- * @return string[]
- */
- public function getCoordinates($sorted = true)
- {
- if ($this->cellCollection == null) {
- return [];
- }
-
- if ($sorted) {
- return $this->cellCollection->getSortedCoordinates();
- }
-
- return $this->cellCollection->getCoordinates();
- }
-
- /**
- * Get collection of row dimensions.
- *
- * @return RowDimension[]
- */
- public function getRowDimensions()
- {
- return $this->rowDimensions;
- }
-
- /**
- * Get default row dimension.
- *
- * @return RowDimension
- */
- public function getDefaultRowDimension()
- {
- return $this->defaultRowDimension;
- }
-
- /**
- * Get collection of column dimensions.
- *
- * @return ColumnDimension[]
- */
- public function getColumnDimensions()
- {
- return $this->columnDimensions;
- }
-
- /**
- * Get default column dimension.
- *
- * @return ColumnDimension
- */
- public function getDefaultColumnDimension()
- {
- return $this->defaultColumnDimension;
- }
-
- /**
- * Get collection of drawings.
- *
- * @return BaseDrawing[]
- */
- public function getDrawingCollection()
- {
- return $this->drawingCollection;
- }
-
- /**
- * Get collection of charts.
- *
- * @return Chart[]
- */
- public function getChartCollection()
- {
- return $this->chartCollection;
- }
-
- /**
- * Add chart.
- *
- * @param null|int $iChartIndex Index where chart should go (0,1,..., or null for last)
- *
- * @return Chart
- */
- public function addChart(Chart $pChart, $iChartIndex = null)
- {
- $pChart->setWorksheet($this);
- if ($iChartIndex === null) {
- $this->chartCollection[] = $pChart;
- } else {
- // Insert the chart at the requested index
- array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
- }
-
- return $pChart;
- }
-
- /**
- * Return the count of charts on this worksheet.
- *
- * @return int The number of charts
- */
- public function getChartCount()
- {
- return count($this->chartCollection);
- }
-
- /**
- * Get a chart by its index position.
- *
- * @param string $index Chart index position
- *
- * @return Chart|false
- */
- public function getChartByIndex($index)
- {
- $chartCount = count($this->chartCollection);
- if ($chartCount == 0) {
- return false;
- }
- if ($index === null) {
- $index = --$chartCount;
- }
- if (!isset($this->chartCollection[$index])) {
- return false;
- }
-
- return $this->chartCollection[$index];
- }
-
- /**
- * Return an array of the names of charts on this worksheet.
- *
- * @return string[] The names of charts
- */
- public function getChartNames()
- {
- $chartNames = [];
- foreach ($this->chartCollection as $chart) {
- $chartNames[] = $chart->getName();
- }
-
- return $chartNames;
- }
-
- /**
- * Get a chart by name.
- *
- * @param string $chartName Chart name
- *
- * @return Chart|false
- */
- public function getChartByName($chartName)
- {
- $chartCount = count($this->chartCollection);
- if ($chartCount == 0) {
- return false;
- }
- foreach ($this->chartCollection as $index => $chart) {
- if ($chart->getName() == $chartName) {
- return $this->chartCollection[$index];
- }
- }
-
- return false;
- }
-
- /**
- * Refresh column dimensions.
- *
- * @return $this
- */
- public function refreshColumnDimensions()
- {
- $currentColumnDimensions = $this->getColumnDimensions();
- $newColumnDimensions = [];
-
- foreach ($currentColumnDimensions as $objColumnDimension) {
- $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
- }
-
- $this->columnDimensions = $newColumnDimensions;
-
- return $this;
- }
-
- /**
- * Refresh row dimensions.
- *
- * @return $this
- */
- public function refreshRowDimensions()
- {
- $currentRowDimensions = $this->getRowDimensions();
- $newRowDimensions = [];
-
- foreach ($currentRowDimensions as $objRowDimension) {
- $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
- }
-
- $this->rowDimensions = $newRowDimensions;
-
- return $this;
- }
-
- /**
- * Calculate worksheet dimension.
- *
- * @return string String containing the dimension of this worksheet
- */
- public function calculateWorksheetDimension()
- {
- // Return
- return 'A1:' . $this->getHighestColumn() . $this->getHighestRow();
- }
-
- /**
- * Calculate worksheet data dimension.
- *
- * @return string String containing the dimension of this worksheet that actually contain data
- */
- public function calculateWorksheetDataDimension()
- {
- // Return
- return 'A1:' . $this->getHighestDataColumn() . $this->getHighestDataRow();
- }
-
- /**
- * Calculate widths for auto-size columns.
- *
- * @return $this
- */
- public function calculateColumnWidths()
- {
- // initialize $autoSizes array
- $autoSizes = [];
- foreach ($this->getColumnDimensions() as $colDimension) {
- if ($colDimension->getAutoSize()) {
- $autoSizes[$colDimension->getColumnIndex()] = -1;
- }
- }
-
- // There is only something to do if there are some auto-size columns
- if (!empty($autoSizes)) {
- // build list of cells references that participate in a merge
- $isMergeCell = [];
- foreach ($this->getMergeCells() as $cells) {
- foreach (Coordinate::extractAllCellReferencesInRange($cells) as $cellReference) {
- $isMergeCell[$cellReference] = true;
- }
- }
-
- // loop through all cells in the worksheet
- foreach ($this->getCoordinates(false) as $coordinate) {
- $cell = $this->getCell($coordinate, false);
- if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
- //Determine if cell is in merge range
- $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentCoordinate()]);
-
- //By default merged cells should be ignored
- $isMergedButProceed = false;
-
- //The only exception is if it's a merge range value cell of a 'vertical' randge (1 column wide)
- if ($isMerged && $cell->isMergeRangeValueCell()) {
- $range = $cell->getMergeRange();
- $rangeBoundaries = Coordinate::rangeDimension($range);
- if ($rangeBoundaries[0] == 1) {
- $isMergedButProceed = true;
- }
- }
-
- // Determine width if cell does not participate in a merge or does and is a value cell of 1-column wide range
- if (!$isMerged || $isMergedButProceed) {
- // Calculated value
- // To formatted string
- $cellValue = NumberFormat::toFormattedString(
- $cell->getCalculatedValue(),
- $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
- );
-
- $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
- (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
- (float) Shared\Font::calculateColumnWidth(
- $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
- $cellValue,
- $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
- $this->getParent()->getDefaultStyle()->getFont()
- )
- );
- }
- }
- }
-
- // adjust column widths
- foreach ($autoSizes as $columnIndex => $width) {
- if ($width == -1) {
- $width = $this->getDefaultColumnDimension()->getWidth();
- }
- $this->getColumnDimension($columnIndex)->setWidth($width);
- }
- }
-
- return $this;
- }
-
- /**
- * Get parent.
- *
- * @return Spreadsheet
- */
- public function getParent()
- {
- return $this->parent;
- }
-
- /**
- * Re-bind parent.
- *
- * @return $this
- */
- public function rebindParent(Spreadsheet $parent)
- {
- if ($this->parent !== null) {
- $definedNames = $this->parent->getDefinedNames();
- foreach ($definedNames as $definedName) {
- $parent->addDefinedName($definedName);
- }
-
- $this->parent->removeSheetByIndex(
- $this->parent->getIndex($this)
- );
- }
- $this->parent = $parent;
-
- return $this;
- }
-
- /**
- * Get title.
- *
- * @return string
- */
- public function getTitle()
- {
- return $this->title;
- }
-
- /**
- * Set title.
- *
- * @param string $pValue String containing the dimension of this worksheet
- * @param bool $updateFormulaCellReferences Flag indicating whether cell references in formulae should
- * be updated to reflect the new sheet name.
- * This should be left as the default true, unless you are
- * certain that no formula cells on any worksheet contain
- * references to this worksheet
- * @param bool $validate False to skip validation of new title. WARNING: This should only be set
- * at parse time (by Readers), where titles can be assumed to be valid.
- *
- * @return $this
- */
- public function setTitle($pValue, $updateFormulaCellReferences = true, $validate = true)
- {
- // Is this a 'rename' or not?
- if ($this->getTitle() == $pValue) {
- return $this;
- }
-
- // Old title
- $oldTitle = $this->getTitle();
-
- if ($validate) {
- // Syntax check
- self::checkSheetTitle($pValue);
-
- if ($this->parent) {
- // Is there already such sheet name?
- if ($this->parent->sheetNameExists($pValue)) {
- // Use name, but append with lowest possible integer
-
- if (Shared\StringHelper::countCharacters($pValue) > 29) {
- $pValue = Shared\StringHelper::substring($pValue, 0, 29);
- }
- $i = 1;
- while ($this->parent->sheetNameExists($pValue . ' ' . $i)) {
- ++$i;
- if ($i == 10) {
- if (Shared\StringHelper::countCharacters($pValue) > 28) {
- $pValue = Shared\StringHelper::substring($pValue, 0, 28);
- }
- } elseif ($i == 100) {
- if (Shared\StringHelper::countCharacters($pValue) > 27) {
- $pValue = Shared\StringHelper::substring($pValue, 0, 27);
- }
- }
- }
-
- $pValue .= " $i";
- }
- }
- }
-
- // Set title
- $this->title = $pValue;
- $this->dirty = true;
-
- if ($this->parent && $this->parent->getCalculationEngine()) {
- // New title
- $newTitle = $this->getTitle();
- $this->parent->getCalculationEngine()
- ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
- if ($updateFormulaCellReferences) {
- ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
- }
- }
-
- return $this;
- }
-
- /**
- * Get sheet state.
- *
- * @return string Sheet state (visible, hidden, veryHidden)
- */
- public function getSheetState()
- {
- return $this->sheetState;
- }
-
- /**
- * Set sheet state.
- *
- * @param string $value Sheet state (visible, hidden, veryHidden)
- *
- * @return $this
- */
- public function setSheetState($value)
- {
- $this->sheetState = $value;
-
- return $this;
- }
-
- /**
- * Get page setup.
- *
- * @return PageSetup
- */
- public function getPageSetup()
- {
- return $this->pageSetup;
- }
-
- /**
- * Set page setup.
- *
- * @return $this
- */
- public function setPageSetup(PageSetup $pValue)
- {
- $this->pageSetup = $pValue;
-
- return $this;
- }
-
- /**
- * Get page margins.
- *
- * @return PageMargins
- */
- public function getPageMargins()
- {
- return $this->pageMargins;
- }
-
- /**
- * Set page margins.
- *
- * @return $this
- */
- public function setPageMargins(PageMargins $pValue)
- {
- $this->pageMargins = $pValue;
-
- return $this;
- }
-
- /**
- * Get page header/footer.
- *
- * @return HeaderFooter
- */
- public function getHeaderFooter()
- {
- return $this->headerFooter;
- }
-
- /**
- * Set page header/footer.
- *
- * @return $this
- */
- public function setHeaderFooter(HeaderFooter $pValue)
- {
- $this->headerFooter = $pValue;
-
- return $this;
- }
-
- /**
- * Get sheet view.
- *
- * @return SheetView
- */
- public function getSheetView()
- {
- return $this->sheetView;
- }
-
- /**
- * Set sheet view.
- *
- * @return $this
- */
- public function setSheetView(SheetView $pValue)
- {
- $this->sheetView = $pValue;
-
- return $this;
- }
-
- /**
- * Get Protection.
- *
- * @return Protection
- */
- public function getProtection()
- {
- return $this->protection;
- }
-
- /**
- * Set Protection.
- *
- * @return $this
- */
- public function setProtection(Protection $pValue)
- {
- $this->protection = $pValue;
- $this->dirty = true;
-
- return $this;
- }
-
- /**
- * Get highest worksheet column.
- *
- * @param string $row Return the data highest column for the specified row,
- * or the highest column of any row if no row number is passed
- *
- * @return string Highest column name
- */
- public function getHighestColumn($row = null)
- {
- if ($row == null) {
- return $this->cachedHighestColumn;
- }
-
- return $this->getHighestDataColumn($row);
- }
-
- /**
- * Get highest worksheet column that contains data.
- *
- * @param string $row Return the highest data column for the specified row,
- * or the highest data column of any row if no row number is passed
- *
- * @return string Highest column name that contains data
- */
- public function getHighestDataColumn($row = null)
- {
- return $this->cellCollection->getHighestColumn($row);
- }
-
- /**
- * Get highest worksheet row.
- *
- * @param string $column Return the highest data row for the specified column,
- * or the highest row of any column if no column letter is passed
- *
- * @return int Highest row number
- */
- public function getHighestRow($column = null)
- {
- if ($column == null) {
- return $this->cachedHighestRow;
- }
-
- return $this->getHighestDataRow($column);
- }
-
- /**
- * Get highest worksheet row that contains data.
- *
- * @param string $column Return the highest data row for the specified column,
- * or the highest data row of any column if no column letter is passed
- *
- * @return int Highest row number that contains data
- */
- public function getHighestDataRow($column = null)
- {
- return $this->cellCollection->getHighestRow($column);
- }
-
- /**
- * Get highest worksheet column and highest row that have cell records.
- *
- * @return array Highest column name and highest row number
- */
- public function getHighestRowAndColumn()
- {
- return $this->cellCollection->getHighestRowAndColumn();
- }
-
- /**
- * Set a cell value.
- *
- * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
- * @param mixed $pValue Value of the cell
- *
- * @return $this
- */
- public function setCellValue($pCoordinate, $pValue)
- {
- $this->getCell($pCoordinate)->setValue($pValue);
-
- return $this;
- }
-
- /**
- * Set a cell value by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- * @param mixed $value Value of the cell
- *
- * @return $this
- */
- public function setCellValueByColumnAndRow($columnIndex, $row, $value)
- {
- $this->getCellByColumnAndRow($columnIndex, $row)->setValue($value);
-
- return $this;
- }
-
- /**
- * Set a cell value.
- *
- * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
- * @param mixed $pValue Value of the cell
- * @param string $pDataType Explicit data type, see DataType::TYPE_*
- *
- * @return $this
- */
- public function setCellValueExplicit($pCoordinate, $pValue, $pDataType)
- {
- // Set value
- $this->getCell($pCoordinate)->setValueExplicit($pValue, $pDataType);
-
- return $this;
- }
-
- /**
- * Set a cell value by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- * @param mixed $value Value of the cell
- * @param string $dataType Explicit data type, see DataType::TYPE_*
- *
- * @return $this
- */
- public function setCellValueExplicitByColumnAndRow($columnIndex, $row, $value, $dataType)
- {
- $this->getCellByColumnAndRow($columnIndex, $row)->setValueExplicit($value, $dataType);
-
- return $this;
- }
-
- /**
- * Get cell at a specific coordinate.
- *
- * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
- * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
- * already exist, or a null should be returned instead
- *
- * @return null|Cell Cell that was found/created or null
- */
- public function getCell($pCoordinate, $createIfNotExists = true)
- {
- // Uppercase coordinate
- $pCoordinateUpper = strtoupper($pCoordinate);
-
- // Check cell collection
- if ($this->cellCollection->has($pCoordinateUpper)) {
- return $this->cellCollection->get($pCoordinateUpper);
- }
-
- // Worksheet reference?
- if (strpos($pCoordinate, '!') !== false) {
- $worksheetReference = self::extractSheetTitle($pCoordinate, true);
-
- return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
- }
-
- // Named range?
- if (
- (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
- (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $pCoordinate, $matches))
- ) {
- $namedRange = DefinedName::resolveName($pCoordinate, $this);
- if ($namedRange !== null) {
- $pCoordinate = $namedRange->getValue();
-
- return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
- }
- }
-
- if (Coordinate::coordinateIsRange($pCoordinate)) {
- throw new Exception('Cell coordinate can not be a range of cells.');
- } elseif (strpos($pCoordinate, '$') !== false) {
- throw new Exception('Cell coordinate must not be absolute.');
- }
-
- // Create new cell object, if required
- return $createIfNotExists ? $this->createNewCell($pCoordinateUpper) : null;
- }
-
- /**
- * Get cell at a specific coordinate by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
- * already exist, or a null should be returned instead
- *
- * @return null|Cell Cell that was found/created or null
- */
- public function getCellByColumnAndRow($columnIndex, $row, $createIfNotExists = true)
- {
- $columnLetter = Coordinate::stringFromColumnIndex($columnIndex);
- $coordinate = $columnLetter . $row;
-
- if ($this->cellCollection->has($coordinate)) {
- return $this->cellCollection->get($coordinate);
- }
-
- // Create new cell object, if required
- return $createIfNotExists ? $this->createNewCell($coordinate) : null;
- }
-
- /**
- * Create a new cell at the specified coordinate.
- *
- * @param string $pCoordinate Coordinate of the cell
- *
- * @return Cell Cell that was created
- */
- private function createNewCell($pCoordinate)
- {
- $cell = new Cell(null, DataType::TYPE_NULL, $this);
- $this->cellCollection->add($pCoordinate, $cell);
- $this->cellCollectionIsSorted = false;
-
- // Coordinates
- $aCoordinates = Coordinate::coordinateFromString($pCoordinate);
- if (Coordinate::columnIndexFromString($this->cachedHighestColumn) < Coordinate::columnIndexFromString($aCoordinates[0])) {
- $this->cachedHighestColumn = $aCoordinates[0];
- }
- if ($aCoordinates[1] > $this->cachedHighestRow) {
- $this->cachedHighestRow = $aCoordinates[1];
- }
-
- // Cell needs appropriate xfIndex from dimensions records
- // but don't create dimension records if they don't already exist
- $rowDimension = $this->getRowDimension($aCoordinates[1], false);
- $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
-
- if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
- // then there is a row dimension with explicit style, assign it to the cell
- $cell->setXfIndex($rowDimension->getXfIndex());
- } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
- // then there is a column dimension, assign it to the cell
- $cell->setXfIndex($columnDimension->getXfIndex());
- }
-
- return $cell;
- }
-
- /**
- * Does the cell at a specific coordinate exist?
- *
- * @param string $pCoordinate Coordinate of the cell eg: 'A1'
- *
- * @return bool
- */
- public function cellExists($pCoordinate)
- {
- // Worksheet reference?
- if (strpos($pCoordinate, '!') !== false) {
- $worksheetReference = self::extractSheetTitle($pCoordinate, true);
-
- return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
- }
-
- // Named range?
- if (
- (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
- (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $pCoordinate, $matches))
- ) {
- $namedRange = DefinedName::resolveName($pCoordinate, $this);
- if ($namedRange !== null) {
- $pCoordinate = $namedRange->getValue();
- if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
- if (!$namedRange->getLocalOnly()) {
- return $namedRange->getWorksheet()->cellExists($pCoordinate);
- }
-
- throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
- }
- } else {
- return false;
- }
- }
-
- // Uppercase coordinate
- $pCoordinate = strtoupper($pCoordinate);
-
- if (Coordinate::coordinateIsRange($pCoordinate)) {
- throw new Exception('Cell coordinate can not be a range of cells.');
- } elseif (strpos($pCoordinate, '$') !== false) {
- throw new Exception('Cell coordinate must not be absolute.');
- }
-
- // Cell exists?
- return $this->cellCollection->has($pCoordinate);
- }
-
- /**
- * Cell at a specific coordinate by using numeric cell coordinates exists?
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- *
- * @return bool
- */
- public function cellExistsByColumnAndRow($columnIndex, $row)
- {
- return $this->cellExists(Coordinate::stringFromColumnIndex($columnIndex) . $row);
- }
-
- /**
- * Get row dimension at a specific row.
- *
- * @param int $pRow Numeric index of the row
- * @param bool $create
- *
- * @return RowDimension
- */
- public function getRowDimension($pRow, $create = true)
- {
- // Found
- $found = null;
-
- // Get row dimension
- if (!isset($this->rowDimensions[$pRow])) {
- if (!$create) {
- return null;
- }
- $this->rowDimensions[$pRow] = new RowDimension($pRow);
-
- $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
- }
-
- return $this->rowDimensions[$pRow];
- }
-
- /**
- * Get column dimension at a specific column.
- *
- * @param string $pColumn String index of the column eg: 'A'
- * @param bool $create
- *
- * @return ColumnDimension
- */
- public function getColumnDimension($pColumn, $create = true)
- {
- // Uppercase coordinate
- $pColumn = strtoupper($pColumn);
-
- // Fetch dimensions
- if (!isset($this->columnDimensions[$pColumn])) {
- if (!$create) {
- return null;
- }
- $this->columnDimensions[$pColumn] = new ColumnDimension($pColumn);
-
- if (Coordinate::columnIndexFromString($this->cachedHighestColumn) < Coordinate::columnIndexFromString($pColumn)) {
- $this->cachedHighestColumn = $pColumn;
- }
- }
-
- return $this->columnDimensions[$pColumn];
- }
-
- /**
- * Get column dimension at a specific column by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- *
- * @return ColumnDimension
- */
- public function getColumnDimensionByColumn($columnIndex)
- {
- return $this->getColumnDimension(Coordinate::stringFromColumnIndex($columnIndex));
- }
-
- /**
- * Get styles.
- *
- * @return Style[]
- */
- public function getStyles()
- {
- return $this->styles;
- }
-
- /**
- * Get style for cell.
- *
- * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1'
- *
- * @return Style
- */
- public function getStyle($pCellCoordinate)
- {
- // set this sheet as active
- $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
-
- // set cell coordinate as active
- $this->setSelectedCells($pCellCoordinate);
-
- return $this->parent->getCellXfSupervisor();
- }
-
- /**
- * Get conditional styles for a cell.
- *
- * @param string $pCoordinate eg: 'A1'
- *
- * @return Conditional[]
- */
- public function getConditionalStyles($pCoordinate)
- {
- $pCoordinate = strtoupper($pCoordinate);
- if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
- $this->conditionalStylesCollection[$pCoordinate] = [];
- }
-
- return $this->conditionalStylesCollection[$pCoordinate];
- }
-
- /**
- * Do conditional styles exist for this cell?
- *
- * @param string $pCoordinate eg: 'A1'
- *
- * @return bool
- */
- public function conditionalStylesExists($pCoordinate)
- {
- return isset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
- }
-
- /**
- * Removes conditional styles for a cell.
- *
- * @param string $pCoordinate eg: 'A1'
- *
- * @return $this
- */
- public function removeConditionalStyles($pCoordinate)
- {
- unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
-
- return $this;
- }
-
- /**
- * Get collection of conditional styles.
- *
- * @return array
- */
- public function getConditionalStylesCollection()
- {
- return $this->conditionalStylesCollection;
- }
-
- /**
- * Set conditional styles.
- *
- * @param string $pCoordinate eg: 'A1'
- * @param $pValue Conditional[]
- *
- * @return $this
- */
- public function setConditionalStyles($pCoordinate, $pValue)
- {
- $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
-
- return $this;
- }
-
- /**
- * Get style for cell by using numeric cell coordinates.
- *
- * @param int $columnIndex1 Numeric column coordinate of the cell
- * @param int $row1 Numeric row coordinate of the cell
- * @param null|int $columnIndex2 Numeric column coordinate of the range cell
- * @param null|int $row2 Numeric row coordinate of the range cell
- *
- * @return Style
- */
- public function getStyleByColumnAndRow($columnIndex1, $row1, $columnIndex2 = null, $row2 = null)
- {
- if ($columnIndex2 !== null && $row2 !== null) {
- $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
-
- return $this->getStyle($cellRange);
- }
-
- return $this->getStyle(Coordinate::stringFromColumnIndex($columnIndex1) . $row1);
- }
-
- /**
- * Duplicate cell style to a range of cells.
- *
- * Please note that this will overwrite existing cell styles for cells in range!
- *
- * @param Style $pCellStyle Cell style to duplicate
- * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
- *
- * @return $this
- */
- public function duplicateStyle(Style $pCellStyle, $pRange)
- {
- // Add the style to the workbook if necessary
- $workbook = $this->parent;
- if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
- // there is already such cell Xf in our collection
- $xfIndex = $existingStyle->getIndex();
- } else {
- // we don't have such a cell Xf, need to add
- $workbook->addCellXf($pCellStyle);
- $xfIndex = $pCellStyle->getIndex();
- }
-
- // Calculate range outer borders
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
-
- // Make sure we can loop upwards on rows and columns
- if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
- $tmp = $rangeStart;
- $rangeStart = $rangeEnd;
- $rangeEnd = $tmp;
- }
-
- // Loop through cells and apply styles
- for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
- for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
- $this->getCell(Coordinate::stringFromColumnIndex($col) . $row)->setXfIndex($xfIndex);
- }
- }
-
- return $this;
- }
-
- /**
- * Duplicate conditional style to a range of cells.
- *
- * Please note that this will overwrite existing cell styles for cells in range!
- *
- * @param Conditional[] $pCellStyle Cell style to duplicate
- * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
- *
- * @return $this
- */
- public function duplicateConditionalStyle(array $pCellStyle, $pRange = '')
- {
- foreach ($pCellStyle as $cellStyle) {
- if (!($cellStyle instanceof Conditional)) {
- throw new Exception('Style is not a conditional style');
- }
- }
-
- // Calculate range outer borders
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
-
- // Make sure we can loop upwards on rows and columns
- if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
- $tmp = $rangeStart;
- $rangeStart = $rangeEnd;
- $rangeEnd = $tmp;
- }
-
- // Loop through cells and apply styles
- for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
- for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
- $this->setConditionalStyles(Coordinate::stringFromColumnIndex($col) . $row, $pCellStyle);
- }
- }
-
- return $this;
- }
-
- /**
- * Set break on a cell.
- *
- * @param string $pCoordinate Cell coordinate (e.g. A1)
- * @param int $pBreak Break type (type of Worksheet::BREAK_*)
- *
- * @return $this
- */
- public function setBreak($pCoordinate, $pBreak)
- {
- // Uppercase coordinate
- $pCoordinate = strtoupper($pCoordinate);
-
- if ($pCoordinate != '') {
- if ($pBreak == self::BREAK_NONE) {
- if (isset($this->breaks[$pCoordinate])) {
- unset($this->breaks[$pCoordinate]);
- }
- } else {
- $this->breaks[$pCoordinate] = $pBreak;
- }
- } else {
- throw new Exception('No cell coordinate specified.');
- }
-
- return $this;
- }
-
- /**
- * Set break on a cell by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- * @param int $break Break type (type of Worksheet::BREAK_*)
- *
- * @return $this
- */
- public function setBreakByColumnAndRow($columnIndex, $row, $break)
- {
- return $this->setBreak(Coordinate::stringFromColumnIndex($columnIndex) . $row, $break);
- }
-
- /**
- * Get breaks.
- *
- * @return array[]
- */
- public function getBreaks()
- {
- return $this->breaks;
- }
-
- /**
- * Set merge on a cell range.
- *
- * @param string $pRange Cell range (e.g. A1:E1)
- *
- * @return $this
- */
- public function mergeCells($pRange)
- {
- // Uppercase coordinate
- $pRange = strtoupper($pRange);
-
- if (strpos($pRange, ':') !== false) {
- $this->mergeCells[$pRange] = $pRange;
-
- // make sure cells are created
-
- // get the cells in the range
- $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
-
- // create upper left cell if it does not already exist
- $upperLeft = $aReferences[0];
- if (!$this->cellExists($upperLeft)) {
- $this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
- }
-
- // Blank out the rest of the cells in the range (if they exist)
- $count = count($aReferences);
- for ($i = 1; $i < $count; ++$i) {
- if ($this->cellExists($aReferences[$i])) {
- $this->getCell($aReferences[$i])->setValueExplicit(null, DataType::TYPE_NULL);
- }
- }
- } else {
- throw new Exception('Merge must be set on a range of cells.');
- }
-
- return $this;
- }
-
- /**
- * Set merge on a cell range by using numeric cell coordinates.
- *
- * @param int $columnIndex1 Numeric column coordinate of the first cell
- * @param int $row1 Numeric row coordinate of the first cell
- * @param int $columnIndex2 Numeric column coordinate of the last cell
- * @param int $row2 Numeric row coordinate of the last cell
- *
- * @return $this
- */
- public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
- {
- $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
-
- return $this->mergeCells($cellRange);
- }
-
- /**
- * Remove merge on a cell range.
- *
- * @param string $pRange Cell range (e.g. A1:E1)
- *
- * @return $this
- */
- public function unmergeCells($pRange)
- {
- // Uppercase coordinate
- $pRange = strtoupper($pRange);
-
- if (strpos($pRange, ':') !== false) {
- if (isset($this->mergeCells[$pRange])) {
- unset($this->mergeCells[$pRange]);
- } else {
- throw new Exception('Cell range ' . $pRange . ' not known as merged.');
- }
- } else {
- throw new Exception('Merge can only be removed from a range of cells.');
- }
-
- return $this;
- }
-
- /**
- * Remove merge on a cell range by using numeric cell coordinates.
- *
- * @param int $columnIndex1 Numeric column coordinate of the first cell
- * @param int $row1 Numeric row coordinate of the first cell
- * @param int $columnIndex2 Numeric column coordinate of the last cell
- * @param int $row2 Numeric row coordinate of the last cell
- *
- * @return $this
- */
- public function unmergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
- {
- $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
-
- return $this->unmergeCells($cellRange);
- }
-
- /**
- * Get merge cells array.
- *
- * @return string[]
- */
- public function getMergeCells()
- {
- return $this->mergeCells;
- }
-
- /**
- * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
- * a single cell range.
- *
- * @param string[] $pValue
- *
- * @return $this
- */
- public function setMergeCells(array $pValue)
- {
- $this->mergeCells = $pValue;
-
- return $this;
- }
-
- /**
- * Set protection on a cell range.
- *
- * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
- * @param string $pPassword Password to unlock the protection
- * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
- *
- * @return $this
- */
- public function protectCells($pRange, $pPassword, $pAlreadyHashed = false)
- {
- // Uppercase coordinate
- $pRange = strtoupper($pRange);
-
- if (!$pAlreadyHashed) {
- $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
- }
- $this->protectedCells[$pRange] = $pPassword;
-
- return $this;
- }
-
- /**
- * Set protection on a cell range by using numeric cell coordinates.
- *
- * @param int $columnIndex1 Numeric column coordinate of the first cell
- * @param int $row1 Numeric row coordinate of the first cell
- * @param int $columnIndex2 Numeric column coordinate of the last cell
- * @param int $row2 Numeric row coordinate of the last cell
- * @param string $password Password to unlock the protection
- * @param bool $alreadyHashed If the password has already been hashed, set this to true
- *
- * @return $this
- */
- public function protectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $password, $alreadyHashed = false)
- {
- $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
-
- return $this->protectCells($cellRange, $password, $alreadyHashed);
- }
-
- /**
- * Remove protection on a cell range.
- *
- * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
- *
- * @return $this
- */
- public function unprotectCells($pRange)
- {
- // Uppercase coordinate
- $pRange = strtoupper($pRange);
-
- if (isset($this->protectedCells[$pRange])) {
- unset($this->protectedCells[$pRange]);
- } else {
- throw new Exception('Cell range ' . $pRange . ' not known as protected.');
- }
-
- return $this;
- }
-
- /**
- * Remove protection on a cell range by using numeric cell coordinates.
- *
- * @param int $columnIndex1 Numeric column coordinate of the first cell
- * @param int $row1 Numeric row coordinate of the first cell
- * @param int $columnIndex2 Numeric column coordinate of the last cell
- * @param int $row2 Numeric row coordinate of the last cell
- *
- * @return $this
- */
- public function unprotectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
- {
- $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
-
- return $this->unprotectCells($cellRange);
- }
-
- /**
- * Get protected cells.
- *
- * @return array[]
- */
- public function getProtectedCells()
- {
- return $this->protectedCells;
- }
-
- /**
- * Get Autofilter.
- *
- * @return AutoFilter
- */
- public function getAutoFilter()
- {
- return $this->autoFilter;
- }
-
- /**
- * Set AutoFilter.
- *
- * @param AutoFilter|string $pValue
- * A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
- *
- * @return $this
- */
- public function setAutoFilter($pValue)
- {
- if (is_string($pValue)) {
- $this->autoFilter->setRange($pValue);
- } elseif (is_object($pValue) && ($pValue instanceof AutoFilter)) {
- $this->autoFilter = $pValue;
- }
-
- return $this;
- }
-
- /**
- * Set Autofilter Range by using numeric cell coordinates.
- *
- * @param int $columnIndex1 Numeric column coordinate of the first cell
- * @param int $row1 Numeric row coordinate of the first cell
- * @param int $columnIndex2 Numeric column coordinate of the second cell
- * @param int $row2 Numeric row coordinate of the second cell
- *
- * @return $this
- */
- public function setAutoFilterByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
- {
- return $this->setAutoFilter(
- Coordinate::stringFromColumnIndex($columnIndex1) . $row1
- . ':' .
- Coordinate::stringFromColumnIndex($columnIndex2) . $row2
- );
- }
-
- /**
- * Remove autofilter.
- *
- * @return $this
- */
- public function removeAutoFilter()
- {
- $this->autoFilter->setRange(null);
-
- return $this;
- }
-
- /**
- * Get Freeze Pane.
- *
- * @return string
- */
- public function getFreezePane()
- {
- return $this->freezePane;
- }
-
- /**
- * Freeze Pane.
- *
- * Examples:
- *
- * - A2 will freeze the rows above cell A2 (i.e row 1)
- * - B1 will freeze the columns to the left of cell B1 (i.e column A)
- * - B2 will freeze the rows above and to the left of cell B2 (i.e row 1 and column A)
- *
- * @param null|string $cell Position of the split
- * @param null|string $topLeftCell default position of the right bottom pane
- *
- * @return $this
- */
- public function freezePane($cell, $topLeftCell = null)
- {
- if (is_string($cell) && Coordinate::coordinateIsRange($cell)) {
- throw new Exception('Freeze pane can not be set on a range of cells.');
- }
-
- if ($cell !== null && $topLeftCell === null) {
- $coordinate = Coordinate::coordinateFromString($cell);
- $topLeftCell = $coordinate[0] . $coordinate[1];
- }
-
- $this->freezePane = $cell;
- $this->topLeftCell = $topLeftCell;
-
- return $this;
- }
-
- /**
- * Freeze Pane by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- *
- * @return $this
- */
- public function freezePaneByColumnAndRow($columnIndex, $row)
- {
- return $this->freezePane(Coordinate::stringFromColumnIndex($columnIndex) . $row);
- }
-
- /**
- * Unfreeze Pane.
- *
- * @return $this
- */
- public function unfreezePane()
- {
- return $this->freezePane(null);
- }
-
- /**
- * Get the default position of the right bottom pane.
- *
- * @return int
- */
- public function getTopLeftCell()
- {
- return $this->topLeftCell;
- }
-
- /**
- * Insert a new row, updating all possible related data.
- *
- * @param int $pBefore Insert before this one
- * @param int $pNumRows Number of rows to insert
- *
- * @return $this
- */
- public function insertNewRowBefore($pBefore, $pNumRows = 1)
- {
- if ($pBefore >= 1) {
- $objReferenceHelper = ReferenceHelper::getInstance();
- $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
- } else {
- throw new Exception('Rows can only be inserted before at least row 1.');
- }
-
- return $this;
- }
-
- /**
- * Insert a new column, updating all possible related data.
- *
- * @param string $pBefore Insert before this one, eg: 'A'
- * @param int $pNumCols Number of columns to insert
- *
- * @return $this
- */
- public function insertNewColumnBefore($pBefore, $pNumCols = 1)
- {
- if (!is_numeric($pBefore)) {
- $objReferenceHelper = ReferenceHelper::getInstance();
- $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
- } else {
- throw new Exception('Column references should not be numeric.');
- }
-
- return $this;
- }
-
- /**
- * Insert a new column, updating all possible related data.
- *
- * @param int $beforeColumnIndex Insert before this one (numeric column coordinate of the cell)
- * @param int $pNumCols Number of columns to insert
- *
- * @return $this
- */
- public function insertNewColumnBeforeByIndex($beforeColumnIndex, $pNumCols = 1)
- {
- if ($beforeColumnIndex >= 1) {
- return $this->insertNewColumnBefore(Coordinate::stringFromColumnIndex($beforeColumnIndex), $pNumCols);
- }
-
- throw new Exception('Columns can only be inserted before at least column A (1).');
- }
-
- /**
- * Delete a row, updating all possible related data.
- *
- * @param int $pRow Remove starting with this one
- * @param int $pNumRows Number of rows to remove
- *
- * @return $this
- */
- public function removeRow($pRow, $pNumRows = 1)
- {
- if ($pRow < 1) {
- throw new Exception('Rows to be deleted should at least start from row 1.');
- }
-
- $highestRow = $this->getHighestDataRow();
- $removedRowsCounter = 0;
-
- for ($r = 0; $r < $pNumRows; ++$r) {
- if ($pRow + $r <= $highestRow) {
- $this->getCellCollection()->removeRow($pRow + $r);
- ++$removedRowsCounter;
- }
- }
-
- $objReferenceHelper = ReferenceHelper::getInstance();
- $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
- for ($r = 0; $r < $removedRowsCounter; ++$r) {
- $this->getCellCollection()->removeRow($highestRow);
- --$highestRow;
- }
-
- return $this;
- }
-
- /**
- * Remove a column, updating all possible related data.
- *
- * @param string $pColumn Remove starting with this one, eg: 'A'
- * @param int $pNumCols Number of columns to remove
- *
- * @return $this
- */
- public function removeColumn($pColumn, $pNumCols = 1)
- {
- if (is_numeric($pColumn)) {
- throw new Exception('Column references should not be numeric.');
- }
-
- $highestColumn = $this->getHighestDataColumn();
- $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
- $pColumnIndex = Coordinate::columnIndexFromString($pColumn);
-
- if ($pColumnIndex > $highestColumnIndex) {
- return $this;
- }
-
- $pColumn = Coordinate::stringFromColumnIndex($pColumnIndex + $pNumCols);
- $objReferenceHelper = ReferenceHelper::getInstance();
- $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
-
- $maxPossibleColumnsToBeRemoved = $highestColumnIndex - $pColumnIndex + 1;
-
- for ($c = 0, $n = min($maxPossibleColumnsToBeRemoved, $pNumCols); $c < $n; ++$c) {
- $this->getCellCollection()->removeColumn($highestColumn);
- $highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
- }
-
- $this->garbageCollect();
-
- return $this;
- }
-
- /**
- * Remove a column, updating all possible related data.
- *
- * @param int $columnIndex Remove starting with this one (numeric column coordinate of the cell)
- * @param int $numColumns Number of columns to remove
- *
- * @return $this
- */
- public function removeColumnByIndex($columnIndex, $numColumns = 1)
- {
- if ($columnIndex >= 1) {
- return $this->removeColumn(Coordinate::stringFromColumnIndex($columnIndex), $numColumns);
- }
-
- throw new Exception('Columns to be deleted should at least start from column A (1)');
- }
-
- /**
- * Show gridlines?
- *
- * @return bool
- */
- public function getShowGridlines()
- {
- return $this->showGridlines;
- }
-
- /**
- * Set show gridlines.
- *
- * @param bool $pValue Show gridlines (true/false)
- *
- * @return $this
- */
- public function setShowGridlines($pValue)
- {
- $this->showGridlines = $pValue;
-
- return $this;
- }
-
- /**
- * Print gridlines?
- *
- * @return bool
- */
- public function getPrintGridlines()
- {
- return $this->printGridlines;
- }
-
- /**
- * Set print gridlines.
- *
- * @param bool $pValue Print gridlines (true/false)
- *
- * @return $this
- */
- public function setPrintGridlines($pValue)
- {
- $this->printGridlines = $pValue;
-
- return $this;
- }
-
- /**
- * Show row and column headers?
- *
- * @return bool
- */
- public function getShowRowColHeaders()
- {
- return $this->showRowColHeaders;
- }
-
- /**
- * Set show row and column headers.
- *
- * @param bool $pValue Show row and column headers (true/false)
- *
- * @return $this
- */
- public function setShowRowColHeaders($pValue)
- {
- $this->showRowColHeaders = $pValue;
-
- return $this;
- }
-
- /**
- * Show summary below? (Row/Column outlining).
- *
- * @return bool
- */
- public function getShowSummaryBelow()
- {
- return $this->showSummaryBelow;
- }
-
- /**
- * Set show summary below.
- *
- * @param bool $pValue Show summary below (true/false)
- *
- * @return $this
- */
- public function setShowSummaryBelow($pValue)
- {
- $this->showSummaryBelow = $pValue;
-
- return $this;
- }
-
- /**
- * Show summary right? (Row/Column outlining).
- *
- * @return bool
- */
- public function getShowSummaryRight()
- {
- return $this->showSummaryRight;
- }
-
- /**
- * Set show summary right.
- *
- * @param bool $pValue Show summary right (true/false)
- *
- * @return $this
- */
- public function setShowSummaryRight($pValue)
- {
- $this->showSummaryRight = $pValue;
-
- return $this;
- }
-
- /**
- * Get comments.
- *
- * @return Comment[]
- */
- public function getComments()
- {
- return $this->comments;
- }
-
- /**
- * Set comments array for the entire sheet.
- *
- * @param Comment[] $pValue
- *
- * @return $this
- */
- public function setComments(array $pValue)
- {
- $this->comments = $pValue;
-
- return $this;
- }
-
- /**
- * Get comment for cell.
- *
- * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1'
- *
- * @return Comment
- */
- public function getComment($pCellCoordinate)
- {
- // Uppercase coordinate
- $pCellCoordinate = strtoupper($pCellCoordinate);
-
- if (Coordinate::coordinateIsRange($pCellCoordinate)) {
- throw new Exception('Cell coordinate string can not be a range of cells.');
- } elseif (strpos($pCellCoordinate, '$') !== false) {
- throw new Exception('Cell coordinate string must not be absolute.');
- } elseif ($pCellCoordinate == '') {
- throw new Exception('Cell coordinate can not be zero-length string.');
- }
-
- // Check if we already have a comment for this cell.
- if (isset($this->comments[$pCellCoordinate])) {
- return $this->comments[$pCellCoordinate];
- }
-
- // If not, create a new comment.
- $newComment = new Comment();
- $this->comments[$pCellCoordinate] = $newComment;
-
- return $newComment;
- }
-
- /**
- * Get comment for cell by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- *
- * @return Comment
- */
- public function getCommentByColumnAndRow($columnIndex, $row)
- {
- return $this->getComment(Coordinate::stringFromColumnIndex($columnIndex) . $row);
- }
-
- /**
- * Get active cell.
- *
- * @return string Example: 'A1'
- */
- public function getActiveCell()
- {
- return $this->activeCell;
- }
-
- /**
- * Get selected cells.
- *
- * @return string
- */
- public function getSelectedCells()
- {
- return $this->selectedCells;
- }
-
- /**
- * Selected cell.
- *
- * @param string $pCoordinate Cell (i.e. A1)
- *
- * @return $this
- */
- public function setSelectedCell($pCoordinate)
- {
- return $this->setSelectedCells($pCoordinate);
- }
-
- /**
- * Select a range of cells.
- *
- * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
- *
- * @return $this
- */
- public function setSelectedCells($pCoordinate)
- {
- // Uppercase coordinate
- $pCoordinate = strtoupper($pCoordinate);
-
- // Convert 'A' to 'A:A'
- $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
-
- // Convert '1' to '1:1'
- $pCoordinate = preg_replace('/^(\d+)$/', '${1}:${1}', $pCoordinate);
-
- // Convert 'A:C' to 'A1:C1048576'
- $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
-
- // Convert '1:3' to 'A1:XFD3'
- $pCoordinate = preg_replace('/^(\d+):(\d+)$/', 'A${1}:XFD${2}', $pCoordinate);
-
- if (Coordinate::coordinateIsRange($pCoordinate)) {
- [$first] = Coordinate::splitRange($pCoordinate);
- $this->activeCell = $first[0];
- } else {
- $this->activeCell = $pCoordinate;
- }
- $this->selectedCells = $pCoordinate;
-
- return $this;
- }
-
- /**
- * Selected cell by using numeric cell coordinates.
- *
- * @param int $columnIndex Numeric column coordinate of the cell
- * @param int $row Numeric row coordinate of the cell
- *
- * @return $this
- */
- public function setSelectedCellByColumnAndRow($columnIndex, $row)
- {
- return $this->setSelectedCells(Coordinate::stringFromColumnIndex($columnIndex) . $row);
- }
-
- /**
- * Get right-to-left.
- *
- * @return bool
- */
- public function getRightToLeft()
- {
- return $this->rightToLeft;
- }
-
- /**
- * Set right-to-left.
- *
- * @param bool $value Right-to-left true/false
- *
- * @return $this
- */
- public function setRightToLeft($value)
- {
- $this->rightToLeft = $value;
-
- return $this;
- }
-
- /**
- * Fill worksheet from values in array.
- *
- * @param array $source Source array
- * @param mixed $nullValue Value in source array that stands for blank cell
- * @param string $startCell Insert array starting from this cell address as the top left coordinate
- * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
- *
- * @return $this
- */
- public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
- {
- // Convert a 1-D array to 2-D (for ease of looping)
- if (!is_array(end($source))) {
- $source = [$source];
- }
-
- // start coordinate
- [$startColumn, $startRow] = Coordinate::coordinateFromString($startCell);
-
- // Loop through $source
- foreach ($source as $rowData) {
- $currentColumn = $startColumn;
- foreach ($rowData as $cellValue) {
- if ($strictNullComparison) {
- if ($cellValue !== $nullValue) {
- // Set cell value
- $this->getCell($currentColumn . $startRow)->setValue($cellValue);
- }
- } else {
- if ($cellValue != $nullValue) {
- // Set cell value
- $this->getCell($currentColumn . $startRow)->setValue($cellValue);
- }
- }
- ++$currentColumn;
- }
- ++$startRow;
- }
-
- return $this;
- }
-
- /**
- * Create array from a range of cells.
- *
- * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
- * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
- * @param bool $calculateFormulas Should formulas be calculated?
- * @param bool $formatData Should formatting be applied to cell values?
- * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
- * True - Return rows and columns indexed by their actual row and column IDs
- *
- * @return array
- */
- public function rangeToArray($pRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
- {
- // Returnvalue
- $returnValue = [];
- // Identify the range that we need to extract from the worksheet
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange);
- $minCol = Coordinate::stringFromColumnIndex($rangeStart[0]);
- $minRow = $rangeStart[1];
- $maxCol = Coordinate::stringFromColumnIndex($rangeEnd[0]);
- $maxRow = $rangeEnd[1];
-
- ++$maxCol;
- // Loop through rows
- $r = -1;
- for ($row = $minRow; $row <= $maxRow; ++$row) {
- $rRef = $returnCellRef ? $row : ++$r;
- $c = -1;
- // Loop through columns in the current row
- for ($col = $minCol; $col != $maxCol; ++$col) {
- $cRef = $returnCellRef ? $col : ++$c;
- // Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
- // so we test and retrieve directly against cellCollection
- if ($this->cellCollection->has($col . $row)) {
- // Cell exists
- $cell = $this->cellCollection->get($col . $row);
- if ($cell->getValue() !== null) {
- if ($cell->getValue() instanceof RichText) {
- $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
- } else {
- if ($calculateFormulas) {
- $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
- } else {
- $returnValue[$rRef][$cRef] = $cell->getValue();
- }
- }
-
- if ($formatData) {
- $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
- $returnValue[$rRef][$cRef] = NumberFormat::toFormattedString(
- $returnValue[$rRef][$cRef],
- ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : NumberFormat::FORMAT_GENERAL
- );
- }
- } else {
- // Cell holds a NULL
- $returnValue[$rRef][$cRef] = $nullValue;
- }
- } else {
- // Cell doesn't exist
- $returnValue[$rRef][$cRef] = $nullValue;
- }
- }
- }
-
- // Return
- return $returnValue;
- }
-
- /**
- * Create array from a range of cells.
- *
- * @param string $pNamedRange Name of the Named Range
- * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
- * @param bool $calculateFormulas Should formulas be calculated?
- * @param bool $formatData Should formatting be applied to cell values?
- * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
- * True - Return rows and columns indexed by their actual row and column IDs
- *
- * @return array
- */
- public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
- {
- $namedRange = DefinedName::resolveName($pNamedRange, $this);
- if ($namedRange !== null) {
- $pWorkSheet = $namedRange->getWorksheet();
- $pCellRange = $namedRange->getValue();
-
- return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
- }
-
- throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
- }
-
- /**
- * Create array from worksheet.
- *
- * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
- * @param bool $calculateFormulas Should formulas be calculated?
- * @param bool $formatData Should formatting be applied to cell values?
- * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
- * True - Return rows and columns indexed by their actual row and column IDs
- *
- * @return array
- */
- public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
- {
- // Garbage collect...
- $this->garbageCollect();
-
- // Identify the range that we need to extract from the worksheet
- $maxCol = $this->getHighestColumn();
- $maxRow = $this->getHighestRow();
-
- // Return
- return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
- }
-
- /**
- * Get row iterator.
- *
- * @param int $startRow The row number at which to start iterating
- * @param int $endRow The row number at which to stop iterating
- *
- * @return RowIterator
- */
- public function getRowIterator($startRow = 1, $endRow = null)
- {
- return new RowIterator($this, $startRow, $endRow);
- }
-
- /**
- * Get column iterator.
- *
- * @param string $startColumn The column address at which to start iterating
- * @param string $endColumn The column address at which to stop iterating
- *
- * @return ColumnIterator
- */
- public function getColumnIterator($startColumn = 'A', $endColumn = null)
- {
- return new ColumnIterator($this, $startColumn, $endColumn);
- }
-
- /**
- * Run PhpSpreadsheet garbage collector.
- *
- * @return $this
- */
- public function garbageCollect()
- {
- // Flush cache
- $this->cellCollection->get('A1');
-
- // Lookup highest column and highest row if cells are cleaned
- $colRow = $this->cellCollection->getHighestRowAndColumn();
- $highestRow = $colRow['row'];
- $highestColumn = Coordinate::columnIndexFromString($colRow['column']);
-
- // Loop through column dimensions
- foreach ($this->columnDimensions as $dimension) {
- $highestColumn = max($highestColumn, Coordinate::columnIndexFromString($dimension->getColumnIndex()));
- }
-
- // Loop through row dimensions
- foreach ($this->rowDimensions as $dimension) {
- $highestRow = max($highestRow, $dimension->getRowIndex());
- }
-
- // Cache values
- if ($highestColumn < 1) {
- $this->cachedHighestColumn = 'A';
- } else {
- $this->cachedHighestColumn = Coordinate::stringFromColumnIndex($highestColumn);
- }
- $this->cachedHighestRow = $highestRow;
-
- // Return
- return $this;
- }
-
- /**
- * Get hash code.
- *
- * @return string Hash code
- */
- public function getHashCode()
- {
- if ($this->dirty) {
- $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
- $this->dirty = false;
- }
-
- return $this->hash;
- }
-
- /**
- * Extract worksheet title from range.
- *
- * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
- * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> ['testSheet 1', 'A1'];
- *
- * @param string $pRange Range to extract title from
- * @param bool $returnRange Return range? (see example)
- *
- * @return mixed
- */
- public static function extractSheetTitle($pRange, $returnRange = false)
- {
- // Sheet title included?
- if (($sep = strrpos($pRange, '!')) === false) {
- return $returnRange ? ['', $pRange] : '';
- }
-
- if ($returnRange) {
- return [substr($pRange, 0, $sep), substr($pRange, $sep + 1)];
- }
-
- return substr($pRange, $sep + 1);
- }
-
- /**
- * Get hyperlink.
- *
- * @param string $pCellCoordinate Cell coordinate to get hyperlink for, eg: 'A1'
- *
- * @return Hyperlink
- */
- public function getHyperlink($pCellCoordinate)
- {
- // return hyperlink if we already have one
- if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
- return $this->hyperlinkCollection[$pCellCoordinate];
- }
-
- // else create hyperlink
- $this->hyperlinkCollection[$pCellCoordinate] = new Hyperlink();
-
- return $this->hyperlinkCollection[$pCellCoordinate];
- }
-
- /**
- * Set hyperlink.
- *
- * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1'
- *
- * @return $this
- */
- public function setHyperlink($pCellCoordinate, ?Hyperlink $pHyperlink = null)
- {
- if ($pHyperlink === null) {
- unset($this->hyperlinkCollection[$pCellCoordinate]);
- } else {
- $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
- }
-
- return $this;
- }
-
- /**
- * Hyperlink at a specific coordinate exists?
- *
- * @param string $pCoordinate eg: 'A1'
- *
- * @return bool
- */
- public function hyperlinkExists($pCoordinate)
- {
- return isset($this->hyperlinkCollection[$pCoordinate]);
- }
-
- /**
- * Get collection of hyperlinks.
- *
- * @return Hyperlink[]
- */
- public function getHyperlinkCollection()
- {
- return $this->hyperlinkCollection;
- }
-
- /**
- * Get data validation.
- *
- * @param string $pCellCoordinate Cell coordinate to get data validation for, eg: 'A1'
- *
- * @return DataValidation
- */
- public function getDataValidation($pCellCoordinate)
- {
- // return data validation if we already have one
- if (isset($this->dataValidationCollection[$pCellCoordinate])) {
- return $this->dataValidationCollection[$pCellCoordinate];
- }
-
- // else create data validation
- $this->dataValidationCollection[$pCellCoordinate] = new DataValidation();
-
- return $this->dataValidationCollection[$pCellCoordinate];
- }
-
- /**
- * Set data validation.
- *
- * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1'
- *
- * @return $this
- */
- public function setDataValidation($pCellCoordinate, ?DataValidation $pDataValidation = null)
- {
- if ($pDataValidation === null) {
- unset($this->dataValidationCollection[$pCellCoordinate]);
- } else {
- $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
- }
-
- return $this;
- }
-
- /**
- * Data validation at a specific coordinate exists?
- *
- * @param string $pCoordinate eg: 'A1'
- *
- * @return bool
- */
- public function dataValidationExists($pCoordinate)
- {
- return isset($this->dataValidationCollection[$pCoordinate]);
- }
-
- /**
- * Get collection of data validations.
- *
- * @return DataValidation[]
- */
- public function getDataValidationCollection()
- {
- return $this->dataValidationCollection;
- }
-
- /**
- * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
- *
- * @param string $range
- *
- * @return string Adjusted range value
- */
- public function shrinkRangeToFit($range)
- {
- $maxCol = $this->getHighestColumn();
- $maxRow = $this->getHighestRow();
- $maxCol = Coordinate::columnIndexFromString($maxCol);
-
- $rangeBlocks = explode(' ', $range);
- foreach ($rangeBlocks as &$rangeSet) {
- $rangeBoundaries = Coordinate::getRangeBoundaries($rangeSet);
-
- if (Coordinate::columnIndexFromString($rangeBoundaries[0][0]) > $maxCol) {
- $rangeBoundaries[0][0] = Coordinate::stringFromColumnIndex($maxCol);
- }
- if ($rangeBoundaries[0][1] > $maxRow) {
- $rangeBoundaries[0][1] = $maxRow;
- }
- if (Coordinate::columnIndexFromString($rangeBoundaries[1][0]) > $maxCol) {
- $rangeBoundaries[1][0] = Coordinate::stringFromColumnIndex($maxCol);
- }
- if ($rangeBoundaries[1][1] > $maxRow) {
- $rangeBoundaries[1][1] = $maxRow;
- }
- $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
- }
- unset($rangeSet);
-
- return implode(' ', $rangeBlocks);
- }
-
- /**
- * Get tab color.
- *
- * @return Color
- */
- public function getTabColor()
- {
- if ($this->tabColor === null) {
- $this->tabColor = new Color();
- }
-
- return $this->tabColor;
- }
-
- /**
- * Reset tab color.
- *
- * @return $this
- */
- public function resetTabColor()
- {
- $this->tabColor = null;
- $this->tabColor = null;
-
- return $this;
- }
-
- /**
- * Tab color set?
- *
- * @return bool
- */
- public function isTabColorSet()
- {
- return $this->tabColor !== null;
- }
-
- /**
- * Copy worksheet (!= clone!).
- *
- * @return static
- */
- public function copy()
- {
- return clone $this;
- }
-
- /**
- * Implement PHP __clone to create a deep clone, not just a shallow copy.
- */
- public function __clone()
- {
- foreach ($this as $key => $val) {
- if ($key == 'parent') {
- continue;
- }
-
- if (is_object($val) || (is_array($val))) {
- if ($key == 'cellCollection') {
- $newCollection = $this->cellCollection->cloneCellCollection($this);
- $this->cellCollection = $newCollection;
- } elseif ($key == 'drawingCollection') {
- $currentCollection = $this->drawingCollection;
- $this->drawingCollection = new ArrayObject();
- foreach ($currentCollection as $item) {
- if (is_object($item)) {
- $newDrawing = clone $item;
- $newDrawing->setWorksheet($this);
- }
- }
- } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) {
- $newAutoFilter = clone $this->autoFilter;
- $this->autoFilter = $newAutoFilter;
- $this->autoFilter->setParent($this);
- } else {
- $this->{$key} = unserialize(serialize($val));
- }
- }
- }
- }
-
- /**
- * Define the code name of the sheet.
- *
- * @param string $pValue Same rule as Title minus space not allowed (but, like Excel, change
- * silently space to underscore)
- * @param bool $validate False to skip validation of new title. WARNING: This should only be set
- * at parse time (by Readers), where titles can be assumed to be valid.
- *
- * @return $this
- */
- public function setCodeName($pValue, $validate = true)
- {
- // Is this a 'rename' or not?
- if ($this->getCodeName() == $pValue) {
- return $this;
- }
-
- if ($validate) {
- $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
-
- // Syntax check
- // throw an exception if not valid
- self::checkSheetCodeName($pValue);
-
- // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
-
- if ($this->getParent()) {
- // Is there already such sheet name?
- if ($this->getParent()->sheetCodeNameExists($pValue)) {
- // Use name, but append with lowest possible integer
-
- if (Shared\StringHelper::countCharacters($pValue) > 29) {
- $pValue = Shared\StringHelper::substring($pValue, 0, 29);
- }
- $i = 1;
- while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) {
- ++$i;
- if ($i == 10) {
- if (Shared\StringHelper::countCharacters($pValue) > 28) {
- $pValue = Shared\StringHelper::substring($pValue, 0, 28);
- }
- } elseif ($i == 100) {
- if (Shared\StringHelper::countCharacters($pValue) > 27) {
- $pValue = Shared\StringHelper::substring($pValue, 0, 27);
- }
- }
- }
-
- $pValue .= '_' . $i; // ok, we have a valid name
- }
- }
- }
-
- $this->codeName = $pValue;
-
- return $this;
- }
-
- /**
- * Return the code name of the sheet.
- *
- * @return null|string
- */
- public function getCodeName()
- {
- return $this->codeName;
- }
-
- /**
- * Sheet has a code name ?
- *
- * @return bool
- */
- public function hasCodeName()
- {
- return $this->codeName !== null;
- }
-}
diff --git a/vendor/PhpSpreadsheet/Writer/BaseWriter.php b/vendor/PhpSpreadsheet/Writer/BaseWriter.php
deleted file mode 100644
index afda5c4..0000000
--- a/vendor/PhpSpreadsheet/Writer/BaseWriter.php
+++ /dev/null
@@ -1,131 +0,0 @@
-includeCharts;
- }
-
- public function setIncludeCharts($pValue)
- {
- $this->includeCharts = (bool) $pValue;
-
- return $this;
- }
-
- public function getPreCalculateFormulas()
- {
- return $this->preCalculateFormulas;
- }
-
- public function setPreCalculateFormulas($pValue)
- {
- $this->preCalculateFormulas = (bool) $pValue;
-
- return $this;
- }
-
- public function getUseDiskCaching()
- {
- return $this->useDiskCaching;
- }
-
- public function setUseDiskCaching($pValue, $pDirectory = null)
- {
- $this->useDiskCaching = $pValue;
-
- if ($pDirectory !== null) {
- if (is_dir($pDirectory)) {
- $this->diskCachingDirectory = $pDirectory;
- } else {
- throw new Exception("Directory does not exist: $pDirectory");
- }
- }
-
- return $this;
- }
-
- public function getDiskCachingDirectory()
- {
- return $this->diskCachingDirectory;
- }
-
- /**
- * Open file handle.
- *
- * @param resource|string $filename
- */
- public function openFileHandle($filename): void
- {
- if (is_resource($filename)) {
- $this->fileHandle = $filename;
- $this->shouldCloseFile = false;
-
- return;
- }
-
- $fileHandle = $filename ? fopen($filename, 'wb+') : false;
- if ($fileHandle === false) {
- throw new Exception('Could not open file "' . $filename . '" for writing.');
- }
-
- $this->fileHandle = $fileHandle;
- $this->shouldCloseFile = true;
- }
-
- /**
- * Close file handle only if we opened it ourselves.
- */
- protected function maybeCloseFileHandle(): void
- {
- if ($this->shouldCloseFile) {
- if (!fclose($this->fileHandle)) {
- throw new Exception('Could not close file after writing.');
- }
- }
- }
-}
diff --git a/vendor/PhpSpreadsheet/Writer/Csv.php b/vendor/PhpSpreadsheet/Writer/Csv.php
deleted file mode 100644
index 74f2863..0000000
--- a/vendor/PhpSpreadsheet/Writer/Csv.php
+++ /dev/null
@@ -1,352 +0,0 @@
-spreadsheet = $spreadsheet;
- }
-
- /**
- * Save PhpSpreadsheet to file.
- *
- * @param resource|string $pFilename
- */
- public function save($pFilename): void
- {
- // Fetch sheet
- $sheet = $this->spreadsheet->getSheet($this->sheetIndex);
-
- $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
- Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
- $saveArrayReturnType = Calculation::getArrayReturnType();
- Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
-
- // Open file
- $this->openFileHandle($pFilename);
-
- if ($this->excelCompatibility) {
- $this->setUseBOM(true); // Enforce UTF-8 BOM Header
- $this->setIncludeSeparatorLine(true); // Set separator line
- $this->setEnclosure('"'); // Set enclosure to "
- $this->setDelimiter(';'); // Set delimiter to a semi-colon
- $this->setLineEnding("\r\n");
- }
-
- if ($this->useBOM) {
- // Write the UTF-8 BOM code if required
- fwrite($this->fileHandle, "\xEF\xBB\xBF");
- }
-
- if ($this->includeSeparatorLine) {
- // Write the separator line if required
- fwrite($this->fileHandle, 'sep=' . $this->getDelimiter() . $this->lineEnding);
- }
-
- // Identify the range that we need to extract from the worksheet
- $maxCol = $sheet->getHighestDataColumn();
- $maxRow = $sheet->getHighestDataRow();
-
- // Write rows to file
- for ($row = 1; $row <= $maxRow; ++$row) {
- // Convert the row to an array...
- $cellsArray = $sheet->rangeToArray('A' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas);
- // ... and write to the file
- $this->writeLine($this->fileHandle, $cellsArray[0]);
- }
-
- $this->maybeCloseFileHandle();
- Calculation::setArrayReturnType($saveArrayReturnType);
- Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
- }
-
- /**
- * Get delimiter.
- *
- * @return string
- */
- public function getDelimiter()
- {
- return $this->delimiter;
- }
-
- /**
- * Set delimiter.
- *
- * @param string $pValue Delimiter, defaults to ','
- *
- * @return $this
- */
- public function setDelimiter($pValue)
- {
- $this->delimiter = $pValue;
-
- return $this;
- }
-
- /**
- * Get enclosure.
- *
- * @return string
- */
- public function getEnclosure()
- {
- return $this->enclosure;
- }
-
- /**
- * Set enclosure.
- *
- * @param string $pValue Enclosure, defaults to "
- *
- * @return $this
- */
- public function setEnclosure($pValue = '"')
- {
- $this->enclosure = $pValue;
-
- return $this;
- }
-
- /**
- * Get line ending.
- *
- * @return string
- */
- public function getLineEnding()
- {
- return $this->lineEnding;
- }
-
- /**
- * Set line ending.
- *
- * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
- *
- * @return $this
- */
- public function setLineEnding($pValue)
- {
- $this->lineEnding = $pValue;
-
- return $this;
- }
-
- /**
- * Get whether BOM should be used.
- *
- * @return bool
- */
- public function getUseBOM()
- {
- return $this->useBOM;
- }
-
- /**
- * Set whether BOM should be used.
- *
- * @param bool $pValue Use UTF-8 byte-order mark? Defaults to false
- *
- * @return $this
- */
- public function setUseBOM($pValue)
- {
- $this->useBOM = $pValue;
-
- return $this;
- }
-
- /**
- * Get whether a separator line should be included.
- *
- * @return bool
- */
- public function getIncludeSeparatorLine()
- {
- return $this->includeSeparatorLine;
- }
-
- /**
- * Set whether a separator line should be included as the first line of the file.
- *
- * @param bool $pValue Use separator line? Defaults to false
- *
- * @return $this
- */
- public function setIncludeSeparatorLine($pValue)
- {
- $this->includeSeparatorLine = $pValue;
-
- return $this;
- }
-
- /**
- * Get whether the file should be saved with full Excel Compatibility.
- *
- * @return bool
- */
- public function getExcelCompatibility()
- {
- return $this->excelCompatibility;
- }
-
- /**
- * Set whether the file should be saved with full Excel Compatibility.
- *
- * @param bool $pValue Set the file to be written as a fully Excel compatible csv file
- * Note that this overrides other settings such as useBOM, enclosure and delimiter
- *
- * @return $this
- */
- public function setExcelCompatibility($pValue)
- {
- $this->excelCompatibility = $pValue;
-
- return $this;
- }
-
- /**
- * Get sheet index.
- *
- * @return int
- */
- public function getSheetIndex()
- {
- return $this->sheetIndex;
- }
-
- /**
- * Set sheet index.
- *
- * @param int $pValue Sheet index
- *
- * @return $this
- */
- public function setSheetIndex($pValue)
- {
- $this->sheetIndex = $pValue;
-
- return $this;
- }
-
- private $enclosureRequired = true;
-
- public function setEnclosureRequired(bool $value): self
- {
- $this->enclosureRequired = $value;
-
- return $this;
- }
-
- public function getEnclosureRequired(): bool
- {
- return $this->enclosureRequired;
- }
-
- /**
- * Write line to CSV file.
- *
- * @param resource $pFileHandle PHP filehandle
- * @param array $pValues Array containing values in a row
- */
- private function writeLine($pFileHandle, array $pValues): void
- {
- // No leading delimiter
- $delimiter = '';
-
- // Build the line
- $line = '';
-
- foreach ($pValues as $element) {
- // Add delimiter
- $line .= $delimiter;
- $delimiter = $this->delimiter;
- // Escape enclosures
- $enclosure = $this->enclosure;
- if ($enclosure) {
- // If enclosure is not required, use enclosure only if
- // element contains newline, delimiter, or enclosure.
- if (!$this->enclosureRequired && strpbrk($element, "$delimiter$enclosure\n") === false) {
- $enclosure = '';
- } else {
- $element = str_replace($enclosure, $enclosure . $enclosure, $element);
- }
- }
- // Add enclosed string
- $line .= $enclosure . $element . $enclosure;
- }
-
- // Add line ending
- $line .= $this->lineEnding;
-
- // Write to file
- fwrite($pFileHandle, $line);
- }
-}
diff --git a/vendor/PhpSpreadsheet/Writer/Exception.php b/vendor/PhpSpreadsheet/Writer/Exception.php
deleted file mode 100644
index 92e6f5f..0000000
--- a/vendor/PhpSpreadsheet/Writer/Exception.php
+++ /dev/null
@@ -1,9 +0,0 @@
-spreadsheet = $spreadsheet;
- $this->defaultFont = $this->spreadsheet->getDefaultStyle()->getFont();
- }
-
- /**
- * Save Spreadsheet to file.
- *
- * @param resource|string $pFilename
- */
- public function save($pFilename): void
- {
- // Open file
- $this->openFileHandle($pFilename);
-
- // Write html
- fwrite($this->fileHandle, $this->generateHTMLAll());
-
- // Close file
- $this->maybeCloseFileHandle();
- }
-
- /**
- * Save Spreadsheet as html to variable.
- *
- * @return string
- */
- public function generateHtmlAll()
- {
- // garbage collect
- $this->spreadsheet->garbageCollect();
-
- $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
- Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
- $saveArrayReturnType = Calculation::getArrayReturnType();
- Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
-
- // Build CSS
- $this->buildCSS(!$this->useInlineCss);
-
- $html = '';
-
- // Write headers
- $html .= $this->generateHTMLHeader(!$this->useInlineCss);
-
- // Write navigation (tabs)
- if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) {
- $html .= $this->generateNavigation();
- }
-
- // Write data
- $html .= $this->generateSheetData();
-
- // Write footer
- $html .= $this->generateHTMLFooter();
- $callback = $this->editHtmlCallback;
- if ($callback) {
- $html = $callback($html);
- }
-
- Calculation::setArrayReturnType($saveArrayReturnType);
- Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
-
- return $html;
- }
-
- /**
- * Set a callback to edit the entire HTML.
- *
- * The callback must accept the HTML as string as first parameter,
- * and it must return the edited HTML as string.
- */
- public function setEditHtmlCallback(?callable $callback): void
- {
- $this->editHtmlCallback = $callback;
- }
-
- const VALIGN_ARR = [
- Alignment::VERTICAL_BOTTOM => 'bottom',
- Alignment::VERTICAL_TOP => 'top',
- Alignment::VERTICAL_CENTER => 'middle',
- Alignment::VERTICAL_JUSTIFY => 'middle',
- ];
-
- /**
- * Map VAlign.
- *
- * @param string $vAlign Vertical alignment
- *
- * @return string
- */
- private function mapVAlign($vAlign)
- {
- return array_key_exists($vAlign, self::VALIGN_ARR) ? self::VALIGN_ARR[$vAlign] : 'baseline';
- }
-
- const HALIGN_ARR = [
- Alignment::HORIZONTAL_LEFT => 'left',
- Alignment::HORIZONTAL_RIGHT => 'right',
- Alignment::HORIZONTAL_CENTER => 'center',
- Alignment::HORIZONTAL_CENTER_CONTINUOUS => 'center',
- Alignment::HORIZONTAL_JUSTIFY => 'justify',
- ];
-
- /**
- * Map HAlign.
- *
- * @param string $hAlign Horizontal alignment
- *
- * @return string
- */
- private function mapHAlign($hAlign)
- {
- return array_key_exists($hAlign, self::HALIGN_ARR) ? self::HALIGN_ARR[$hAlign] : '';
- }
-
- const BORDER_ARR = [
- Border::BORDER_NONE => 'none',
- Border::BORDER_DASHDOT => '1px dashed',
- Border::BORDER_DASHDOTDOT => '1px dotted',
- Border::BORDER_DASHED => '1px dashed',
- Border::BORDER_DOTTED => '1px dotted',
- Border::BORDER_DOUBLE => '3px double',
- Border::BORDER_HAIR => '1px solid',
- Border::BORDER_MEDIUM => '2px solid',
- Border::BORDER_MEDIUMDASHDOT => '2px dashed',
- Border::BORDER_MEDIUMDASHDOTDOT => '2px dotted',
- Border::BORDER_SLANTDASHDOT => '2px dashed',
- Border::BORDER_THICK => '3px solid',
- ];
-
- /**
- * Map border style.
- *
- * @param int $borderStyle Sheet index
- *
- * @return string
- */
- private function mapBorderStyle($borderStyle)
- {
- return array_key_exists($borderStyle, self::BORDER_ARR) ? self::BORDER_ARR[$borderStyle] : '1px solid';
- }
-
- /**
- * Get sheet index.
- *
- * @return int
- */
- public function getSheetIndex()
- {
- return $this->sheetIndex;
- }
-
- /**
- * Set sheet index.
- *
- * @param int $pValue Sheet index
- *
- * @return $this
- */
- public function setSheetIndex($pValue)
- {
- $this->sheetIndex = $pValue;
-
- return $this;
- }
-
- /**
- * Get sheet index.
- *
- * @return bool
- */
- public function getGenerateSheetNavigationBlock()
- {
- return $this->generateSheetNavigationBlock;
- }
-
- /**
- * Set sheet index.
- *
- * @param bool $pValue Flag indicating whether the sheet navigation block should be generated or not
- *
- * @return $this
- */
- public function setGenerateSheetNavigationBlock($pValue)
- {
- $this->generateSheetNavigationBlock = (bool) $pValue;
-
- return $this;
- }
-
- /**
- * Write all sheets (resets sheetIndex to NULL).
- *
- * @return $this
- */
- public function writeAllSheets()
- {
- $this->sheetIndex = null;
-
- return $this;
- }
-
- private static function generateMeta($val, $desc)
- {
- return $val ? (' ' . PHP_EOL) : '';
- }
-
- /**
- * Generate HTML header.
- *
- * @param bool $pIncludeStyles Include styles?
- *
- * @return string
- */
- public function generateHTMLHeader($pIncludeStyles = false)
- {
- // Construct HTML
- $properties = $this->spreadsheet->getProperties();
- $html = '' . PHP_EOL;
- $html .= '' . PHP_EOL;
- $html .= ' ' . PHP_EOL;
- $html .= ' ' . PHP_EOL;
- $html .= ' ' . PHP_EOL;
- $html .= ' ';
- for ($col = 'A'; $col != $colMax; ++$col) {
- $htmlx = $this->writeImageInCell($pSheet, $col . $row);
- $htmlx .= $this->includeCharts ? $this->writeChartInCell($pSheet, $col . $row) : '';
- if ($htmlx) {
- $html .= " ' . PHP_EOL;
- }
-
- return $html;
- }
-
- /**
- * Convert Windows file name to file protocol URL.
- *
- * @param string $filename file name on local system
- *
- * @return string
- */
- public static function winFileToUrl($filename)
- {
- // Windows filename
- if (substr($filename, 1, 2) === ':\\') {
- $filename = 'file:///' . str_replace('\\', '/', $filename);
- }
-
- return $filename;
- }
-
- /**
- * Generate image tag in cell.
- *
- * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
- * @param string $coordinates Cell coordinates
- *
- * @return string
- */
- private function writeImageInCell(Worksheet $pSheet, $coordinates)
- {
- // Construct HTML
- $html = '';
-
- // Write images
- foreach ($pSheet->getDrawingCollection() as $drawing) {
- if ($drawing->getCoordinates() != $coordinates) {
- continue;
- }
- $filedesc = $drawing->getDescription();
- $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
- if ($drawing instanceof Drawing) {
- $filename = $drawing->getPath();
-
- // Strip off eventual '.'
- $filename = preg_replace('/^[.]/', '', $filename);
-
- // Prepend images root
- $filename = $this->getImagesRoot() . $filename;
-
- // Strip off eventual '.' if followed by non-/
- $filename = preg_replace('@^[.]([^/])@', '$1', $filename);
-
- // Convert UTF8 data to PCDATA
- $filename = htmlspecialchars($filename);
-
- $html .= PHP_EOL;
- $imageData = self::winFileToUrl($filename);
-
- if ($this->embedImages && !$this->isPdf) {
- $picture = @file_get_contents($filename);
- if ($picture !== false) {
- $imageDetails = getimagesize($filename);
- // base64 encode the binary data
- $base64 = base64_encode($picture);
- $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
- }
- }
-
- $html .= '';
- } elseif ($drawing instanceof MemoryDrawing) {
- ob_start(); // Let's start output buffering.
- imagepng($drawing->getImageResource()); // This will normally output the image, but because of ob_start(), it won't.
- $contents = ob_get_contents(); // Instead, output above is saved to $contents
- ob_end_clean(); // End the output buffer.
-
- $dataUri = 'data:image/jpeg;base64,' . base64_encode($contents);
-
- // Because of the nature of tables, width is more important than height.
- // max-width: 100% ensures that image doesnt overflow containing cell
- // width: X sets width of supplied image.
- // As a result, images bigger than cell will be contained and images smaller will not get stretched
- $html .= '';
- }
- }
-
- return $html;
- }
-
- /**
- * Generate chart tag in cell.
- * This code should be exercised by sample:
- * Chart/32_Chart_read_write_PDF.php.
- * However, that test is suppressed due to out-of-date
- * Jpgraph code issuing warnings. So, don't measure
- * code coverage for this function till that is fixed.
- *
- * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
- * @param string $coordinates Cell coordinates
- *
- * @return string
- *
- * @codeCoverageIgnore
- */
- private function writeChartInCell(Worksheet $pSheet, $coordinates)
- {
- // Construct HTML
- $html = '';
-
- // Write charts
- foreach ($pSheet->getChartCollection() as $chart) {
- if ($chart instanceof Chart) {
- $chartCoordinates = $chart->getTopLeftPosition();
- if ($chartCoordinates['cell'] == $coordinates) {
- $chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
- if (!$chart->render($chartFileName)) {
- return;
- }
-
- $html .= PHP_EOL;
- $imageDetails = getimagesize($chartFileName);
- $filedesc = $chart->getTitle();
- $filedesc = $filedesc ? self::getChartCaption($filedesc->getCaption()) : '';
- $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart';
- if ($fp = fopen($chartFileName, 'rb', 0)) {
- $picture = fread($fp, filesize($chartFileName));
- fclose($fp);
- // base64 encode the binary data
- $base64 = base64_encode($picture);
- $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
-
- $html .= '' . PHP_EOL;
-
- unlink($chartFileName);
- }
- }
- }
- }
-
- // Return
- return $html;
- }
-
- /**
- * Extend Row if chart is placed after nominal end of row.
- * This code should be exercised by sample:
- * Chart/32_Chart_read_write_PDF.php.
- * However, that test is suppressed due to out-of-date
- * Jpgraph code issuing warnings. So, don't measure
- * code coverage for this function till that is fixed.
- * Caption is described in documentation as fixed,
- * but in 32_Chart it is somehow an array of RichText.
- *
- * @param mixed $cap
- *
- * @return string
- *
- * @codeCoverageIgnore
- */
- private static function getChartCaption($cap)
- {
- return is_array($cap) ? implode(' ', $cap) : $cap;
- }
-
- /**
- * Generate CSS styles.
- *
- * @param bool $generateSurroundingHTML Generate surrounding HTML tags? (<style> and </style>)
- *
- * @return string
- */
- public function generateStyles($generateSurroundingHTML = true)
- {
- // Build CSS
- $css = $this->buildCSS($generateSurroundingHTML);
-
- // Construct HTML
- $html = '';
-
- // Start styles
- if ($generateSurroundingHTML) {
- $html .= ' ' . PHP_EOL;
- }
-
- // Return
- return $html;
- }
-
- private function buildCssRowHeights(Worksheet $sheet, array &$css, int $sheetIndex): void
- {
- // Calculate row heights
- foreach ($sheet->getRowDimensions() as $rowDimension) {
- $row = $rowDimension->getRowIndex() - 1;
-
- // table.sheetN tr.rowYYYYYY { }
- $css['table.sheet' . $sheetIndex . ' tr.row' . $row] = [];
-
- if ($rowDimension->getRowHeight() != -1) {
- $pt_height = $rowDimension->getRowHeight();
- $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['height'] = $pt_height . 'pt';
- }
- if ($rowDimension->getVisible() === false) {
- $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['display'] = 'none';
- $css['table.sheet' . $sheetIndex . ' tr.row' . $row]['visibility'] = 'hidden';
- }
- }
- }
-
- private function buildCssPerSheet(Worksheet $sheet, array &$css): void
- {
- // Calculate hash code
- $sheetIndex = $sheet->getParent()->getIndex($sheet);
-
- // Build styles
- // Calculate column widths
- $sheet->calculateColumnWidths();
-
- // col elements, initialize
- $highestColumnIndex = Coordinate::columnIndexFromString($sheet->getHighestColumn()) - 1;
- $column = -1;
- while ($column++ < $highestColumnIndex) {
- $this->columnWidths[$sheetIndex][$column] = 42; // approximation
- $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
- }
-
- // col elements, loop through columnDimensions and set width
- foreach ($sheet->getColumnDimensions() as $columnDimension) {
- $column = Coordinate::columnIndexFromString($columnDimension->getColumnIndex()) - 1;
- $width = SharedDrawing::cellDimensionToPixels($columnDimension->getWidth(), $this->defaultFont);
- $width = SharedDrawing::pixelsToPoints($width);
- if ($columnDimension->getVisible() === false) {
- $css['table.sheet' . $sheetIndex . ' .column' . $column]['display'] = 'none';
- }
- if ($width >= 0) {
- $this->columnWidths[$sheetIndex][$column] = $width;
- $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = $width . 'pt';
- }
- }
-
- // Default row height
- $rowDimension = $sheet->getDefaultRowDimension();
-
- // table.sheetN tr { }
- $css['table.sheet' . $sheetIndex . ' tr'] = [];
-
- if ($rowDimension->getRowHeight() == -1) {
- $pt_height = SharedFont::getDefaultRowHeightByFont($this->spreadsheet->getDefaultStyle()->getFont());
- } else {
- $pt_height = $rowDimension->getRowHeight();
- }
- $css['table.sheet' . $sheetIndex . ' tr']['height'] = $pt_height . 'pt';
- if ($rowDimension->getVisible() === false) {
- $css['table.sheet' . $sheetIndex . ' tr']['display'] = 'none';
- $css['table.sheet' . $sheetIndex . ' tr']['visibility'] = 'hidden';
- }
-
- $this->buildCssRowHeights($sheet, $css, $sheetIndex);
- }
-
- /**
- * Build CSS styles.
- *
- * @param bool $generateSurroundingHTML Generate surrounding HTML style? (html { })
- *
- * @return array
- */
- public function buildCSS($generateSurroundingHTML = true)
- {
- // Cached?
- if ($this->cssStyles !== null) {
- return $this->cssStyles;
- }
-
- // Ensure that spans have been calculated
- $this->calculateSpans();
-
- // Construct CSS
- $css = [];
-
- // Start styles
- if ($generateSurroundingHTML) {
- // html { }
- $css['html']['font-family'] = 'Calibri, Arial, Helvetica, sans-serif';
- $css['html']['font-size'] = '11pt';
- $css['html']['background-color'] = 'white';
- }
-
- // CSS for comments as found in LibreOffice
- $css['a.comment-indicator:hover + div.comment'] = [
- 'background' => '#ffd',
- 'position' => 'absolute',
- 'display' => 'block',
- 'border' => '1px solid black',
- 'padding' => '0.5em',
- ];
-
- $css['a.comment-indicator'] = [
- 'background' => 'red',
- 'display' => 'inline-block',
- 'border' => '1px solid black',
- 'width' => '0.5em',
- 'height' => '0.5em',
- ];
-
- $css['div.comment']['display'] = 'none';
-
- // table { }
- $css['table']['border-collapse'] = 'collapse';
-
- // .b {}
- $css['.b']['text-align'] = 'center'; // BOOL
-
- // .e {}
- $css['.e']['text-align'] = 'center'; // ERROR
-
- // .f {}
- $css['.f']['text-align'] = 'right'; // FORMULA
-
- // .inlineStr {}
- $css['.inlineStr']['text-align'] = 'left'; // INLINE
-
- // .n {}
- $css['.n']['text-align'] = 'right'; // NUMERIC
-
- // .s {}
- $css['.s']['text-align'] = 'left'; // STRING
-
- // Calculate cell style hashes
- foreach ($this->spreadsheet->getCellXfCollection() as $index => $style) {
- $css['td.style' . $index] = $this->createCSSStyle($style);
- $css['th.style' . $index] = $this->createCSSStyle($style);
- }
-
- // Fetch sheets
- $sheets = [];
- if ($this->sheetIndex === null) {
- $sheets = $this->spreadsheet->getAllSheets();
- } else {
- $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex);
- }
-
- // Build styles per sheet
- foreach ($sheets as $sheet) {
- $this->buildCssPerSheet($sheet, $css);
- }
-
- // Cache
- if ($this->cssStyles === null) {
- $this->cssStyles = $css;
- }
-
- // Return
- return $css;
- }
-
- /**
- * Create CSS style.
- *
- * @return array
- */
- private function createCSSStyle(Style $pStyle)
- {
- // Create CSS
- return array_merge(
- $this->createCSSStyleAlignment($pStyle->getAlignment()),
- $this->createCSSStyleBorders($pStyle->getBorders()),
- $this->createCSSStyleFont($pStyle->getFont()),
- $this->createCSSStyleFill($pStyle->getFill())
- );
- }
-
- /**
- * Create CSS style (\PhpOffice\PhpSpreadsheet\Style\Alignment).
- *
- * @param Alignment $pStyle \PhpOffice\PhpSpreadsheet\Style\Alignment
- *
- * @return array
- */
- private function createCSSStyleAlignment(Alignment $pStyle)
- {
- // Construct CSS
- $css = [];
-
- // Create CSS
- $css['vertical-align'] = $this->mapVAlign($pStyle->getVertical());
- $textAlign = $this->mapHAlign($pStyle->getHorizontal());
- if ($textAlign) {
- $css['text-align'] = $textAlign;
- if (in_array($textAlign, ['left', 'right'])) {
- $css['padding-' . $textAlign] = (string) ((int) $pStyle->getIndent() * 9) . 'px';
- }
- }
-
- return $css;
- }
-
- /**
- * Create CSS style (\PhpOffice\PhpSpreadsheet\Style\Font).
- *
- * @return array
- */
- private function createCSSStyleFont(Font $pStyle)
- {
- // Construct CSS
- $css = [];
-
- // Create CSS
- if ($pStyle->getBold()) {
- $css['font-weight'] = 'bold';
- }
- if ($pStyle->getUnderline() != Font::UNDERLINE_NONE && $pStyle->getStrikethrough()) {
- $css['text-decoration'] = 'underline line-through';
- } elseif ($pStyle->getUnderline() != Font::UNDERLINE_NONE) {
- $css['text-decoration'] = 'underline';
- } elseif ($pStyle->getStrikethrough()) {
- $css['text-decoration'] = 'line-through';
- }
- if ($pStyle->getItalic()) {
- $css['font-style'] = 'italic';
- }
-
- $css['color'] = '#' . $pStyle->getColor()->getRGB();
- $css['font-family'] = '\'' . $pStyle->getName() . '\'';
- $css['font-size'] = $pStyle->getSize() . 'pt';
-
- return $css;
- }
-
- /**
- * Create CSS style (Borders).
- *
- * @param Borders $pStyle Borders
- *
- * @return array
- */
- private function createCSSStyleBorders(Borders $pStyle)
- {
- // Construct CSS
- $css = [];
-
- // Create CSS
- $css['border-bottom'] = $this->createCSSStyleBorder($pStyle->getBottom());
- $css['border-top'] = $this->createCSSStyleBorder($pStyle->getTop());
- $css['border-left'] = $this->createCSSStyleBorder($pStyle->getLeft());
- $css['border-right'] = $this->createCSSStyleBorder($pStyle->getRight());
-
- return $css;
- }
-
- /**
- * Create CSS style (Border).
- *
- * @param Border $pStyle Border
- *
- * @return string
- */
- private function createCSSStyleBorder(Border $pStyle)
- {
- // Create CSS - add !important to non-none border styles for merged cells
- $borderStyle = $this->mapBorderStyle($pStyle->getBorderStyle());
-
- return $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
- }
-
- /**
- * Create CSS style (Fill).
- *
- * @param Fill $pStyle Fill
- *
- * @return array
- */
- private function createCSSStyleFill(Fill $pStyle)
- {
- // Construct HTML
- $css = [];
-
- // Create CSS
- $value = $pStyle->getFillType() == Fill::FILL_NONE ?
- 'white' : '#' . $pStyle->getStartColor()->getRGB();
- $css['background-color'] = $value;
-
- return $css;
- }
-
- /**
- * Generate HTML footer.
- */
- public function generateHTMLFooter()
- {
- // Construct HTML
- $html = '';
- $html .= ' ' . PHP_EOL;
- $html .= '' . PHP_EOL;
-
- return $html;
- }
-
- private function generateTableTagInline($pSheet, $id)
- {
- $style = isset($this->cssStyles['table']) ?
- $this->assembleCSS($this->cssStyles['table']) : '';
-
- $prntgrid = $pSheet->getPrintGridlines();
- $viewgrid = $this->isPdf ? $prntgrid : $pSheet->getShowGridlines();
- if ($viewgrid && $prntgrid) {
- $html = " $htmlx ";
- } else {
- $html .= "";
- }
- }
- ++$row;
- $html .= ' " . PHP_EOL;
- } elseif ($viewgrid) {
- $html = "
" . PHP_EOL;
- } elseif ($prntgrid) {
- $html = "
" . PHP_EOL;
- } else {
- $html = "
" . PHP_EOL;
- }
-
- return $html;
- }
-
- private function generateTableTag($pSheet, $id, &$html, $sheetIndex): void
- {
- if (!$this->useInlineCss) {
- $gridlines = $pSheet->getShowGridlines() ? ' gridlines' : '';
- $gridlinesp = $pSheet->getPrintGridlines() ? ' gridlinesp' : '';
- $html .= "
- $html .= $this->generateTableFooter();
- if ($this->isPdf && $this->useInlineCss) {
- $html .= '';
- }
-
- // open table again: " . PHP_EOL;
- } else {
- $html .= $this->generateTableTagInline($pSheet, $id);
- }
- }
-
- /**
- * Generate table header.
- *
- * @param Worksheet $pSheet The worksheet for the table we are writing
- * @param bool $showid whether or not to add id to table tag
- *
- * @return string
- */
- private function generateTableHeader($pSheet, $showid = true)
- {
- $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
-
- // Construct HTML
- $html = '';
- $id = $showid ? "id='sheet$sheetIndex'" : '';
- if ($showid) {
- $html .= "
' . PHP_EOL . '' . PHP_EOL;
- }
-
- /**
- * Generate row start.
- *
- * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
- * @param int $sheetIndex Sheet index (0-based)
- * @param int $pRow row number
- *
- * @return string
- */
- private function generateRowStart(Worksheet $pSheet, $sheetIndex, $pRow)
- {
- $html = '';
- if (count($pSheet->getBreaks()) > 0) {
- $breaks = $pSheet->getBreaks();
-
- // check if a break is needed before this row
- if (isset($breaks['A' . $pRow])) {
- // close table: +
' . PHP_EOL;
- } else {
- $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
- ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
-
- $html .= ' ' . PHP_EOL;
- }
-
- return $html;
- }
-
- private function generateRowCellCss($pSheet, $cellAddress, $pRow, $colNum)
- {
- $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : '';
- $coordinate = Coordinate::stringFromColumnIndex($colNum + 1) . ($pRow + 1);
- if (!$this->useInlineCss) {
- $cssClass = 'column' . $colNum;
- } else {
- $cssClass = [];
- // The statements below do nothing.
- // Commenting out the code rather than deleting it
- // in case someone can figure out what their intent was.
- //if ($cellType == 'th') {
- // if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) {
- // $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum];
- // }
- //} else {
- // if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
- // $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
- // }
- //}
- // End of mystery statements.
- }
-
- return [$cell, $cssClass, $coordinate];
- }
-
- private function generateRowCellDataValueRich($cell, &$cellData): void
- {
- // Loop through rich text elements
- $elements = $cell->getValue()->getRichTextElements();
- foreach ($elements as $element) {
- // Rich text start?
- if ($element instanceof Run) {
- $cellData .= '';
-
- $cellEnd = '';
- if ($element->getFont()->getSuperscript()) {
- $cellData .= '';
- $cellEnd = '';
- } elseif ($element->getFont()->getSubscript()) {
- $cellData .= '';
- $cellEnd = '';
- }
-
- // Convert UTF8 data to PCDATA
- $cellText = $element->getText();
- $cellData .= htmlspecialchars($cellText);
-
- $cellData .= $cellEnd;
-
- $cellData .= '';
- } else {
- // Convert UTF8 data to PCDATA
- $cellText = $element->getText();
- $cellData .= htmlspecialchars($cellText);
- }
- }
- }
-
- private function generateRowCellDataValue($pSheet, $cell, &$cellData): void
- {
- if ($cell->getValue() instanceof RichText) {
- $this->generateRowCellDataValueRich($cell, $cellData);
- } else {
- $origData = $this->preCalculateFormulas ? $cell->getCalculatedValue() : $cell->getValue();
- $cellData = NumberFormat::toFormattedString(
- $origData,
- $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
- [$this, 'formatColor']
- );
- if ($cellData === $origData) {
- $cellData = htmlspecialchars($cellData);
- }
- if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperscript()) {
- $cellData = '' . $cellData . '';
- } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubscript()) {
- $cellData = '' . $cellData . '';
- }
- }
- }
-
- private function generateRowCellData($pSheet, $cell, &$cssClass, $cellType)
- {
- $cellData = ' ';
- if ($cell instanceof Cell) {
- $cellData = '';
- // Don't know what this does, and no test cases.
- //if ($cell->getParent() === null) {
- // $cell->attach($pSheet);
- //}
- // Value
- $this->generateRowCellDataValue($pSheet, $cell, $cellData);
-
- // Converts the cell content so that spaces occuring at beginning of each new line are replaced by
- // Example: " Hello\n to the world" is converted to " Hello\n to the world"
- $cellData = preg_replace('/(?m)(?:^|\\G) /', ' ', $cellData);
-
- // convert newline "\n" to '
'
- $cellData = nl2br($cellData);
-
- // Extend CSS class?
- if (!$this->useInlineCss) {
- $cssClass .= ' style' . $cell->getXfIndex();
- $cssClass .= ' ' . $cell->getDataType();
- } else {
- if ($cellType == 'th') {
- if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) {
- $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
- }
- } else {
- if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
- $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
- }
- }
-
- // General horizontal alignment: Actual horizontal alignment depends on dataType
- $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex());
- if (
- $sharedStyle->getAlignment()->getHorizontal() == Alignment::HORIZONTAL_GENERAL
- && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])
- ) {
- $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align'];
- }
- }
- } else {
- // Use default borders for empty cell
- if (is_string($cssClass)) {
- $cssClass .= ' style0';
- }
- }
-
- return $cellData;
- }
-
- private function generateRowIncludeCharts($pSheet, $coordinate)
- {
- return $this->includeCharts ? $this->writeChartInCell($pSheet, $coordinate) : '';
- }
-
- private function generateRowSpans($html, $rowSpan, $colSpan)
- {
- $html .= ($colSpan > 1) ? (' colspan="' . $colSpan . '"') : '';
- $html .= ($rowSpan > 1) ? (' rowspan="' . $rowSpan . '"') : '';
-
- return $html;
- }
-
- private function generateRowWriteCell(&$html, $pSheet, $coordinate, $cellType, $cellData, $colSpan, $rowSpan, $cssClass, $colNum, $sheetIndex, $pRow): void
- {
- // Image?
- $htmlx = $this->writeImageInCell($pSheet, $coordinate);
- // Chart?
- $htmlx .= $this->generateRowIncludeCharts($pSheet, $coordinate);
- // Column start
- $html .= ' <' . $cellType;
- if (!$this->useInlineCss && !$this->isPdf) {
- $html .= ' class="' . $cssClass . '"';
- if ($htmlx) {
- $html .= " style='position: relative;'";
- }
- } else {
- //** Necessary redundant code for the sake of \PhpOffice\PhpSpreadsheet\Writer\Pdf **
- // We must explicitly write the width of the element because TCPDF
- // does not recognize e.g. element because TCPDF
- // does not recognize e.g.
- if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
- $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
- $xcssClass['height'] = $height;
- }
- //** end of redundant code **
-
- if ($htmlx) {
- $xcssClass['position'] = 'relative';
- }
- $html .= ' style="' . $this->assembleCSS($xcssClass) . '"';
- }
- $html = $this->generateRowSpans($html, $rowSpan, $colSpan);
-
- $html .= '>';
- $html .= $htmlx;
-
- $html .= $this->writeComment($pSheet, $coordinate);
-
- // Cell data
- $html .= $cellData;
-
- // Column end
- $html .= '' . $cellType . '>' . PHP_EOL;
- }
-
- /**
- * Generate row.
- *
- * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
- * @param array $pValues Array containing cells in a row
- * @param int $pRow Row number (0-based)
- * @param string $cellType eg: 'td'
- *
- * @return string
- */
- private function generateRow(Worksheet $pSheet, array $pValues, $pRow, $cellType)
- {
- // Sheet index
- $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
- $html = $this->generateRowStart($pSheet, $sheetIndex, $pRow);
-
- // Write cells
- $colNum = 0;
- foreach ($pValues as $cellAddress) {
- [$cell, $cssClass, $coordinate] = $this->generateRowCellCss($pSheet, $cellAddress, $pRow, $colNum);
-
- $colSpan = 1;
- $rowSpan = 1;
-
- // Cell Data
- $cellData = $this->generateRowCellData($pSheet, $cell, $cssClass, $cellType);
-
- // Hyperlink?
- if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
- $cellData = '' . $cellData . '';
- }
-
- // Should the cell be written or is it swallowed by a rowspan or colspan?
- $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
- && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]);
-
- // Colspan and Rowspan
- $colspan = 1;
- $rowspan = 1;
- if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
- $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
- $rowSpan = $spans['rowspan'];
- $colSpan = $spans['colspan'];
-
- // Also apply style from last cell in merge to fix borders -
- // relies on !important for non-none border declarations in createCSSStyleBorder
- $endCellCoord = Coordinate::stringFromColumnIndex($colNum + $colSpan) . ($pRow + $rowSpan);
- if (!$this->useInlineCss) {
- $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex();
- }
- }
-
- // Write
- if ($writeCell) {
- $this->generateRowWriteCell($html, $pSheet, $coordinate, $cellType, $cellData, $colSpan, $rowSpan, $cssClass, $colNum, $sheetIndex, $pRow);
- }
-
- // Next column
- ++$colNum;
- }
-
- // Write row end
- $html .= ' ' . PHP_EOL;
-
- // Return
- return $html;
- }
-
- /**
- * Takes array where of CSS properties / values and converts to CSS string.
- *
- * @return string
- */
- private function assembleCSS(array $pValue = [])
- {
- $pairs = [];
- foreach ($pValue as $property => $value) {
- $pairs[] = $property . ':' . $value;
- }
- $string = implode('; ', $pairs);
-
- return $string;
- }
-
- /**
- * Get images root.
- *
- * @return string
- */
- public function getImagesRoot()
- {
- return $this->imagesRoot;
- }
-
- /**
- * Set images root.
- *
- * @param string $pValue
- *
- * @return $this
- */
- public function setImagesRoot($pValue)
- {
- $this->imagesRoot = $pValue;
-
- return $this;
- }
-
- /**
- * Get embed images.
- *
- * @return bool
- */
- public function getEmbedImages()
- {
- return $this->embedImages;
- }
-
- /**
- * Set embed images.
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setEmbedImages($pValue)
- {
- $this->embedImages = $pValue;
-
- return $this;
- }
-
- /**
- * Get use inline CSS?
- *
- * @return bool
- */
- public function getUseInlineCss()
- {
- return $this->useInlineCss;
- }
-
- /**
- * Set use inline CSS?
- *
- * @param bool $pValue
- *
- * @return $this
- */
- public function setUseInlineCss($pValue)
- {
- $this->useInlineCss = $pValue;
-
- return $this;
- }
-
- /**
- * Get use embedded CSS?
- *
- * @return bool
- *
- * @codeCoverageIgnore
- *
- * @deprecated no longer used
- */
- public function getUseEmbeddedCSS()
- {
- return $this->useEmbeddedCSS;
- }
-
- /**
- * Set use embedded CSS?
- *
- * @param bool $pValue
- *
- * @return $this
- *
- * @codeCoverageIgnore
- *
- * @deprecated no longer used
- */
- public function setUseEmbeddedCSS($pValue)
- {
- $this->useEmbeddedCSS = $pValue;
-
- return $this;
- }
-
- /**
- * Add color to formatted string as inline style.
- *
- * @param string $pValue Plain formatted value without color
- * @param string $pFormat Format code
- *
- * @return string
- */
- public function formatColor($pValue, $pFormat)
- {
- // Color information, e.g. [Red] is always at the beginning
- $color = null; // initialize
- $matches = [];
-
- $color_regex = '/^\\[[a-zA-Z]+\\]/';
- if (preg_match($color_regex, $pFormat, $matches)) {
- $color = str_replace(['[', ']'], '', $matches[0]);
- $color = strtolower($color);
- }
-
- // convert to PCDATA
- $value = htmlspecialchars($pValue);
-
- // color span tag
- if ($color !== null) {
- $value = '' . $value . '';
- }
-
- return $value;
- }
-
- /**
- * Calculate information about HTML colspan and rowspan which is not always the same as Excel's.
- */
- private function calculateSpans(): void
- {
- if ($this->spansAreCalculated) {
- return;
- }
- // Identify all cells that should be omitted in HTML due to cell merge.
- // In HTML only the upper-left cell should be written and it should have
- // appropriate rowspan / colspan attribute
- $sheetIndexes = $this->sheetIndex !== null ?
- [$this->sheetIndex] : range(0, $this->spreadsheet->getSheetCount() - 1);
-
- foreach ($sheetIndexes as $sheetIndex) {
- $sheet = $this->spreadsheet->getSheet($sheetIndex);
-
- $candidateSpannedRow = [];
-
- // loop through all Excel merged cells
- foreach ($sheet->getMergeCells() as $cells) {
- [$cells] = Coordinate::splitRange($cells);
- $first = $cells[0];
- $last = $cells[1];
-
- [$fc, $fr] = Coordinate::coordinateFromString($first);
- $fc = Coordinate::columnIndexFromString($fc) - 1;
-
- [$lc, $lr] = Coordinate::coordinateFromString($last);
- $lc = Coordinate::columnIndexFromString($lc) - 1;
-
- // loop through the individual cells in the individual merge
- $r = $fr - 1;
- while ($r++ < $lr) {
- // also, flag this row as a HTML row that is candidate to be omitted
- $candidateSpannedRow[$r] = $r;
-
- $c = $fc - 1;
- while ($c++ < $lc) {
- if (!($c == $fc && $r == $fr)) {
- // not the upper-left cell (should not be written in HTML)
- $this->isSpannedCell[$sheetIndex][$r][$c] = [
- 'baseCell' => [$fr, $fc],
- ];
- } else {
- // upper-left is the base cell that should hold the colspan/rowspan attribute
- $this->isBaseCell[$sheetIndex][$r][$c] = [
- 'xlrowspan' => $lr - $fr + 1, // Excel rowspan
- 'rowspan' => $lr - $fr + 1, // HTML rowspan, value may change
- 'xlcolspan' => $lc - $fc + 1, // Excel colspan
- 'colspan' => $lc - $fc + 1, // HTML colspan, value may change
- ];
- }
- }
- }
- }
-
- $this->calculateSpansOmitRows($sheet, $sheetIndex, $candidateSpannedRow);
-
- // TODO: Same for columns
- }
-
- // We have calculated the spans
- $this->spansAreCalculated = true;
- }
-
- private function calculateSpansOmitRows($sheet, $sheetIndex, $candidateSpannedRow): void
- {
- // Identify which rows should be omitted in HTML. These are the rows where all the cells
- // participate in a merge and the where base cells are somewhere above.
- $countColumns = Coordinate::columnIndexFromString($sheet->getHighestColumn());
- foreach ($candidateSpannedRow as $rowIndex) {
- if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) {
- if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
- $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
- }
- }
- }
-
- // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
- if (isset($this->isSpannedRow[$sheetIndex])) {
- foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) {
- $adjustedBaseCells = [];
- $c = -1;
- $e = $countColumns - 1;
- while ($c++ < $e) {
- $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
-
- if (!in_array($baseCell, $adjustedBaseCells)) {
- // subtract rowspan by 1
- --$this->isBaseCell[$sheetIndex][$baseCell[0]][$baseCell[1]]['rowspan'];
- $adjustedBaseCells[] = $baseCell;
- }
- }
- }
- }
- }
-
- /**
- * Write a comment in the same format as LibreOffice.
- *
- * @see https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092
- *
- * @param string $coordinate
- *
- * @return string
- */
- private function writeComment(Worksheet $pSheet, $coordinate)
- {
- $result = '';
- if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) {
- $result .= '';
- $result .= '