feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles
- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,21 +12,25 @@ trait ArrayEnabled
|
||||
private static ArrayArgumentHelper $arrayArgumentHelper;
|
||||
|
||||
/**
|
||||
* @param array|false $arguments Can be changed to array for Php8.1+
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
private static function initialiseHelper($arguments): void
|
||||
private static function initialiseHelper(array $arguments): void
|
||||
{
|
||||
if (self::$initializationNeeded === true) {
|
||||
self::$arrayArgumentHelper = new ArrayArgumentHelper();
|
||||
self::$initializationNeeded = false;
|
||||
}
|
||||
self::$arrayArgumentHelper->initialise(($arguments === false) ? [] : $arguments);
|
||||
self::$arrayArgumentHelper->initialise($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles array argument processing when the function accepts a single argument that can be an array argument.
|
||||
* Example use for:
|
||||
* DAYOFMONTH() or FACT().
|
||||
*
|
||||
* @param mixed[] $values
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateSingleArgumentArray(callable $method, array $values): array
|
||||
{
|
||||
@@ -43,6 +47,8 @@ trait ArrayEnabled
|
||||
* and any of them can be an array argument.
|
||||
* Example use for:
|
||||
* ROUND() or DATE().
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArguments(callable $method, mixed ...$arguments): array
|
||||
{
|
||||
@@ -58,6 +64,8 @@ trait ArrayEnabled
|
||||
* Example use for:
|
||||
* NETWORKDAYS() or CONCATENATE(), where the last argument is a matrix (or a series of values) that need
|
||||
* to be treated as a such rather than as an array arguments.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, mixed ...$arguments): array
|
||||
{
|
||||
@@ -80,6 +88,8 @@ trait ArrayEnabled
|
||||
* Example use for:
|
||||
* Z.TEST() or INDEX(), where the first argument 1 is a matrix that needs to be treated as a dataset
|
||||
* rather than as an array argument.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, mixed ...$arguments): array
|
||||
{
|
||||
@@ -105,6 +115,8 @@ trait ArrayEnabled
|
||||
* Example use for:
|
||||
* HLOOKUP() and VLOOKUP(), where argument 1 is a matrix that needs to be treated as a database
|
||||
* rather than as an array argument.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, mixed ...$arguments): array
|
||||
{
|
||||
|
||||
@@ -14,13 +14,15 @@ class BinaryComparison
|
||||
/**
|
||||
* Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
|
||||
*
|
||||
* @param null|string $str1 First string value for the comparison
|
||||
* @param null|string $str2 Second string value for the comparison
|
||||
* @param mixed $str1 First string value for the comparison, expect ?string
|
||||
* @param mixed $str2 Second string value for the comparison, expect ?string
|
||||
*/
|
||||
private static function strcmpLowercaseFirst(?string $str1, ?string $str2): int
|
||||
private static function strcmpLowercaseFirst(mixed $str1, mixed $str2): int
|
||||
{
|
||||
$inversedStr1 = StringHelper::strCaseReverse($str1 ?? '');
|
||||
$inversedStr2 = StringHelper::strCaseReverse($str2 ?? '');
|
||||
$str1 = StringHelper::convertToString($str1);
|
||||
$str2 = StringHelper::convertToString($str2);
|
||||
$inversedStr1 = StringHelper::strCaseReverse($str1);
|
||||
$inversedStr2 = StringHelper::strCaseReverse($str2);
|
||||
|
||||
return strcmp($inversedStr1, $inversedStr2);
|
||||
}
|
||||
@@ -28,12 +30,15 @@ class BinaryComparison
|
||||
/**
|
||||
* PHP8.1 deprecates passing null to strcmp.
|
||||
*
|
||||
* @param null|string $str1 First string value for the comparison
|
||||
* @param null|string $str2 Second string value for the comparison
|
||||
* @param mixed $str1 First string value for the comparison, expect ?string
|
||||
* @param mixed $str2 Second string value for the comparison, expect ?string
|
||||
*/
|
||||
private static function strcmpAllowNull(?string $str1, ?string $str2): int
|
||||
private static function strcmpAllowNull(mixed $str1, mixed $str2): int
|
||||
{
|
||||
return strcmp($str1 ?? '', $str2 ?? '');
|
||||
$str1 = StringHelper::convertToString($str1);
|
||||
$str2 = StringHelper::convertToString($str2);
|
||||
|
||||
return strcmp($str1, $str2);
|
||||
}
|
||||
|
||||
public static function compare(mixed $operand1, mixed $operand2, string $operator): bool
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,4 +18,5 @@ abstract class Category
|
||||
const CATEGORY_TEXT_AND_DATA = 'Text and Data';
|
||||
const CATEGORY_WEB = 'Web';
|
||||
const CATEGORY_UNCATEGORISED = 'Uncategorised';
|
||||
const CATEGORY_MICROSOFT_INTERNAL = 'MS Internal';
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ class DAverage extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -20,12 +20,12 @@ class DCount extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -19,12 +19,12 @@ class DCountA extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -19,12 +19,12 @@ class DGet extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
@@ -42,6 +42,7 @@ class DGet extends DatabaseAbstract
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
/** @var array<null|float|int|string> */
|
||||
$row = array_pop($columnData);
|
||||
|
||||
return array_pop($row);
|
||||
|
||||
@@ -20,12 +20,12 @@ class DMax extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -20,12 +20,12 @@ class DMin extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -19,12 +19,12 @@ class DProduct extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -20,12 +20,12 @@ class DStDev extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -20,12 +20,12 @@ class DStDevP extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -19,12 +19,12 @@ class DSum extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -20,12 +20,12 @@ class DVar extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -20,12 +20,12 @@ class DVarP extends DatabaseAbstract
|
||||
* 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 null|array|int|string $field Indicates which column is used in the function. Enter the
|
||||
* @param null|array<mixed>|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.
|
||||
* @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
|
||||
|
||||
@@ -5,9 +5,26 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
abstract class DatabaseAbstract
|
||||
{
|
||||
/**
|
||||
* @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 null|array<mixed>|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.
|
||||
*/
|
||||
abstract public static function evaluate(array $database, array|null|int|string $field, array $criteria): null|float|int|string;
|
||||
|
||||
/**
|
||||
@@ -27,12 +44,16 @@ abstract class DatabaseAbstract
|
||||
*/
|
||||
protected static function fieldExtract(array $database, mixed $field): ?int
|
||||
{
|
||||
$field = strtoupper(Functions::flattenSingleValue($field) ?? '');
|
||||
/** @var ?string */
|
||||
$single = Functions::flattenSingleValue($field);
|
||||
$field = strtoupper($single ?? '');
|
||||
if ($field === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fieldNames = array_map('strtoupper', array_shift($database));
|
||||
/** @var callable */
|
||||
$callable = 'strtoupper';
|
||||
$fieldNames = array_map($callable, array_shift($database)); //* @phpstan-ignore-line
|
||||
if (is_numeric($field)) {
|
||||
$field = (int) $field - 1;
|
||||
if ($field < 0 || $field >= count($fieldNames)) {
|
||||
@@ -56,7 +77,7 @@ abstract class DatabaseAbstract
|
||||
* 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 mixed[] $criteria The range of cells that contains the conditions you specify.
|
||||
* @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
|
||||
@@ -66,16 +87,25 @@ abstract class DatabaseAbstract
|
||||
*/
|
||||
protected static function filter(array $database, array $criteria): array
|
||||
{
|
||||
/** @var mixed[] */
|
||||
$fieldNames = array_shift($database);
|
||||
$criteriaNames = array_shift($criteria);
|
||||
|
||||
// Convert the criteria into a set of AND/OR conditions with [:placeholders]
|
||||
/** @var string[] $criteriaNames */
|
||||
$query = self::buildQuery($criteriaNames, $criteria);
|
||||
|
||||
// Loop through each row of the database
|
||||
/** @var mixed[][] $criteriaNames */
|
||||
return self::executeQuery($database, $query, $criteriaNames, $fieldNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $database The range of cells that makes up the list or database
|
||||
* @param mixed[][] $criteria
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function getFilteredColumn(array $database, ?int $field, array $criteria): array
|
||||
{
|
||||
// reduce the database to a set of rows that match all the criteria
|
||||
@@ -84,6 +114,7 @@ abstract class DatabaseAbstract
|
||||
|
||||
// extract an array of values for the requested column
|
||||
$columnData = [];
|
||||
/** @var mixed[] $row */
|
||||
foreach ($database as $rowKey => $row) {
|
||||
$keys = array_keys($row);
|
||||
$key = $keys[$field] ?? null;
|
||||
@@ -94,6 +125,10 @@ abstract class DatabaseAbstract
|
||||
return $columnData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $criteriaNames
|
||||
* @param mixed[][] $criteria
|
||||
*/
|
||||
private static function buildQuery(array $criteriaNames, array $criteria): string
|
||||
{
|
||||
$baseQuery = [];
|
||||
@@ -108,7 +143,7 @@ abstract class DatabaseAbstract
|
||||
}
|
||||
|
||||
$rowQuery = array_map(
|
||||
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''),
|
||||
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''), // @phpstan-ignore-line
|
||||
$baseQuery
|
||||
);
|
||||
|
||||
@@ -135,12 +170,21 @@ abstract class DatabaseAbstract
|
||||
return $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $database
|
||||
* @param mixed[][] $criteria
|
||||
* @param array<mixed> $fields
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
|
||||
{
|
||||
foreach ($database as $dataRow => $dataValues) {
|
||||
// Substitute actual values from the database row for our [:placeholders]
|
||||
$conditions = $query;
|
||||
foreach ($criteria as $criterion) {
|
||||
/** @var string $criterion */
|
||||
/** @var mixed[] $dataValues */
|
||||
$conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
|
||||
}
|
||||
|
||||
@@ -156,6 +200,10 @@ abstract class DatabaseAbstract
|
||||
return $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $fields
|
||||
* @param array<mixed> $dataValues
|
||||
*/
|
||||
private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions): string
|
||||
{
|
||||
$key = array_search($criterion, $fields, true);
|
||||
@@ -169,7 +217,10 @@ abstract class DatabaseAbstract
|
||||
if (is_string($dataValue) && str_contains($dataValue, '"')) {
|
||||
$dataValue = str_replace('"', '""', $dataValue);
|
||||
}
|
||||
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
|
||||
if (is_string($dataValue)) {
|
||||
$dataValue = Calculation::wrapResult(strtoupper($dataValue));
|
||||
}
|
||||
$dataValue = StringHelper::convertToString($dataValue);
|
||||
}
|
||||
|
||||
return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
|
||||
|
||||
@@ -28,7 +28,7 @@ class Date
|
||||
* 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 array|float|int|string $year The value of the year argument can include one to four digits.
|
||||
* @param array<mixed>|float|int|string $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
|
||||
@@ -39,7 +39,7 @@ class Date
|
||||
* 2008.
|
||||
* If year is less than 0 or is 10000 or greater, Excel returns the
|
||||
* #NUM! error value.
|
||||
* @param array|float|int|string $month A positive or negative integer representing the month of the year
|
||||
* @param array<mixed>|float|int|string $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)
|
||||
@@ -48,7 +48,7 @@ class Date
|
||||
* 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 array|float|int|string $day A positive or negative integer representing the day of the month
|
||||
* @param array<mixed>|float|int|string $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
|
||||
@@ -59,12 +59,12 @@ class Date
|
||||
* example, DATE(2008,1,-15) returns the serial number representing
|
||||
* December 16, 2007.
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function fromYMD(array|float|int|string $year, array|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array
|
||||
public static function fromYMD(array|float|int|string $year, null|array|bool|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array
|
||||
{
|
||||
if (is_array($year) || is_array($month) || is_array($day)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $year, $month, $day);
|
||||
@@ -92,7 +92,11 @@ class Date
|
||||
*/
|
||||
private static function getYear(mixed $year, int $baseYear): int
|
||||
{
|
||||
$year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
|
||||
if ($year === null) {
|
||||
$year = 0;
|
||||
} elseif (is_scalar($year)) {
|
||||
$year = StringHelper::testStringAsNumeric((string) $year);
|
||||
}
|
||||
if (!is_numeric($year)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
@@ -117,11 +121,15 @@ class Date
|
||||
*/
|
||||
private static function getMonth(mixed $month): int
|
||||
{
|
||||
if (($month !== null) && (!is_numeric($month))) {
|
||||
$month = SharedDateHelper::monthStringToNumber($month);
|
||||
if (is_string($month)) {
|
||||
if (!is_numeric($month)) {
|
||||
$month = SharedDateHelper::monthStringToNumber($month);
|
||||
}
|
||||
} elseif ($month === null) {
|
||||
$month = 0;
|
||||
} elseif (is_bool($month)) {
|
||||
$month = (int) $month;
|
||||
}
|
||||
|
||||
$month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
|
||||
if (!is_numeric($month)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
@@ -134,11 +142,15 @@ class Date
|
||||
*/
|
||||
private static function getDay(mixed $day): int
|
||||
{
|
||||
if (($day !== null) && (!is_numeric($day))) {
|
||||
if (is_string($day) && !is_numeric($day)) {
|
||||
$day = SharedDateHelper::dayStringToNumber($day);
|
||||
}
|
||||
|
||||
$day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
|
||||
if ($day === null) {
|
||||
$day = 0;
|
||||
} elseif (is_scalar($day)) {
|
||||
$day = StringHelper::testStringAsNumeric((string) $day);
|
||||
}
|
||||
if (!is_numeric($day)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
@@ -151,11 +163,11 @@ class Date
|
||||
if ($month < 1) {
|
||||
// Handle year/month adjustment if month < 1
|
||||
--$month;
|
||||
$year += ceil($month / 12) - 1;
|
||||
$year += (int) (ceil($month / 12) - 1);
|
||||
$month = 13 - abs($month % 12);
|
||||
} elseif ($month > 12) {
|
||||
// Handle year/month adjustment if month > 12
|
||||
$year += floor($month / 12);
|
||||
$year += intdiv($month, 12);
|
||||
$month = ($month % 12);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class DateParts
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Day of the month
|
||||
* @return array<mixed>|int|string Day of the month
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ class DateParts
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Month of the year
|
||||
* @return array<mixed>|int|string Month of the year
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -104,7 +104,7 @@ class DateParts
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Year
|
||||
* @return array<mixed>|int|string Year
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,7 @@ class DateValue
|
||||
* Excel Function:
|
||||
* DATEVALUE(dateValue)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $dateValue Text that represents a date in a Microsoft Excel date format.
|
||||
* @param null|array<mixed>|bool|float|int|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
|
||||
@@ -35,7 +35,7 @@ class DateValue
|
||||
* #VALUE! error value if date_text is out of this range.
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -47,7 +47,7 @@ class DateValue
|
||||
}
|
||||
|
||||
// try to parse as date iff there is at least one digit
|
||||
if (is_string($dateValue) && preg_match('/\\d/', $dateValue) !== 1) {
|
||||
if (is_string($dateValue) && preg_match('/\d/', $dateValue) !== 1) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ class DateValue
|
||||
return self::finalResults($PHPDateArray, $dti, $baseYear);
|
||||
}
|
||||
|
||||
/** @param mixed[] $t1 */
|
||||
private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
|
||||
{
|
||||
if (count($t1) == 2) {
|
||||
@@ -108,6 +109,8 @@ class DateValue
|
||||
|
||||
/**
|
||||
* Parse date.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
|
||||
{
|
||||
@@ -132,6 +135,8 @@ class DateValue
|
||||
/**
|
||||
* Final results.
|
||||
*
|
||||
* @param mixed[] $PHPDateArray
|
||||
*
|
||||
* @return DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
*/
|
||||
@@ -139,6 +144,7 @@ class DateValue
|
||||
{
|
||||
$retValue = ExcelError::Value();
|
||||
if (Helpers::dateParseSucceeded($PHPDateArray)) {
|
||||
/** @var array{year: int, month: int, day: int, hour: int, minute: int, second: int} $PHPDateArray */
|
||||
// Execute function
|
||||
Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
|
||||
if ($PHPDateArray['year'] < $baseYear) {
|
||||
@@ -146,12 +152,13 @@ class DateValue
|
||||
}
|
||||
Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
|
||||
Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
|
||||
/** @var array{year: int, month: int, day: int, hour: int, minute: int, second: int} $PHPDateArray */
|
||||
$PHPDateArray['hour'] = 0;
|
||||
$PHPDateArray['minute'] = 0;
|
||||
$PHPDateArray['second'] = 0;
|
||||
$month = (int) $PHPDateArray['month'];
|
||||
$day = (int) $PHPDateArray['day'];
|
||||
$year = (int) $PHPDateArray['year'];
|
||||
$month = self::getInt($PHPDateArray, 'month');
|
||||
$day = self::getInt($PHPDateArray, 'day');
|
||||
$year = self::getInt($PHPDateArray, 'year');
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
|
||||
}
|
||||
@@ -160,4 +167,10 @@ class DateValue
|
||||
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/** @param mixed[] $array */
|
||||
private static function getInt(array $array, string $index): int
|
||||
{
|
||||
return (array_key_exists($index, $array) && is_numeric($array[$index])) ? (int) $array[$index] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ class Days
|
||||
* Excel Function:
|
||||
* DAYS(endDate, startDate)
|
||||
*
|
||||
* @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
|
||||
* @param array<mixed>|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
|
||||
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
|
||||
* @param array<mixed>|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
|
||||
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Number of days between start date and end date or an error
|
||||
* @return array<mixed>|int|string Number of days between start date and end date or an error
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
@@ -50,7 +50,7 @@ class Days
|
||||
|
||||
$days = ExcelError::VALUE();
|
||||
$diff = $PHPStartDateObject->diff($PHPEndDateObject);
|
||||
if ($diff !== false && !is_bool($diff->days)) {
|
||||
if (!is_bool($diff->days)) {
|
||||
$days = $diff->days;
|
||||
if ($diff->invert) {
|
||||
$days = -$days;
|
||||
|
||||
@@ -40,7 +40,7 @@ class Days360
|
||||
* same month.
|
||||
* Or can be an array of methods
|
||||
*
|
||||
* @return array|int|string Number of days between start date and end date
|
||||
* @return array<mixed>|int|string Number of days between start date and end date
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
|
||||
@@ -22,9 +22,9 @@ class Difference
|
||||
* @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
|
||||
* or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|string $unit Or can be an array of unit values
|
||||
* @param array<mixed>|string $unit Or can be an array of unit values
|
||||
*
|
||||
* @return array|int|string Interval between the dates
|
||||
* @return array<mixed>|int|string Interval between the dates
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,9 @@ class Helpers
|
||||
if (!is_numeric($dateValue)) {
|
||||
$saveReturnDateType = Functions::getReturnDateType();
|
||||
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
|
||||
$dateValue = DateValue::fromString($dateValue);
|
||||
if (is_string($dateValue)) {
|
||||
$dateValue = DateValue::fromString($dateValue);
|
||||
}
|
||||
Functions::setReturnDateType($saveReturnDateType);
|
||||
if (!is_numeric($dateValue)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
@@ -75,8 +77,10 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Adjust date by given months.
|
||||
*
|
||||
* @param float|int $dateValue date to be adjusted
|
||||
*/
|
||||
public static function adjustDateByMonths(mixed $dateValue = 0, float $adjustmentMonths = 0): DateTime
|
||||
public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
|
||||
{
|
||||
// Execute function
|
||||
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
|
||||
@@ -119,7 +123,7 @@ class Helpers
|
||||
if (!is_numeric($testVal1) || $testVal1 < 31) {
|
||||
if (!is_numeric($testVal2) || $testVal2 < 12) {
|
||||
if (is_numeric($testVal3) && $testVal3 < 12) {
|
||||
$testVal3 += 2000;
|
||||
$testVal3 = (string) ($testVal3 + 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,6 +131,8 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Return result in one of three formats.
|
||||
*
|
||||
* @param array{year: int, month: int, day: int, hour: int, minute: int, second: int} $dateArray
|
||||
*/
|
||||
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
|
||||
{
|
||||
@@ -264,11 +270,16 @@ class Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array{year: int, month: int, day: int, hour: int, minute: int, second: int} */
|
||||
public static function dateParse(string $string): array
|
||||
{
|
||||
return self::forceArray(date_parse($string));
|
||||
/** @var array{year: int, month: int, day: int, hour: int, minute: int, second: int} */
|
||||
$temp = self::forceArray(date_parse($string));
|
||||
|
||||
return $temp;
|
||||
}
|
||||
|
||||
/** @param mixed[] $dateArray */
|
||||
public static function dateParseSucceeded(array $dateArray): bool
|
||||
{
|
||||
return $dateArray['error_count'] === 0;
|
||||
@@ -278,10 +289,19 @@ class Helpers
|
||||
* Despite documentation, date_parse probably never returns false.
|
||||
* Just in case, this routine helps guarantee it.
|
||||
*
|
||||
* @param array|false $dateArray
|
||||
* @param array<mixed>|false $dateArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function forceArray(array|bool $dateArray): array
|
||||
{
|
||||
return is_array($dateArray) ? $dateArray : ['error_count' => 1];
|
||||
}
|
||||
|
||||
public static function floatOrInt(mixed $value): float|int
|
||||
{
|
||||
$result = Functions::scalar($value);
|
||||
|
||||
return is_numeric($result) ? ($result + 0) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ class Month
|
||||
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $adjustmentMonths The number of months before or after start_date.
|
||||
* @param array<mixed>|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.
|
||||
* Or can be an array of adjustment values
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -68,12 +68,12 @@ class Month
|
||||
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $adjustmentMonths The number of months before or after start_date.
|
||||
* @param array<mixed>|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.
|
||||
* Or can be an array of adjustment values
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
|
||||
@@ -29,7 +29,7 @@ class NetworkDays
|
||||
* Or can be an array of date values
|
||||
* @param mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
|
||||
*
|
||||
* @return array|int|string Interval between the dates
|
||||
* @return array<mixed>|int|string Interval between the dates
|
||||
* If an array of values is passed for the $startDate or $endDate arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
|
||||
@@ -24,21 +24,21 @@ class Time
|
||||
* Excel Function:
|
||||
* TIME(hour,minute,second)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $hour A number from 0 (zero) to 32767 representing the hour.
|
||||
* @param null|array<mixed>|bool|float|int|string $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 null|array|bool|float|int|string $minute A number from 0 to 32767 representing the minute.
|
||||
* @param null|array<mixed>|bool|float|int|string $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 null|array|bool|float|int|string $second A number from 0 to 32767 representing the second.
|
||||
* @param null|array<mixed>|bool|float|int|string $second A number from 0 to 32767 representing the second.
|
||||
* Any value greater than 59 will be converted to hours, minutes,
|
||||
* and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
|
||||
* or 12:33:20 AM
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -87,13 +87,13 @@ class Time
|
||||
private static function adjustSecond(int &$second, int &$minute): void
|
||||
{
|
||||
if ($second < 0) {
|
||||
$minute += floor($second / 60);
|
||||
$minute += (int) floor($second / 60);
|
||||
$second = 60 - abs($second % 60);
|
||||
if ($second == 60) {
|
||||
$second = 0;
|
||||
}
|
||||
} elseif ($second >= 60) {
|
||||
$minute += floor($second / 60);
|
||||
$minute += intdiv($second, 60);
|
||||
$second = $second % 60;
|
||||
}
|
||||
}
|
||||
@@ -101,13 +101,13 @@ class Time
|
||||
private static function adjustMinute(int &$minute, int &$hour): void
|
||||
{
|
||||
if ($minute < 0) {
|
||||
$hour += floor($minute / 60);
|
||||
$hour += (int) floor($minute / 60);
|
||||
$minute = 60 - abs($minute % 60);
|
||||
if ($minute == 60) {
|
||||
$minute = 0;
|
||||
}
|
||||
} elseif ($minute >= 60) {
|
||||
$hour += floor($minute / 60);
|
||||
$hour += intdiv($minute, 60);
|
||||
$minute = $minute % 60;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class TimeParts
|
||||
* PHP DateTime object, or a standard time string
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|int|string Hour
|
||||
* @return array<mixed>|int|string Hour
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ class TimeParts
|
||||
|
||||
try {
|
||||
Helpers::nullFalseTrueToNumber($timeValue);
|
||||
if (!is_numeric($timeValue)) {
|
||||
if (is_string($timeValue) && !is_numeric($timeValue)) {
|
||||
$timeValue = Helpers::getTimeValue($timeValue);
|
||||
}
|
||||
Helpers::validateNotNegative($timeValue);
|
||||
@@ -64,7 +64,7 @@ class TimeParts
|
||||
* PHP DateTime object, or a standard time string
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|int|string Minute
|
||||
* @return array<mixed>|int|string Minute
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -76,7 +76,7 @@ class TimeParts
|
||||
|
||||
try {
|
||||
Helpers::nullFalseTrueToNumber($timeValue);
|
||||
if (!is_numeric($timeValue)) {
|
||||
if (is_string($timeValue) && !is_numeric($timeValue)) {
|
||||
$timeValue = Helpers::getTimeValue($timeValue);
|
||||
}
|
||||
Helpers::validateNotNegative($timeValue);
|
||||
@@ -105,7 +105,7 @@ class TimeParts
|
||||
* PHP DateTime object, or a standard time string
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|int|string Second
|
||||
* @return array<mixed>|int|string Second
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -117,7 +117,7 @@ class TimeParts
|
||||
|
||||
try {
|
||||
Helpers::nullFalseTrueToNumber($timeValue);
|
||||
if (!is_numeric($timeValue)) {
|
||||
if (is_string($timeValue) && !is_numeric($timeValue)) {
|
||||
$timeValue = Helpers::getTimeValue($timeValue);
|
||||
}
|
||||
Helpers::validateNotNegative($timeValue);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
||||
|
||||
use Composer\Pcre\Preg;
|
||||
use Datetime;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
@@ -12,6 +13,19 @@ class TimeValue
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
private const EXTRACT_TIME = '/\b'
|
||||
. '(\d+)' // match[1] - hour
|
||||
. '(:' // start of match[2] (rest of string) - colon
|
||||
. '(\d+' // start of match[3] - minute
|
||||
. '(:\d+' // start of match[4] - colon and seconds
|
||||
. '([.]\d+)?' // match[5] - optional decimal point followed by fractional seconds
|
||||
. ')?' // end of match[4], which is optional
|
||||
. ')' // end of match 3
|
||||
// Excel does not require 'm' to trail 'a' or 'p'; Php does
|
||||
. '(\s*(a|p))?' // match[6] optional whitespace followed by optional match[7] a or p
|
||||
. ')' // end of match[2]
|
||||
. '/i';
|
||||
|
||||
/**
|
||||
* TIMEVALUE.
|
||||
*
|
||||
@@ -25,13 +39,13 @@ class TimeValue
|
||||
* Excel Function:
|
||||
* TIMEVALUE(timeValue)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $timeValue A text string that represents a time in any one of the Microsoft
|
||||
* @param null|array<mixed>|bool|float|int|string $timeValue A text string that represents a time in any one of the Microsoft
|
||||
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
|
||||
* within quotation marks that represent time.
|
||||
* Date information in time_text is ignored.
|
||||
* Or can be an array of date/time values
|
||||
*
|
||||
* @return array|Datetime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|Datetime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
@@ -43,17 +57,20 @@ class TimeValue
|
||||
}
|
||||
|
||||
// try to parse as time iff there is at least one digit
|
||||
if (is_string($timeValue) && preg_match('/\\d/', $timeValue) !== 1) {
|
||||
if (is_string($timeValue) && !Preg::isMatch('/\d/', $timeValue)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
$timeValue = trim((string) $timeValue, '"');
|
||||
$timeValue = str_replace(['/', '.'], '-', $timeValue);
|
||||
|
||||
$arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: [];
|
||||
if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
|
||||
$arraySplit[0] = ((int) $arraySplit[0] % 24);
|
||||
$timeValue = implode(':', $arraySplit);
|
||||
if (Preg::isMatch(self::EXTRACT_TIME, $timeValue, $matches)) {
|
||||
if (empty($matches[6])) { // am/pm
|
||||
$hour = (int) $matches[0];
|
||||
$timeValue = ($hour % 24) . $matches[2];
|
||||
} elseif ($matches[6] === $matches[7]) { // Excel wants space before am/pm
|
||||
return ExcelError::VALUE();
|
||||
} else {
|
||||
$timeValue = $matches[0] . 'm';
|
||||
}
|
||||
}
|
||||
|
||||
$PHPDateArray = Helpers::dateParse($timeValue);
|
||||
|
||||
@@ -28,7 +28,7 @@ class Week
|
||||
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $method Week begins on Sunday or Monday
|
||||
* @param array<mixed>|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.
|
||||
@@ -41,7 +41,7 @@ class Week
|
||||
* 21 ISO (Jan. 4 is week 1, begins on Monday).
|
||||
* Or can be an array of methods
|
||||
*
|
||||
* @return array|int|string Week Number
|
||||
* @return array<mixed>|int|string Week Number
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -101,7 +101,7 @@ class Week
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
*
|
||||
* @return array|int|string Week Number
|
||||
* @return array<mixed>|int|string Week Number
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -137,7 +137,7 @@ class Week
|
||||
* Excel Function:
|
||||
* WEEKDAY(dateValue[,style])
|
||||
*
|
||||
* @param null|array|bool|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* @param null|array<mixed>|bool|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param mixed $style A number that determines the type of return value
|
||||
@@ -146,7 +146,7 @@ class Week
|
||||
* 3 Numbers 0 (Monday) through 6 (Sunday).
|
||||
* Or can be an array of styles
|
||||
*
|
||||
* @return array|int|string Day of the week value
|
||||
* @return array<mixed>|int|string Day of the week value
|
||||
* If an array of values is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -22,16 +22,16 @@ class WorkDay
|
||||
* Excel Function:
|
||||
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
|
||||
*
|
||||
* @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
|
||||
* @param array<mixed>|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of date values
|
||||
* @param array|int $endDays The number of nonweekend and nonholiday days before or after
|
||||
* @param array<mixed>|int $endDays The number of nonweekend and nonholiday days before or after
|
||||
* startDate. A positive value for days yields a future date; a
|
||||
* negative value yields a past date.
|
||||
* Or can be an array of int values
|
||||
* @param null|mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
|
||||
*
|
||||
* @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* @return array<mixed>|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||
* depending on the value of the ReturnDateType flag
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
@@ -72,6 +72,8 @@ class WorkDay
|
||||
|
||||
/**
|
||||
* Use incrementing logic to determine Workday.
|
||||
*
|
||||
* @param array<mixed> $holidayArray
|
||||
*/
|
||||
private static function incrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
|
||||
{
|
||||
@@ -103,10 +105,12 @@ class WorkDay
|
||||
return Helpers::returnIn3FormatsFloat($endDate);
|
||||
}
|
||||
|
||||
/** @param array<mixed> $holidayArray */
|
||||
private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
|
||||
{
|
||||
$holidayCountedArray = $holidayDates = [];
|
||||
foreach ($holidayArray as $holidayDate) {
|
||||
/** @var float $holidayDate */
|
||||
if (self::getWeekDay($holidayDate, 3) < 5) {
|
||||
$holidayDates[] = $holidayDate;
|
||||
}
|
||||
@@ -131,6 +135,8 @@ class WorkDay
|
||||
|
||||
/**
|
||||
* Use decrementing logic to determine Workday.
|
||||
*
|
||||
* @param array<mixed> $holidayArray
|
||||
*/
|
||||
private static function decrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
|
||||
{
|
||||
@@ -162,10 +168,12 @@ class WorkDay
|
||||
return Helpers::returnIn3FormatsFloat($endDate);
|
||||
}
|
||||
|
||||
/** @param array<mixed> $holidayArray */
|
||||
private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
|
||||
{
|
||||
$holidayCountedArray = $holidayDates = [];
|
||||
foreach ($holidayArray as $holidayDate) {
|
||||
/** @var float $holidayDate */
|
||||
if (self::getWeekDay($holidayDate, 3) < 5) {
|
||||
$holidayDates[] = $holidayDate;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class YearFrac
|
||||
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
|
||||
* PHP DateTime object, or a standard date string
|
||||
* Or can be an array of methods
|
||||
* @param array|int $method Method used for the calculation
|
||||
* @param array<mixed>|int $method Method used for the calculation
|
||||
* 0 or omitted US (NASD) 30/360
|
||||
* 1 Actual/actual
|
||||
* 2 Actual/360
|
||||
@@ -39,7 +39,7 @@ class YearFrac
|
||||
* 4 European 30/360
|
||||
* Or can be an array of methods
|
||||
*
|
||||
* @return array|float|int|string fraction of the year, or a string containing an error
|
||||
* @return array<mixed>|float|int|string fraction of the year, or a string containing an error
|
||||
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
@@ -62,11 +62,11 @@ class YearFrac
|
||||
}
|
||||
|
||||
return match ($method) {
|
||||
0 => Functions::scalar(Days360::between($startDate, $endDate)) / 360,
|
||||
0 => Helpers::floatOrInt(Days360::between($startDate, $endDate)) / 360,
|
||||
1 => self::method1($startDate, $endDate),
|
||||
2 => Functions::scalar(Difference::interval($startDate, $endDate)) / 360,
|
||||
3 => Functions::scalar(Difference::interval($startDate, $endDate)) / 365,
|
||||
4 => Functions::scalar(Days360::between($startDate, $endDate, true)) / 360,
|
||||
2 => Helpers::floatOrInt(Difference::interval($startDate, $endDate)) / 360,
|
||||
3 => Helpers::floatOrInt(Difference::interval($startDate, $endDate)) / 365,
|
||||
4 => Helpers::floatOrInt(Days360::between($startDate, $endDate, true)) / 360,
|
||||
default => ExcelError::NAN(),
|
||||
};
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class YearFrac
|
||||
|
||||
private static function method1(float $startDate, float $endDate): float
|
||||
{
|
||||
$days = Functions::scalar(Difference::interval($startDate, $endDate));
|
||||
$days = Helpers::floatOrInt(Difference::interval($startDate, $endDate));
|
||||
$startYear = (int) DateParts::year($startDate);
|
||||
$endYear = (int) DateParts::year($endDate);
|
||||
$years = $endYear - $startYear + 1;
|
||||
|
||||
@@ -8,14 +8,18 @@ class ArrayArgumentHelper
|
||||
{
|
||||
protected int $indexStart = 0;
|
||||
|
||||
/** @var mixed[] */
|
||||
protected array $arguments;
|
||||
|
||||
protected int $argumentCount;
|
||||
|
||||
/** @var int[] */
|
||||
protected array $rows;
|
||||
|
||||
/** @var int[] */
|
||||
protected array $columns;
|
||||
|
||||
/** @param mixed[] $arguments */
|
||||
public function initialise(array $arguments): void
|
||||
{
|
||||
$keys = array_keys($arguments);
|
||||
@@ -34,6 +38,7 @@ class ArrayArgumentHelper
|
||||
}
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
@@ -65,6 +70,7 @@ class ArrayArgumentHelper
|
||||
return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
private function getRowVectors(): array
|
||||
{
|
||||
$rowVectors = [];
|
||||
@@ -84,6 +90,7 @@ class ArrayArgumentHelper
|
||||
return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
private function getColumnVectors(): array
|
||||
{
|
||||
$columnVectors = [];
|
||||
@@ -96,6 +103,7 @@ class ArrayArgumentHelper
|
||||
return $columnVectors;
|
||||
}
|
||||
|
||||
/** @return int[] */
|
||||
public function getMatrixPair(): array
|
||||
{
|
||||
for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
|
||||
@@ -134,6 +142,11 @@ class ArrayArgumentHelper
|
||||
return $this->columns[$argument];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function rows(array $arguments): array
|
||||
{
|
||||
return array_map(
|
||||
@@ -142,14 +155,17 @@ class ArrayArgumentHelper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function columns(array $arguments): array
|
||||
{
|
||||
return array_map(
|
||||
function (mixed $argument): int {
|
||||
return is_array($argument) && is_array($argument[array_keys($argument)[0]])
|
||||
fn (mixed $argument): int => is_array($argument) && is_array($argument[array_keys($argument)[0]])
|
||||
? count($argument[array_keys($argument)[0]])
|
||||
: 1;
|
||||
},
|
||||
: 1,
|
||||
$arguments
|
||||
);
|
||||
}
|
||||
@@ -166,6 +182,13 @@ class ArrayArgumentHelper
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $arguments
|
||||
* @param int[] $rows
|
||||
* @param int[] $columns
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
|
||||
{
|
||||
foreach ($arguments as $index => $argument) {
|
||||
@@ -180,6 +203,11 @@ class ArrayArgumentHelper
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $array
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function filterArray(array $array): array
|
||||
{
|
||||
return array_filter(
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class ArrayArgumentProcessor
|
||||
{
|
||||
private static ArrayArgumentHelper $arrayArgumentHelper;
|
||||
|
||||
/** @return mixed[] */
|
||||
public static function processArguments(
|
||||
ArrayArgumentHelper $arrayArgumentHelper,
|
||||
callable $method,
|
||||
@@ -54,23 +56,30 @@ class ArrayArgumentProcessor
|
||||
return ['#VALUE!'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $matrixIndexes
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
|
||||
{
|
||||
$matrix2 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues2 */
|
||||
$matrix2 = array_pop($matrixIndexes) ?? throw new Exception('empty array 2');
|
||||
/** @var mixed[][] $matrixValues2 */
|
||||
$matrixValues2 = $arguments[$matrix2];
|
||||
$matrix1 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues1 */
|
||||
$matrix1 = array_pop($matrixIndexes) ?? throw new Exception('empty array 1');
|
||||
/** @var mixed[][] $matrixValues1 */
|
||||
$matrixValues1 = $arguments[$matrix1];
|
||||
|
||||
$rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
|
||||
$columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
|
||||
/** @var non-empty-array<int> */
|
||||
$matrix12 = [$matrix1, $matrix2];
|
||||
$rows = min(array_map(self::$arrayArgumentHelper->rowCount(...), $matrix12));
|
||||
$columns = min(array_map(self::$arrayArgumentHelper->columnCount(...), $matrix12));
|
||||
|
||||
if ($rows === 1) {
|
||||
$rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
|
||||
$rows = max(array_map(self::$arrayArgumentHelper->rowCount(...), $matrix12));
|
||||
}
|
||||
if ($columns === 1) {
|
||||
$columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
|
||||
$columns = max(array_map(self::$arrayArgumentHelper->columnCount(...), $matrix12));
|
||||
}
|
||||
|
||||
$result = [];
|
||||
@@ -92,13 +101,18 @@ class ArrayArgumentProcessor
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $matrixIndexes
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function evaluateMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
|
||||
{
|
||||
$matrix2 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues2 */
|
||||
/** @var mixed[][] $matrixValues2 */
|
||||
$matrixValues2 = $arguments[$matrix2];
|
||||
$matrix1 = array_pop($matrixIndexes);
|
||||
/** @var array $matrixValues1 */
|
||||
/** @var mixed[][] $matrixValues1 */
|
||||
$matrixValues1 = $arguments[$matrix1];
|
||||
|
||||
$result = [];
|
||||
@@ -119,6 +133,7 @@ class ArrayArgumentProcessor
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, mixed ...$arguments): array
|
||||
{
|
||||
$rowVector = Functions::flattenArray($arguments[$rowIndex]);
|
||||
@@ -141,11 +156,13 @@ class ArrayArgumentProcessor
|
||||
|
||||
/**
|
||||
* Note, offset is from 1 (for the first argument) rather than from 0.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, mixed ...$arguments): array
|
||||
{
|
||||
$values = array_slice($arguments, $nthArgument - 1, 1);
|
||||
/** @var array $values */
|
||||
/** @var mixed[] $values */
|
||||
$values = array_pop($values);
|
||||
|
||||
$result = [];
|
||||
|
||||
@@ -16,40 +16,30 @@ class FormattedNumber
|
||||
// preg_quoted string for major currency symbols, with a %s for locale currency
|
||||
private const CURRENCY_CONVERSION_LIST = '\$€£¥%s';
|
||||
|
||||
private const STRING_CONVERSION_LIST = [
|
||||
[self::class, 'convertToNumberIfNumeric'],
|
||||
[self::class, 'convertToNumberIfFraction'],
|
||||
[self::class, 'convertToNumberIfPercent'],
|
||||
[self::class, 'convertToNumberIfCurrency'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Identify whether a string contains a formatted numeric value,
|
||||
* and convert it to a numeric if it is.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfFormatted(string &$operand): bool
|
||||
public static function convertToNumberIfFormatted(float|string &$operand): bool
|
||||
{
|
||||
foreach (self::STRING_CONVERSION_LIST as $conversionMethod) {
|
||||
if ($conversionMethod($operand) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return self::convertToNumberIfNumeric($operand)
|
||||
|| self::convertToNumberIfFraction($operand)
|
||||
|| self::convertToNumberIfPercent($operand)
|
||||
|| self::convertToNumberIfCurrency($operand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify whether a string contains a numeric value,
|
||||
* and convert it to a numeric if it is.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfNumeric(string &$operand): bool
|
||||
public static function convertToNumberIfNumeric(float|string &$operand): bool
|
||||
{
|
||||
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
|
||||
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
|
||||
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim("$operand"));
|
||||
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
|
||||
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
|
||||
|
||||
@@ -68,13 +58,15 @@ class FormattedNumber
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfFraction(string &$operand): bool
|
||||
public static function convertToNumberIfFraction(float|string &$operand): bool
|
||||
{
|
||||
if (preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
|
||||
if (is_string($operand) && preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
|
||||
$sign = ($match[1] === '-') ? '-' : '+';
|
||||
$wholePart = ($match[3] === '') ? '' : ($sign . $match[3]);
|
||||
$fractionFormula = '=' . $wholePart . $sign . $match[4];
|
||||
$operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
|
||||
/** @var string */
|
||||
$operandx = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
|
||||
$operand = $operandx;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -86,12 +78,12 @@ class FormattedNumber
|
||||
* Identify whether a string contains a percentage, and if so,
|
||||
* convert it to a numeric.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfPercent(string &$operand): bool
|
||||
public static function convertToNumberIfPercent(float|string &$operand): bool
|
||||
{
|
||||
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim($operand));
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim("$operand"));
|
||||
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
|
||||
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
|
||||
|
||||
@@ -111,13 +103,13 @@ class FormattedNumber
|
||||
* Identify whether a string contains a currency value, and if so,
|
||||
* convert it to a numeric.
|
||||
*
|
||||
* @param string $operand string value to test
|
||||
* @param float|string $operand string value to test
|
||||
*/
|
||||
public static function convertToNumberIfCurrency(string &$operand): bool
|
||||
public static function convertToNumberIfCurrency(float|string &$operand): bool
|
||||
{
|
||||
$currencyRegexp = self::currencyMatcherRegexp();
|
||||
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
|
||||
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', "$operand");
|
||||
|
||||
$match = [];
|
||||
if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
|
||||
|
||||
@@ -78,7 +78,7 @@ class Logger
|
||||
{
|
||||
// Only write the debug log if logging is enabled
|
||||
if ($this->writeDebugLog) {
|
||||
$message = sprintf($message, ...$args);
|
||||
$message = sprintf($message, ...$args); //* @phpstan-ignore-line
|
||||
$cellReference = implode(' -> ', $this->cellStack->showStack());
|
||||
if ($this->echoDebugLog) {
|
||||
echo $cellReference,
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engine\Operands;
|
||||
|
||||
interface Operand
|
||||
{
|
||||
/** @param string[] $matches */
|
||||
public static function fromParser(string $formula, int $index, array $matches): self;
|
||||
|
||||
public function value(): string;
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||
use Stringable;
|
||||
|
||||
@@ -29,7 +30,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
self::ITEM_SPECIFIER_TOTALS,
|
||||
];
|
||||
|
||||
private const TABLE_REFERENCE = '/([\p{L}_\\\\][\p{L}\p{N}\._]+)?(\[(?:[^\]\[]+|(?R))*+\])/miu';
|
||||
private const TABLE_REFERENCE = '/([\p{L}_\\\][\p{L}\p{N}\._]+)?(\[(?:[^\]\[]+|(?R))*+\])/miu';
|
||||
|
||||
private string $value;
|
||||
|
||||
@@ -47,6 +48,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
|
||||
private ?int $totalsRow;
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $columns;
|
||||
|
||||
public function __construct(string $structuredReference)
|
||||
@@ -54,6 +56,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
$this->value = $structuredReference;
|
||||
}
|
||||
|
||||
/** @param string[] $matches */
|
||||
public static function fromParser(string $formula, int $index, array $matches): self
|
||||
{
|
||||
$val = $matches[0];
|
||||
@@ -171,14 +174,20 @@ final class StructuredReference implements Operand, Stringable
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{array{string, int}, array{string, int}} $tableRange
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function getColumns(Cell $cell, array $tableRange): array
|
||||
{
|
||||
$worksheet = $cell->getWorksheet();
|
||||
$cellReference = $cell->getCoordinate();
|
||||
|
||||
$columns = [];
|
||||
$lastColumn = ++$tableRange[1][0];
|
||||
for ($column = $tableRange[0][0]; $column !== $lastColumn; ++$column) {
|
||||
$lastColumn = StringHelper::stringIncrement($tableRange[1][0]);
|
||||
for ($column = $tableRange[0][0]; $column !== $lastColumn; StringHelper::stringIncrement($column)) {
|
||||
/** @var string $column */
|
||||
$columns[$column] = $worksheet
|
||||
->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
|
||||
->getCalculatedValue();
|
||||
@@ -196,7 +205,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
$reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
|
||||
|
||||
foreach ($this->columns as $columnId => $columnName) {
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName);
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName); //* @phpstan-ignore-line
|
||||
$reference = $this->adjustRowReference($columnName, $reference, $cell, $columnId);
|
||||
}
|
||||
|
||||
@@ -330,7 +339,7 @@ final class StructuredReference implements Operand, Stringable
|
||||
{
|
||||
$columnsSelected = false;
|
||||
foreach ($this->columns as $columnId => $columnName) {
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName ?? '');
|
||||
$columnName = str_replace("\u{a0}", ' ', $columnName ?? ''); //* @phpstan-ignore-line
|
||||
$cellFrom = "{$columnId}{$startRow}";
|
||||
$cellTo = "{$columnId}{$endRow}";
|
||||
$cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
|
||||
|
||||
@@ -31,7 +31,7 @@ class BesselI
|
||||
* If $ord < 0, BESSELI returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ class BesselJ
|
||||
* If $ord < 0, BESSELJ returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ class BesselK
|
||||
* If $ord < 0, BESSELKI returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@ class BesselY
|
||||
* If $ord < 0, BESSELY returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return array<mixed>|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -30,10 +30,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITAND(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
|
||||
@@ -62,10 +62,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITOR(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
|
||||
@@ -95,10 +95,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITXOR(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
|
||||
@@ -128,10 +128,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITLSHIFT(number, shift_amount)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITLSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
|
||||
@@ -163,10 +163,10 @@ class BitWise
|
||||
* Excel Function:
|
||||
* BITRSHIFT(number, shift_amount)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array<mixed>|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITRSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
|
||||
@@ -221,7 +221,7 @@ class BitWise
|
||||
$value = self::nullFalseTrueToNumber($value);
|
||||
|
||||
if (is_numeric($value)) {
|
||||
if (abs($value) > 53) {
|
||||
if (abs($value + 0) > 53) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ class Compare
|
||||
* functions you calculate the count of equal pairs. This function is also known as the
|
||||
* Kronecker Delta function.
|
||||
*
|
||||
* @param array|bool|float|int|string $a the first number
|
||||
* @param array<mixed>|bool|float|int|string $a the first number
|
||||
* Or can be an array of values
|
||||
* @param array|bool|float|int|string $b The second number. If omitted, b is assumed to be zero.
|
||||
* @param array<mixed>|bool|float|int|string $b The second number. If omitted, b is assumed to be zero.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|int|string (string in the event of an error)
|
||||
* @return array<mixed>|int|string (string in the event of an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -55,12 +55,12 @@ class Compare
|
||||
* 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 array|bool|float|int|string $number the value to test against step
|
||||
* @param array<mixed>|bool|float|int|string $number the value to test against step
|
||||
* Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $step The threshold value. If you omit a value for step, GESTEP uses zero.
|
||||
* @param null|array<mixed>|bool|float|int|string $step The threshold value. If you omit a value for step, GESTEP uses zero.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|int|string (string in the event of an error)
|
||||
* @return array<mixed>|int|string (string in the event of an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@ class Complex
|
||||
* If omitted, the suffix is assumed to be "i".
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function COMPLEX(mixed $realNumber = 0.0, mixed $imaginary = 0.0, mixed $suffix = 'i'): array|string
|
||||
@@ -65,11 +65,11 @@ class Complex
|
||||
* Excel Function:
|
||||
* IMAGINARY(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the imaginary
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the imaginary
|
||||
* coefficient
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string (string if an error)
|
||||
* @return array<mixed>|float|string (string if an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -96,10 +96,10 @@ class Complex
|
||||
* Excel Function:
|
||||
* IMREAL(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the real coefficient
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the real coefficient
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string (string if an error)
|
||||
* @return array<mixed>|float|string (string if an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -19,10 +19,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMABS(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the absolute value
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the absolute value
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMABS(array|string $complexNumber): array|float|string
|
||||
@@ -49,10 +49,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMARGUMENT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the argument theta
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the argument theta
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMARGUMENT(array|string $complexNumber): array|float|string
|
||||
@@ -82,10 +82,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCONJUGATE(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the conjugate
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the conjugate
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCONJUGATE(array|string $complexNumber): array|string
|
||||
@@ -111,10 +111,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCOS(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cosine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the cosine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOS(array|string $complexNumber): array|string
|
||||
@@ -140,10 +140,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCOSH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic cosine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOSH(array|string $complexNumber): array|string
|
||||
@@ -169,10 +169,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCOT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cotangent
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the cotangent
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOT(array|string $complexNumber): array|string
|
||||
@@ -198,10 +198,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCSC(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cosecant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the cosecant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCSC(array|string $complexNumber): array|string
|
||||
@@ -227,10 +227,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMCSCH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic cosecant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCSCH(array|string $complexNumber): array|string
|
||||
@@ -256,10 +256,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSIN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the sine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the sine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSIN(array|string $complexNumber): array|string
|
||||
@@ -285,10 +285,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSINH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic sine
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic sine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSINH(array|string $complexNumber): array|string
|
||||
@@ -314,10 +314,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSEC(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the secant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the secant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSEC(array|string $complexNumber): array|string
|
||||
@@ -343,10 +343,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSECH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic secant
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the hyperbolic secant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSECH(array|string $complexNumber): array|string
|
||||
@@ -372,10 +372,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMTAN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the tangent
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the tangent
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMTAN(array|string $complexNumber): array|string
|
||||
@@ -401,10 +401,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMSQRT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the square root
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the square root
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSQRT(array|string $complexNumber): array|string
|
||||
@@ -435,10 +435,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMLN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the natural logarithm
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the natural logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLN(array|string $complexNumber): array|string
|
||||
@@ -468,10 +468,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMLOG10(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the common logarithm
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the common logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLOG10(array|string $complexNumber): array|string
|
||||
@@ -501,10 +501,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMLOG2(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the base-2 logarithm
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the base-2 logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLOG2(array|string $complexNumber): array|string
|
||||
@@ -534,10 +534,10 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMEXP(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the exponential
|
||||
* @param array<mixed>|string $complexNumber the complex number for which you want the exponential
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMEXP(array|string $complexNumber): array|string
|
||||
@@ -563,12 +563,12 @@ class ComplexFunctions
|
||||
* Excel Function:
|
||||
* IMPOWER(complexNumber,realNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number you want to raise to a power
|
||||
* @param array<mixed>|string $complexNumber the complex number you want to raise to a power
|
||||
* Or can be an array of values
|
||||
* @param array|float|int|string $realNumber the power to which you want to raise the complex number
|
||||
* @param array<mixed>|float|int|string $realNumber the power to which you want to raise the complex number
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMPOWER(array|string $complexNumber, array|float|int|string $realNumber): array|string
|
||||
|
||||
@@ -20,12 +20,12 @@ class ComplexOperations
|
||||
* Excel Function:
|
||||
* IMDIV(complexDividend,complexDivisor)
|
||||
*
|
||||
* @param array|string $complexDividend the complex numerator or dividend
|
||||
* @param array<mixed>|string $complexDividend the complex numerator or dividend
|
||||
* Or can be an array of values
|
||||
* @param array|string $complexDivisor the complex denominator or divisor
|
||||
* @param array<mixed>|string $complexDivisor the complex denominator or divisor
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMDIV(array|string $complexDividend, array|string $complexDivisor): array|string
|
||||
@@ -49,12 +49,12 @@ class ComplexOperations
|
||||
* Excel Function:
|
||||
* IMSUB(complexNumber1,complexNumber2)
|
||||
*
|
||||
* @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
|
||||
* @param array<mixed>|string $complexNumber1 the complex number from which to subtract complexNumber2
|
||||
* Or can be an array of values
|
||||
* @param array|string $complexNumber2 the complex number to subtract from complexNumber1
|
||||
* @param array<mixed>|string $complexNumber2 the complex number to subtract from complexNumber1
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSUB(array|string $complexNumber1, array|string $complexNumber2): array|string
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
abstract class ConvertBase
|
||||
{
|
||||
@@ -26,7 +27,7 @@ abstract class ConvertBase
|
||||
}
|
||||
}
|
||||
|
||||
return strtoupper((string) $value);
|
||||
return strtoupper(StringHelper::convertToString($value));
|
||||
}
|
||||
|
||||
protected static function validatePlaces(mixed $places = null): ?int
|
||||
|
||||
@@ -15,7 +15,7 @@ class ConvertBinary extends ConvertBase
|
||||
* Excel Function:
|
||||
* BIN2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* @param array<mixed>|bool|float|int|string $value 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.
|
||||
@@ -23,7 +23,7 @@ class ConvertBinary extends ConvertBase
|
||||
* 10 characters (10 bits), BIN2DEC returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|float|int|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -44,10 +44,10 @@ class ConvertBinary extends ConvertBase
|
||||
// Two's Complement
|
||||
$value = substr($value, -9);
|
||||
|
||||
return '-' . (512 - bindec($value));
|
||||
return -(512 - bindec($value));
|
||||
}
|
||||
|
||||
return (string) bindec($value);
|
||||
return bindec($value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,14 +58,14 @@ class ConvertBinary extends ConvertBase
|
||||
* Excel Function:
|
||||
* BIN2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* @param array<mixed>|bool|float|int|string $value 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.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, BIN2HEX uses the
|
||||
* @param null|array<mixed>|float|int|string $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.
|
||||
@@ -73,7 +73,7 @@ class ConvertBinary extends ConvertBase
|
||||
* If places is negative, BIN2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -111,14 +111,14 @@ class ConvertBinary extends ConvertBase
|
||||
* Excel Function:
|
||||
* BIN2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* @param array<mixed>|bool|float|int|string $value 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.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, BIN2OCT uses the
|
||||
* @param null|array<mixed>|float|int|string $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.
|
||||
@@ -126,7 +126,7 @@ class ConvertBinary extends ConvertBase
|
||||
* If places is negative, BIN2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* Excel Function:
|
||||
* DEC2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -33,7 +33,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If DEC2BIN requires more than places characters, it returns the #NUM!
|
||||
* error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, DEC2BIN uses
|
||||
* @param null|array<mixed>|float|int|string $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.
|
||||
@@ -41,7 +41,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If places is zero or negative, DEC2BIN returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -79,7 +79,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* Excel Function:
|
||||
* DEC2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -90,7 +90,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If DEC2HEX requires more than places characters, it returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, DEC2HEX uses
|
||||
* @param null|array<mixed>|float|int|string $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.
|
||||
@@ -98,7 +98,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If places is zero or negative, DEC2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -155,7 +155,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* Excel Function:
|
||||
* DEC2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -166,7 +166,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If DEC2OCT requires more than places characters, it returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
|
||||
* @param array<mixed>|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.
|
||||
@@ -174,7 +174,7 @@ class ConvertDecimal extends ConvertBase
|
||||
* If places is zero or negative, DEC2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ class ConvertHex extends ConvertBase
|
||||
* Excel Function:
|
||||
* HEX2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|string $value The hexadecimal number you want to convert.
|
||||
* @param array<mixed>|bool|float|string $value 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.
|
||||
@@ -26,7 +26,7 @@ class ConvertHex extends ConvertBase
|
||||
* 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.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted,
|
||||
* @param array<mixed>|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.
|
||||
@@ -34,7 +34,7 @@ class ConvertHex extends ConvertBase
|
||||
* If places is negative, HEX2BIN returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ class ConvertHex extends ConvertBase
|
||||
* Excel Function:
|
||||
* HEX2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The hexadecimal number you want to convert. This number cannot
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -74,7 +74,7 @@ class ConvertHex extends ConvertBase
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|float|int|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ class ConvertHex extends ConvertBase
|
||||
}
|
||||
|
||||
$binX = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
foreach (mb_str_split($value, 1, 'UTF-8') as $char) {
|
||||
$binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
if (strlen($binX) == 40 && $binX[0] == '1') {
|
||||
@@ -104,10 +104,10 @@ class ConvertHex extends ConvertBase
|
||||
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
|
||||
}
|
||||
|
||||
return (string) ((bindec($binX) + 1) * -1);
|
||||
return (bindec($binX) + 1) * -1;
|
||||
}
|
||||
|
||||
return (string) bindec($binX);
|
||||
return bindec($binX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ class ConvertHex extends ConvertBase
|
||||
* Excel Function:
|
||||
* HEX2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The hexadecimal number you want to convert. Number cannot
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -132,7 +132,7 @@ class ConvertHex extends ConvertBase
|
||||
* If HEX2OCT requires more than places characters, it returns
|
||||
* the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
|
||||
* @param array<mixed>|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.
|
||||
@@ -141,7 +141,7 @@ class ConvertHex extends ConvertBase
|
||||
* If places is negative, HEX2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ class ConvertOctal extends ConvertBase
|
||||
* Excel Function:
|
||||
* OCT2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -29,7 +29,7 @@ class ConvertOctal extends ConvertBase
|
||||
* If OCT2BIN requires more than places characters, it
|
||||
* returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted,
|
||||
* @param array<mixed>|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).
|
||||
@@ -40,7 +40,7 @@ class ConvertOctal extends ConvertBase
|
||||
* value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -69,7 +69,7 @@ class ConvertOctal extends ConvertBase
|
||||
* Excel Function:
|
||||
* OCT2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -78,7 +78,7 @@ class ConvertOctal extends ConvertBase
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|float|int|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ class ConvertOctal extends ConvertBase
|
||||
}
|
||||
|
||||
$binX = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
foreach (mb_str_split($value, 1, 'UTF-8') as $char) {
|
||||
$binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
if (strlen($binX) == 30 && $binX[0] == '1') {
|
||||
@@ -104,10 +104,10 @@ class ConvertOctal extends ConvertBase
|
||||
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
|
||||
}
|
||||
|
||||
return (string) ((bindec($binX) + 1) * -1);
|
||||
return (bindec($binX) + 1) * -1;
|
||||
}
|
||||
|
||||
return (string) bindec($binX);
|
||||
return bindec($binX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ class ConvertOctal extends ConvertBase
|
||||
* Excel Function:
|
||||
* OCT2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* @param array<mixed>|bool|float|int|string $value 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
|
||||
@@ -130,7 +130,7 @@ class ConvertOctal extends ConvertBase
|
||||
* If OCT2HEX requires more than places characters, it returns
|
||||
* the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
|
||||
* @param array<mixed>|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.
|
||||
@@ -138,7 +138,7 @@ class ConvertOctal extends ConvertBase
|
||||
* If places is negative, OCT2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* @return array<mixed>|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -230,7 +230,7 @@ class ConvertUOM
|
||||
/**
|
||||
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
** @var array<string, array{multiplier: float|int, name: string}>
|
||||
* @var array<string, array{multiplier: float|int, name: string}>
|
||||
*/
|
||||
private static array $binaryConversionMultipliers = [
|
||||
'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
|
||||
@@ -435,6 +435,8 @@ class ConvertUOM
|
||||
/**
|
||||
* getConversionGroups
|
||||
* Returns a list of the different conversion groups for UOM conversions.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getConversionCategories(): array
|
||||
{
|
||||
@@ -451,6 +453,8 @@ class ConvertUOM
|
||||
* Returns an array of units of measure, for a specified conversion group, or for all groups.
|
||||
*
|
||||
* @param ?string $category The group whose units of measure you want to retrieve
|
||||
*
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function getConversionCategoryUnits(?string $category = null): array
|
||||
{
|
||||
@@ -468,6 +472,8 @@ class ConvertUOM
|
||||
* getConversionGroupUnitDetails.
|
||||
*
|
||||
* @param ?string $category The group whose units of measure you want to retrieve
|
||||
*
|
||||
* @return array<string, list<array<string, string>>>
|
||||
*/
|
||||
public static function getConversionCategoryUnitDetails(?string $category = null): array
|
||||
{
|
||||
@@ -488,7 +494,7 @@ class ConvertUOM
|
||||
* getConversionMultipliers
|
||||
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return array<string, array{multiplier: float, name: string}>
|
||||
*/
|
||||
public static function getConversionMultipliers(): array
|
||||
{
|
||||
@@ -499,7 +505,7 @@ class ConvertUOM
|
||||
* getBinaryConversionMultipliers
|
||||
* Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @return mixed[]
|
||||
* @return array<string, array{multiplier: float|int, name: string}>
|
||||
*/
|
||||
public static function getBinaryConversionMultipliers(): array
|
||||
{
|
||||
@@ -516,14 +522,14 @@ class ConvertUOM
|
||||
* Excel Function:
|
||||
* CONVERT(value,fromUOM,toUOM)
|
||||
*
|
||||
* @param array|float|int|string $value the value in fromUOM to convert
|
||||
* @param array<mixed>|float|int|string $value the value in fromUOM to convert
|
||||
* Or can be an array of values
|
||||
* @param array|string $fromUOM the units for value
|
||||
* @param string|string[] $fromUOM the units for value
|
||||
* Or can be an array of values
|
||||
* @param array|string $toUOM the units for the result
|
||||
* @param string|string[] $toUOM the units for the result
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* @return float|mixed[]|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -566,6 +572,7 @@ class ConvertUOM
|
||||
return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
|
||||
}
|
||||
|
||||
/** @return array{0: string, 1: string, 2: float} */
|
||||
private static function getUOMDetails(string $uom): array
|
||||
{
|
||||
if (isset(self::$conversionUnits[$uom])) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class Erf
|
||||
* If omitted, ERF integrates between zero and lower_limit
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERF(mixed $lower, mixed $upper = null): array|float|string
|
||||
@@ -63,7 +63,7 @@ class Erf
|
||||
* @param mixed $limit Float bound for integrating ERF, other bound is zero
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERFPRECISE(mixed $limit)
|
||||
|
||||
@@ -26,7 +26,7 @@ class ErfC
|
||||
* @param mixed $value The float lower bound for integrating ERFC
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERFC(mixed $value)
|
||||
|
||||
@@ -54,9 +54,7 @@ class Amortization
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$cost = FinancialValidations::validateFloat($cost);
|
||||
@@ -141,9 +139,7 @@ class Amortization
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$cost = FinancialValidations::validateFloat($cost);
|
||||
@@ -171,9 +167,13 @@ class Amortization
|
||||
if (
|
||||
$basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
|
||||
&& $yearFrac < 1
|
||||
&& DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear))
|
||||
) {
|
||||
$yearFrac *= 365 / 366;
|
||||
$temp = Functions::scalar($purchasedYear);
|
||||
if (is_int($temp) || is_string($temp)) {
|
||||
if (DateTimeExcel\Helpers::isLeapYear($temp)) {
|
||||
$yearFrac *= 365 / 366;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$f0Rate = $yearFrac * $rate * $cost;
|
||||
|
||||
@@ -38,9 +38,9 @@ class Periodic
|
||||
): string|float {
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
|
||||
$presentValue = ($presentValue === null) ? 0.0 : Functions::flattenSingleValue($presentValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$payment = Functions::flattenSingleValue($payment) ?? 0.0;
|
||||
$presentValue = Functions::flattenSingleValue($presentValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -77,9 +77,9 @@ class Periodic
|
||||
): string|float {
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$payment = Functions::flattenSingleValue($payment) ?? 0.0;
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -122,8 +122,8 @@ class Periodic
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$payment = Functions::flattenSingleValue($payment);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -150,7 +150,7 @@ class Periodic
|
||||
float $presentValue,
|
||||
int $type
|
||||
): float {
|
||||
if ($rate !== null && $rate != 0) {
|
||||
if ($rate != 0) {
|
||||
return -$presentValue
|
||||
* (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
|
||||
/ $rate;
|
||||
|
||||
@@ -41,7 +41,7 @@ class Cumulative
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$start = Functions::flattenSingleValue($start);
|
||||
$end = Functions::flattenSingleValue($end);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
@@ -104,7 +104,7 @@ class Cumulative
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$start = Functions::flattenSingleValue($start);
|
||||
$end = Functions::flattenSingleValue($end);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$rate = CashFlowValidations::validateRate($rate);
|
||||
|
||||
@@ -43,7 +43,7 @@ class Interest
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$interestRate = CashFlowValidations::validateRate($interestRate);
|
||||
@@ -160,9 +160,9 @@ class Interest
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$payment = Functions::flattenSingleValue($payment);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
$guess = Functions::flattenSingleValue($guess) ?? 0.1;
|
||||
|
||||
try {
|
||||
$numberOfPeriods = CashFlowValidations::validateFloat($numberOfPeriods);
|
||||
|
||||
@@ -27,14 +27,14 @@ class Payments
|
||||
mixed $interestRate,
|
||||
mixed $numberOfPeriods,
|
||||
mixed $presentValue,
|
||||
mixed $futureValue = 0,
|
||||
mixed $futureValue = 0.0,
|
||||
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
|
||||
): string|float {
|
||||
$interestRate = Functions::flattenSingleValue($interestRate);
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$futureValue = Functions::flattenSingleValue($futureValue) ?? 0.0;
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$interestRate = CashFlowValidations::validateRate($interestRate);
|
||||
@@ -83,7 +83,7 @@ class Payments
|
||||
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
$futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
|
||||
$type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
|
||||
$type = Functions::flattenSingleValue($type) ?? FinancialConstants::PAYMENT_END_OF_PERIOD;
|
||||
|
||||
try {
|
||||
$interestRate = CashFlowValidations::validateRate($interestRate);
|
||||
|
||||
@@ -77,13 +77,13 @@ class Single
|
||||
*
|
||||
* Calculates the interest rate required for an investment to grow to a specified future value .
|
||||
*
|
||||
* @param array|float $periods The number of periods over which the investment is made
|
||||
* @param array|float $presentValue Present Value
|
||||
* @param array|float $futureValue Future Value
|
||||
* @param mixed $periods The number of periods over which the investment is made, expect array|float
|
||||
* @param mixed $presentValue Present Value, expect array|float
|
||||
* @param mixed $futureValue Future Value, expect array|float
|
||||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function interestRate(array|float $periods = 0.0, array|float $presentValue = 0.0, array|float $futureValue = 0.0): string|float
|
||||
public static function interestRate(mixed $periods = 0.0, mixed $presentValue = 0.0, mixed $futureValue = 0.0): string|float
|
||||
{
|
||||
$periods = Functions::flattenSingleValue($periods);
|
||||
$presentValue = Functions::flattenSingleValue($presentValue);
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class NonPeriodic
|
||||
{
|
||||
@@ -23,16 +24,17 @@ class NonPeriodic
|
||||
* Excel Function:
|
||||
* =XIRR(values,dates,guess)
|
||||
*
|
||||
* @param float[] $values A series of cash flow payments
|
||||
* @param array<int, float|int|numeric-string> $values A series of cash flow payments, expecting float[]
|
||||
* The series of values must contain at least one positive value & one negative value
|
||||
* @param mixed[] $dates A series of payment dates
|
||||
* @param array<int, float|int|numeric-string> $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 mixed $guess An optional guess at the expected answer
|
||||
*/
|
||||
public static function rate(array $values, array $dates, mixed $guess = self::DEFAULT_GUESS): float|string
|
||||
public static function rate(mixed $values, $dates, mixed $guess = self::DEFAULT_GUESS): float|string
|
||||
{
|
||||
$rslt = self::xirrPart1($values, $dates);
|
||||
/** @var array<int, float|int|numeric-string> $dates */
|
||||
if ($rslt !== '') {
|
||||
return $rslt;
|
||||
}
|
||||
@@ -91,6 +93,7 @@ class NonPeriodic
|
||||
$x2 += 0.5;
|
||||
}
|
||||
if ($found) {
|
||||
/** @var array<int, float|int|numeric-string> $dates */
|
||||
return self::xirrBisection($values, $dates, $x1, $x2);
|
||||
}
|
||||
|
||||
@@ -106,18 +109,18 @@ class NonPeriodic
|
||||
* Excel Function:
|
||||
* =XNPV(rate,values,dates)
|
||||
*
|
||||
* @param array|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.
|
||||
* @param mixed $rate the discount rate to apply to the cash flows, expect array|float
|
||||
* @param array<int,float|int|numeric-string> $values A series of cash flows that corresponds to a schedule of payments in dates, expecting float[].
|
||||
* 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.
|
||||
* @param mixed $dates A schedule of payment dates that corresponds to the cash flow payments, expecting mixed[].
|
||||
* 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.
|
||||
*/
|
||||
public static function presentValue(array|float $rate, array $values, array $dates): float|string
|
||||
public static function presentValue(mixed $rate, mixed $values, mixed $dates): float|string
|
||||
{
|
||||
return self::xnpvOrdered($rate, $values, $dates, true);
|
||||
}
|
||||
@@ -127,9 +130,12 @@ class NonPeriodic
|
||||
return $neg && $pos;
|
||||
}
|
||||
|
||||
/** @param array<int, float|int|numeric-string> $values */
|
||||
private static function xirrPart1(mixed &$values, mixed &$dates): string
|
||||
{
|
||||
$values = Functions::flattenArray($values);
|
||||
/** @var array<int, float|int|numeric-string> */
|
||||
$temp = Functions::flattenArray($values);
|
||||
$values = $temp;
|
||||
$dates = Functions::flattenArray($dates);
|
||||
$valuesIsArray = count($values) > 1;
|
||||
$datesIsArray = count($dates) > 1;
|
||||
@@ -152,6 +158,7 @@ class NonPeriodic
|
||||
return self::xirrPart2($values);
|
||||
}
|
||||
|
||||
/** @param array<int, float|int|numeric-string> $values */
|
||||
private static function xirrPart2(array &$values): string
|
||||
{
|
||||
$valCount = count($values);
|
||||
@@ -159,7 +166,7 @@ class NonPeriodic
|
||||
$foundneg = false;
|
||||
for ($i = 0; $i < $valCount; ++$i) {
|
||||
$fld = $values[$i];
|
||||
if (!is_numeric($fld)) {
|
||||
if (!is_numeric($fld)) { //* @phpstan-ignore-line
|
||||
return ExcelError::VALUE();
|
||||
} elseif ($fld > 0) {
|
||||
$foundpos = true;
|
||||
@@ -174,6 +181,10 @@ class NonPeriodic
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, float|int|numeric-string> $values
|
||||
* @param array<int, float|int|numeric-string> $dates
|
||||
*/
|
||||
private static function xirrPart3(array $values, array $dates, float $x1, float $x2): float|string
|
||||
{
|
||||
$f = self::xnpvOrdered($x1, $values, $dates, false);
|
||||
@@ -203,6 +214,10 @@ class NonPeriodic
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, float|int|numeric-string> $values
|
||||
* @param array<int, float|int|numeric-string> $dates
|
||||
*/
|
||||
private static function xirrBisection(array $values, array $dates, float $x1, float $x2): string|float
|
||||
{
|
||||
$rslt = ExcelError::NAN();
|
||||
@@ -239,9 +254,13 @@ class NonPeriodic
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
/** @param array<int,float|int|numeric-string> $values> */
|
||||
private static function xnpvOrdered(mixed $rate, mixed $values, mixed $dates, bool $ordered = true, bool $capAtNegative1 = false): float|string
|
||||
{
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
if (!is_numeric($rate)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
$values = Functions::flattenArray($values);
|
||||
$dates = Functions::flattenArray($dates);
|
||||
$valCount = count($values);
|
||||
@@ -273,10 +292,10 @@ class NonPeriodic
|
||||
$dif = Functions::scalar(DateTimeExcel\Difference::interval($date0, $datei, 'd'));
|
||||
}
|
||||
if (!is_numeric($dif)) {
|
||||
return $dif;
|
||||
return StringHelper::convertToString($dif);
|
||||
}
|
||||
if ($rate <= -1.0) {
|
||||
$xnpv += -abs($values[$i]) / (-1 - $rate) ** ($dif / 365);
|
||||
$xnpv += -abs($values[$i] + 0) / (-1 - $rate) ** ($dif / 365);
|
||||
} else {
|
||||
$xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
|
||||
}
|
||||
@@ -285,6 +304,10 @@ class NonPeriodic
|
||||
return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $values
|
||||
* @param mixed[] $dates
|
||||
*/
|
||||
private static function validateXnpv(mixed $rate, array $values, array $dates): void
|
||||
{
|
||||
if (!is_numeric($rate)) {
|
||||
@@ -294,7 +317,7 @@ class NonPeriodic
|
||||
if ($valCount != count($dates)) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
|
||||
if (count($values) > 1 && ((min($values) > 0) || (max($values) < 0))) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ class Periodic
|
||||
}
|
||||
$values = Functions::flattenArray($values);
|
||||
$guess = Functions::flattenSingleValue($guess);
|
||||
if (!is_numeric($guess)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
// create an initial range, with a root somewhere between 0 and guess
|
||||
$x1 = 0.0;
|
||||
@@ -103,7 +106,9 @@ class Periodic
|
||||
return ExcelError::DIV0();
|
||||
}
|
||||
$values = Functions::flattenArray($values);
|
||||
/** @var float */
|
||||
$financeRate = Functions::flattenSingleValue($financeRate);
|
||||
/** @var float */
|
||||
$reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate);
|
||||
$n = count($values);
|
||||
|
||||
@@ -112,6 +117,7 @@ class Periodic
|
||||
|
||||
$npvPos = $npvNeg = 0.0;
|
||||
foreach ($values as $i => $v) {
|
||||
/** @var float $v */
|
||||
if ($v >= 0) {
|
||||
$npvPos += $v / $rr ** $i;
|
||||
} else {
|
||||
@@ -134,12 +140,13 @@ class Periodic
|
||||
*
|
||||
* Returns the Net Present Value of a cash flow series given a discount rate.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array<mixed> $args
|
||||
*/
|
||||
public static function presentValue(mixed $rate, ...$args): int|float
|
||||
{
|
||||
$returnValue = 0;
|
||||
|
||||
/** @var float */
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
|
||||
@@ -49,9 +49,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -110,9 +108,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -179,9 +175,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -244,9 +238,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -296,9 +288,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
@@ -355,9 +345,7 @@ class Coupons
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = FinancialValidations::validateSettlementDate($settlement);
|
||||
|
||||
@@ -25,7 +25,7 @@ class Dollar
|
||||
* If you omit precision, it is assumed to be 2
|
||||
* Or can be an array of precision values
|
||||
*
|
||||
* @return array|string If an array of values is passed for either of the arguments, then the returned result
|
||||
* @return array<mixed>|string If an array of values is passed for either of the arguments, then the returned result
|
||||
* will also be an array with matching dimensions
|
||||
*/
|
||||
public static function format(mixed $number, mixed $precision = 2)
|
||||
@@ -47,6 +47,8 @@ class Dollar
|
||||
* Or can be an array of values
|
||||
* @param mixed $fraction Fraction
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array<mixed>|float|string
|
||||
*/
|
||||
public static function decimal(mixed $fractionalDollar = null, mixed $fraction = 0): array|string|float
|
||||
{
|
||||
@@ -93,6 +95,8 @@ class Dollar
|
||||
* Or can be an array of values
|
||||
* @param mixed $fraction Fraction
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array<mixed>|float|string
|
||||
*/
|
||||
public static function fractional(mixed $decimalDollar = null, mixed $fraction = 0): array|string|float
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ class Helpers
|
||||
*
|
||||
* 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 mixed $year The year against which we're testing, expect int|string
|
||||
* @param int|string $basis The type of day count:
|
||||
* 0 or omitted US (NASD) 360
|
||||
* 1 Actual (365 or 366 in a leap year)
|
||||
@@ -24,8 +24,11 @@ class Helpers
|
||||
*
|
||||
* @return int|string Result, or a string containing an error
|
||||
*/
|
||||
public static function daysPerYear($year, $basis = 0): string|int
|
||||
public static function daysPerYear(mixed $year, $basis = 0): string|int
|
||||
{
|
||||
if (!is_int($year) && !is_string($year)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
if (!is_numeric($basis)) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\YearFrac;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class AccruedInterest
|
||||
{
|
||||
@@ -59,12 +60,8 @@ class AccruedInterest
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
|
||||
$frequency = ($frequency === null)
|
||||
? FinancialConstants::FREQUENCY_ANNUAL
|
||||
: Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$frequency = Functions::flattenSingleValue($frequency) ?? FinancialConstants::FREQUENCY_ANNUAL;
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$issue = SecurityValidations::validateIssueDate($issue);
|
||||
@@ -81,12 +78,12 @@ class AccruedInterest
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
$daysBetweenFirstInterestAndSettlement = Functions::scalar(YearFrac::fraction($firstInterest, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenFirstInterestAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenFirstInterestAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenFirstInterestAndSettlement);
|
||||
}
|
||||
|
||||
return $parValue * $rate * $daysBetweenIssueAndSettlement;
|
||||
@@ -125,9 +122,7 @@ class AccruedInterest
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$issue = SecurityValidations::validateIssueDate($issue);
|
||||
@@ -143,7 +138,7 @@ class AccruedInterest
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
|
||||
return $parValue * $rate * $daysBetweenIssueAndSettlement;
|
||||
|
||||
@@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Price
|
||||
{
|
||||
@@ -52,9 +53,7 @@ class Price
|
||||
$yield = Functions::flattenSingleValue($yield);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$frequency = Functions::flattenSingleValue($frequency);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -119,9 +118,7 @@ class Price
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$discount = Functions::flattenSingleValue($discount);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -137,7 +134,7 @@ class Price
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
|
||||
@@ -178,9 +175,7 @@ class Price
|
||||
$issue = Functions::flattenSingleValue($issue);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$yield = Functions::flattenSingleValue($yield);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -201,19 +196,19 @@ class Price
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
$daysBetweenIssueAndSettlement *= $daysPerYear;
|
||||
$daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndMaturity);
|
||||
}
|
||||
$daysBetweenIssueAndMaturity *= $daysPerYear;
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
@@ -254,9 +249,7 @@ class Price
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$investment = Functions::flattenSingleValue($investment);
|
||||
$discount = Functions::flattenSingleValue($discount);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -275,7 +268,7 @@ class Price
|
||||
$daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return Functions::scalar($daysBetweenSettlementAndMaturity);
|
||||
return StringHelper::convertToString(Functions::scalar($daysBetweenSettlementAndMaturity));
|
||||
}
|
||||
|
||||
return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
|
||||
|
||||
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Rates
|
||||
{
|
||||
@@ -43,9 +44,7 @@ class Rates
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -65,7 +64,7 @@ class Rates
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
|
||||
@@ -104,9 +103,7 @@ class Rates
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$investment = Functions::flattenSingleValue($investment);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -126,7 +123,7 @@ class Rates
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
|
||||
|
||||
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Yields
|
||||
{
|
||||
@@ -42,9 +43,7 @@ class Yields
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -64,7 +63,7 @@ class Yields
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
@@ -106,9 +105,7 @@ class Yields
|
||||
$issue = Functions::flattenSingleValue($issue);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$basis = ($basis === null)
|
||||
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
||||
: Functions::flattenSingleValue($basis);
|
||||
$basis = Functions::flattenSingleValue($basis) ?? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD;
|
||||
|
||||
try {
|
||||
$settlement = SecurityValidations::validateSettlementDate($settlement);
|
||||
@@ -129,19 +126,19 @@ class Yields
|
||||
$daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndSettlement);
|
||||
}
|
||||
$daysBetweenIssueAndSettlement *= $daysPerYear;
|
||||
$daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenIssueAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenIssueAndMaturity);
|
||||
}
|
||||
$daysBetweenIssueAndMaturity *= $daysPerYear;
|
||||
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ class FormulaParser
|
||||
// scientific notation check
|
||||
if (str_contains(self::OPERATORS_SN, $this->formula[$index])) {
|
||||
if (strlen($value) > 1) {
|
||||
if (preg_match('/^[1-9]{1}(\\.\\d+)?E{1}$/', $this->formula[$index]) != 0) {
|
||||
if (preg_match('/^[1-9]{1}(\.\d+)?E{1}$/', $this->formula[$index]) != 0) {
|
||||
$value .= $this->formula[$index];
|
||||
++$index;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Functions
|
||||
{
|
||||
@@ -130,16 +131,22 @@ class Functions
|
||||
|
||||
public static function isMatrixValue(mixed $idx): bool
|
||||
{
|
||||
$idx = StringHelper::convertToString($idx);
|
||||
|
||||
return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
|
||||
}
|
||||
|
||||
public static function isValue(mixed $idx): bool
|
||||
{
|
||||
$idx = StringHelper::convertToString($idx);
|
||||
|
||||
return substr_count($idx, '.') === 0;
|
||||
}
|
||||
|
||||
public static function isCellValue(mixed $idx): bool
|
||||
{
|
||||
$idx = StringHelper::convertToString($idx);
|
||||
|
||||
return substr_count($idx, '.') > 1;
|
||||
}
|
||||
|
||||
@@ -154,7 +161,8 @@ class Functions
|
||||
$condition = self::operandSpecialHandling($condition);
|
||||
if (is_bool($condition)) {
|
||||
return '=' . ($condition ? 'TRUE' : 'FALSE');
|
||||
} elseif (!is_numeric($condition)) {
|
||||
}
|
||||
if (!is_numeric($condition)) {
|
||||
if ($condition !== '""') { // Not an empty string
|
||||
// Escape any quotes in the string value
|
||||
$condition = (string) preg_replace('/"/ui', '""', $condition);
|
||||
@@ -162,27 +170,32 @@ class Functions
|
||||
$condition = Calculation::wrapResult(strtoupper($condition));
|
||||
}
|
||||
|
||||
return str_replace('""""', '""', '=' . $condition);
|
||||
return str_replace('""""', '""', '=' . StringHelper::convertToString($condition));
|
||||
}
|
||||
$operator = $operand = '';
|
||||
if (1 === preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches)) {
|
||||
[, $operator, $operand] = $matches;
|
||||
}
|
||||
preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
|
||||
[, $operator, $operand] = $matches;
|
||||
|
||||
$operand = self::operandSpecialHandling($operand);
|
||||
$operand = (string) self::operandSpecialHandling($operand);
|
||||
if (is_numeric(trim($operand, '"'))) {
|
||||
$operand = trim($operand, '"');
|
||||
} elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
|
||||
$operand = str_replace('"', '""', $operand);
|
||||
$operand = Calculation::wrapResult(strtoupper($operand));
|
||||
$operand = StringHelper::convertToString($operand);
|
||||
}
|
||||
|
||||
return str_replace('""""', '""', $operator . $operand);
|
||||
}
|
||||
|
||||
private static function operandSpecialHandling(mixed $operand): mixed
|
||||
private static function operandSpecialHandling(mixed $operand): bool|float|int|string
|
||||
{
|
||||
if (is_numeric($operand) || is_bool($operand)) {
|
||||
return $operand;
|
||||
} elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
|
||||
}
|
||||
$operand = StringHelper::convertToString($operand);
|
||||
if (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
|
||||
return strtoupper($operand);
|
||||
}
|
||||
|
||||
@@ -204,7 +217,7 @@ class Functions
|
||||
*
|
||||
* @param mixed $array Array to be flattened
|
||||
*
|
||||
* @return array Flattened array
|
||||
* @return array<mixed> Flattened array
|
||||
*/
|
||||
public static function flattenArray(mixed $array): array
|
||||
{
|
||||
@@ -228,6 +241,32 @@ class Functions
|
||||
return $flattened;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a multi-dimensional array to a simple 1-dimensional array.
|
||||
* Same as above but argument is specified in ... format.
|
||||
*
|
||||
* @param mixed $array Array to be flattened
|
||||
*
|
||||
* @return array<mixed> Flattened array
|
||||
*/
|
||||
public static function flattenArray2(mixed ...$array): array
|
||||
{
|
||||
$flattened = [];
|
||||
$stack = array_values($array);
|
||||
|
||||
while (!empty($stack)) {
|
||||
$value = array_shift($stack);
|
||||
|
||||
if (is_array($value)) {
|
||||
array_unshift($stack, ...array_values($value));
|
||||
} else {
|
||||
$flattened[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $flattened;
|
||||
}
|
||||
|
||||
public static function scalar(mixed $value): mixed
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
@@ -246,7 +285,7 @@ class Functions
|
||||
*
|
||||
* @param array|mixed $array Array to be flattened
|
||||
*
|
||||
* @return array Flattened array
|
||||
* @return array<mixed> Flattened array
|
||||
*/
|
||||
public static function flattenArrayIndexed($array): array
|
||||
{
|
||||
@@ -310,7 +349,7 @@ class Functions
|
||||
|
||||
public static function trimTrailingRange(string $coordinate): string
|
||||
{
|
||||
return (string) preg_replace('/:[\\w\$]+$/', '', $coordinate);
|
||||
return (string) preg_replace('/:[\w\$]+$/', '', $coordinate);
|
||||
}
|
||||
|
||||
public static function trimSheetFromCellReference(string $coordinate): string
|
||||
|
||||
@@ -15,7 +15,7 @@ class ErrorValue
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isErr(mixed $value = ''): array|bool
|
||||
@@ -33,7 +33,7 @@ class ErrorValue
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isError(mixed $value = '', bool $tryNotImplemented = false): array|bool
|
||||
@@ -58,7 +58,7 @@ class ErrorValue
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isNa(mixed $value = ''): array|bool
|
||||
|
||||
@@ -39,6 +39,8 @@ class ExcelError
|
||||
* ERROR_TYPE.
|
||||
*
|
||||
* @param mixed $value Value to check
|
||||
*
|
||||
* @return array<mixed>|int|string
|
||||
*/
|
||||
public static function type(mixed $value = ''): array|int|string
|
||||
{
|
||||
@@ -152,4 +154,14 @@ class ExcelError
|
||||
{
|
||||
return self::ERROR_CODES['calculation'];
|
||||
}
|
||||
|
||||
/**
|
||||
* SPILL.
|
||||
*
|
||||
* @return string #SPILL!
|
||||
*/
|
||||
public static function SPILL(): string
|
||||
{
|
||||
return self::ERROR_CODES['spill'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\NamedRange;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class Value
|
||||
@@ -20,7 +22,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isBlank(mixed $value = null): array|bool
|
||||
@@ -39,13 +41,14 @@ class Value
|
||||
*/
|
||||
public static function isRef(mixed $value, ?Cell $cell = null): bool
|
||||
{
|
||||
if ($cell === null || $value === $cell->getCoordinate()) {
|
||||
if ($cell === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = StringHelper::convertToString($value);
|
||||
$cellValue = Functions::trimTrailingRange($value);
|
||||
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) {
|
||||
[$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true);
|
||||
[$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true, true);
|
||||
if (!empty($worksheet) && $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheet) === null) {
|
||||
return false;
|
||||
}
|
||||
@@ -68,7 +71,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isEven(mixed $value = null): array|string|bool
|
||||
@@ -79,11 +82,12 @@ class Value
|
||||
|
||||
if ($value === null) {
|
||||
return ExcelError::NAME();
|
||||
} elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
return ((int) fmod($value, 2)) === 0;
|
||||
return ((int) fmod($value + 0, 2)) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +96,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isOdd(mixed $value = null): array|string|bool
|
||||
@@ -103,11 +107,12 @@ class Value
|
||||
|
||||
if ($value === null) {
|
||||
return ExcelError::NAME();
|
||||
} elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
return ((int) fmod($value, 2)) !== 0;
|
||||
return ((int) fmod($value + 0, 2)) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +121,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isNumber(mixed $value = null): array|bool
|
||||
@@ -138,7 +143,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isLogical(mixed $value = null): array|bool
|
||||
@@ -156,7 +161,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isText(mixed $value = null): array|bool
|
||||
@@ -174,7 +179,7 @@ class Value
|
||||
* @param mixed $value Value to check
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* @return array<mixed>|bool If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function isNonText(mixed $value = null): array|bool
|
||||
@@ -191,14 +196,17 @@ class Value
|
||||
*
|
||||
* @param mixed $cellReference The cell to check
|
||||
* @param ?Cell $cell The current cell (containing this formula)
|
||||
*
|
||||
* @return array<mixed>|bool|string
|
||||
*/
|
||||
public static function isFormula(mixed $cellReference = '', ?Cell $cell = null): array|bool|string
|
||||
{
|
||||
if ($cell === null) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
$cellReference = StringHelper::convertToString($cellReference);
|
||||
|
||||
$fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
|
||||
$fullCellReference = Functions::expandDefinedName($cellReference, $cell);
|
||||
|
||||
if (str_contains($cellReference, '!')) {
|
||||
$cellReference = Functions::trimSheetFromCellReference($cellReference);
|
||||
@@ -210,16 +218,24 @@ class Value
|
||||
|
||||
$fullCellReference = Functions::trimTrailingRange($fullCellReference);
|
||||
|
||||
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
|
||||
|
||||
$fullCellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
|
||||
$worksheetName = '';
|
||||
if (1 == preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches)) {
|
||||
$fullCellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
|
||||
}
|
||||
|
||||
$worksheet = (!empty($worksheetName))
|
||||
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
|
||||
: $cell->getWorksheet();
|
||||
if ($worksheet === null) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
|
||||
return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
|
||||
try {
|
||||
return $worksheet->getCell($fullCellReference)->isFormula();
|
||||
} catch (SpreadsheetException) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,21 +259,14 @@ class Value
|
||||
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 (($value !== '') && ($value[0] == '#')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
break;
|
||||
if (is_float($value) || is_int($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (is_bool($value)) {
|
||||
return (int) $value;
|
||||
}
|
||||
if (is_string($value) && substr($value, 0, 1) === '#') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -281,7 +290,7 @@ class Value
|
||||
public static function type($value = null): int
|
||||
{
|
||||
$value = Functions::flattenArrayIndexed($value);
|
||||
if (is_array($value) && (count($value) > 1)) {
|
||||
if (count($value) > 1) {
|
||||
end($value);
|
||||
$a = key($value);
|
||||
// Range of cells is an error
|
||||
|
||||
@@ -4,7 +4,11 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Internal;
|
||||
|
||||
class MakeMatrix
|
||||
{
|
||||
/** @param array $args */
|
||||
/**
|
||||
* @param mixed[] $args
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function make(...$args): array
|
||||
{
|
||||
return $args;
|
||||
|
||||
@@ -6,10 +6,10 @@ class WildcardMatch
|
||||
{
|
||||
private const SEARCH_SET = [
|
||||
'~~', // convert double tilde to unprintable value
|
||||
'~\\*', // convert tilde backslash asterisk to [*] (matches literal asterisk in regexp)
|
||||
'\\*', // convert backslash asterisk to .* (matches string of any length in regexp)
|
||||
'~\\?', // convert tilde backslash question to [?] (matches literal question mark in regexp)
|
||||
'\\?', // convert backslash question to . (matches one character in regexp)
|
||||
'~\*', // convert tilde backslash asterisk to [*] (matches literal asterisk in regexp)
|
||||
'\*', // convert backslash asterisk to .* (matches string of any length in regexp)
|
||||
'~\?', // convert tilde backslash question to [?] (matches literal question mark in regexp)
|
||||
'\?', // convert backslash question to . (matches one character in regexp)
|
||||
"\x1c", // convert original double tilde to single tilde
|
||||
];
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class Operations
|
||||
* @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|bool|string the boolean inverse of the argument
|
||||
* @return array<mixed>|bool|string the boolean inverse of the argument
|
||||
* If an array of values is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -130,6 +130,10 @@ class Operations
|
||||
return !$logical;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $args
|
||||
* @param callable(int, int): bool $func
|
||||
*/
|
||||
private static function countTrueValues(array $args, callable $func): bool|string
|
||||
{
|
||||
$trueValueCount = 0;
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Address
|
||||
{
|
||||
@@ -44,7 +45,7 @@ class Address
|
||||
* @param mixed $sheetName Optional Name of worksheet to use
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of values is passed as the $testValue argument, then the returned result will also be
|
||||
* @return mixed[]|string If an array of values is passed as the $testValue argument, then the returned result will also be
|
||||
* an array with the same dimensions
|
||||
*/
|
||||
public static function cell(mixed $row, mixed $column, mixed $relativity = 1, mixed $referenceStyle = true, mixed $sheetName = ''): array|string
|
||||
@@ -63,14 +64,16 @@ class Address
|
||||
);
|
||||
}
|
||||
|
||||
$relativity = $relativity ?? 1;
|
||||
$relativity = ($relativity === null) ? 1 : (int) StringHelper::convertToString($relativity);
|
||||
$referenceStyle = $referenceStyle ?? true;
|
||||
$row = (int) StringHelper::convertToString($row);
|
||||
$column = (int) StringHelper::convertToString($column);
|
||||
|
||||
if (($row < 1) || ($column < 1)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
$sheetName = self::sheetName($sheetName);
|
||||
$sheetName = self::sheetName(StringHelper::convertToString($sheetName));
|
||||
|
||||
if (is_int($referenceStyle)) {
|
||||
$referenceStyle = (bool) $referenceStyle;
|
||||
|
||||
@@ -30,7 +30,7 @@ class ExcelMatch
|
||||
* @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 array|float|int|string The relative position of the found item
|
||||
* @return array<mixed>|float|int|string The relative position of the found item
|
||||
*/
|
||||
public static function MATCH(mixed $lookupValue, mixed $lookupArray, mixed $matchType = self::MATCHTYPE_LARGEST_VALUE): array|string|int|float
|
||||
{
|
||||
@@ -70,13 +70,14 @@ class ExcelMatch
|
||||
};
|
||||
|
||||
if ($valueKey !== null) {
|
||||
return ++$valueKey;
|
||||
return ++$valueKey; //* @phpstan-ignore-line
|
||||
}
|
||||
|
||||
// Unsuccessful in finding a match, return #N/A error value
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupArray */
|
||||
private static function matchFirstValue(array $lookupArray, mixed $lookupValue): int|string|null
|
||||
{
|
||||
if (is_string($lookupValue)) {
|
||||
@@ -113,6 +114,10 @@ class ExcelMatch
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param mixed[] $keySet
|
||||
*/
|
||||
private static function matchLargestValue(array $lookupArray, mixed $lookupValue, array $keySet): mixed
|
||||
{
|
||||
if (is_string($lookupValue)) {
|
||||
@@ -147,6 +152,7 @@ class ExcelMatch
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupArray */
|
||||
private static function matchSmallestValue(array $lookupArray, mixed $lookupValue): int|string|null
|
||||
{
|
||||
$valueKey = null;
|
||||
@@ -215,6 +221,7 @@ class ExcelMatch
|
||||
return self::MATCHTYPE_FIRST_VALUE;
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupArray */
|
||||
private static function validateLookupArray(array $lookupArray): void
|
||||
{
|
||||
// Lookup_array should not be empty
|
||||
@@ -224,6 +231,11 @@ class ExcelMatch
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function prepareLookupArray(array $lookupArray, mixed $matchType): array
|
||||
{
|
||||
// Lookup_array should contain only number, text, or logical values, or empty (null) cells
|
||||
|
||||
@@ -6,8 +6,12 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class Filter
|
||||
{
|
||||
public static function filter(array $lookupArray, mixed $matchArray, mixed $ifEmpty = null): mixed
|
||||
public static function filter(mixed $lookupArray, mixed $matchArray, mixed $ifEmpty = null): mixed
|
||||
{
|
||||
if (!is_array($lookupArray)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
/** @var mixed[] $lookupArray */
|
||||
if (!is_array($matchArray)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
@@ -21,10 +25,17 @@ class Filter
|
||||
if (empty($result)) {
|
||||
return $ifEmpty ?? ExcelError::CALC();
|
||||
}
|
||||
/** @var callable(mixed): mixed */
|
||||
$func = 'array_values';
|
||||
|
||||
return array_values(array_map('array_values', $result));
|
||||
return array_values(array_map($func, $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function enumerateArrayKeys(array $sortArray): array
|
||||
{
|
||||
array_walk(
|
||||
@@ -39,17 +50,29 @@ class Filter
|
||||
return array_values($sortArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param mixed[] $matchArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function filterByRow(array $lookupArray, array $matchArray): array
|
||||
{
|
||||
$matchArray = array_values(array_column($matchArray, 0));
|
||||
$matchArray = array_values(array_column($matchArray, 0)); // @phpstan-ignore-line
|
||||
|
||||
return array_filter(
|
||||
array_values($lookupArray),
|
||||
fn ($index): bool => (bool) $matchArray[$index],
|
||||
fn ($index): bool => (bool) ($matchArray[$index] ?? null),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param mixed[] $matchArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function filterByColumn(array $lookupArray, array $matchArray): array
|
||||
{
|
||||
$lookupArray = Matrix::transpose($lookupArray);
|
||||
@@ -57,7 +80,7 @@ class Filter
|
||||
if (count($matchArray) === 1) {
|
||||
$matchArray = array_pop($matchArray);
|
||||
}
|
||||
|
||||
/** @var mixed[] $matchArray */
|
||||
array_walk(
|
||||
$matchArray,
|
||||
function (&$value): void {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Formula
|
||||
{
|
||||
@@ -20,13 +21,15 @@ class Formula
|
||||
return ExcelError::REF();
|
||||
}
|
||||
|
||||
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
|
||||
|
||||
$cellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = trim($matches[3], "'");
|
||||
$worksheet = (!empty($worksheetName))
|
||||
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
|
||||
: $cell->getWorksheet();
|
||||
$worksheet = null;
|
||||
$cellReference = StringHelper::convertToString($cellReference);
|
||||
if (1 === preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches)) {
|
||||
$cellReference = $matches[6] . $matches[7];
|
||||
$worksheetName = trim($matches[3], "'");
|
||||
$worksheet = (!empty($worksheetName))
|
||||
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
|
||||
: $cell->getWorksheet();
|
||||
}
|
||||
|
||||
if (
|
||||
$worksheet === null
|
||||
@@ -36,6 +39,6 @@ class Formula
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
return $worksheet->getCell($cellReference)->getValue();
|
||||
return $worksheet->getCell($cellReference)->getValueString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ class HLookup extends LookupBase
|
||||
* in the same column based on the index_number.
|
||||
*
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed $lookupArray The range of cells being searched
|
||||
* @param mixed $indexNumber The row number in table_array from which the matching value must be returned.
|
||||
* @param mixed[][] $lookupArray The range of cells being searched
|
||||
* @param array<mixed>|float|int|string $indexNumber The row number in table_array from which the matching value must be returned.
|
||||
* The first row is 1.
|
||||
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
|
||||
*
|
||||
* @return mixed The value of the found cell
|
||||
*/
|
||||
public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
|
||||
public static function lookup(mixed $lookupValue, $lookupArray, $indexNumber, mixed $notExactMatch = true): mixed
|
||||
{
|
||||
if (is_array($lookupValue) || is_array($indexNumber)) {
|
||||
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
|
||||
@@ -49,6 +49,7 @@ class HLookup extends LookupBase
|
||||
|
||||
$firstkey = $f[0] - 1;
|
||||
$returnColumn = $firstkey + $indexNumber;
|
||||
/** @var mixed[][] $lookupArray */
|
||||
$firstColumn = array_shift($f) ?? 1;
|
||||
$rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
|
||||
|
||||
@@ -62,17 +63,20 @@ class HLookup extends LookupBase
|
||||
|
||||
/**
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed[][] $lookupArray
|
||||
* @param int|string $column
|
||||
*/
|
||||
private static function hLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
|
||||
{
|
||||
$lookupLower = StringHelper::strToLower((string) $lookupValue);
|
||||
$lookupLower = StringHelper::strToLower(StringHelper::convertToString($lookupValue));
|
||||
|
||||
$rowNumber = null;
|
||||
foreach ($lookupArray[$column] as $rowKey => $rowData) {
|
||||
// break if we have passed possible keys
|
||||
/** @var string $rowKey */
|
||||
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
|
||||
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
|
||||
/** @var scalar $rowData */
|
||||
$cellDataLower = StringHelper::strToLower((string) $rowData);
|
||||
|
||||
if (
|
||||
@@ -96,6 +100,11 @@ class HLookup extends LookupBase
|
||||
return $rowNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function convertLiteralArray(array $lookupArray): array
|
||||
{
|
||||
if (array_key_exists(0, $lookupArray)) {
|
||||
|
||||
@@ -35,6 +35,7 @@ class Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array{string, ?string, string} */
|
||||
public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = '', ?int $baseRow = null, ?int $baseCol = null): array
|
||||
{
|
||||
$cellAddress1 = $cellAddress;
|
||||
@@ -57,12 +58,12 @@ class Helpers
|
||||
return [$cellAddress1, $cellAddress2, $cellAddress];
|
||||
}
|
||||
|
||||
/** @return array{string, ?Worksheet, string} */
|
||||
public static function extractWorksheet(string $cellAddress, Cell $cell): array
|
||||
{
|
||||
$sheetName = '';
|
||||
if (str_contains($cellAddress, '!')) {
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true, true);
|
||||
}
|
||||
|
||||
$worksheet = ($sheetName !== '')
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Hyperlink
|
||||
{
|
||||
@@ -22,18 +23,23 @@ class Hyperlink
|
||||
*/
|
||||
public static function set(mixed $linkURL = '', mixed $displayName = null, ?Cell $cell = null): string
|
||||
{
|
||||
$linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
|
||||
$linkURL = ($linkURL === null) ? '' : StringHelper::convertToString(Functions::flattenSingleValue($linkURL));
|
||||
$displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
|
||||
|
||||
if ((!is_object($cell)) || (trim($linkURL) == '')) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
|
||||
if ((is_object($displayName)) || trim($displayName) == '') {
|
||||
if (is_object($displayName)) {
|
||||
$displayName = $linkURL;
|
||||
}
|
||||
$displayName = StringHelper::convertToString($displayName);
|
||||
if (trim($displayName) === '') {
|
||||
$displayName = $linkURL;
|
||||
}
|
||||
|
||||
$cell->getHyperlink()->setUrl($linkURL);
|
||||
$cell->getHyperlink()
|
||||
->setUrl($linkURL);
|
||||
$cell->getHyperlink()->setTooltip($displayName);
|
||||
|
||||
return $displayName;
|
||||
|
||||
@@ -34,6 +34,8 @@ class Indirect
|
||||
|
||||
/**
|
||||
* Convert cellAddress to string, verify not null string.
|
||||
*
|
||||
* @param null|mixed[]|string $cellAddress
|
||||
*/
|
||||
private static function validateAddress(array|string|null $cellAddress): string
|
||||
{
|
||||
@@ -54,12 +56,12 @@ class Indirect
|
||||
* Excel Function:
|
||||
* =INDIRECT(cellAddress, bool) where the bool argument is optional
|
||||
*
|
||||
* @param array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
|
||||
* @param mixed[]|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
|
||||
* @param mixed $a1fmt Expect bool Helpers::CELLADDRESS_USE_A1 or CELLADDRESS_USE_R1C1,
|
||||
* but can be provided as numeric which is cast to bool
|
||||
* @param Cell $cell The current cell (containing this formula)
|
||||
*
|
||||
* @return array|string An array containing a cell or range of cells, or a string on error
|
||||
* @return mixed[]|string An array containing a cell or range of cells, or a string on error
|
||||
*/
|
||||
public static function INDIRECT($cellAddress, mixed $a1fmt, Cell $cell): string|array
|
||||
{
|
||||
@@ -99,13 +101,13 @@ class Indirect
|
||||
/**
|
||||
* Extract range values.
|
||||
*
|
||||
* @return array Array of values in range if range contains more than one element.
|
||||
* @return mixed[] Array of values in range if range contains more than one element.
|
||||
* Otherwise, a single value is returned.
|
||||
*/
|
||||
private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
|
||||
{
|
||||
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
|
||||
->extractCellRange($cellAddress, $worksheet, false);
|
||||
->extractCellRange($cellAddress, $worksheet, false, createCell: true);
|
||||
}
|
||||
|
||||
private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
|
||||
|
||||
@@ -28,6 +28,7 @@ class Lookup
|
||||
if (!is_array($lookupVector)) {
|
||||
return ExcelError::NA();
|
||||
}
|
||||
/** @var mixed[][] $lookupVector */
|
||||
$hasResultVector = isset($resultVector);
|
||||
$lookupRows = self::rowCount($lookupVector);
|
||||
$lookupColumns = self::columnCount($lookupVector);
|
||||
@@ -35,16 +36,19 @@ class Lookup
|
||||
if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
|
||||
$lookupVector = Matrix::transpose($lookupVector);
|
||||
$lookupRows = self::rowCount($lookupVector);
|
||||
/** @var mixed[][] $lookupVector */
|
||||
$lookupColumns = self::columnCount($lookupVector);
|
||||
}
|
||||
|
||||
$resultVector = self::verifyResultVector($resultVector ?? $lookupVector);
|
||||
$resultVector = self::verifyResultVector($resultVector ?? $lookupVector); //* @phpstan-ignore-line
|
||||
|
||||
if ($lookupRows === 2 && !$hasResultVector) {
|
||||
$resultVector = array_pop($lookupVector);
|
||||
$lookupVector = array_shift($lookupVector);
|
||||
}
|
||||
|
||||
/** @var mixed[] $lookupVector */
|
||||
/** @var mixed[] $resultVector */
|
||||
if ($lookupColumns !== 2) {
|
||||
$lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
|
||||
}
|
||||
@@ -52,6 +56,12 @@ class Lookup
|
||||
return VLookup::lookup($lookupValue, $lookupVector, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $lookupVector
|
||||
* @param mixed[] $resultVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function verifyLookupValues(array $lookupVector, array $resultVector): array
|
||||
{
|
||||
foreach ($lookupVector as &$value) {
|
||||
@@ -77,6 +87,11 @@ class Lookup
|
||||
return $lookupVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[][] $resultVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function verifyResultVector(array $resultVector): array
|
||||
{
|
||||
$resultRows = self::rowCount($resultVector);
|
||||
@@ -90,11 +105,13 @@ class Lookup
|
||||
return $resultVector;
|
||||
}
|
||||
|
||||
/** @param mixed[] $dataArray */
|
||||
private static function rowCount(array $dataArray): int
|
||||
{
|
||||
return count($dataArray);
|
||||
}
|
||||
|
||||
/** @param mixed[][] $dataArray */
|
||||
private static function columnCount(array $dataArray): int
|
||||
{
|
||||
$rowKeys = array_keys($dataArray);
|
||||
|
||||
@@ -7,15 +7,18 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
abstract class LookupBase
|
||||
{
|
||||
protected static function validateLookupArray(mixed $lookup_array): void
|
||||
protected static function validateLookupArray(mixed $lookupArray): void
|
||||
{
|
||||
if (!is_array($lookup_array)) {
|
||||
if (!is_array($lookupArray)) {
|
||||
throw new Exception(ExcelError::REF());
|
||||
}
|
||||
}
|
||||
|
||||
/** @param float|int|string $index_number */
|
||||
protected static function validateIndexLookup(array $lookup_array, $index_number): int
|
||||
/**
|
||||
* @param mixed[] $lookupArray
|
||||
* @param float|int|string $index_number number >= 1
|
||||
*/
|
||||
protected static function validateIndexLookup(array $lookupArray, $index_number): int
|
||||
{
|
||||
// index_number must be a number greater than or equal to 1.
|
||||
// Excel results are inconsistent when index is non-numeric.
|
||||
@@ -30,8 +33,8 @@ abstract class LookupBase
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
// index_number must be less than or equal to the number of columns in lookup_array
|
||||
if (empty($lookup_array)) {
|
||||
// index_number must be less than or equal to the number of columns in lookupArray
|
||||
if (empty($lookupArray)) {
|
||||
throw new Exception(ExcelError::REF());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class LookupRefValidations
|
||||
public static function validateInt(mixed $value): int
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
if (ErrorValue::isError($value)) {
|
||||
if (is_string($value) && ErrorValue::isError($value)) {
|
||||
throw new Exception($value);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ class Matrix
|
||||
|
||||
/**
|
||||
* Helper function; NOT an implementation of any Excel Function.
|
||||
*
|
||||
* @param mixed[] $values
|
||||
*/
|
||||
public static function isColumnVector(array $values): bool
|
||||
{
|
||||
@@ -20,6 +22,8 @@ class Matrix
|
||||
|
||||
/**
|
||||
* Helper function; NOT an implementation of any Excel Function.
|
||||
*
|
||||
* @param mixed[] $values
|
||||
*/
|
||||
public static function isRowVector(array $values): bool
|
||||
{
|
||||
@@ -30,7 +34,9 @@ class Matrix
|
||||
/**
|
||||
* TRANSPOSE.
|
||||
*
|
||||
* @param array|mixed $matrixData A matrix of values
|
||||
* @param mixed $matrixData A matrix of values
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function transpose($matrixData): array
|
||||
{
|
||||
@@ -38,8 +44,12 @@ class Matrix
|
||||
if (!is_array($matrixData)) {
|
||||
$matrixData = [[$matrixData]];
|
||||
}
|
||||
if (!is_array(end($matrixData))) {
|
||||
$matrixData = [$matrixData];
|
||||
}
|
||||
|
||||
$column = 0;
|
||||
/** @var mixed[][] $matrixData */
|
||||
foreach ($matrixData as $matrixRow) {
|
||||
$row = 0;
|
||||
foreach ($matrixRow as $matrixCell) {
|
||||
@@ -82,6 +92,15 @@ class Matrix
|
||||
|
||||
$rowNum = $rowNum ?? 0;
|
||||
$columnNum = $columnNum ?? 0;
|
||||
if (is_scalar($matrix)) {
|
||||
if ($rowNum === 0 || $rowNum === 1) {
|
||||
if ($columnNum === 0 || $columnNum === 1) {
|
||||
if ($columnNum === 1 || $rowNum === 1) {
|
||||
return $matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$rowNum = LookupRefValidations::validatePositiveInt($rowNum);
|
||||
@@ -106,7 +125,7 @@ class Matrix
|
||||
}
|
||||
|
||||
$rowKeys = array_keys($matrix);
|
||||
$columnKeys = @array_keys($matrix[$rowKeys[0]]);
|
||||
$columnKeys = @array_keys($matrix[$rowKeys[0]]); //* @phpstan-ignore-line
|
||||
|
||||
if ($columnNum > count($columnKeys)) {
|
||||
return ExcelError::REF();
|
||||
@@ -124,10 +143,15 @@ class Matrix
|
||||
);
|
||||
}
|
||||
$rowNum = $rowKeys[--$rowNum];
|
||||
/** @var mixed[][] $matrix */
|
||||
|
||||
return $matrix[$rowNum][$columnNum];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $matrix
|
||||
* @param mixed[] $rowKeys
|
||||
*/
|
||||
private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum): mixed
|
||||
{
|
||||
if ($rowNum === 0) {
|
||||
|
||||
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Validations;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class Offset
|
||||
@@ -24,28 +25,32 @@ class Offset
|
||||
* @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.
|
||||
* @param int $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
|
||||
* @param int $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.
|
||||
* @param ?int $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.
|
||||
* @param ?int $width The width, in number of columns, that you want the returned reference to be.
|
||||
* Width must be a positive number.
|
||||
*
|
||||
* @return array|string An array containing a cell or range of cells, or a string on error
|
||||
* @return array<mixed>|string An array containing a cell or range of cells, or a string on error
|
||||
*/
|
||||
public static function OFFSET(?string $cellAddress = null, mixed $rows = 0, mixed $columns = 0, mixed $height = null, mixed $width = null, ?Cell $cell = null): string|array
|
||||
public static function OFFSET(?string $cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null): string|array
|
||||
{
|
||||
/** @var int */
|
||||
$rows = Functions::flattenSingleValue($rows);
|
||||
/** @var int */
|
||||
$columns = Functions::flattenSingleValue($columns);
|
||||
/** @var int */
|
||||
$height = Functions::flattenSingleValue($height);
|
||||
/** @var int */
|
||||
$width = Functions::flattenSingleValue($width);
|
||||
|
||||
if ($cellAddress === null || $cellAddress === '') {
|
||||
@@ -55,6 +60,10 @@ class Offset
|
||||
if (!is_object($cell)) {
|
||||
return ExcelError::REF();
|
||||
}
|
||||
$sheet = $cell->getParent()?->getParent(); // worksheet
|
||||
if ($sheet !== null) {
|
||||
$cellAddress = Validations::definedNameToCoordinate($cellAddress, $sheet);
|
||||
}
|
||||
|
||||
[$cellAddress, $worksheet] = self::extractWorksheet($cellAddress, $cell);
|
||||
|
||||
@@ -62,12 +71,11 @@ class Offset
|
||||
if (strpos($cellAddress, ':')) {
|
||||
[$startCell, $endCell] = explode(':', $cellAddress);
|
||||
}
|
||||
[$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
|
||||
[$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
|
||||
[$startCellColumn, $startCellRow] = Coordinate::indexesFromString($startCell);
|
||||
[, $endCellRow, $endCellColumn] = Coordinate::indexesFromString($endCell);
|
||||
|
||||
$startCellRow += $rows;
|
||||
$startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
|
||||
$startCellColumn += $columns;
|
||||
$startCellColumn += $columns - 1;
|
||||
|
||||
if (($startCellRow <= 0) || ($startCellColumn < 0)) {
|
||||
return ExcelError::REF();
|
||||
@@ -91,20 +99,21 @@ class Offset
|
||||
return self::extractRequiredCells($worksheet, $cellAddress);
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
|
||||
{
|
||||
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
|
||||
->extractCellRange($cellAddress, $worksheet, false);
|
||||
}
|
||||
|
||||
/** @return array{string, ?Worksheet} */
|
||||
private static function extractWorksheet(?string $cellAddress, Cell $cell): array
|
||||
{
|
||||
$cellAddress = self::assessCellAddress($cellAddress ?? '', $cell);
|
||||
|
||||
$sheetName = '';
|
||||
if (str_contains($cellAddress, '!')) {
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true, true);
|
||||
}
|
||||
|
||||
$worksheet = ($sheetName !== '')
|
||||
@@ -123,7 +132,11 @@ class Offset
|
||||
return $cellAddress;
|
||||
}
|
||||
|
||||
private static function adjustEndCellColumnForWidth(string $endCellColumn, mixed $width, int $startCellColumn, mixed $columns): int
|
||||
/**
|
||||
* @param null|object|scalar $width
|
||||
* @param scalar $columns
|
||||
*/
|
||||
private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns): int
|
||||
{
|
||||
$endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
|
||||
if (($width !== null) && (!is_object($width))) {
|
||||
@@ -135,7 +148,11 @@ class Offset
|
||||
return $endCellColumn;
|
||||
}
|
||||
|
||||
private static function adustEndCellRowForHeight(mixed $height, int $startCellRow, mixed $rows, mixed $endCellRow): int
|
||||
/**
|
||||
* @param null|object|scalar $height
|
||||
* @param scalar $rows
|
||||
*/
|
||||
private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, int $endCellRow): int
|
||||
{
|
||||
if (($height !== null) && (!is_object($height))) {
|
||||
$endCellRow = $startCellRow + (int) $height - 1;
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class RowColumnInformation
|
||||
@@ -13,7 +15,7 @@ class RowColumnInformation
|
||||
/**
|
||||
* Test if cellAddress is null or whitespace string.
|
||||
*
|
||||
* @param null|array|string $cellAddress A reference to a range of cells
|
||||
* @param null|mixed[]|string $cellAddress A reference to a range of cells
|
||||
*/
|
||||
private static function cellAddressNullOrWhitespace($cellAddress): bool
|
||||
{
|
||||
@@ -38,11 +40,11 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =COLUMN([cellAddress])
|
||||
*
|
||||
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
|
||||
* @param null|mixed[]|string $cellAddress A reference to a range of cells for which you want the column numbers
|
||||
*
|
||||
* @return int|int[]
|
||||
* @return int|int[]|string
|
||||
*/
|
||||
public static function COLUMN($cellAddress = null, ?Cell $cell = null): int|array
|
||||
public static function COLUMN($cellAddress = null, ?Cell $cell = null): int|string|array
|
||||
{
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return self::cellColumn($cell);
|
||||
@@ -79,7 +81,11 @@ class RowColumnInformation
|
||||
|
||||
$cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
|
||||
|
||||
return Coordinate::columnIndexFromString($cellAddress);
|
||||
try {
|
||||
return Coordinate::columnIndexFromString($cellAddress);
|
||||
} catch (SpreadsheetException) {
|
||||
return ExcelError::NAME();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +96,7 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =COLUMNS(cellAddress)
|
||||
*
|
||||
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
|
||||
* @param null|mixed[]|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
|
||||
@@ -100,6 +106,9 @@ class RowColumnInformation
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return 1;
|
||||
}
|
||||
if (is_string($cellAddress) && ErrorValue::isError($cellAddress)) {
|
||||
return $cellAddress;
|
||||
}
|
||||
if (!is_array($cellAddress)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
@@ -115,9 +124,18 @@ class RowColumnInformation
|
||||
return $columns;
|
||||
}
|
||||
|
||||
private static function cellRow(?Cell $cell): int
|
||||
private static function cellRow(?Cell $cell): int|string
|
||||
{
|
||||
return ($cell !== null) ? $cell->getRow() : 1;
|
||||
return ($cell !== null) ? self::convert0ToName($cell->getRow()) : 1;
|
||||
}
|
||||
|
||||
private static function convert0ToName(int|string $result): int|string
|
||||
{
|
||||
if (is_int($result) && ($result <= 0 || $result > 1048576)) {
|
||||
return ExcelError::NAME();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,11 +151,11 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =ROW([cellAddress])
|
||||
*
|
||||
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
|
||||
* @param null|mixed[][]|string $cellAddress A reference to a range of cells for which you want the row numbers
|
||||
*
|
||||
* @return int|mixed[]
|
||||
* @return int|mixed[]|string
|
||||
*/
|
||||
public static function ROW($cellAddress = null, ?Cell $cell = null): int|array
|
||||
public static function ROW($cellAddress = null, ?Cell $cell = null): int|string|array
|
||||
{
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return self::cellRow($cell);
|
||||
@@ -172,7 +190,7 @@ class RowColumnInformation
|
||||
}
|
||||
[$cellAddress] = explode(':', $cellAddress);
|
||||
|
||||
return (int) preg_replace('/\D/', '', $cellAddress);
|
||||
return self::convert0ToName((int) preg_replace('/\D/', '', $cellAddress));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +201,7 @@ class RowColumnInformation
|
||||
* Excel Function:
|
||||
* =ROWS(cellAddress)
|
||||
*
|
||||
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
|
||||
* @param null|mixed[]|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
|
||||
@@ -193,6 +211,9 @@ class RowColumnInformation
|
||||
if (self::cellAddressNullOrWhitespace($cellAddress)) {
|
||||
return 1;
|
||||
}
|
||||
if (is_string($cellAddress) && ErrorValue::isError($cellAddress)) {
|
||||
return $cellAddress;
|
||||
}
|
||||
if (!is_array($cellAddress)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class Sort extends LookupRefValidations
|
||||
return $sortArray;
|
||||
}
|
||||
|
||||
/** @var mixed[][] */
|
||||
$sortArray = self::enumerateArrayKeys($sortArray);
|
||||
|
||||
$byColumn = (bool) $byColumn;
|
||||
@@ -43,7 +44,7 @@ class Sort extends LookupRefValidations
|
||||
|
||||
try {
|
||||
// If $sortIndex and $sortOrder are scalars, then convert them into arrays
|
||||
if (is_scalar($sortIndex)) {
|
||||
if (!is_array($sortIndex)) {
|
||||
$sortIndex = [$sortIndex];
|
||||
$sortOrder = is_scalar($sortOrder) ? [$sortOrder] : $sortOrder;
|
||||
}
|
||||
@@ -55,7 +56,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
// We want a simple, enumrated array of arrays where we can reference column by its index number.
|
||||
$sortArray = array_values(array_map('array_values', $sortArray));
|
||||
/** @var callable(mixed): mixed */
|
||||
$temp = 'array_values';
|
||||
/** @var array<int> $sortOrder */
|
||||
$sortArray = array_values(array_map($temp, $sortArray));
|
||||
/** @var int[] $sortIndex */
|
||||
|
||||
return ($byColumn === true)
|
||||
? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
|
||||
@@ -104,6 +109,11 @@ class Sort extends LookupRefValidations
|
||||
return self::processSortBy($sortArray, $sortBy, $sortOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function enumerateArrayKeys(array $sortArray): array
|
||||
{
|
||||
array_walk(
|
||||
@@ -133,6 +143,7 @@ class Sort extends LookupRefValidations
|
||||
$sortOrder = self::validateSortOrder($sortOrder);
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
private static function validateSortVector(mixed $sortVector, int $sortArraySize): array
|
||||
{
|
||||
if (!is_array($sortVector)) {
|
||||
@@ -158,6 +169,7 @@ class Sort extends LookupRefValidations
|
||||
return $sortOrder;
|
||||
}
|
||||
|
||||
/** @param mixed[] $sortIndex */
|
||||
private static function validateArrayArgumentsForSort(array &$sortIndex, mixed &$sortOrder, int $sortArraySize): void
|
||||
{
|
||||
// It doesn't matter if they're row or column vectors, it works either way
|
||||
@@ -184,6 +196,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function prepareSortVectorValues(array $sortVector): array
|
||||
{
|
||||
// Strings should be sorted case-insensitive; with booleans converted to locale-strings
|
||||
@@ -202,14 +219,19 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array[] $sortIndex
|
||||
* @param mixed[] $sortArray
|
||||
* @param mixed[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function processSortBy(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
$sortArguments = [];
|
||||
/** @var mixed[] */
|
||||
$sortData = [];
|
||||
foreach ($sortIndex as $index => $sortValues) {
|
||||
/** @var mixed[] $sortValues */
|
||||
$sortData[] = $sortValues;
|
||||
$sortArguments[] = self::prepareSortVectorValues($sortValues);
|
||||
$sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
|
||||
@@ -221,8 +243,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param int[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
@@ -232,8 +257,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param int[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
@@ -244,8 +272,11 @@ class Sort extends LookupRefValidations
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param int[] $sortIndex
|
||||
* @param int[] $sortOrder
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
|
||||
{
|
||||
@@ -263,6 +294,12 @@ class Sort extends LookupRefValidations
|
||||
return $sortData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortData
|
||||
* @param mixed[] $sortArguments
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
|
||||
{
|
||||
$sortData = Matrix::transpose($sortData);
|
||||
@@ -287,6 +324,12 @@ class Sort extends LookupRefValidations
|
||||
return $sortedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $sortArray
|
||||
* @param mixed[] $sortVector
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
|
||||
{
|
||||
// Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
|
||||
|
||||
@@ -33,17 +33,31 @@ class Unique
|
||||
: self::uniqueByRow($lookupVector, $exactlyOnce);
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupVector */
|
||||
private static function uniqueByRow(array $lookupVector, bool $exactlyOnce): mixed
|
||||
{
|
||||
// When not $byColumn, we count whole rows or values, not individual values
|
||||
// so implode each row into a single string value
|
||||
array_walk(
|
||||
$lookupVector,
|
||||
//* @phpstan-ignore-next-line
|
||||
function (array &$value): void {
|
||||
$value = implode(chr(0x00), $value);
|
||||
$valuex = '';
|
||||
$separator = '';
|
||||
$numericIndicator = "\x01";
|
||||
foreach ($value as $cellValue) {
|
||||
/** @var scalar $cellValue */
|
||||
$valuex .= $separator . $cellValue;
|
||||
$separator = "\x00";
|
||||
if (is_int($cellValue) || is_float($cellValue)) {
|
||||
$valuex .= $numericIndicator;
|
||||
}
|
||||
}
|
||||
$value = $valuex;
|
||||
}
|
||||
);
|
||||
|
||||
/** @var string[] $lookupVector */
|
||||
$result = self::countValuesCaseInsensitive($lookupVector);
|
||||
|
||||
if ($exactlyOnce === true) {
|
||||
@@ -60,15 +74,24 @@ class Unique
|
||||
array_walk(
|
||||
$result,
|
||||
function (string &$value): void {
|
||||
$value = explode(chr(0x00), $value);
|
||||
$value = explode("\x00", $value);
|
||||
foreach ($value as &$stringValue) {
|
||||
if (str_ends_with($stringValue, "\x01")) {
|
||||
// x01 should only end a string which is otherwise a float or int,
|
||||
// so phpstan is technically correct but what it fears should not happen.
|
||||
$stringValue = 0 + substr($stringValue, 0, -1); //@phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return (count($result) === 1) ? array_pop($result) : $result;
|
||||
}
|
||||
|
||||
/** @param mixed[] $lookupVector */
|
||||
private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce): mixed
|
||||
{
|
||||
/** @var string[] */
|
||||
$flattenedLookupVector = Functions::flattenArray($lookupVector);
|
||||
|
||||
if (count($lookupVector, COUNT_RECURSIVE) > count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
|
||||
@@ -94,6 +117,11 @@ class Unique
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $caseSensitiveLookupValues
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
|
||||
{
|
||||
$caseInsensitiveCounts = array_count_values(
|
||||
@@ -121,6 +149,11 @@ class Unique
|
||||
return $caseSensitiveCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $values
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private static function exactlyOnceFilter(array $values): array
|
||||
{
|
||||
return array_filter(
|
||||
|
||||
@@ -17,14 +17,14 @@ class VLookup extends LookupBase
|
||||
* in the same row based on the index_number.
|
||||
*
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed $lookupArray The range of cells being searched
|
||||
* @param mixed $indexNumber The column number in table_array from which the matching value must be returned.
|
||||
* @param mixed[] $lookupArray The range of cells being searched
|
||||
* @param array<mixed>|float|int|string $indexNumber The column number in table_array from which the matching value must be returned.
|
||||
* The first column is 1.
|
||||
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
|
||||
*
|
||||
* @return mixed The value of the found cell
|
||||
*/
|
||||
public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
|
||||
public static function lookup(mixed $lookupValue, $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
|
||||
{
|
||||
if (is_array($lookupValue) || is_array($indexNumber)) {
|
||||
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
|
||||
@@ -54,6 +54,7 @@ class VLookup extends LookupBase
|
||||
uasort($lookupArray, $callable);
|
||||
}
|
||||
|
||||
/** @var string[][] $lookupArray */
|
||||
$rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
|
||||
|
||||
if ($rowNumber !== null) {
|
||||
@@ -64,6 +65,10 @@ class VLookup extends LookupBase
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scalar[] $a
|
||||
* @param scalar[] $b
|
||||
*/
|
||||
private static function vlookupSort(array $a, array $b): int
|
||||
{
|
||||
reset($a);
|
||||
@@ -80,16 +85,17 @@ class VLookup extends LookupBase
|
||||
|
||||
/**
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param string[][] $lookupArray
|
||||
* @param int|string $column
|
||||
*/
|
||||
private static function vLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
|
||||
{
|
||||
$lookupLower = StringHelper::strToLower((string) $lookupValue);
|
||||
$lookupLower = StringHelper::strToLower(StringHelper::convertToString($lookupValue));
|
||||
|
||||
$rowNumber = null;
|
||||
foreach ($lookupArray as $rowKey => $rowData) {
|
||||
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
|
||||
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
|
||||
$bothNumeric = self::numeric($lookupValue) && self::numeric($rowData[$column]);
|
||||
$bothNotNumeric = !self::numeric($lookupValue) && !self::numeric($rowData[$column]);
|
||||
$cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
|
||||
|
||||
// break if we have passed possible keys
|
||||
@@ -114,4 +120,9 @@ class VLookup extends LookupBase
|
||||
|
||||
return $rowNumber;
|
||||
}
|
||||
|
||||
private static function numeric(mixed $value): bool
|
||||
{
|
||||
return is_int($value) || is_float($value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class Absolute
|
||||
*
|
||||
* @param mixed $number Should be numeric, or can be an array of numbers
|
||||
*
|
||||
* @return array|float|int|string rounded number
|
||||
* @return array<mixed>|float|int|string rounded number
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ class Angle
|
||||
*
|
||||
* @param mixed $number Should be numeric, or can be an array of numbers
|
||||
*
|
||||
* @return array|float|string Rounded number
|
||||
* @return array<mixed>|float|string Rounded number
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -42,7 +42,7 @@ class Angle
|
||||
*
|
||||
* @param mixed $number Should be numeric, or can be an array of numbers
|
||||
*
|
||||
* @return array|float|string Rounded number
|
||||
* @return array<mixed>|float|string Rounded number
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,8 @@ class Arabic
|
||||
|
||||
/**
|
||||
* Recursively calculate the arabic value of a roman numeral.
|
||||
*
|
||||
* @param mixed[] $roman
|
||||
*/
|
||||
private static function calculateArabic(array $roman, int &$sum = 0, int $subtract = 0): int
|
||||
{
|
||||
@@ -55,7 +57,7 @@ class Arabic
|
||||
*
|
||||
* @param string|string[] $roman Should be a string, or can be an array of strings
|
||||
*
|
||||
* @return array|int|string the arabic numberal contrived from the roman numeral
|
||||
* @return array<mixed>|int|string the arabic numberal contrived from the roman numeral
|
||||
* If an array of numbers is passed as the argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -74,11 +76,14 @@ class Arabic
|
||||
// Convert the roman numeral to an arabic number
|
||||
$negativeNumber = $roman[0] === '-';
|
||||
if ($negativeNumber) {
|
||||
$roman = substr($roman, 1);
|
||||
$roman = trim(substr($roman, 1));
|
||||
if ($roman === '') {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$arabic = self::calculateArabic(str_split($roman));
|
||||
$arabic = self::calculateArabic(mb_str_split($roman, 1, 'UTF-8'));
|
||||
} catch (Exception) {
|
||||
return ExcelError::VALUE(); // Invalid character detected
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class Base
|
||||
* @param mixed $minLength expect int or null
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string the text representation with the given radix (base)
|
||||
* @return array<mixed>|string the text representation with the given radix (base)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
|
||||
@@ -22,12 +22,12 @@ class Ceiling
|
||||
* Excel Function:
|
||||
* CEILING(number[,significance])
|
||||
*
|
||||
* @param array|float $number the number you want the ceiling
|
||||
* @param array<mixed>|float $number the number you want the ceiling
|
||||
* Or can be an array of values
|
||||
* @param array|float $significance the multiple to which you want to round
|
||||
* @param array<mixed>|float $significance the multiple to which you want to round
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Rounded Number, or a string containing an error
|
||||
* @return array<mixed>|float|string Rounded Number, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -63,14 +63,14 @@ class Ceiling
|
||||
* Or can be an array of values
|
||||
* @param mixed $significance Significance
|
||||
* Or can be an array of values
|
||||
* @param array|int $mode direction to round negative numbers
|
||||
* @param array<mixed>|int $mode direction to round negative numbers
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Rounded Number, or a string containing an error
|
||||
* @return array<mixed>|float|string Rounded Number, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function math(mixed $number, mixed $significance = null, $mode = 0): array|string|float
|
||||
public static function math(mixed $number, mixed $significance = null, $mode = 0, bool $checkSigns = false): array|string|float
|
||||
{
|
||||
if (is_array($number) || is_array($significance) || is_array($mode)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
|
||||
@@ -87,6 +87,11 @@ class Ceiling
|
||||
if (empty($significance * $number)) {
|
||||
return 0.0;
|
||||
}
|
||||
if ($checkSigns) {
|
||||
if (($number > 0 && $significance < 0) || ($number < 0 && $significance > 0)) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
}
|
||||
if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
|
||||
return floor($number / $significance) * $significance;
|
||||
}
|
||||
@@ -104,10 +109,10 @@ class Ceiling
|
||||
*
|
||||
* @param mixed $number the number you want to round
|
||||
* Or can be an array of values
|
||||
* @param array|float $significance the multiple to which you want to round
|
||||
* @param array<mixed>|float $significance the multiple to which you want to round
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Rounded Number, or a string containing an error
|
||||
* @return array<mixed>|float|string Rounded Number, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
@@ -132,6 +137,23 @@ class Ceiling
|
||||
return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* CEILING.ODS, pseudo-function - CEILING as implemented in ODS.
|
||||
*
|
||||
* ODS Function (theoretical):
|
||||
* CEILING.ODS(number[,significance[,mode]])
|
||||
*
|
||||
* @param mixed $number Number to round
|
||||
* @param mixed $significance Significance
|
||||
* @param array<mixed>|int $mode direction to round negative numbers
|
||||
*
|
||||
* @return array<mixed>|float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function mathOds(mixed $number, mixed $significance = null, $mode = 0): array|string|float
|
||||
{
|
||||
return self::math($number, $significance, $mode, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let CEILINGMATH complexity pass Scrutinizer.
|
||||
*/
|
||||
@@ -148,7 +170,12 @@ class Ceiling
|
||||
if (empty($number * $significance)) {
|
||||
return 0.0;
|
||||
}
|
||||
if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
|
||||
$signSig = Helpers::returnSign($significance);
|
||||
$signNum = Helpers::returnSign($number);
|
||||
if (
|
||||
($signSig === 1 && ($signNum === 1 || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC))
|
||||
|| ($signSig === -1 && $signNum === -1)
|
||||
) {
|
||||
return ceil($number / $significance) * $significance;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user