changes for filter and print

This commit is contained in:
2024-09-29 16:59:27 +05:45
parent 497f567cba
commit 684e01bf48
1335 changed files with 38709 additions and 74987 deletions

View File

@@ -2,12 +2,15 @@
$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->notPath('src/PhpSpreadsheet/Writer/ZipStream3.php')
->name('/(\.php|^generate-document|^generate-locales|^check-phpdoc-types)$/')
->in(__DIR__);
$config = new PhpCsFixer\Config();
$config
->setRiskyAllowed(true)
->setFinder($finder)
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__))
->setRules([
'align_multiline_comment' => true,
@@ -21,8 +24,8 @@ $config
'braces' => true,
'cast_spaces' => true,
'class_attributes_separation' => ['elements' => ['method' => 'one', 'property' => 'one']], // const are often grouped with other related const
'class_definition' => false,
'class_keyword_remove' => false, // ::class keyword gives us better support in IDE
'class_definition' => false, // phpcs disagree
'class_keyword_remove' => false, // Deprecated, and ::class keyword gives us better support in IDE
'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true,
'combine_nested_dirname' => true,
@@ -39,15 +42,17 @@ $config
'doctrine_annotation_indentation' => true,
'doctrine_annotation_spaces' => true,
'elseif' => true,
'empty_loop_body' => true,
'empty_loop_condition' => true,
'encoding' => true,
'ereg_to_preg' => true,
'error_suppression' => false, // it breaks \PhpOffice\PhpSpreadsheet\Helper\Handler
'escape_implicit_backslashes' => true,
'explicit_indirect_variable' => false, // I feel it makes the code actually harder to read
'explicit_string_variable' => false, // I feel it makes the code actually harder to read
'final_class' => false, // We need non-final classes
'final_internal_class' => true,
'final_public_method_for_abstract_class' => false, // We need non-final methods
'self_static_accessor' => true,
'fopen_flag_order' => true,
'fopen_flags' => true,
'full_opening_tag' => true,
@@ -56,7 +61,9 @@ $config
'function_to_constant' => true,
'function_typehint_space' => true,
'general_phpdoc_annotation_remove' => ['annotations' => ['access', 'category', 'copyright']],
'general_phpdoc_tag_rename' => true,
'global_namespace_import' => true,
'group_import' => false, // I feel it makes the code actually harder to read
'header_comment' => false, // We don't use common header in all our files
'heredoc_indentation' => true,
'heredoc_to_nowdoc' => false, // Not sure about this one
@@ -64,7 +71,9 @@ $config
'include' => true,
'increment_style' => true,
'indentation_type' => true,
'integer_literal_case' => true,
'is_null' => true,
'lambda_not_used_import' => true,
'line_ending' => true,
'linebreak_after_opening_tag' => true,
'list_syntax' => ['syntax' => 'short'],
@@ -77,6 +86,7 @@ $config
'mb_str_functions' => false, // No, too dangerous to change that
'method_argument_space' => true,
'method_chaining_indentation' => true,
'modernize_strpos' => true,
'modernize_types_casting' => true,
'multiline_comment_opening_closing' => true,
'multiline_whitespace_before_semicolons' => true,
@@ -86,6 +96,7 @@ $config
'native_function_type_declaration_casing' => true,
'new_with_braces' => true,
'no_alias_functions' => true,
'no_alias_language_construct_call' => true,
'no_alternative_syntax' => true,
'no_binary_string' => true,
'no_blank_lines_after_class_opening' => true,
@@ -107,6 +118,7 @@ $config
'no_short_bool_cast' => true,
'echo_tag_syntax' => ['format' => 'long'],
'no_singleline_whitespace_before_semicolons' => true,
'no_space_around_double_colon' => true,
'no_spaces_after_function_name' => true,
'no_spaces_around_offset' => true,
'no_spaces_inside_parenthesis' => true,
@@ -116,15 +128,17 @@ $config
'no_trailing_comma_in_singleline_array' => true,
'no_trailing_whitespace' => true,
'no_trailing_whitespace_in_comment' => true,
'no_trailing_whitespace_in_string' => false, // Too dangerous
'no_unneeded_control_parentheses' => true,
'no_unneeded_curly_braces' => true,
'no_unneeded_final_method' => true,
'no_unreachable_default_argument_value' => true,
'no_unset_cast' => true,
'no_unset_on_property' => true,
'no_unset_on_property' => false,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_useless_sprintf' => true,
'no_whitespace_before_comma_in_array' => true,
'no_whitespace_in_blank_line' => true,
'non_printable_character' => true,
@@ -133,9 +147,12 @@ $config
'not_operator_with_successor_space' => false, // idem
'nullable_type_declaration_for_default_null_value' => true,
'object_operator_without_whitespace' => true,
'octal_notation' => true,
'operator_linebreak' => true,
'ordered_class_elements' => false, // We prefer to keep some freedom
'ordered_imports' => true,
'ordered_interfaces' => true,
'ordered_traits' => true,
'php_unit_construct' => true,
'php_unit_dedicate_assert' => true,
'php_unit_dedicate_assert_internal_type' => true,
@@ -171,9 +188,12 @@ $config
'phpdoc_separation' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_summary' => true,
'phpdoc_tag_casing' => true,
'phpdoc_tag_type' => true,
'phpdoc_to_comment' => false, // interferes with annotations
'phpdoc_to_param_type' => false, // Because experimental, but interesting for one shot use
'phpdoc_to_return_type' => false, // idem
'phpdoc_to_property_type' => false, // Because experimental, but interesting for one shot use
'phpdoc_to_return_type' => false, // Because experimental, but interesting for one shot use
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'phpdoc_types' => true,
@@ -182,8 +202,7 @@ $config
'phpdoc_var_without_name' => true,
'pow_to_exponentiation' => true,
'protected_to_private' => true,
//'psr0' => true,
//'psr4' => true,
'psr_autoloading' => true,
'random_api_migration' => true,
'return_assignment' => false, // Sometimes useful for clarity or debug
'return_type_declaration' => true,
@@ -193,6 +212,7 @@ $config
'set_type_to_cast' => true,
'short_scalar_cast' => true,
'simple_to_complex_string_variable' => false, // Would differ from TypeScript without obvious advantages
'simplified_if_return' => false, // Even if technically correct we prefer to be explicit
'simplified_null_return' => false, // Even if technically correct we prefer to be explicit
'single_blank_line_at_eof' => true,
'single_blank_line_before_namespace' => true,
@@ -202,6 +222,7 @@ $config
'single_line_comment_style' => true,
'single_line_throw' => false, // I don't see any reason for having a special case for Exception
'single_quote' => true,
'single_space_after_construct' => true,
'single_trait_insert_per_statement' => true,
'space_after_semicolon' => true,
'standardize_increment' => true,
@@ -209,14 +230,19 @@ $config
'static_lambda' => false, // Risky if we can't guarantee nobody use `bindTo()`
'strict_comparison' => false, // No, too dangerous to change that
'strict_param' => false, // No, too dangerous to change that
'string_length_to_empty' => true,
'string_line_ending' => true,
'switch_case_semicolon_to_colon' => true,
'switch_case_space' => true,
'switch_continue_to_break' => true,
'ternary_operator_spaces' => true,
'ternary_to_elvis_operator' => true,
'ternary_to_null_coalescing' => true,
'trailing_comma_in_multiline' => true,
'trim_array_spaces' => true,
'types_spaces' => true,
'unary_operator_spaces' => true,
'use_arrow_functions' => true,
'visibility_required' => ['elements' => ['property', 'method']], // not const
'void_return' => true,
'whitespace_after_comma_in_array' => true,

View File

@@ -5,6 +5,10 @@
<file>samples</file>
<file>src</file>
<file>tests</file>
<file>infra</file>
<file>bin/generate-document</file>
<file>bin/generate-locales</file>
<file>bin/check-phpdoc-types</file>
<exclude-pattern>samples/Header.php</exclude-pattern>
<exclude-pattern>*/tests/Core/*/*Test\.(inc|css|js)$</exclude-pattern>

View File

@@ -0,0 +1,12 @@
# Read the Docs configuration file for MkDocs projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html
version: 2
build:
os: ubuntu-22.04
tools:
python: "3"
mkdocs:
configuration: mkdocs.yml

View File

@@ -5,6 +5,379 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com)
and this project adheres to [Semantic Versioning](https://semver.org).
## 2024-08-07 - 2.2.2
### Added
- Nothing yet.
### Changed
- Nothing yet.
### Deprecated
- Nothing yet.
### Moved
- Nothing yet.
### Fixed
- Html Reader Preserve Unicode Whitespace. [Issue #1284](https://github.com/PHPOffice/PhpSpreadsheet/issues/1284) [PR #4106](https://github.com/PHPOffice/PhpSpreadsheet/pull/4106)
- RATE Function Floating Point Number of Periods. [PR #4107](https://github.com/PHPOffice/PhpSpreadsheet/pull/4107)
- Parameter Name Change Xlsx Writer Workbook. [Issue #4108](https://github.com/PHPOffice/PhpSpreadsheet/issues/4108) [PR #4111](https://github.com/PHPOffice/PhpSpreadsheet/pull/4111)
- New Algorithm for TRUNC, ROUNDUP, ROUNDDOWN. [Issue #4113](https://github.com/PHPOffice/PhpSpreadsheet/issues/4113) [PR #4115](https://github.com/PHPOffice/PhpSpreadsheet/pull/4115)
- Worksheet applyStylesFromArray Retain Active Cell (Excel 16 was having a problem with some files). [Issue #4128](https://github.com/PHPOffice/PhpSpreadsheet/issues/4128) [PR #4132](https://github.com/PHPOffice/PhpSpreadsheet/pull/4132)
## 2024-07-29 - 2.2.1
### Security Fix
- Prevent XXE when loading files [PR #4119](https://github.com/PHPOffice/PhpSpreadsheet/pull/4119)
### Fixed
- Add Sheet may leave Active Sheet uninitialized. [Issue #4112](https://github.com/PHPOffice/PhpSpreadsheet/issues/4112) [PR #4114](https://github.com/PHPOffice/PhpSpreadsheet/pull/4114)
- Reference to Defined Name Specifying Worksheet. [Issue #206](https://github.com/PHPOffice/PhpSpreadsheet/issues/296) [PR #4096](https://github.com/PHPOffice/PhpSpreadsheet/pull/4096)
- Xls Reader Print/Show Gridlines. [Issue #912](https://github.com/PHPOffice/PhpSpreadsheet/issues/912) [PR #4098](https://github.com/PHPOffice/PhpSpreadsheet/pull/4098)
- ODS Reader Allow Omission of Page Settings Tags. [Issue #4099](https://github.com/PHPOffice/PhpSpreadsheet/issues/4099) [PR #4101](https://github.com/PHPOffice/PhpSpreadsheet/pull/4101)
## 2024-07-24 - 2.2.0
### Added
- Xlsx Reader Optionally Ignore Rows With No Cells. [Issue #3982](https://github.com/PHPOffice/PhpSpreadsheet/issues/3982) [PR #4035](https://github.com/PHPOffice/PhpSpreadsheet/pull/4035)
- Means to change style without affecting current cell/sheet. [PR #4073](https://github.com/PHPOffice/PhpSpreadsheet/pull/4073)
- Option for CSV output file to have varying numbers of columns for each row. [Issue #1415](https://github.com/PHPOffice/PhpSpreadsheet/issues/1415) [PR #4076](https://github.com/PHPOffice/PhpSpreadsheet/pull/4076)
### Changed
- On read, Xlsx Reader had been breaking up union ranges into separate individual ranges. It will now try to preserve range as it was read in. [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
- Xlsx/Xls spreadsheet calculation and formatting of dates will use base date of spreadsheet even when spreadsheets with different base dates are simultaneously open. [Issue #1036](https://github.com/PHPOffice/PhpSpreadsheet/issues/1036) [Issue #1635](https://github.com/PHPOffice/PhpSpreadsheet/issues/1635) [PR #4071](https://github.com/PHPOffice/PhpSpreadsheet/pull/4071)
### Deprecated
- Writer\Xls\Style\ColorMap is no longer needed.
### Moved
- Nothing
### Fixed
- Incorrect Reader CSV with BOM. [Issue #4028](https://github.com/PHPOffice/PhpSpreadsheet/issues/4028) [PR #4029](https://github.com/PHPOffice/PhpSpreadsheet/pull/4029)
- POWER Null/Bool Args. [PR #4031](https://github.com/PHPOffice/PhpSpreadsheet/pull/4031)
- Do Not Output Alignment and Protection for Conditional Format. [Issue #4025](https://github.com/PHPOffice/PhpSpreadsheet/issues/4025) [PR #4027](https://github.com/PHPOffice/PhpSpreadsheet/pull/4027)
- Conditional Color Scale Improvements. [Issue #4049](https://github.com/PHPOffice/PhpSpreadsheet/issues/4049) [PR #4050](https://github.com/PHPOffice/PhpSpreadsheet/pull/4050)
- Mpdf and Tcpdf Borders on Merged Cells. [Issue #3557](https://github.com/PHPOffice/PhpSpreadsheet/issues/3557) [PR #4047](https://github.com/PHPOffice/PhpSpreadsheet/pull/4047)
- Xls Conditional Format Improvements. [PR #4030](https://github.com/PHPOffice/PhpSpreadsheet/pull/4030) [PR #4033](https://github.com/PHPOffice/PhpSpreadsheet/pull/4033)
- Conditional Range Unions and Intersections [Issue #4039](https://github.com/PHPOffice/PhpSpreadsheet/issues/4039) [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
- Ods comments with newlines. [Issue #4081](https://github.com/PHPOffice/PhpSpreadsheet/issues/4081) [PR #4086](https://github.com/PHPOffice/PhpSpreadsheet/pull/4086)
- Propagate errors in Text functions. [Issue #2581](https://github.com/PHPOffice/PhpSpreadsheet/issues/2581) [PR #4080](https://github.com/PHPOffice/PhpSpreadsheet/pull/4080)
- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4040](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
- Problem rendering line chart with missing plot label. [PR #4074](https://github.com/PHPOffice/PhpSpreadsheet/pull/4074)
- More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065)
- Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064)
- Xlsx Writer RichText and TYPE_STRING. [Issue #476](https://github.com/PHPOffice/PhpSpreadsheet/issues/476) [PR #4094](https://github.com/PHPOffice/PhpSpreadsheet/pull/4094)
- Ods boolean data. [Issue #460](https://github.com/PHPOffice/PhpSpreadsheet/issues/460) [PR #4093](https://github.com/PHPOffice/PhpSpreadsheet/pull/4093)
- Html Writer Minor Fixes. [PR #4089](https://github.com/PHPOffice/PhpSpreadsheet/pull/4089)
- Changes to INDEX function. [Issue #64](https://github.com/PHPOffice/PhpSpreadsheet/issues/64) [PR #4088](https://github.com/PHPOffice/PhpSpreadsheet/pull/4088)
- Ods Reader and Whitespace Text Nodes. [Issue #804](https://github.com/PHPOffice/PhpSpreadsheet/issues/804) [PR #4087](https://github.com/PHPOffice/PhpSpreadsheet/pull/4087)
- Ods Xml Reader and Whitespace Text Nodes. [Issue #804](https://github.com/PHPOffice/PhpSpreadsheet/issues/804) [PR #4087](https://github.com/PHPOffice/PhpSpreadsheet/pull/4087)
- Treat invalid formulas as strings. [Issue #1310](https://github.com/PHPOffice/PhpSpreadsheet/issues/1310) [PR #4073](https://github.com/PHPOffice/PhpSpreadsheet/pull/4073)
## 2024-05-11 - 2.1.0
### MINOR BREAKING CHANGE
- Writing of cell comments to Html will now sanitize all Html tags within the comment, so the tags will be rendered as plaintext and have no other effects when rendered. Styling can be achieved by using the Font property of of the TextRuns which make up the comment, as is already the cases for Xlsx. [PR #3957](https://github.com/PHPOffice/PhpSpreadsheet/pull/3957)
### Added
- Default Style Alignment Property (workaround for bug in non-Excel spreadsheet apps) [Issue #3918](https://github.com/PHPOffice/PhpSpreadsheet/issues/3918) [PR #3924](https://github.com/PHPOffice/PhpSpreadsheet/pull/3924)
- Additional Support for Date/Time Styles [PR #3939](https://github.com/PHPOffice/PhpSpreadsheet/pull/3939)
### Changed
- Nothing
### Deprecated
- Reader/Xml trySimpleXMLLoadString should not have had public visibility, and will be removed.
### Removed
- Nothing
### Fixed
- IF Empty Arguments. [Issue #3875](https://github.com/PHPOffice/PhpSpreadsheet/issues/3875) [Issue #2146](https://github.com/PHPOffice/PhpSpreadsheet/issues/2146) [PR #3879](https://github.com/PHPOffice/PhpSpreadsheet/pull/3879)
- Changes to floating point in Php8.4. [Issue #3896](https://github.com/PHPOffice/PhpSpreadsheet/issues/3896) [PR #3897](https://github.com/PHPOffice/PhpSpreadsheet/pull/3897)
- Handling User-supplied Decimal and Thousands Separators. [Issue #3900](https://github.com/PHPOffice/PhpSpreadsheet/issues/3900) [PR #3903](https://github.com/PHPOffice/PhpSpreadsheet/pull/3903)
- Improve Performance of CSV Writer. [Issue #3904](https://github.com/PHPOffice/PhpSpreadsheet/issues/3904) [PR #3906](https://github.com/PHPOffice/PhpSpreadsheet/pull/3906)
- Fix issue with prepending zero in percentage [Issue #3920](https://github.com/PHPOffice/PhpSpreadsheet/issues/3920) [PR #3921](https://github.com/PHPOffice/PhpSpreadsheet/pull/3921)
- Incorrect SUMPRODUCT Calculation [Issue #3909](https://github.com/PHPOffice/PhpSpreadsheet/issues/3909) [PR #3916](https://github.com/PHPOffice/PhpSpreadsheet/pull/3916)
- Formula Misidentifying Text as Cell After Insertion/Deletion [Issue #3907](https://github.com/PHPOffice/PhpSpreadsheet/issues/3907) [PR #3915](https://github.com/PHPOffice/PhpSpreadsheet/pull/3915)
- Unexpected Absolute Address in Xlsx Rels [Issue #3730](https://github.com/PHPOffice/PhpSpreadsheet/issues/3730) [PR #3923](https://github.com/PHPOffice/PhpSpreadsheet/pull/3923)
- Unallocated Cells Affected by Column/Row Insert/Delete [Issue #3933](https://github.com/PHPOffice/PhpSpreadsheet/issues/3933) [PR #3940](https://github.com/PHPOffice/PhpSpreadsheet/pull/3940)
- Invalid Builtin Defined Name in Xls Reader [Issue #3935](https://github.com/PHPOffice/PhpSpreadsheet/issues/3935) [PR #3942](https://github.com/PHPOffice/PhpSpreadsheet/pull/3942)
- Hidden Rows and Columns Tcpdf/Mpdf [PR #3945](https://github.com/PHPOffice/PhpSpreadsheet/pull/3945)
- RTL Text Alignment in Xlsx Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4006](https://github.com/PHPOffice/PhpSpreadsheet/pull/4006)
- Protect Sheet But Allow Sort [Issue #3951](https://github.com/PHPOffice/PhpSpreadsheet/issues/3951) [PR #3956](https://github.com/PHPOffice/PhpSpreadsheet/pull/3956)
- Default Value for Conditional::$text [PR #3946](https://github.com/PHPOffice/PhpSpreadsheet/pull/3946)
- Table Filter Buttons [Issue #3988](https://github.com/PHPOffice/PhpSpreadsheet/issues/3988) [PR #3992](https://github.com/PHPOffice/PhpSpreadsheet/pull/3992)
- Improvements to Xml Reader [Issue #3999](https://github.com/PHPOffice/PhpSpreadsheet/issues/3999) [Issue #4000](https://github.com/PHPOffice/PhpSpreadsheet/issues/4000) [Issue #4001](https://github.com/PHPOffice/PhpSpreadsheet/issues/4001) [Issue #4002](https://github.com/PHPOffice/PhpSpreadsheet/issues/4002) [PR #4003](https://github.com/PHPOffice/PhpSpreadsheet/pull/4003) [PR #4007](https://github.com/PHPOffice/PhpSpreadsheet/pull/4007)
- Html Reader non-UTF8 [Issue #3995](https://github.com/PHPOffice/PhpSpreadsheet/issues/3995) [Issue #866](https://github.com/PHPOffice/PhpSpreadsheet/issues/866) [Issue #1681](https://github.com/PHPOffice/PhpSpreadsheet/issues/1681) [PR #4019](https://github.com/PHPOffice/PhpSpreadsheet/pull/4019)
## 2.0.0 - 2024-01-04
### BREAKING CHANGE
- Typing was strengthened by leveraging native typing. This should not change any behavior. However, if you implement
any interfaces or inherit from any classes, you will need to adapt your typing accordingly. If you use static analysis
tools such as PHPStan or Psalm, new errors might be found. If you find actual bugs because of the new typing, please
open a PR that fixes it with a **detailed** explanation of the reason. We'll try to merge and release typing-related
fixes quickly in the coming days. [PR #3718](https://github.com/PHPOffice/PhpSpreadsheet/pull/3718)
- All deprecated things have been removed, for details, see [816b91d0b4](https://github.com/PHPOffice/PhpSpreadsheet/commit/816b91d0b4a0c7285a9e3fc88c58f7730d922044)
### Added
- Split screens (Xlsx and Xml only, not 100% complete). [Issue #3601](https://github.com/PHPOffice/PhpSpreadsheet/issues/3601) [PR #3622](https://github.com/PHPOffice/PhpSpreadsheet/pull/3622)
- Permit Meta Viewport in Html. [Issue #3565](https://github.com/PHPOffice/PhpSpreadsheet/issues/3565) [PR #3623](https://github.com/PHPOffice/PhpSpreadsheet/pull/3623)
- Hyperlink support for Ods. [Issue #3660](https://github.com/PHPOffice/PhpSpreadsheet/issues/3660) [PR #3669](https://github.com/PHPOffice/PhpSpreadsheet/pull/3669)
- ListWorksheetInfo/Names for Html/Csv/Slk. [Issue #3706](https://github.com/PHPOffice/PhpSpreadsheet/issues/3706) [PR #3709](https://github.com/PHPOffice/PhpSpreadsheet/pull/3709)
- Methods to determine if cell is actually locked, or hidden on formula bar. [PR #3722](https://github.com/PHPOffice/PhpSpreadsheet/pull/3722)
- Add iterateOnlyExistingCells to Constructors. [Issue #3721](https://github.com/PHPOffice/PhpSpreadsheet/issues/3721) [PR #3727](https://github.com/PHPOffice/PhpSpreadsheet/pull/3727)
- Support for Conditional Formatting Color Scale. [PR #3738](https://github.com/PHPOffice/PhpSpreadsheet/pull/3738)
- Support Additional Tags in Helper/Html. [Issue #3751](https://github.com/PHPOffice/PhpSpreadsheet/issues/3751) [PR #3752](https://github.com/PHPOffice/PhpSpreadsheet/pull/3752)
- Writer ODS : Write Border Style for cells [Issue #3690](https://github.com/PHPOffice/PhpSpreadsheet/issues/3690) [PR #3693](https://github.com/PHPOffice/PhpSpreadsheet/pull/3693)
- Sheet Background Images [Issue #1649](https://github.com/PHPOffice/PhpSpreadsheet/issues/1649) [PR #3795](https://github.com/PHPOffice/PhpSpreadsheet/pull/3795)
- Check if Coordinate is Inside Range [PR #3779](https://github.com/PHPOffice/PhpSpreadsheet/pull/3779)
- Flipping Images [Issue #731](https://github.com/PHPOffice/PhpSpreadsheet/issues/731) [PR #3801](https://github.com/PHPOffice/PhpSpreadsheet/pull/3801)
- Chart Dynamic Title and Font Properties [Issue #3797](https://github.com/PHPOffice/PhpSpreadsheet/issues/3797) [PR #3800](https://github.com/PHPOffice/PhpSpreadsheet/pull/3800)
- Chart Axis Display Units and Logarithmic Scale. [Issue #3833](https://github.com/PHPOffice/PhpSpreadsheet/issues/3833) [PR #3836](https://github.com/PHPOffice/PhpSpreadsheet/pull/3836)
- Partial Support of Fill Handles. [Discussion #3847](https://github.com/PHPOffice/PhpSpreadsheet/discussions/3847) [PR #3855](https://github.com/PHPOffice/PhpSpreadsheet/pull/3855)
### Changed
- **Drop support for PHP 7.4**, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support [PR #3713](https://github.com/PHPOffice/PhpSpreadsheet/pull/3713)
- RLM Added to NumberFormatter Currency. This happens depending on release of ICU which Php is using (it does not yet happen with any official release). PhpSpreadsheet will continue to use the value returned by Php, but a method is added to keep the result unchanged from release to release. [Issue #3571](https://github.com/PHPOffice/PhpSpreadsheet/issues/3571) [PR #3640](https://github.com/PHPOffice/PhpSpreadsheet/pull/3640)
- `toFormattedString` will now always return a string. This was introduced with 1.28.0, but was not properly documented at the time. This can affect the results of `toArray`, `namedRangeToArray`, and `rangeToArray`. [PR #3304](https://github.com/PHPOffice/PhpSpreadsheet/pull/3304)
- Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD was changed in 1.28.0, but was not properly documented at the time. [Issue #3577](https://github.com/PHPOffice/PhpSpreadsheet/issues/3577)
- Html Writer will attempt to use Chart coordinates to determine image size. [Issue #3783](https://github.com/PHPOffice/PhpSpreadsheet/issues/3783) [PR #3787](https://github.com/PHPOffice/PhpSpreadsheet/pull/3787)
### Deprecated
- Functions `_translateFormulaToLocale` and `_translateFormulaEnglish` are replaced by versions without leading underscore. [PR #3828](https://github.com/PHPOffice/PhpSpreadsheet/pull/3828)
### Removed
- Nothing
### Fixed
- Take advantage of mitoteam/jpgraph Extended mode to enable rendering of more graphs. [PR #3603](https://github.com/PHPOffice/PhpSpreadsheet/pull/3603)
- Column widths, especially for ODS. [Issue #3609](https://github.com/PHPOffice/PhpSpreadsheet/issues/3609) [PR #3610](https://github.com/PHPOffice/PhpSpreadsheet/pull/3610)
- Avoid NULL in String Function call (partial solution). [Issue #3613](https://github.com/PHPOffice/PhpSpreadsheet/issues/3613) [PR #3617](https://github.com/PHPOffice/PhpSpreadsheet/pull/3617)
- Preserve transparency in Memory Drawing. [Issue #3624](https://github.com/PHPOffice/PhpSpreadsheet/issues/3624) [PR #3627](https://github.com/PHPOffice/PhpSpreadsheet/pull/3627)
- Customizable padding for Exact Column Width. [Issue #3626](https://github.com/PHPOffice/PhpSpreadsheet/issues/3626) [PR #3628](https://github.com/PHPOffice/PhpSpreadsheet/pull/3628)
- Ensure ROW function returns int (problem exposed in unreleased Php). [PR #3641](https://github.com/PHPOffice/PhpSpreadsheet/pull/3641)
- Minor changes to Mpdf and Html Writers. [PR #3645](https://github.com/PHPOffice/PhpSpreadsheet/pull/3645)
- Xlsx Reader Namespacing for Tables, Autofilters. [Issue #3665](https://github.com/PHPOffice/PhpSpreadsheet/issues/3665) [PR #3668](https://github.com/PHPOffice/PhpSpreadsheet/pull/3668)
- Read Code Page for Xls ListWorksheetInfo/Names BIFF5. [Issue #3671](https://github.com/PHPOffice/PhpSpreadsheet/issues/3671) [PR #3672](https://github.com/PHPOffice/PhpSpreadsheet/pull/3672)
- Read Data from Table on Different Sheet. [Issue #3635](https://github.com/PHPOffice/PhpSpreadsheet/issues/3635) [PR #3659](https://github.com/PHPOffice/PhpSpreadsheet/pull/3659)
- Html Writer Styles Using Inline Css. [Issue #3678](https://github.com/PHPOffice/PhpSpreadsheet/issues/3678) [PR #3680](https://github.com/PHPOffice/PhpSpreadsheet/pull/3680)
- Xlsx Read Ignoring Some Comments. [Issue #3654](https://github.com/PHPOffice/PhpSpreadsheet/issues/3654) [PR #3655](https://github.com/PHPOffice/PhpSpreadsheet/pull/3655)
- Fractional Seconds in Date/Time Values. [PR #3677](https://github.com/PHPOffice/PhpSpreadsheet/pull/3677)
- SetCalculatedValue Avoid Casting String to Numeric. [Issue #3658](https://github.com/PHPOffice/PhpSpreadsheet/issues/3658) [PR #3685](https://github.com/PHPOffice/PhpSpreadsheet/pull/3685)
- Several Problems in a Very Complicated Spreadsheet. [Issue #3679](https://github.com/PHPOffice/PhpSpreadsheet/issues/3679) [PR #3681](https://github.com/PHPOffice/PhpSpreadsheet/pull/3681)
- Inconsistent String Handling for Sum Functions. [Issue #3652](https://github.com/PHPOffice/PhpSpreadsheet/issues/3652) [PR #3653](https://github.com/PHPOffice/PhpSpreadsheet/pull/3653)
- Recomputation of Relative Addresses in Defined Names. [Issue #3661](https://github.com/PHPOffice/PhpSpreadsheet/issues/3661) [PR #3673](https://github.com/PHPOffice/PhpSpreadsheet/pull/3673)
- Writer Xls Characters Outside BMP (emojis). [Issue #642](https://github.com/PHPOffice/PhpSpreadsheet/issues/642) [PR #3696](https://github.com/PHPOffice/PhpSpreadsheet/pull/3696)
- Xlsx Reader Improve Handling of Row and Column Styles. [Issue #3533](https://github.com/PHPOffice/PhpSpreadsheet/issues/3533) [Issue #3534](https://github.com/PHPOffice/PhpSpreadsheet/issues/3534) [PR #3688](https://github.com/PHPOffice/PhpSpreadsheet/pull/3688)
- Avoid Allocating RowDimension Unneccesarily. [PR #3686](https://github.com/PHPOffice/PhpSpreadsheet/pull/3686)
- Use Column Style when Row Dimension Exists Without Style. [Issue #3534](https://github.com/PHPOffice/PhpSpreadsheet/issues/3534) [PR #3688](https://github.com/PHPOffice/PhpSpreadsheet/pull/3688)
- Inconsistency Between Cell Data and Explicitly Declared Type. [Issue #3711](https://github.com/PHPOffice/PhpSpreadsheet/issues/3711) [PR #3715](https://github.com/PHPOffice/PhpSpreadsheet/pull/3715)
- Unexpected Namespacing in rels File. [Issue #3720](https://github.com/PHPOffice/PhpSpreadsheet/issues/3720) [PR #3722](https://github.com/PHPOffice/PhpSpreadsheet/pull/3722)
- Break Some Circular References. [PR #3716](https://github.com/PHPOffice/PhpSpreadsheet/pull/3716) [PR #3707](https://github.com/PHPOffice/PhpSpreadsheet/pull/3707)
- Missing Font Index in Some Xls. [PR #3734](https://github.com/PHPOffice/PhpSpreadsheet/pull/3734)
- Load Tables even with READ_DATA_ONLY. [PR #3726](https://github.com/PHPOffice/PhpSpreadsheet/pull/3726)
- Theme File Missing but Referenced in Spreadsheet. [Issue #3770](https://github.com/PHPOffice/PhpSpreadsheet/issues/3770) [PR #3772](https://github.com/PHPOffice/PhpSpreadsheet/pull/3772)
- Slk Shared Formulas. [Issue #2267](https://github.com/PHPOffice/PhpSpreadsheet/issues/2267) [PR #3776](https://github.com/PHPOffice/PhpSpreadsheet/pull/3776)
- Html omitting some charts. [Issue #3767](https://github.com/PHPOffice/PhpSpreadsheet/issues/3767) [PR #3771](https://github.com/PHPOffice/PhpSpreadsheet/pull/3771)
- Case Insensitive Comparison for Sheet Names [PR #3791](https://github.com/PHPOffice/PhpSpreadsheet/pull/3791)
- Performance improvement for Xlsx Reader. [Issue #3683](https://github.com/PHPOffice/PhpSpreadsheet/issues/3683) [PR #3810](https://github.com/PHPOffice/PhpSpreadsheet/pull/3810)
- Prevent loop in Shared/File. [Issue #3807](https://github.com/PHPOffice/PhpSpreadsheet/issues/3807) [PR #3809](https://github.com/PHPOffice/PhpSpreadsheet/pull/3809)
- Consistent handling of decimal/thousands separators between StringHelper and Php setlocale. [Issue #3811](https://github.com/PHPOffice/PhpSpreadsheet/issues/3811) [PR #3815](https://github.com/PHPOffice/PhpSpreadsheet/pull/3815)
- Clone worksheet with tables or charts. [Issue #3820](https://github.com/PHPOffice/PhpSpreadsheet/issues/3820) [PR #3821](https://github.com/PHPOffice/PhpSpreadsheet/pull/3821)
- COUNTIFS Does Not Require xlfn. [Issue #3819](https://github.com/PHPOffice/PhpSpreadsheet/issues/3819) [PR #3827](https://github.com/PHPOffice/PhpSpreadsheet/pull/3827)
- Strip `xlfn.` and `xlws.` from Formula Translations. [Issue #3819](https://github.com/PHPOffice/PhpSpreadsheet/issues/3819) [PR #3828](https://github.com/PHPOffice/PhpSpreadsheet/pull/3828)
- Recurse directories searching for font file. [Issue #2809](https://github.com/PHPOffice/PhpSpreadsheet/issues/2809) [PR #3830](https://github.com/PHPOffice/PhpSpreadsheet/pull/3830)
- Reduce memory consumption of Worksheet::rangeToArray() when many empty rows are read. [Issue #3814](https://github.com/PHPOffice/PhpSpreadsheet/issues/3814) [PR #3834](https://github.com/PHPOffice/PhpSpreadsheet/pull/3834)
- Reduce time used by Worksheet::rangeToArray() when many empty rows are read. [PR #3839](https://github.com/PHPOffice/PhpSpreadsheet/pull/3839)
- Html Reader Tolerate Invalid Sheet Title. [PR #3845](https://github.com/PHPOffice/PhpSpreadsheet/pull/3845)
- Do not include unparsed drawings when new drawing added. [Issue #3843](https://github.com/PHPOffice/PhpSpreadsheet/issues/3843) [PR #3846](https://github.com/PHPOffice/PhpSpreadsheet/pull/3846)
- Do not include unparsed drawings when new drawing added. [Issue #3861](https://github.com/PHPOffice/PhpSpreadsheet/issues/3861) [PR #3862](https://github.com/PHPOffice/PhpSpreadsheet/pull/3862)
- Excel omits `between` operator for data validation. [Issue #3863](https://github.com/PHPOffice/PhpSpreadsheet/issues/3863) [PR #3865](https://github.com/PHPOffice/PhpSpreadsheet/pull/3865)
- Use less space when inserting rows and columns. [Issue #3687](https://github.com/PHPOffice/PhpSpreadsheet/issues/3687) [PR #3856](https://github.com/PHPOffice/PhpSpreadsheet/pull/3856)
- Excel inconsistent handling of MIN/MAX/MINA/MAXA. [Issue #3866](https://github.com/PHPOffice/PhpSpreadsheet/issues/3866) [PR #3868](https://github.com/PHPOffice/PhpSpreadsheet/pull/3868)
## 1.29.0 - 2023-06-15
### Added
- Wizards for defining Number Format masks for Dates and Times, including Durations/Intervals. [PR #3458](https://github.com/PHPOffice/PhpSpreadsheet/pull/3458)
- Specify data type in html tags. [Issue #3444](https://github.com/PHPOffice/PhpSpreadsheet/issues/3444) [PR #3445](https://github.com/PHPOffice/PhpSpreadsheet/pull/3445)
- Provide option to ignore hidden rows/columns in `toArray()` methods. [PR #3494](https://github.com/PHPOffice/PhpSpreadsheet/pull/3494)
- Font/Effects/Theme support for Chart Data Labels and Axis. [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476)
- Font Themes support. [PR #3486](https://github.com/PHPOffice/PhpSpreadsheet/pull/3486)
- Ability to Ignore Cell Errors in Excel. [Issue #1141](https://github.com/PHPOffice/PhpSpreadsheet/issues/1141) [PR #3508](https://github.com/PHPOffice/PhpSpreadsheet/pull/3508)
- Unzipped Gnumeric file [PR #3591](https://github.com/PHPOffice/PhpSpreadsheet/pull/3591)
### Changed
- Xlsx Color schemes read in will be written out (previously Excel 2007-2010 Color scheme was always written); manipulation of those schemes before write, including restoring prior behavior, is provided [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476)
- Memory and speed optimisations for Read Filters with Xlsx Files and Shared Formulae. [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474)
- Allow `CellRange` and `CellAddress` objects for the `range` argument in the `rangeToArray()` method. [PR #3494](https://github.com/PHPOffice/PhpSpreadsheet/pull/3494)
- Stock charts will now read and reproduce `upDownBars` and subsidiary tags; these were previously ignored on read and hard-coded on write. [PR #3515](https://github.com/PHPOffice/PhpSpreadsheet/pull/3515)
### Deprecated
- Nothing
### Removed
- Nothing
### Fixed
- Updates Cell formula absolute ranges/references, and Defined Name absolute ranges/references when inserting/deleting rows/columns. [Issue #3368](https://github.com/PHPOffice/PhpSpreadsheet/issues/3368) [PR #3402](https://github.com/PHPOffice/PhpSpreadsheet/pull/3402)
- EOMONTH() and EDATE() Functions should round date value before evaluation. [Issue #3436](https://github.com/PHPOffice/PhpSpreadsheet/issues/3436) [PR #3437](https://github.com/PHPOffice/PhpSpreadsheet/pull/3437)
- NETWORKDAYS function erroneously being converted to NETWORK_xlfn.DAYS in Xlsx Writer. [Issue #3461](https://github.com/PHPOffice/PhpSpreadsheet/issues/3461) [PR #3463](https://github.com/PHPOffice/PhpSpreadsheet/pull/3463)
- Getting a style for a CellAddress instance fails if the worksheet is set in the CellAddress instance. [Issue #3439](https://github.com/PHPOffice/PhpSpreadsheet/issues/3439) [PR #3469](https://github.com/PHPOffice/PhpSpreadsheet/pull/3469)
- Shared Formulae outside the filter range when reading with a filter are not always being identified. [Issue #3473](https://github.com/PHPOffice/PhpSpreadsheet/issues/3473) [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474)
- Xls Reader Conditional Styles. [PR #3400](https://github.com/PHPOffice/PhpSpreadsheet/pull/3400)
- Allow use of # and 0 digit placeholders in fraction masks. [PR #3401](https://github.com/PHPOffice/PhpSpreadsheet/pull/3401)
- Modify Date/Time check in the NumberFormatter for decimal/fractional times. [PR #3413](https://github.com/PHPOffice/PhpSpreadsheet/pull/3413)
- Misplaced Xml Writing Chart Label FillColor. [Issue #3397](https://github.com/PHPOffice/PhpSpreadsheet/issues/3397) [PR #3404](https://github.com/PHPOffice/PhpSpreadsheet/pull/3404)
- TEXT function ignores Time in DateTimeStamp. [Issue #3409](https://github.com/PHPOffice/PhpSpreadsheet/issues/3409) [PR #3411](https://github.com/PHPOffice/PhpSpreadsheet/pull/3411)
- Xlsx Column Autosize Approximate for CJK. [Issue #3405](https://github.com/PHPOffice/PhpSpreadsheet/issues/3405) [PR #3416](https://github.com/PHPOffice/PhpSpreadsheet/pull/3416)
- Correct Xlsx Parsing of quotePrefix="0". [Issue #3435](https://github.com/PHPOffice/PhpSpreadsheet/issues/3435) [PR #3438](https://github.com/PHPOffice/PhpSpreadsheet/pull/3438)
- More Display Options for Chart Axis and Legend. [Issue #3414](https://github.com/PHPOffice/PhpSpreadsheet/issues/3414) [PR #3434](https://github.com/PHPOffice/PhpSpreadsheet/pull/3434)
- Apply strict type checking to Complex suffix. [PR #3452](https://github.com/PHPOffice/PhpSpreadsheet/pull/3452)
- Incorrect Font Color Read Xlsx Rich Text Indexed Color Custom Palette. [Issue #3464](https://github.com/PHPOffice/PhpSpreadsheet/issues/3464) [PR #3465](https://github.com/PHPOffice/PhpSpreadsheet/pull/3465)
- Xlsx Writer Honor Alignment in Default Font. [Issue #3443](https://github.com/PHPOffice/PhpSpreadsheet/issues/3443) [PR #3459](https://github.com/PHPOffice/PhpSpreadsheet/pull/3459)
- Support Border for Charts. [PR #3462](https://github.com/PHPOffice/PhpSpreadsheet/pull/3462)
- Error in "this row" structured reference calculation (cached result from first row when using a range) [Issue #3504](https://github.com/PHPOffice/PhpSpreadsheet/issues/3504) [PR #3505](https://github.com/PHPOffice/PhpSpreadsheet/pull/3505)
- Allow colour palette index references in Number Format masks [Issue #3511](https://github.com/PHPOffice/PhpSpreadsheet/issues/3511) [PR #3512](https://github.com/PHPOffice/PhpSpreadsheet/pull/3512)
- Xlsx Reader formula with quotePrefix [Issue #3495](https://github.com/PHPOffice/PhpSpreadsheet/issues/3495) [PR #3497](https://github.com/PHPOffice/PhpSpreadsheet/pull/3497)
- Handle REF error as part of range [Issue #3453](https://github.com/PHPOffice/PhpSpreadsheet/issues/3453) [PR #3467](https://github.com/PHPOffice/PhpSpreadsheet/pull/3467)
- Handle Absolute Pathnames in Rels File [Issue #3553](https://github.com/PHPOffice/PhpSpreadsheet/issues/3553) [PR #3554](https://github.com/PHPOffice/PhpSpreadsheet/pull/3554)
- Return Page Breaks in Order [Issue #3552](https://github.com/PHPOffice/PhpSpreadsheet/issues/3552) [PR #3555](https://github.com/PHPOffice/PhpSpreadsheet/pull/3555)
- Add position attribute for MemoryDrawing in Html [Issue #3529](https://github.com/PHPOffice/PhpSpreadsheet/issues/3529 [PR #3535](https://github.com/PHPOffice/PhpSpreadsheet/pull/3535)
- Allow Index_number as Array for VLOOKUP/HLOOKUP [Issue #3561](https://github.com/PHPOffice/PhpSpreadsheet/issues/3561 [PR #3570](https://github.com/PHPOffice/PhpSpreadsheet/pull/3570)
- Add Unsupported Options in Xml Spreadsheet [Issue #3566](https://github.com/PHPOffice/PhpSpreadsheet/issues/3566 [Issue #3568](https://github.com/PHPOffice/PhpSpreadsheet/issues/3568 [Issue #3569](https://github.com/PHPOffice/PhpSpreadsheet/issues/3569 [PR #3567](https://github.com/PHPOffice/PhpSpreadsheet/pull/3567)
- Changes to NUMBERVALUE, VALUE, DATEVALUE, TIMEVALUE [Issue #3574](https://github.com/PHPOffice/PhpSpreadsheet/issues/3574 [PR #3575](https://github.com/PHPOffice/PhpSpreadsheet/pull/3575)
- Redo calculation of color tinting [Issue #3550](https://github.com/PHPOffice/PhpSpreadsheet/issues/3550) [PR #3580](https://github.com/PHPOffice/PhpSpreadsheet/pull/3580)
- Accommodate Slash with preg_quote [PR #3582](https://github.com/PHPOffice/PhpSpreadsheet/pull/3582) [PR #3583](https://github.com/PHPOffice/PhpSpreadsheet/pull/3583) [PR #3584](https://github.com/PHPOffice/PhpSpreadsheet/pull/3584)
- HyperlinkBase Property and Html Handling of Properties [Issue #3573](https://github.com/PHPOffice/PhpSpreadsheet/issues/3573) [PR #3589](https://github.com/PHPOffice/PhpSpreadsheet/pull/3589)
- Improvements for Data Validation [Issue #3592](https://github.com/PHPOffice/PhpSpreadsheet/issues/3592) [Issue #3594](https://github.com/PHPOffice/PhpSpreadsheet/issues/3594) [PR #3605](https://github.com/PHPOffice/PhpSpreadsheet/pull/3605)
## 1.28.0 - 2023-02-25
### Added
- Support for configuring a Chart Title's overlay [PR #3325](https://github.com/PHPOffice/PhpSpreadsheet/pull/3325)
- Wizards for defining Number Format masks for Numbers, Percentages, Scientific, Currency and Accounting [PR #3334](https://github.com/PHPOffice/PhpSpreadsheet/pull/3334)
- Support for fixed value divisor in fractional Number Format Masks [PR #3339](https://github.com/PHPOffice/PhpSpreadsheet/pull/3339)
- Allow More Fonts/Fontnames for Exact Width Calculation [PR #3326](https://github.com/PHPOffice/PhpSpreadsheet/pull/3326) [Issue #3190](https://github.com/PHPOffice/PhpSpreadsheet/issues/3190)
- Allow override of the Value Binder when setting a Cell value [PR #3361](https://github.com/PHPOffice/PhpSpreadsheet/pull/3361)
### Changed
- Improved handling for @ placeholder in Number Format Masks [PR #3344](https://github.com/PHPOffice/PhpSpreadsheet/pull/3344)
- Improved handling for ? placeholder in Number Format Masks [PR #3394](https://github.com/PHPOffice/PhpSpreadsheet/pull/3394)
- Improved support for locale settings and currency codes when matching formatted strings to numerics in the Calculation Engine [PR #3373](https://github.com/PHPOffice/PhpSpreadsheet/pull/3373) and [PR #3374](https://github.com/PHPOffice/PhpSpreadsheet/pull/3374)
- Improved support for locale settings and matching in the Advanced Value Binder [PR #3376](https://github.com/PHPOffice/PhpSpreadsheet/pull/3376)
- `toFormattedString` will now always return a string. This can affect the results of `toArray`, `namedRangeToArray`, and `rangeToArray`. [PR #3304](https://github.com/PHPOffice/PhpSpreadsheet/pull/3304)
- Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD is changed. [Issue #3577](https://github.com/PHPOffice/PhpSpreadsheet/issues/3577) [PR #3377](https://github.com/PHPOffice/PhpSpreadsheet/pull/3377)
### Deprecated
- Rationalisation of Pre-defined Currency Format Masks [PR #3377](https://github.com/PHPOffice/PhpSpreadsheet/pull/3377)
### Removed
- Nothing
### Fixed
- Calculation Engine doesn't evaluate Defined Name when default cell A1 is quote-prefixed [Issue #3335](https://github.com/PHPOffice/PhpSpreadsheet/issues/3335) [PR #3336](https://github.com/PHPOffice/PhpSpreadsheet/pull/3336)
- XLSX Writer - Array Formulas do not include function prefix [Issue #3337](https://github.com/PHPOffice/PhpSpreadsheet/issues/3337) [PR #3338](https://github.com/PHPOffice/PhpSpreadsheet/pull/3338)
- Permit Max Column for Row Breaks [Issue #3143](https://github.com/PHPOffice/PhpSpreadsheet/issues/3143) [PR #3345](https://github.com/PHPOffice/PhpSpreadsheet/pull/3345)
- AutoSize Columns should allow for dropdown icon when AutoFilter is for a Table [Issue #3356](https://github.com/PHPOffice/PhpSpreadsheet/issues/3356) [PR #3358](https://github.com/PHPOffice/PhpSpreadsheet/pull/3358) and for Center Alignment of Headers [Issue #3395](https://github.com/PHPOffice/PhpSpreadsheet/issues/3395) [PR #3399](https://github.com/PHPOffice/PhpSpreadsheet/pull/3399)
- Decimal Precision for Scientific Number Format Mask [Issue #3381](https://github.com/PHPOffice/PhpSpreadsheet/issues/3381) [PR #3382](https://github.com/PHPOffice/PhpSpreadsheet/pull/3382)
- Xls Writer Parser Handle Boolean Literals as Function Arguments [Issue #3369](https://github.com/PHPOffice/PhpSpreadsheet/issues/3369) [PR #3391](https://github.com/PHPOffice/PhpSpreadsheet/pull/3391)
- Conditional Formatting Improvements for Xlsx [Issue #3370](https://github.com/PHPOffice/PhpSpreadsheet/issues/3370) [Issue #3202](https://github.com/PHPOffice/PhpSpreadsheet/issues/3302) [PR #3372](https://github.com/PHPOffice/PhpSpreadsheet/pull/3372)
- Coerce Bool to Int for Mathematical Operations on Arrays [Issue #3389](https://github.com/PHPOffice/PhpSpreadsheet/issues/3389) [Issue #3396](https://github.com/PHPOffice/PhpSpreadsheet/issues/3396) [PR #3392](https://github.com/PHPOffice/PhpSpreadsheet/pull/3392)
## 1.27.1 - 2023-02-08
### Added
- Nothing
### Changed
- Nothing
### Deprecated
- Nothing
### Removed
- Nothing
### Fixed
- Fix Composer --dev dependency issue with dealerdirect/phpcodesniffer-composer-installer renaming their `master` branch to `main`
## 1.27.0 - 2023-01-24
### Added
- Option to specify a range of columns/rows for the Row/Column `isEmpty()` methods [PR #3315](https://github.com/PHPOffice/PhpSpreadsheet/pull/3315)
- Option for Cell Iterator to return a null value or create and return a new cell when accessing a cell that doesn't exist [PR #3314](https://github.com/PHPOffice/PhpSpreadsheet/pull/3314)
- Support for Structured References in the Calculation Engine [PR #3261](https://github.com/PHPOffice/PhpSpreadsheet/pull/3261)
- Limited Support for Form Controls [PR #3130](https://github.com/PHPOffice/PhpSpreadsheet/pull/3130) [Issue #2396](https://github.com/PHPOffice/PhpSpreadsheet/issues/2396) [Issue #1770](https://github.com/PHPOffice/PhpSpreadsheet/issues/1770) [Issue #2388](https://github.com/PHPOffice/PhpSpreadsheet/issues/2388) [Issue #2904](https://github.com/PHPOffice/PhpSpreadsheet/issues/2904) [Issue #2661](https://github.com/PHPOffice/PhpSpreadsheet/issues/2661)
### Changed
- Nothing
### Deprecated
- Nothing
### Removed
- Shared/JAMA is removed. [PR #3260](https://github.com/PHPOffice/PhpSpreadsheet/pull/3260)
### Fixed
- Namespace-Aware Code for SheetViewOptions, SheetProtection [PR #3230](https://github.com/PHPOffice/PhpSpreadsheet/pull/3230)
- Additional Method for XIRR if Newton-Raphson Doesn't Converge [Issue #689](https://github.com/PHPOffice/PhpSpreadsheet/issues/689) [PR #3262](https://github.com/PHPOffice/PhpSpreadsheet/pull/3262)
- Better Handling of Composite Charts [Issue #2333](https://github.com/PHPOffice/PhpSpreadsheet/issues/2333) [PR #3265](https://github.com/PHPOffice/PhpSpreadsheet/pull/3265)
- Update Column Reference for Columns Beginning with Y and Z [Issue #3263](https://github.com/PHPOffice/PhpSpreadsheet/issues/3263) [PR #3264](https://github.com/PHPOffice/PhpSpreadsheet/pull/3264)
- Honor Fit to 1-Page Height Html/Pdf [Issue #3266](https://github.com/PHPOffice/PhpSpreadsheet/issues/3266) [PR #3279](https://github.com/PHPOffice/PhpSpreadsheet/pull/3279)
- AND/OR/XOR Handling of Literal Strings [PR #3287](https://github.com/PHPOffice/PhpSpreadsheet/pull/3287)
- Xls Reader Vertical Break and Writer Page Order [Issue #3305](https://github.com/PHPOffice/PhpSpreadsheet/issues/3305) [PR #3306](https://github.com/PHPOffice/PhpSpreadsheet/pull/3306)
## 1.26.0 - 2022-12-21
### Added
@@ -112,7 +485,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](https://github.com/PHPOffice/PhpSpreadsheet/issues/3076) [PR #3077](https://github.com/PHPOffice/PhpSpreadsheet/pull/3077)
- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](https://github.com/PHPOffice/PhpSpreadsheet/issues/3076) [PR #3077](https://github.com/PHPOffice/PhpSpreadsheet/pull/3077)
- Fix DataValidation sqRef when inserting/deleting rows/columns [Issue #3056](https://github.com/PHPOffice/PhpSpreadsheet/issues/3056) [PR #3074](https://github.com/PHPOffice/PhpSpreadsheet/pull/3074)
- Named ranges not usable as anchors in OFFSET function [Issue #3013](https://github.com/PHPOffice/PhpSpreadsheet/issues/3013)
- Fully flatten an array [Issue #2955](https://github.com/PHPOffice/PhpSpreadsheet/issues/2955) [PR #2956](https://github.com/PHPOffice/PhpSpreadsheet/pull/2956)
@@ -302,7 +675,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
- Use color palette if supplied [Issue #2499](https://github.com/PHPOffice/PhpSpreadsheet/issues/2499) [PR #2595](https://github.com/PHPOffice/PhpSpreadsheet/pull/2595)
- Xls reader treat drawing offsets as int rather than float [PR #2648](https://github.com/PHPOffice/PhpSpreadsheet/pull/2648)
- Handle booleans in conditional styles properly [PR #2654](https://github.com/PHPOffice/PhpSpreadsheet/pull/2654)
- Fix for reading files in the root directory of a ZipFile, which should not be prefixed by relative paths ("./") as dirname($filename) does by default.
- Fix for reading files in the root directory of a ZipFile, which should not be prefixed by relative paths ("./") as dirname($filename) does by default.
- Fix invalid style of cells in empty columns with columnDimensions and rows with rowDimensions in added external sheet. [PR #2739](https://github.com/PHPOffice/PhpSpreadsheet/pull/2739)
- Time Interval Formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
@@ -315,9 +688,9 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
- Improved support for passing of array arguments to Excel function implementations to return array results (where appropriate). [Issue #2551](https://github.com/PHPOffice/PhpSpreadsheet/issues/2551)
This is the first stage in an ongoing process of adding array support to all appropriate function implementations,
This is the first stage in an ongoing process of adding array support to all appropriate function implementations,
- Support for the Excel365 Math/Trig SEQUENCE() function [PR #2536](https://github.com/PHPOffice/PhpSpreadsheet/pull/2536)
- Support for the Excel365 Math/Trig RANDARRAY() function [PR #2540](https://github.com/PHPOffice/PhpSpreadsheet/pull/2540)
- Support for the Excel365 Math/Trig RANDARRAY() function [PR #2540](https://github.com/PHPOffice/PhpSpreadsheet/pull/2540)
Note that the Spill Operator is not yet supported in the Calculation Engine; but this can still be useful for defining array constants.
- Improved support for Conditional Formatting Rules [PR #2491](https://github.com/PHPOffice/PhpSpreadsheet/pull/2491)
@@ -331,7 +704,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
- Expression
- Provision of CF Wizards (for all the above listed rule types) to help create/modify CF Rules without having to manage all the combinations of types/operators, and the complexities of formula expressions, or the text/timePeriod attributes.
See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/conditional-formatting/) for details
See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/conditional-formatting/) for details
- Full support of the above CF Rules for the Xlsx Reader and Writer; even when the file being loaded has CF rules listed in the `<extLst><ext><ConditionalFormattings>` element for the worksheet rather than the `<ConditionalFormatting>` element.
- Provision of a CellMatcher to identify if rules are matched for a cell, and which matching style will be applied.
@@ -521,7 +894,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
- Xls Reader changing grey background to black in Excel template [Issue #2147](https://github.com/PHPOffice/PhpSpreadsheet/issues/2147) [PR #2156](https://github.com/PHPOffice/PhpSpreadsheet/pull/2156)
- Column width and Row height styles in the Html Reader when the value includes a unit of measure [Issue #2145](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145).
- Data Validation flags not set correctly when reading XLSX files [Issue #2224](https://github.com/PHPOffice/PhpSpreadsheet/issues/2224) [PR #2225](https://github.com/PHPOffice/PhpSpreadsheet/pull/2225)
- Reading XLSX files without styles.xml throws an exception [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246)
- Reading XLSX files without styles.xml throws an exception [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246)
- Improved performance of `Style::applyFromArray()` when applied to several cells [PR #1785](https://github.com/PHPOffice/PhpSpreadsheet/issues/1785).
- Improve XLSX parsing speed if no readFilter is applied (again) - [#772](https://github.com/PHPOffice/PhpSpreadsheet/issues/772)
@@ -586,7 +959,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
`ABS()`, `ACOS()`, `ACOSH()`, `ASIN()`, `ASINH()`, `ATAN()`, `ATANH()`,
`COS()`, `COSH()`, `DEGREES()` (rad2deg), `EXP()`, `LN()` (log), `LOG10()`,
`RADIANS()` (deg2rad), `SIN()`, `SINH()`, `SQRT()`, `TAN()`, `TANH()`.
One TextData function is also affected: `REPT()` (str_repeat).
- `formatAsDate` correctly matches language metadata, reverting c55272e
- Formulae that previously crashed on sub function call returning excel error value now return said value.
@@ -1216,7 +1589,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
- Ignore inlineStr type if formula element exists - @ncrypthic [#570](https://github.com/PHPOffice/PHPExcel/issues/570)
- Excel 2007 Reader freezes because of conditional formatting - @rentalhost [#575](https://github.com/PHPOffice/PHPExcel/issues/575)
- Readers will now parse files containing worksheet titles over 31 characters [#176](https://github.com/PHPOffice/PhpSpreadsheet/pull/176)
- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625)
- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625)
### General
@@ -1239,3 +1612,6 @@ For a comprehensive list of all class changes, and a semi-automated migration pa
## Previous versions of PHPExcel
The changelog for the project when it was called PHPExcel is [still available](./CHANGELOG.PHPExcel.md).
### Changed
- Replace ezyang/htmlpurifier (LGPL2.1) with voku/anti-xss (MIT)

View File

@@ -2,19 +2,44 @@
If you would like to contribute, here are some notes and guidelines:
- All new development happens on feature/fix branches, and are then merged to the `master` branch once stable; so the `master` branch is always the most up-to-date, working code
- Tagged releases are made from the `master` branch
- If you are going to be submitting a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
- Code style might be automatically fixed by `composer fix`
- All code changes must be validated by `composer check`
- All new development should be on feature/fix branches, which are then merged to the `master` branch once stable and approved; so the `master` branch is always the most up-to-date, working code
- If you are going to submit a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
- The code must work with all PHP versions that we support.
- You can call `composer versions` to test version compatibility.
- Code style should be maintained.
- `composer style` will identify any issues with Coding Style`.
- `composer fix` will fix most issues with Coding Style.
- All code changes must be validated by `composer check`.
- Please include Unit Tests to verify that a bug exists, and that this PR fixes it.
- Please include Unit Tests to show that a new Feature works as expected.
- Please don't "bundle" several changes into a single PR; submit a PR for each discrete change/fix.
- Remember to update documentation if necessary.
- [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository")
- [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests")
## Unit Tests
When writing Unit Tests, please
- Always try to write Unit Tests for both the happy and unhappy paths.
- Put all assertions in the Test itself, not in an abstract class that the Test extends (even if this means code duplication between tests).
- Include any necessary `setup()` and `tearDown()` in the Test itself.
- If you change any global settings (such as system locale, or Compatibility Mode for Excel Function tests), make sure that you reset to the default in the `tearDown()`.
- Use the `ExcelError` functions in assertions for Excel Error values in Excel Function implementations.
<br />Not only does it reduce the risk of typos; but at some point in the future, ExcelError values will be an object rather than a string, and we won't then need to update all the tests.
- Don't over-complicate test code by testing happy and unhappy paths in the same test.
This makes it easier to see exactly what is being tested when reviewing the PR. I want to be able to see it in the PR, not have to hunt in other unchanged classes to see what the test is doing.
## How to release
1. Complete CHANGELOG.md and commit
2. Create an annotated tag
1. `git tag -a 1.2.3`
2. Tag subject must be the version number, eg: `1.2.3`
3. Tag body must be a copy-paste of the changelog entries
3. Push tag with `git push --tags`, GitHub Actions will create a GitHub release automatically
3. Tag body must be a copy-paste of the changelog entries.
3. Push the tag with `git push --tags`, GitHub Actions will create a GitHub release automatically, and the release details will automatically be sent to packagist.
4. Github seems to remove markdown headings in the Release Notes, so you should edit to restore these.
> **Note:** Tagged releases are made from the `master` branch. Only in an emergency should a tagged release be made from the `release` branch. (i.e. cherry-picked hot-fixes.)

View File

@@ -11,79 +11,9 @@
PhpSpreadsheet is a library written in pure PHP and offers a set of classes that
allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc.
## PHP Version Support
LTS: Support for PHP versions will only be maintained for a period of six months beyond the
[end of life](https://www.php.net/supported-versions) of that PHP version.
Currently the required PHP minimum version is PHP __7.4__, and we [will support that version](https://www.php.net/eol.php) until 28th June 2023.
See the `composer.json` for other requirements.
## Installation
Use [composer](https://getcomposer.org) to install PhpSpreadsheet into your project:
```sh
composer require phpoffice/phpspreadsheet
```
If you are building your installation on a development machine that is on a different PHP version to the server where it will be deployed, or if your PHP CLI version is not the same as your run-time such as `php-fpm` or Apache's `mod_php`, then you might want to add the following to your `composer.json` before installing:
```json
{
"require": {
"phpoffice/phpspreadsheet": "^1.23"
},
"config": {
"platform": {
"php": "7.4"
}
}
}
```
and then run
```sh
composer install
```
to ensure that the correct dependencies are retrieved to match your deployment environment.
See [CLI vs Application run-time](https://php.watch/articles/composer-platform-check) for more details.
### Additional Installation Options
If you want to write to PDF, or to include Charts when you write to HTML or PDF, then you will need to install additional libraries:
#### PDF
For PDF Generation, you can install any of the following, and then configure PhpSpreadsheet to indicate which library you are going to use:
- mpdf/mpdf
- dompdf/dompdf
- tecnickcom/tcpdf
and configure PhpSpreadsheet using:
```php
// Dompdf, Mpdf or Tcpdf (as appropriate)
$className = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class;
IOFactory::registerWriter('Pdf', $className);
```
or the appropriate PDF Writer wrapper for the library that you have chosen to install.
#### Chart Export
For Chart export, we support following packages, which you will also need to install yourself using `composer require`
- [jpgraph/jpgraph](https://packagist.org/packages/jpgraph/jpgraph) (this package was abandoned at version 4.0.
You can manually download the latest version that supports PHP 8 and above from [jpgraph.net](https://jpgraph.net/))
- [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) (fork with php 8.1 support)
and then configure PhpSpreadsheet using:
```php
Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph::class); // to use jpgraph/jpgraph
//or
Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer::class); // to use mitoteam/jpgraph
```
One or the other of these libraries is necessary if you want to generate HTML or PDF files that include charts.
See the [install instructions](https://phpspreadsheet.readthedocs.io/en/latest/#installation).
## Documentation
@@ -91,6 +21,42 @@ Read more about it, including install instructions, in the [official documentati
Please ask your support questions on [StackOverflow](https://stackoverflow.com/questions/tagged/phpspreadsheet), or have a quick chat on [Gitter](https://gitter.im/PHPOffice/PhpSpreadsheet).
## Patreon
I am now running a [Patreon](https://www.patreon.com/MarkBaker) to support the work that I do on PhpSpreadsheet.
Supporters will receive access to articles about working with PhpSpreadsheet, and how to use some of its more advanced features.
Posts already available to Patreon supporters:
- The Dating Game
- A look at how MS Excel (and PhpSpreadsheet) handle date and time values.
- Looping the Loop
- Advice on Iterating through the rows and cells in a worksheet.
And for Patrons at levels actively using PhpSpreadsheet:
- Behind the Mask
- A look at Number Format Masks.
The Next Article (currently Work in Progress):
- Formula for Success
- How to debug formulae that don't produce the expected result.
My aim is to post at least one article each month, taking a detailed look at some feature of MS Excel and how to use that feature in PhpSpreadsheet, or on how to perform different activities in PhpSpreadsheet.
Planned posts for the future include topics like:
- Tables
- Structured References
- AutoFiltering
- Array Formulae
- Conditional Formatting
- Data Validation
- Value Binders
- Images
- Charts
After a period of six months exclusive to Patreon supporters, articles will be incorporated into the public documentation for the library.
## PHPExcel vs PhpSpreadsheet ?
PhpSpreadsheet is the next version of PHPExcel. It breaks compatibility to dramatically improve the code base quality (namespaces, PSR compliance, use of latest PHP language features, etc.).

View File

@@ -12,6 +12,9 @@
"spreadsheet"
],
"config": {
"platform": {
"php" : "8.1.99"
},
"sort-packages": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
@@ -42,20 +45,27 @@
],
"scripts": {
"check": [
"./bin/check-phpdoc-types",
"phpcs samples/ src/ tests/ --report=checkstyle",
"phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- -n",
"php-cs-fixer fix --ansi --dry-run --diff",
"phpcs",
"phpunit --color=always",
"phpstan analyse --ansi"
"phpstan analyse --ansi --memory-limit=2048M"
],
"style": [
"phpcs samples/ src/ tests/ --report=checkstyle",
"php-cs-fixer fix --ansi --dry-run --diff"
],
"fix": [
"php-cs-fixer fix --ansi"
"phpcbf samples/ src/ tests/ --report=checkstyle",
"php-cs-fixer fix"
],
"versions": [
"phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.4- -n"
"phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- -n"
]
},
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.1",
"ext-ctype": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
@@ -69,8 +79,7 @@
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
"ezyang/htmlpurifier": "^4.15",
"maennchen/zipstream-php": "^2.1",
"maennchen/zipstream-php": "^2.1 || ^3.0",
"markbaker/complex": "^3.0",
"markbaker/matrix": "^3.0",
"psr/http-client": "^1.0",
@@ -78,15 +87,15 @@
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
"dompdf/dompdf": "^1.0 || ^2.0",
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
"dompdf/dompdf": "^2.0 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.2",
"mitoteam/jpgraph": "^10.2.4",
"mitoteam/jpgraph": "^10.3",
"mpdf/mpdf": "^8.1.1",
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.0",
"phpunit/phpunit": "^9.6 || ^10.5",
"squizlabs/php_codesniffer": "^3.7",
"tecnickcom/tcpdf": "^6.5"
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,91 +0,0 @@
<?php
$config = [];
if (PHP_VERSION_ID < 80000) {
// GdImage not available before PHP8
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Method .* has invalid return type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/Drawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Property .* has unknown class GdImage as its type\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Method .* has invalid return type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Parameter .* of method .* has invalid type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Class GdImage not found\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xls/Worksheet.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Parameter .* of method .* has invalid type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xls/Worksheet.php',
'count' => 1,
];
// GdImage with Phpstan 1.9.2
$config['parameters']['ignoreErrors'][] = [
'message' => '~Class GdImage not found.*$~',
'path' => __DIR__ . '/tests/PhpSpreadsheetTests/Worksheet/MemoryDrawingTest.php',
'count' => 3,
];
// Erroneous analysis by Phpstan before PHP8 - 3rd parameter is nullable
// Fixed for Php7 with Phpstan 1.9.
//$config['parameters']['ignoreErrors'][] = [
// 'message' => '#^Parameter \\#3 \\$namespace of method XMLWriter\\:\\:startElementNs\\(\\) expects string, null given\\.$#',
// 'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php',
// 'count' => 8,
//];
// Erroneous analysis by Phpstan before PHP8 - mb_strlen does not return false
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:countCharacters\\(\\) should return int but returns int(<0, max>)?\\|false\\.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/StringHelper.php',
'count' => 1,
];
// New with Phpstan 1.9.2 for Php7 only
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, array<int, mixed>\\|false given.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Parameter \\#1 \\$input of function array_chunk expects array, array<int, float\\|int>\\|false given.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Parameter \\#2 \\$array of function array_map expects array, array<int, float|int>\\|false given.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/MathTrig/Random.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, array<int, mixed>\\|false given.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/TextData/Text.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Property PhpOffice\\PhpSpreadsheet\\Shared\\JAMA\\Matrix::$A (array) does not accept array<int, array<int, int>|false>|false.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/JAMA/Matrix.php',
'count' => 2,
];
} else {
// Flagged in Php8+ - unsure how to correct code
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Binary operation "/" between float and array[|]float[|]int[|]string results in an error.#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php',
'count' => 2,
];
}
return $config;

View File

@@ -1,23 +1,29 @@
includes:
- phpstan-baseline.neon
- phpstan-conditional.php
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
parameters:
level: 8
paths:
- samples/
- src/
- tests/
- infra/
- bin/generate-document
- bin/generate-locales
- bin/check-phpdoc-types
excludePaths:
- src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
- src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
- src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php
- src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
- src/PhpSpreadsheet/Writer/ZipStream2.php
- src/PhpSpreadsheet/Writer/ZipStream3.php
parallel:
processTimeout: 300.0
checkMissingIterableValueType: false
ignoreErrors:
- '~^Parameter \#1 \$im(age)? of function (imagedestroy|imageistruecolor|imagealphablending|imagesavealpha|imagecolortransparent|imagecolorsforindex|imagesavealpha|imagesx|imagesy|imagepng) expects (GdImage|resource), GdImage\|resource given\.$~'
- '~^Parameter \#2 \$src_im(age)? of function imagecopy expects (GdImage|resource), GdImage\|resource given\.$~'
# Accept a bit anything for assert methods
- '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~'
- '~^Method PhpOffice\\PhpSpreadsheetTests\\.*\:\:test.*\(\) has parameter \$args with no type specified\.$~'
- '~^Variable \$helper might not be defined\.$~'
- identifier: missingType.iterableValue

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="./tests/bootstrap.php" backupGlobals="true" colors="true" cacheResultFile="/tmp/.phpspreadsheet.phpunit.result.cache" convertDeprecationsToExceptions="true">
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
<php>
<ini name="memory_limit" value="2048M"/>
<ini name="error_reporting" value="E_ALL"/>
</php>
<testsuite name="PhpSpreadsheet Unit Test Suite">
<directory>./tests/PhpSpreadsheetTests</directory>
</testsuite>
</phpunit>

View File

@@ -7,18 +7,18 @@ use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentProcessor;
trait ArrayEnabled
{
/**
* @var ArrayArgumentHelper
*/
private static $arrayArgumentHelper;
private static bool $initializationNeeded = true;
private static ArrayArgumentHelper $arrayArgumentHelper;
/**
* @param array|false $arguments Can be changed to array for Php8.1+
*/
private static function initialiseHelper($arguments): void
{
if (self::$arrayArgumentHelper === null) {
if (self::$initializationNeeded === true) {
self::$arrayArgumentHelper = new ArrayArgumentHelper();
self::$initializationNeeded = false;
}
self::$arrayArgumentHelper->initialise(($arguments === false) ? [] : $arguments);
}
@@ -43,10 +43,8 @@ trait ArrayEnabled
* and any of them can be an array argument.
* Example use for:
* ROUND() or DATE().
*
* @param mixed ...$arguments
*/
protected static function evaluateArrayArguments(callable $method, ...$arguments): array
protected static function evaluateArrayArguments(callable $method, mixed ...$arguments): array
{
self::initialiseHelper($arguments);
$arguments = self::$arrayArgumentHelper->arguments();
@@ -60,10 +58,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.
*
* @param mixed ...$arguments
*/
protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, ...$arguments): array
protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, mixed ...$arguments): array
{
self::initialiseHelper(array_slice($arguments, 0, $limit));
$trailingArguments = array_slice($arguments, $limit);
@@ -73,10 +69,7 @@ trait ArrayEnabled
return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
}
/**
* @param mixed $value
*/
private static function testFalse($value): bool
private static function testFalse(mixed $value): bool
{
return $value === false;
}
@@ -87,10 +80,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.
*
* @param mixed ...$arguments
*/
protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, ...$arguments): array
protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, mixed ...$arguments): array
{
$arrayArgumentsSubset = array_combine(
range($start, count($arguments) - $start),
@@ -114,10 +105,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.
*
* @param mixed ...$arguments
*/
protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, ...$arguments): array
protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, mixed ...$arguments): array
{
$leadingArguments = array_slice($arguments, 0, $ignore);
$ignoreArgument = array_slice($arguments, $ignore, 1);

View File

@@ -17,7 +17,7 @@ class BinaryComparison
* @param null|string $str1 First string value for the comparison
* @param null|string $str2 Second string value for the comparison
*/
private static function strcmpLowercaseFirst($str1, $str2): int
private static function strcmpLowercaseFirst(?string $str1, ?string $str2): int
{
$inversedStr1 = StringHelper::strCaseReverse($str1 ?? '');
$inversedStr2 = StringHelper::strCaseReverse($str2 ?? '');
@@ -31,16 +31,12 @@ class BinaryComparison
* @param null|string $str1 First string value for the comparison
* @param null|string $str2 Second string value for the comparison
*/
private static function strcmpAllowNull($str1, $str2): int
private static function strcmpAllowNull(?string $str1, ?string $str2): int
{
return strcmp($str1 ?? '', $str2 ?? '');
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
public static function compare($operand1, $operand2, string $operator): bool
public static function compare(mixed $operand1, mixed $operand2, string $operator): bool
{
// Simple validate the two operands if they are string values
if (is_string($operand1) && $operand1 > '' && $operand1[0] == Calculation::FORMULA_STRING_QUOTE) {
@@ -60,48 +56,27 @@ class BinaryComparison
}
}
$useLowercaseFirstComparison = is_string($operand1) &&
is_string($operand2) &&
Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
$useLowercaseFirstComparison = is_string($operand1)
&& is_string($operand2)
&& Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
return self::evaluateComparison($operand1, $operand2, $operator, $useLowercaseFirstComparison);
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function evaluateComparison($operand1, $operand2, string $operator, bool $useLowercaseFirstComparison): bool
private static function evaluateComparison(mixed $operand1, mixed $operand2, string $operator, bool $useLowercaseFirstComparison): bool
{
switch ($operator) {
// Equality
case '=':
return self::equal($operand1, $operand2);
// Greater than
case '>':
return self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison);
// Less than
case '<':
return self::lessThan($operand1, $operand2, $useLowercaseFirstComparison);
// Greater than or equal
case '>=':
return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison);
// Less than or equal
case '<=':
return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison);
// Inequality
case '<>':
return self::notEqual($operand1, $operand2);
default:
throw new Exception('Unsupported binary comparison operator');
}
return match ($operator) {
'=' => self::equal($operand1, $operand2),
'>' => self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison),
'<' => self::lessThan($operand1, $operand2, $useLowercaseFirstComparison),
'>=' => self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
'<=' => self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
'<>' => self::notEqual($operand1, $operand2),
default => throw new Exception('Unsupported binary comparison operator'),
};
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function equal($operand1, $operand2): bool
private static function equal(mixed $operand1, mixed $operand2): bool
{
if (is_numeric($operand1) && is_numeric($operand2)) {
$result = (abs($operand1 - $operand2) < self::DELTA);
@@ -114,11 +89,7 @@ class BinaryComparison
return $result;
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function greaterThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
private static function greaterThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
{
if (is_numeric($operand1) && is_numeric($operand2)) {
$result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 > $operand2));
@@ -133,11 +104,7 @@ class BinaryComparison
return $result;
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function lessThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
private static function lessThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
{
if (is_numeric($operand1) && is_numeric($operand2)) {
$result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 < $operand2));
@@ -152,29 +119,17 @@ class BinaryComparison
return $result;
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function greaterThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
private static function greaterThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
{
return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function lessThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
private static function lessThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
{
return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
}
/**
* @param mixed $operand1
* @param mixed $operand2
*/
private static function notEqual($operand1, $operand2): bool
private static function notEqual(mixed $operand1, mixed $operand2): bool
{
return self::equal($operand1, $operand2) !== true;
}

View File

@@ -1,430 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation;
/**
* @deprecated 1.17.0
*
* @codeCoverageIgnore
*/
class Database
{
/**
* DAVERAGE.
*
* Averages the values in a column of a list or database that match conditions you specify.
*
* Excel Function:
* DAVERAGE(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DAverage class instead
* @see Database\DAverage::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function DAVERAGE($database, $field, $criteria)
{
return Database\DAverage::evaluate($database, $field, $criteria);
}
/**
* DCOUNT.
*
* Counts the cells that contain numbers in a column of a list or database that match conditions
* that you specify.
*
* Excel Function:
* DCOUNT(database,[field],criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DCount class instead
* @see Database\DCount::evaluate()
*
* @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|int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return int|string
*
* @TODO The field argument is optional. If field is omitted, DCOUNT counts all records in the
* database that match the criteria.
*/
public static function DCOUNT($database, $field, $criteria)
{
return Database\DCount::evaluate($database, $field, $criteria);
}
/**
* DCOUNTA.
*
* Counts the nonblank cells in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DCOUNTA(database,[field],criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DCountA class instead
* @see Database\DCountA::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return int|string
*/
public static function DCOUNTA($database, $field, $criteria)
{
return Database\DCountA::evaluate($database, $field, $criteria);
}
/**
* DGET.
*
* Extracts a single value from a column of a list or database that matches conditions that you
* specify.
*
* Excel Function:
* DGET(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DGet class instead
* @see Database\DGet::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return mixed
*/
public static function DGET($database, $field, $criteria)
{
return Database\DGet::evaluate($database, $field, $criteria);
}
/**
* DMAX.
*
* Returns the largest number in a column of a list or database that matches conditions you that
* specify.
*
* Excel Function:
* DMAX(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DMax class instead
* @see Database\DMax::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function DMAX($database, $field, $criteria)
{
return Database\DMax::evaluate($database, $field, $criteria);
}
/**
* DMIN.
*
* Returns the smallest number in a column of a list or database that matches conditions you that
* specify.
*
* Excel Function:
* DMIN(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DMin class instead
* @see Database\DMin::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function DMIN($database, $field, $criteria)
{
return Database\DMin::evaluate($database, $field, $criteria);
}
/**
* DPRODUCT.
*
* Multiplies the values in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DPRODUCT(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DProduct class instead
* @see Database\DProduct::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function DPRODUCT($database, $field, $criteria)
{
return Database\DProduct::evaluate($database, $field, $criteria);
}
/**
* DSTDEV.
*
* Estimates the standard deviation of a population based on a sample by using the numbers in a
* column of a list or database that match conditions that you specify.
*
* Excel Function:
* DSTDEV(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DStDev class instead
* @see Database\DStDev::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function DSTDEV($database, $field, $criteria)
{
return Database\DStDev::evaluate($database, $field, $criteria);
}
/**
* DSTDEVP.
*
* Calculates the standard deviation of a population based on the entire population by using the
* numbers in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DSTDEVP(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DStDevP class instead
* @see Database\DStDevP::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function DSTDEVP($database, $field, $criteria)
{
return Database\DStDevP::evaluate($database, $field, $criteria);
}
/**
* DSUM.
*
* Adds the numbers in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DSUM(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DSum class instead
* @see Database\DSum::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function DSUM($database, $field, $criteria)
{
return Database\DSum::evaluate($database, $field, $criteria);
}
/**
* DVAR.
*
* Estimates the variance of a population based on a sample by using the numbers in a column
* of a list or database that match conditions that you specify.
*
* Excel Function:
* DVAR(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DVar class instead
* @see Database\DVar::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string (string if result is an error)
*/
public static function DVAR($database, $field, $criteria)
{
return Database\DVar::evaluate($database, $field, $criteria);
}
/**
* DVARP.
*
* Calculates the variance of a population based on the entire population by using the numbers
* in a column of a list or database that match conditions that you specify.
*
* Excel Function:
* DVARP(database,field,criteria)
*
* @deprecated 1.17.0
* Use the evaluate() method in the Database\DVarP class instead
* @see Database\DVarP::evaluate()
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param int|string $field Indicates which column is used in the function. Enter the
* column label enclosed between double quotation marks, such as
* "Age" or "Yield," or a number (without quotation marks) that
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string (string if result is an error)
*/
public static function DVARP($database, $field, $criteria)
{
return Database\DVarP::evaluate($database, $field, $criteria);
}
}

View File

@@ -19,7 +19,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -29,10 +29,8 @@ class DAverage extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|int|float
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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|int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -30,10 +30,8 @@ class DCount extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return int|string
*/
public static function evaluate($database, $field, $criteria, bool $returnError = true)
public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnError = true): string|int
{
$field = self::fieldExtract($database, $field);
if ($returnError && $field === null) {

View File

@@ -19,7 +19,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -29,10 +29,8 @@ class DCountA extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return int|string
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|int
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -19,7 +19,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -29,10 +29,8 @@ class DGet extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return mixed
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): null|float|int|string
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -30,10 +30,8 @@ class DMax extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria, bool $returnError = true)
public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnError = true): null|float|string
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -30,10 +30,8 @@ class DMin extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria, bool $returnError = true)
public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnError = true): float|string|null
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -19,7 +19,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -29,10 +29,8 @@ class DProduct extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|float
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -30,10 +30,8 @@ class DStDev extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): float|string
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -30,10 +30,8 @@ class DStDevP extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return float|string
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): float|string
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -19,7 +19,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -29,10 +29,8 @@ class DSum extends DatabaseAbstract
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return null|float|string
*/
public static function evaluate($database, $field, $criteria, bool $returnNull = false)
public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnNull = false): null|float|string
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -33,7 +33,7 @@ class DVar extends DatabaseAbstract
*
* @return float|string (string if result is an error)
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|float
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -20,7 +20,7 @@ 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 int|string $field Indicates which column is used in the function. Enter the
* @param null|array|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
@@ -33,7 +33,7 @@ class DVarP extends DatabaseAbstract
*
* @return float|string (string if result is an error)
*/
public static function evaluate($database, $field, $criteria)
public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|float
{
$field = self::fieldExtract($database, $field);
if ($field === null) {

View File

@@ -8,14 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
abstract class DatabaseAbstract
{
/**
* @param array $database
* @param int|string $field
* @param array $criteria
*
* @return null|float|int|string
*/
abstract public static function evaluate($database, $field, $criteria);
abstract public static function evaluate(array $database, array|null|int|string $field, array $criteria): null|float|int|string;
/**
* fieldExtract.
@@ -32,7 +25,7 @@ abstract class DatabaseAbstract
* represents the position of the column within the list: 1 for
* the first column, 2 for the second column, and so on.
*/
protected static function fieldExtract(array $database, $field): ?int
protected static function fieldExtract(array $database, mixed $field): ?int
{
$field = strtoupper(Functions::flattenSingleValue($field) ?? '');
if ($field === '') {
@@ -115,19 +108,14 @@ abstract class DatabaseAbstract
}
$rowQuery = array_map(
function ($rowValue) {
return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? '');
},
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''),
$baseQuery
);
return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
}
/**
* @param mixed $criterion
*/
private static function buildCondition($criterion, string $criterionName): string
private static function buildCondition(mixed $criterion, string $criterionName): string
{
$ifCondition = Functions::ifCondition($criterion);
@@ -168,10 +156,7 @@ abstract class DatabaseAbstract
return $database;
}
/**
* @return mixed
*/
private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions): string
{
$key = array_search($criterion, $fields, true);
@@ -181,7 +166,7 @@ abstract class DatabaseAbstract
} elseif ($dataValues[$key] !== null) {
$dataValue = $dataValues[$key];
// escape quotes if we have a string containing quotes
if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
if (is_string($dataValue) && str_contains($dataValue, '"')) {
$dataValue = str_replace('"', '""', $dataValue);
}
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;

View File

@@ -1,890 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation;
use DateTimeInterface;
/**
* @deprecated 1.18.0
*/
class DateTime
{
/**
* Identify if a year is a leap year or not.
*
* @deprecated 1.18.0
* Use the isLeapYear method in the DateTimeExcel\Helpers class instead
* @see DateTimeExcel\Helpers::isLeapYear()
*
* @param int|string $year The year to test
*
* @return bool TRUE if the year is a leap year, otherwise FALSE
*/
public static function isLeapYear($year)
{
return DateTimeExcel\Helpers::isLeapYear($year);
}
/**
* getDateValue.
*
* @deprecated 1.18.0
* Use the getDateValue method in the DateTimeExcel\Helpers class instead
* @see DateTimeExcel\Helpers::getDateValue()
*
* @param mixed $dateValue
*
* @return mixed Excel date/time serial value, or string if error
*/
public static function getDateValue($dateValue)
{
try {
return DateTimeExcel\Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
}
/**
* DATETIMENOW.
*
* Returns the current date and time.
* The NOW function is useful when you need to display the current date and time on a worksheet or
* calculate a value based on the current date and time, and have that value updated each time you
* open the worksheet.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* NOW()
*
* @deprecated 1.18.0
* Use the now method in the DateTimeExcel\Current class instead
* @see DateTimeExcel\Current::now()
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function DATETIMENOW()
{
return DateTimeExcel\Current::now();
}
/**
* DATENOW.
*
* Returns the current date.
* The NOW function is useful when you need to display the current date and time on a worksheet or
* calculate a value based on the current date and time, and have that value updated each time you
* open the worksheet.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TODAY()
*
* @deprecated 1.18.0
* Use the today method in the DateTimeExcel\Current class instead
* @see DateTimeExcel\Current::today()
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function DATENOW()
{
return DateTimeExcel\Current::today();
}
/**
* DATE.
*
* The DATE function returns a value that represents a particular date.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
*
* Excel Function:
* DATE(year,month,day)
*
* @deprecated 1.18.0
* Use the fromYMD method in the DateTimeExcel\Date class instead
* @see DateTimeExcel\Date::fromYMD()
*
* PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
* A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
* as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
*
* @param int $year The value of the year argument can include one to four digits.
* Excel interprets the year argument according to the configured
* date system: 1900 or 1904.
* If year is between 0 (zero) and 1899 (inclusive), Excel adds that
* value to 1900 to calculate the year. For example, DATE(108,1,2)
* returns January 2, 2008 (1900+108).
* If year is between 1900 and 9999 (inclusive), Excel uses that
* value as the year. For example, DATE(2008,1,2) returns January 2,
* 2008.
* If year is less than 0 or is 10000 or greater, Excel returns the
* #NUM! error value.
* @param int $month A positive or negative integer representing the month of the year
* from 1 to 12 (January to December).
* If month is greater than 12, month adds that number of months to
* the first month in the year specified. For example, DATE(2008,14,2)
* returns the serial number representing February 2, 2009.
* If month is less than 1, month subtracts the magnitude of that
* number of months, plus 1, from the first month in the year
* specified. For example, DATE(2008,-3,2) returns the serial number
* representing September 2, 2007.
* @param int $day A positive or negative integer representing the day of the month
* from 1 to 31.
* If day is greater than the number of days in the month specified,
* day adds that number of days to the first day in the month. For
* example, DATE(2008,1,35) returns the serial number representing
* February 4, 2008.
* If day is less than 1, day subtracts the magnitude that number of
* days, plus one, from the first day of the month specified. For
* example, DATE(2008,1,-15) returns the serial number representing
* December 16, 2007.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function DATE($year = 0, $month = 1, $day = 1)
{
return DateTimeExcel\Date::fromYMD($year, $month, $day);
}
/**
* TIME.
*
* The TIME function returns a value that represents a particular time.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TIME(hour,minute,second)
*
* @deprecated 1.18.0
* Use the fromHMS method in the DateTimeExcel\Time class instead
* @see DateTimeExcel\Time::fromHMS()
*
* @param int $hour A number from 0 (zero) to 32767 representing the hour.
* Any value greater than 23 will be divided by 24 and the remainder
* will be treated as the hour value. For example, TIME(27,0,0) =
* TIME(3,0,0) = .125 or 3:00 AM.
* @param int $minute A number from 0 to 32767 representing the minute.
* Any value greater than 59 will be converted to hours and minutes.
* For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
* @param int $second A number from 0 to 32767 representing the second.
* Any value greater than 59 will be converted to hours, minutes,
* and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
* or 12:33:20 AM
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function TIME($hour = 0, $minute = 0, $second = 0)
{
return DateTimeExcel\Time::fromHMS($hour, $minute, $second);
}
/**
* DATEVALUE.
*
* Returns a value that represents a particular date.
* Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
* value.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* DATEVALUE(dateValue)
*
* @deprecated 1.18.0
* Use the fromString method in the DateTimeExcel\DateValue class instead
* @see DateTimeExcel\DateValue::fromString()
*
* @param string $dateValue Text that represents a date in a Microsoft Excel date format.
* For example, "1/30/2008" or "30-Jan-2008" are text strings within
* quotation marks that represent dates. Using the default date
* system in Excel for Windows, date_text must represent a date from
* January 1, 1900, to December 31, 9999. Using the default date
* system in Excel for the Macintosh, date_text must represent a date
* from January 1, 1904, to December 31, 9999. DATEVALUE returns the
* #VALUE! error value if date_text is out of this range.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function DATEVALUE($dateValue)
{
return DateTimeExcel\DateValue::fromString($dateValue);
}
/**
* TIMEVALUE.
*
* Returns a value that represents a particular time.
* Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
* value.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TIMEVALUE(timeValue)
*
* @deprecated 1.18.0
* Use the fromString method in the DateTimeExcel\TimeValue class instead
* @see DateTimeExcel\TimeValue::fromString()
*
* @param string $timeValue A text string that represents a time in any one of the Microsoft
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
* within quotation marks that represent time.
* Date information in time_text is ignored.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function TIMEVALUE($timeValue)
{
return DateTimeExcel\TimeValue::fromString($timeValue);
}
/**
* DATEDIF.
*
* Excel Function:
* DATEDIF(startdate, enddate, unit)
*
* @deprecated 1.18.0
* Use the interval method in the DateTimeExcel\Difference class instead
* @see DateTimeExcel\Difference::interval()
*
* @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
* @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
* or a standard date string
* @param array|string $unit
*
* @return array|int|string Interval between the dates
*/
public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
{
return DateTimeExcel\Difference::interval($startDate, $endDate, $unit);
}
/**
* DAYS.
*
* Returns the number of days between two dates
*
* Excel Function:
* DAYS(endDate, startDate)
*
* @deprecated 1.18.0
* Use the between method in the DateTimeExcel\Days class instead
* @see DateTimeExcel\Days::between()
*
* @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
* @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
* PHP date timestamp (integer), PHP DateTime object, or a standard date string
*
* @return array|int|string Number of days between start date and end date or an error
*/
public static function DAYS($endDate = 0, $startDate = 0)
{
return DateTimeExcel\Days::between($endDate, $startDate);
}
/**
* DAYS360.
*
* Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
* which is used in some accounting calculations. Use this function to help compute payments if
* your accounting system is based on twelve 30-day months.
*
* Excel Function:
* DAYS360(startDate,endDate[,method])
*
* @deprecated 1.18.0
* Use the between method in the DateTimeExcel\Days360 class instead
* @see DateTimeExcel\Days360::between()
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param array|bool $method US or European Method
* FALSE or omitted: U.S. (NASD) method. If the starting date is
* the last day of a month, it becomes equal to the 30th of the
* same month. If the ending date is the last day of a month and
* the starting date is earlier than the 30th of a month, the
* ending date becomes equal to the 1st of the next month;
* otherwise the ending date becomes equal to the 30th of the
* same month.
* TRUE: European method. Starting dates and ending dates that
* occur on the 31st of a month become equal to the 30th of the
* same month.
*
* @return array|int|string Number of days between start date and end date
*/
public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
{
return DateTimeExcel\Days360::between($startDate, $endDate, $method);
}
/**
* YEARFRAC.
*
* Calculates the fraction of the year represented by the number of whole days between two dates
* (the start_date and the end_date).
* Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
* obligations to assign to a specific term.
*
* Excel Function:
* YEARFRAC(startDate,endDate[,method])
*
* @deprecated 1.18.0
* Use the fraction method in the DateTimeExcel\YearFrac class instead
* @see DateTimeExcel\YearFrac::fraction()
*
* See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
* for description of algorithm used in Excel
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param array|int $method Method used for the calculation
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return array|float|string fraction of the year, or a string containing an error
*/
public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
{
return DateTimeExcel\YearFrac::fraction($startDate, $endDate, $method);
}
/**
* NETWORKDAYS.
*
* Returns the number of whole working days between start_date and end_date. Working days
* exclude weekends and any dates identified in holidays.
* Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
* worked during a specific term.
*
* Excel Function:
* NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
*
* @deprecated 1.18.0
* Use the count method in the DateTimeExcel\NetworkDays class instead
* @see DateTimeExcel\NetworkDays::count()
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param mixed $dateArgs
*
* @return array|int|string Interval between the dates
*/
public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
{
return DateTimeExcel\NetworkDays::count($startDate, $endDate, ...$dateArgs);
}
/**
* WORKDAY.
*
* Returns the date that is the indicated number of working days before or after a date (the
* starting date). Working days exclude weekends and any dates identified as holidays.
* Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
* delivery times, or the number of days of work performed.
*
* Excel Function:
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
*
* @deprecated 1.18.0
* Use the date method in the DateTimeExcel\WorkDay class instead
* @see DateTimeExcel\WorkDay::date()
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $endDays The number of nonweekend and nonholiday days before or after
* startDate. A positive value for days yields a future date; a
* negative value yields a past date.
* @param mixed $dateArgs
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function WORKDAY($startDate, $endDays, ...$dateArgs)
{
return DateTimeExcel\WorkDay::date($startDate, $endDays, ...$dateArgs);
}
/**
* DAYOFMONTH.
*
* Returns the day of the month, for a specified date. The day is given as an integer
* ranging from 1 to 31.
*
* Excel Function:
* DAY(dateValue)
*
* @deprecated 1.18.0
* Use the day method in the DateTimeExcel\DateParts class instead
* @see DateTimeExcel\DateParts::day()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return array|int|string Day of the month
*/
public static function DAYOFMONTH($dateValue = 1)
{
return DateTimeExcel\DateParts::day($dateValue);
}
/**
* WEEKDAY.
*
* Returns the day of the week for a specified date. The day is given as an integer
* ranging from 0 to 7 (dependent on the requested style).
*
* Excel Function:
* WEEKDAY(dateValue[,style])
*
* @deprecated 1.18.0
* Use the day method in the DateTimeExcel\Week class instead
* @see DateTimeExcel\Week::day()
*
* @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $style A number that determines the type of return value
* 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
* 2 Numbers 1 (Monday) through 7 (Sunday).
* 3 Numbers 0 (Monday) through 6 (Sunday).
*
* @return array|int|string Day of the week value
*/
public static function WEEKDAY($dateValue = 1, $style = 1)
{
return DateTimeExcel\Week::day($dateValue, $style);
}
/**
* STARTWEEK_SUNDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_SUNDAY
* @see DateTimeExcel\Constants::STARTWEEK_SUNDAY
*/
const STARTWEEK_SUNDAY = 1;
/**
* STARTWEEK_MONDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_MONDAY
* @see DateTimeExcel\Constants::STARTWEEK_MONDAY
*/
const STARTWEEK_MONDAY = 2;
/**
* STARTWEEK_MONDAY_ALT.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
* @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
*/
const STARTWEEK_MONDAY_ALT = 11;
/**
* STARTWEEK_TUESDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_TUESDAY
* @see DateTimeExcel\Constants::STARTWEEK_TUESDAY
*/
const STARTWEEK_TUESDAY = 12;
/**
* STARTWEEK_WEDNESDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
* @see DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
*/
const STARTWEEK_WEDNESDAY = 13;
/**
* STARTWEEK_THURSDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_THURSDAY
* @see DateTimeExcel\Constants::STARTWEEK_THURSDAY
*/
const STARTWEEK_THURSDAY = 14;
/**
* STARTWEEK_FRIDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_FRIDAY
* @see DateTimeExcel\Constants::STARTWEEK_FRIDAY
*/
const STARTWEEK_FRIDAY = 15;
/**
* STARTWEEK_SATURDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_SATURDAY
* @see DateTimeExcel\Constants::STARTWEEK_SATURDAY
*/
const STARTWEEK_SATURDAY = 16;
/**
* STARTWEEK_SUNDAY_ALT.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
* @see DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
*/
const STARTWEEK_SUNDAY_ALT = 17;
/**
* DOW_SUNDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_SUNDAY
* @see DateTimeExcel\Constants::DOW_SUNDAY
*/
const DOW_SUNDAY = 1;
/**
* DOW_MONDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_MONDAY
* @see DateTimeExcel\Constants::DOW_MONDAY
*/
const DOW_MONDAY = 2;
/**
* DOW_TUESDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_TUESDAY
* @see DateTimeExcel\Constants::DOW_TUESDAY
*/
const DOW_TUESDAY = 3;
/**
* DOW_WEDNESDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_WEDNESDAY
* @see DateTimeExcel\Constants::DOW_WEDNESDAY
*/
const DOW_WEDNESDAY = 4;
/**
* DOW_THURSDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_THURSDAY
* @see DateTimeExcel\Constants::DOW_THURSDAY
*/
const DOW_THURSDAY = 5;
/**
* DOW_FRIDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_FRIDAY
* @see DateTimeExcel\Constants::DOW_FRIDAY
*/
const DOW_FRIDAY = 6;
/**
* DOW_SATURDAY.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::DOW_SATURDAY
* @see DateTimeExcel\Constants::DOW_SATURDAY
*/
const DOW_SATURDAY = 7;
/**
* STARTWEEK_MONDAY_ISO.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
* @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
*/
const STARTWEEK_MONDAY_ISO = 21;
/**
* METHODARR.
*
* @deprecated 1.18.0
* Use DateTimeExcel\Constants::METHODARR
* @see DateTimeExcel\Constants::METHODARR
*/
const METHODARR = [
self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
self::DOW_MONDAY,
self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
self::DOW_TUESDAY,
self::DOW_WEDNESDAY,
self::DOW_THURSDAY,
self::DOW_FRIDAY,
self::DOW_SATURDAY,
self::DOW_SUNDAY,
self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
];
/**
* WEEKNUM.
*
* Returns the week of the year for a specified date.
* The WEEKNUM function considers the week containing January 1 to be the first week of the year.
* However, there is a European standard that defines the first week as the one with the majority
* of days (four or more) falling in the new year. This means that for years in which there are
* three days or less in the first week of January, the WEEKNUM function returns week numbers
* that are incorrect according to the European standard.
*
* Excel Function:
* WEEKNUM(dateValue[,style])
*
* @deprecated 1.18.0
* Use the number method in the DateTimeExcel\Week class instead
* @see DateTimeExcel\Week::number()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $method Week begins on Sunday or Monday
* 1 or omitted Week begins on Sunday.
* 2 Week begins on Monday.
* 11 Week begins on Monday.
* 12 Week begins on Tuesday.
* 13 Week begins on Wednesday.
* 14 Week begins on Thursday.
* 15 Week begins on Friday.
* 16 Week begins on Saturday.
* 17 Week begins on Sunday.
* 21 ISO (Jan. 4 is week 1, begins on Monday).
*
* @return array|int|string Week Number
*/
public static function WEEKNUM($dateValue = 1, $method = /** @scrutinizer ignore-deprecated */ self::STARTWEEK_SUNDAY)
{
return DateTimeExcel\Week::number($dateValue, $method);
}
/**
* ISOWEEKNUM.
*
* Returns the ISO 8601 week number of the year for a specified date.
*
* Excel Function:
* ISOWEEKNUM(dateValue)
*
* @deprecated 1.18.0
* Use the isoWeekNumber method in the DateTimeExcel\Week class instead
* @see DateTimeExcel\Week::isoWeekNumber()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return array|int|string Week Number
*/
public static function ISOWEEKNUM($dateValue = 1)
{
return DateTimeExcel\Week::isoWeekNumber($dateValue);
}
/**
* MONTHOFYEAR.
*
* Returns the month of a date represented by a serial number.
* The month is given as an integer, ranging from 1 (January) to 12 (December).
*
* Excel Function:
* MONTH(dateValue)
*
* @deprecated 1.18.0
* Use the month method in the DateTimeExcel\DateParts class instead
* @see DateTimeExcel\DateParts::month()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return array|int|string Month of the year
*/
public static function MONTHOFYEAR($dateValue = 1)
{
return DateTimeExcel\DateParts::month($dateValue);
}
/**
* YEAR.
*
* Returns the year corresponding to a date.
* The year is returned as an integer in the range 1900-9999.
*
* Excel Function:
* YEAR(dateValue)
*
* @deprecated 1.18.0
* Use the ear method in the DateTimeExcel\DateParts class instead
* @see DateTimeExcel\DateParts::year()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*
* @return array|int|string Year
*/
public static function YEAR($dateValue = 1)
{
return DateTimeExcel\DateParts::year($dateValue);
}
/**
* HOUROFDAY.
*
* Returns the hour of a time value.
* The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
*
* Excel Function:
* HOUR(timeValue)
*
* @deprecated 1.18.0
* Use the hour method in the DateTimeExcel\TimeParts class instead
* @see DateTimeExcel\TimeParts::hour()
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
* @return array|int|string Hour
*/
public static function HOUROFDAY($timeValue = 0)
{
return DateTimeExcel\TimeParts::hour($timeValue);
}
/**
* MINUTE.
*
* Returns the minutes of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* MINUTE(timeValue)
*
* @deprecated 1.18.0
* Use the minute method in the DateTimeExcel\TimeParts class instead
* @see DateTimeExcel\TimeParts::minute()
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
* @return array|int|string Minute
*/
public static function MINUTE($timeValue = 0)
{
return DateTimeExcel\TimeParts::minute($timeValue);
}
/**
* SECOND.
*
* Returns the seconds of a time value.
* The second is given as an integer in the range 0 (zero) to 59.
*
* Excel Function:
* SECOND(timeValue)
*
* @deprecated 1.18.0
* Use the second method in the DateTimeExcel\TimeParts class instead
* @see DateTimeExcel\TimeParts::second()
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
*
* @return array|int|string Second
*/
public static function SECOND($timeValue = 0)
{
return DateTimeExcel\TimeParts::second($timeValue);
}
/**
* EDATE.
*
* Returns the serial number that represents the date that is the indicated number of months
* before or after a specified date (the start_date).
* Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
* as the date of issue.
*
* Excel Function:
* EDATE(dateValue,adjustmentMonths)
*
* @deprecated 1.18.0
* Use the adjust method in the DateTimeExcel\Edate class instead
* @see DateTimeExcel\Month::adjust()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
* A positive value for months yields a future date;
* a negative value yields a past date.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
{
return DateTimeExcel\Month::adjust($dateValue, $adjustmentMonths);
}
/**
* EOMONTH.
*
* Returns the date value for the last day of the month that is the indicated number of months
* before or after start_date.
* Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
*
* Excel Function:
* EOMONTH(dateValue,adjustmentMonths)
*
* @deprecated 1.18.0
* Use the lastDay method in the DateTimeExcel\EoMonth class instead
* @see DateTimeExcel\Month::lastDay()
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* @param int $adjustmentMonths The number of months before or after start_date.
* A positive value for months yields a future date;
* a negative value yields a past date.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
{
return DateTimeExcel\Month::lastDay($dateValue, $adjustmentMonths);
}
}

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -21,10 +22,10 @@ class Current
* Excel Function:
* TODAY()
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @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
*/
public static function today()
public static function today(): DateTime|float|int|string
{
$dti = new DateTimeImmutable();
$dateArray = Helpers::dateParse($dti->format('c'));
@@ -46,10 +47,10 @@ class Current
* Excel Function:
* NOW()
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @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
*/
public static function now()
public static function now(): DateTime|float|int|string
{
$dti = new DateTimeImmutable();
$dateArray = Helpers::dateParse($dti->format('c'));

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -27,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|int $year The value of the year argument can include one to four digits.
* @param array|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
@@ -38,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|int $month A positive or negative integer representing the month of the year
* @param array|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)
@@ -47,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|int $day A positive or negative integer representing the day of the month
* @param array|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
@@ -58,12 +59,12 @@ class Date
* example, DATE(2008,1,-15) returns the serial number representing
* December 16, 2007.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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($year, $month, $day)
public static function fromYMD(array|float|int|string $year, array|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);
@@ -72,26 +73,24 @@ class Date
$baseYear = SharedDateHelper::getExcelCalendar();
try {
$year = self::getYear($year, $baseYear); // must be int - Scrutinizer is wrong
$year = self::getYear($year, $baseYear);
$month = self::getMonth($month);
$day = self::getDay($day);
self::adjustYearMonth(/** @scrutinizer ignore-type */ $year, $month, $baseYear);
self::adjustYearMonth($year, $month, $baseYear);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$excelDateValue = SharedDateHelper::formattedPHPToExcel(/** @scrutinizer ignore-type */ $year, $month, $day);
$excelDateValue = SharedDateHelper::formattedPHPToExcel($year, $month, $day);
return Helpers::returnIn3FormatsFloat($excelDateValue);
}
/**
* Convert year from multiple formats to int.
*
* @param mixed $year
*/
private static function getYear($year, int $baseYear): int
private static function getYear(mixed $year, int $baseYear): int
{
$year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
if (!is_numeric($year)) {
@@ -115,10 +114,8 @@ class Date
/**
* Convert month from multiple formats to int.
*
* @param mixed $month
*/
private static function getMonth($month): int
private static function getMonth(mixed $month): int
{
if (($month !== null) && (!is_numeric($month))) {
$month = SharedDateHelper::monthStringToNumber($month);
@@ -134,10 +131,8 @@ class Date
/**
* Convert day from multiple formats to int.
*
* @param mixed $day
*/
private static function getDay($day): int
private static function getDay(mixed $day): int
{
if (($day !== null) && (!is_numeric($day))) {
$day = SharedDateHelper::dayStringToNumber($day);

View File

@@ -28,7 +28,7 @@ class DateParts
* 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 day($dateValue)
public static function day(mixed $dateValue): array|int|string
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -47,6 +47,7 @@ class DateParts
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
SharedDateHelper::roundMicroseconds($PHPDateObject);
return (int) $PHPDateObject->format('j');
}
@@ -68,7 +69,7 @@ class DateParts
* 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 month($dateValue)
public static function month(mixed $dateValue): array|string|int
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -85,6 +86,7 @@ class DateParts
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
SharedDateHelper::roundMicroseconds($PHPDateObject);
return (int) $PHPDateObject->format('n');
}
@@ -106,7 +108,7 @@ class DateParts
* 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 year($dateValue)
public static function year(mixed $dateValue): array|string|int
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -123,6 +125,7 @@ class DateParts
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
SharedDateHelper::roundMicroseconds($PHPDateObject);
return (int) $PHPDateObject->format('Y');
}
@@ -131,7 +134,7 @@ class DateParts
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*/
private static function weirdCondition($dateValue): int
private static function weirdCondition(mixed $dateValue): int
{
// Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -24,7 +25,7 @@ class DateValue
* Excel Function:
* DATEVALUE(dateValue)
*
* @param array|string $dateValue Text that represents a date in a Microsoft Excel date format.
* @param null|array|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
@@ -34,20 +35,25 @@ class DateValue
* #VALUE! error value if date_text is out of this range.
* Or can be an array of date values
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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 fromString($dateValue)
public static function fromString(null|array|string|int|bool|float $dateValue): array|string|float|int|DateTime
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
}
// try to parse as date iff there is at least one digit
if (is_string($dateValue) && preg_match('/\\d/', $dateValue) !== 1) {
return ExcelError::VALUE();
}
$dti = new DateTimeImmutable();
$baseYear = SharedDateHelper::getExcelCalendar();
$dateValue = trim($dateValue ?? '', '"');
$dateValue = trim((string) $dateValue, '"');
// Strip any ordinals because they're allowed in Excel (English only)
$dateValue = (string) preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
// Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
@@ -69,7 +75,7 @@ class DateValue
}
if (count($t1) === 1) {
// We've been fed a time value without any date
return ((strpos((string) $t, ':') === false)) ? ExcelError::Value() : 0.0;
return ((!str_contains((string) $t, ':'))) ? ExcelError::Value() : 0.0;
}
unset($t);
@@ -126,10 +132,10 @@ class DateValue
/**
* Final results.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @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
*/
private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear): string|float|int|DateTime
{
$retValue = ExcelError::Value();
if (Helpers::dateParseSucceeded($PHPDateArray)) {

View File

@@ -31,7 +31,7 @@ class Days
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function between($endDate, $startDate)
public static function between(array|DateTimeInterface|float|int|string $endDate, array|DateTimeInterface|float|int|string $startDate): array|int|string
{
if (is_array($endDate) || is_array($startDate)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $endDate, $startDate);

View File

@@ -44,7 +44,7 @@ class Days360
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function between($startDate = 0, $endDate = 0, $method = false)
public static function between(mixed $startDate = 0, mixed $endDate = 0, mixed $method = false): array|string|int
{
if (is_array($startDate) || is_array($endDate) || is_array($method)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);

View File

@@ -22,14 +22,13 @@ 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|string $unit Or can be an array of unit values
*
* @return array|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
*/
public static function interval($startDate, $endDate, $unit = 'D')
public static function interval(mixed $startDate, mixed $endDate, array|string $unit = 'D')
{
if (is_array($startDate) || is_array($endDate) || is_array($unit)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $unit);
@@ -80,12 +79,8 @@ class Difference
/**
* Decide whether it's time to set retVal.
*
* @param bool|int $retVal
*
* @return null|bool|int
*/
private static function replaceRetValue($retVal, string $unit, string $compare)
private static function replaceRetValue(bool|int $retVal, string $unit, string $compare): null|bool|int
{
if ($retVal !== false || $unit !== $compare) {
return $retVal;

View File

@@ -17,19 +17,19 @@ class Helpers
*
* @return bool TRUE if the year is a leap year, otherwise FALSE
*/
public static function isLeapYear($year): bool
public static function isLeapYear(int|string $year): bool
{
$year = (int) $year;
return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
}
/**
* getDateValue.
*
* @param mixed $dateValue
*
* @return float Excel date/time serial value
*/
public static function getDateValue($dateValue, bool $allowBool = true): float
public static function getDateValue(mixed $dateValue, bool $allowBool = true): float
{
if (is_object($dateValue)) {
$retval = SharedDateHelper::PHPToExcel($dateValue);
@@ -60,14 +60,13 @@ class Helpers
/**
* getTimeValue.
*
* @param string $timeValue
*
* @return mixed Excel date/time serial value, or string if error
* @return float|string Excel date/time serial value, or string if error
*/
public static function getTimeValue($timeValue)
public static function getTimeValue(string $timeValue): string|float
{
$saveReturnDateType = Functions::getReturnDateType();
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
/** @var float|string $timeValue */
$timeValue = TimeValue::fromString($timeValue);
Functions::setReturnDateType($saveReturnDateType);
@@ -76,10 +75,8 @@ class Helpers
/**
* Adjust date by given months.
*
* @param mixed $dateValue
*/
public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
public static function adjustDateByMonths(mixed $dateValue = 0, float $adjustmentMonths = 0): DateTime
{
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
@@ -108,11 +105,8 @@ class Helpers
/**
* Help reduce perceived complexity of some tests.
*
* @param mixed $value
* @param mixed $altValue
*/
public static function replaceIfEmpty(&$value, $altValue): void
public static function replaceIfEmpty(mixed &$value, mixed $altValue): void
{
$value = $value ?: $altValue;
}
@@ -133,10 +127,8 @@ class Helpers
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
@@ -149,8 +141,8 @@ class Helpers
. ':' . $dateArray['second']
);
}
$excelDateValue =
SharedDateHelper::formattedPHPToExcel(
$excelDateValue
= SharedDateHelper::formattedPHPToExcel(
$dateArray['year'],
$dateArray['month'],
$dateArray['day'],
@@ -159,26 +151,24 @@ class Helpers
$dateArray['second']
);
if ($retType === Functions::RETURNDATE_EXCEL) {
return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
return $noFrac ? floor($excelDateValue) : $excelDateValue;
}
// RETURNDATE_UNIX_TIMESTAMP)
return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
return SharedDateHelper::excelToTimestamp($excelDateValue);
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsFloat(float $excelDateValue)
public static function returnIn3FormatsFloat(float $excelDateValue): float|int|DateTime
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
return $excelDateValue;
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
return SharedDateHelper::excelToTimestamp($excelDateValue);
}
// RETURNDATE_PHP_DATETIME_OBJECT
@@ -187,10 +177,8 @@ class Helpers
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsObject(DateTime $PHPDateObject)
public static function returnIn3FormatsObject(DateTime $PHPDateObject): DateTime|float|int
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
@@ -203,7 +191,7 @@ class Helpers
$stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
$stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
return (int) SharedDateHelper::excelToTimestamp($stamp);
return SharedDateHelper::excelToTimestamp($stamp);
}
private static function baseDate(): int
@@ -220,10 +208,8 @@ class Helpers
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*/
public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
public static function nullFalseTrueToNumber(mixed &$number, bool $allowBool = true): void
{
$number = Functions::flattenSingleValue($number);
$nullVal = self::baseDate();
@@ -236,12 +222,8 @@ class Helpers
/**
* Many functions accept null argument treated as 0.
*
* @param mixed $number
*
* @return float|int
*/
public static function validateNumericNull($number)
public static function validateNumericNull(mixed $number): int|float
{
$number = Functions::flattenSingleValue($number);
if ($number === null) {
@@ -260,11 +242,9 @@ class Helpers
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*
* @return float
* @phpstan-assert float $number
*/
public static function validateNotNegative($number)
public static function validateNotNegative(mixed $number): float
{
if (!is_numeric($number)) {
throw new Exception(ExcelError::VALUE());
@@ -300,7 +280,7 @@ class Helpers
*
* @param array|false $dateArray
*/
private static function forceArray($dateArray): array
private static function forceArray(array|bool $dateArray): array
{
return is_array($dateArray) ? $dateArray : ['error_count' => 1];
}

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
@@ -28,12 +29,12 @@ class Month
* a negative value yields a past date.
* Or can be an array of adjustment values
*
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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
*/
public static function adjust($dateValue, $adjustmentMonths)
public static function adjust(mixed $dateValue, array|string|bool|float|int $adjustmentMonths): DateTime|float|int|string|array
{
if (is_array($dateValue) || is_array($adjustmentMonths)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
@@ -45,6 +46,7 @@ class Month
} catch (Exception $e) {
return $e->getMessage();
}
$dateValue = floor($dateValue);
$adjustmentMonths = floor($adjustmentMonths);
// Execute function
@@ -71,12 +73,12 @@ class Month
* a negative value yields a past date.
* Or can be an array of adjustment values
*
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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
*/
public static function lastDay($dateValue, $adjustmentMonths)
public static function lastDay(mixed $dateValue, array|float|int|bool|string $adjustmentMonths): array|string|DateTime|float|int
{
if (is_array($dateValue) || is_array($adjustmentMonths)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
@@ -88,6 +90,7 @@ class Month
} catch (Exception $e) {
return $e->getMessage();
}
$dateValue = floor($dateValue);
$adjustmentMonths = floor($adjustmentMonths);
// Execute function

View File

@@ -33,7 +33,7 @@ class NetworkDays
* 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
*/
public static function count($startDate, $endDate, ...$dateArgs)
public static function count(mixed $startDate, mixed $endDate, mixed ...$dateArgs): array|string|int
{
if (is_array($startDate) || is_array($endDate)) {
return self::evaluateArrayArgumentsSubset(

View File

@@ -24,26 +24,26 @@ class Time
* Excel Function:
* TIME(hour,minute,second)
*
* @param array|int $hour A number from 0 (zero) to 32767 representing the hour.
* @param null|array|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 array|int $minute A number from 0 to 32767 representing the minute.
* @param null|array|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 array|int $second A number from 0 to 32767 representing the second.
* @param null|array|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|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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 fromHMS($hour, $minute, $second)
public static function fromHMS(array|int|float|bool|null|string $hour, array|int|float|bool|null|string $minute, array|int|float|bool|null|string $second): array|string|float|int|DateTime
{
if (is_array($hour) || is_array($minute) || is_array($second)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $hour, $minute, $second);
@@ -115,7 +115,7 @@ class Time
/**
* @param mixed $value expect int
*/
private static function toIntWithNullBool($value): int
private static function toIntWithNullBool(mixed $value): int
{
$value = $value ?? 0;
if (is_bool($value)) {

View File

@@ -27,7 +27,7 @@ class TimeParts
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function hour($timeValue)
public static function hour(mixed $timeValue): array|string|int
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -46,6 +46,7 @@ class TimeParts
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
SharedDateHelper::roundMicroseconds($timeValue);
return (int) $timeValue->format('H');
}
@@ -67,7 +68,7 @@ class TimeParts
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function minute($timeValue)
public static function minute(mixed $timeValue): array|string|int
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -86,6 +87,7 @@ class TimeParts
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
SharedDateHelper::roundMicroseconds($timeValue);
return (int) $timeValue->format('i');
}
@@ -107,7 +109,7 @@ class TimeParts
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function second($timeValue)
public static function second(mixed $timeValue): array|string|int
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -126,6 +128,7 @@ class TimeParts
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
SharedDateHelper::roundMicroseconds($timeValue);
return (int) $timeValue->format('s');
}

View File

@@ -25,40 +25,42 @@ class TimeValue
* Excel Function:
* TIMEVALUE(timeValue)
*
* @param array|string $timeValue A text string that represents a time in any one of the Microsoft
* @param null|array|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 mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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 fromString($timeValue)
public static function fromString(null|array|string|int|bool|float $timeValue): array|string|Datetime|int|float
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
$timeValue = trim($timeValue ?? '', '"');
// try to parse as time iff there is at least one digit
if (is_string($timeValue) && preg_match('/\\d/', $timeValue) !== 1) {
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] = ($arraySplit[0] % 24);
$arraySplit[0] = ((int) $arraySplit[0] % 24);
$timeValue = implode(':', $arraySplit);
}
$PHPDateArray = Helpers::dateParse($timeValue);
$retValue = ExcelError::VALUE();
if (Helpers::dateParseSucceeded($PHPDateArray)) {
/** @var int */
$hour = $PHPDateArray['hour'];
/** @var int */
$minute = $PHPDateArray['minute'];
/** @var int */
$second = $PHPDateArray['second'];
// OpenOffice-specific code removed - it works just like Excel
$excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
@@ -69,7 +71,7 @@ class TimeValue
} elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
$retValue = (int) SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
} else {
$retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
$retValue = new Datetime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
}
}

View File

@@ -45,7 +45,7 @@ class Week
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function number($dateValue, $method = Constants::STARTWEEK_SUNDAY)
public static function number(mixed $dateValue, array|int|string|null $method = Constants::STARTWEEK_SUNDAY): array|int|string
{
if (is_array($dateValue) || is_array($method)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $method);
@@ -105,7 +105,7 @@ class Week
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isoWeekNumber($dateValue)
public static function isoWeekNumber(mixed $dateValue): array|int|string
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -137,7 +137,7 @@ class Week
* Excel Function:
* WEEKDAY(dateValue[,style])
*
* @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* @param null|array|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
@@ -150,7 +150,7 @@ class Week
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function day($dateValue, $style = 1)
public static function day(null|array|float|int|string|bool $dateValue, mixed $style = 1): array|string|int
{
if (is_array($dateValue) || is_array($style)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
@@ -189,7 +189,7 @@ class Week
/**
* @param mixed $style expect int
*/
private static function validateStyle($style): int
private static function validateStyle(mixed $style): int
{
if (!is_numeric($style)) {
throw new Exception(ExcelError::VALUE());
@@ -211,7 +211,7 @@ class Week
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*/
private static function apparentBug($dateValue): bool
private static function apparentBug(mixed $dateValue): bool
{
if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
if (is_bool($dateValue)) {
@@ -227,10 +227,8 @@ class Week
/**
* Validate dateValue parameter.
*
* @param mixed $dateValue
*/
private static function validateDateValue($dateValue): float
private static function validateDateValue(mixed $dateValue): float
{
if (is_bool($dateValue)) {
throw new Exception(ExcelError::VALUE());
@@ -241,10 +239,8 @@ class Week
/**
* Validate method parameter.
*
* @param mixed $method
*/
private static function validateMethod($method): int
private static function validateMethod(mixed $method): int
{
if ($method === null) {
$method = Constants::STARTWEEK_SUNDAY;
@@ -272,7 +268,7 @@ class Week
{
// This appears to be another Excel bug.
return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
!$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904
&& !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
}
}

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
@@ -30,12 +31,12 @@ class WorkDay
* Or can be an array of int values
* @param null|mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
*
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* @return array|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
*/
public static function date($startDate, $endDays, ...$dateArgs)
public static function date(mixed $startDate, array|int|string $endDays, mixed ...$dateArgs): array|float|int|DateTime|string
{
if (is_array($startDate) || is_array($endDays)) {
return self::evaluateArrayArgumentsSubset(
@@ -71,10 +72,8 @@ class WorkDay
/**
* Use incrementing logic to determine Workday.
*
* @return mixed
*/
private static function incrementing(float $startDate, int $endDays, array $holidayArray)
private static function incrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
{
// Adjust the start date if it falls over a weekend
$startDoW = self::getWeekDay($startDate, 3);
@@ -132,10 +131,8 @@ class WorkDay
/**
* Use decrementing logic to determine Workday.
*
* @return mixed
*/
private static function decrementing(float $startDate, int $endDays, array $holidayArray)
private static function decrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
{
// Adjust the start date if it falls over a weekend
$startDoW = self::getWeekDay($startDate, 3);

View File

@@ -39,11 +39,11 @@ class YearFrac
* 4 European 30/360
* Or can be an array of methods
*
* @return array|float|string fraction of the year, or a string containing an error
* @return array|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
*/
public static function fraction($startDate, $endDate, $method = 0)
public static function fraction(mixed $startDate, mixed $endDate, array|int|string|null $method = 0): array|string|int|float
{
if (is_array($startDate) || is_array($endDate) || is_array($method)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);
@@ -61,29 +61,20 @@ class YearFrac
return $e->getMessage();
}
switch ($method) {
case 0:
return Functions::scalar(Days360::between($startDate, $endDate)) / 360;
case 1:
return self::method1($startDate, $endDate);
case 2:
return Functions::scalar(Difference::interval($startDate, $endDate)) / 360;
case 3:
return Functions::scalar(Difference::interval($startDate, $endDate)) / 365;
case 4:
return Functions::scalar(Days360::between($startDate, $endDate, true)) / 360;
}
return ExcelError::NAN();
return match ($method) {
0 => Functions::scalar(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,
default => ExcelError::NAN(),
};
}
/**
* Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
*
* @param mixed $startDate
* @param mixed $endDate
*/
private static function excelBug(float $sDate, $startDate, $endDate, int $method): float
private static function excelBug(float $sDate, mixed $startDate, mixed $endDate, int $method): float
{
if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
if ($endDate === null && $startDate !== null) {

View File

@@ -6,30 +6,15 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
class ArrayArgumentHelper
{
/**
* @var int
*/
protected $indexStart = 0;
protected int $indexStart = 0;
/**
* @var array
*/
protected $arguments;
protected array $arguments;
/**
* @var int
*/
protected $argumentCount;
protected int $argumentCount;
/**
* @var array
*/
protected $rows;
protected array $rows;
/**
* @var array
*/
protected $columns;
protected array $columns;
public function initialise(array $arguments): void
{
@@ -152,9 +137,7 @@ class ArrayArgumentHelper
private function rows(array $arguments): array
{
return array_map(
function ($argument) {
return is_countable($argument) ? count($argument) : 1;
},
fn ($argument): int => is_countable($argument) ? count($argument) : 1,
$arguments
);
}
@@ -162,7 +145,7 @@ class ArrayArgumentHelper
private function columns(array $arguments): array
{
return array_map(
function ($argument) {
function (mixed $argument): int {
return is_array($argument) && is_array($argument[array_keys($argument)[0]])
? count($argument[array_keys($argument)[0]])
: 1;
@@ -201,9 +184,7 @@ class ArrayArgumentHelper
{
return array_filter(
$array,
function ($value) {
return $value > 1;
}
fn ($value): bool => $value > 1
);
}
}

View File

@@ -6,18 +6,12 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class ArrayArgumentProcessor
{
/**
* @var ArrayArgumentHelper
*/
private static $arrayArgumentHelper;
private static ArrayArgumentHelper $arrayArgumentHelper;
/**
* @param mixed ...$arguments
*/
public static function processArguments(
ArrayArgumentHelper $arrayArgumentHelper,
callable $method,
...$arguments
mixed ...$arguments
): array {
self::$arrayArgumentHelper = $arrayArgumentHelper;
@@ -42,14 +36,15 @@ class ArrayArgumentProcessor
$matrixPair = self::$arrayArgumentHelper->getMatrixPair();
if ($matrixPair !== []) {
if (
(self::$arrayArgumentHelper->isVector($matrixPair[0]) === true &&
self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) ||
(self::$arrayArgumentHelper->isVector($matrixPair[0]) === false &&
self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
(self::$arrayArgumentHelper->isVector($matrixPair[0]) === true
&& self::$arrayArgumentHelper->isVector($matrixPair[1]) === false)
|| (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false
&& self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
) {
// Logic for a matrix and a vector (row or column)
return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments);
}
// Logic for matrix/matrix, column vector/column vector or row vector/row vector
return self::evaluateMatrixPair($method, $matrixPair, ...$arguments);
}
@@ -59,10 +54,7 @@ class ArrayArgumentProcessor
return ['#VALUE!'];
}
/**
* @param mixed ...$arguments
*/
private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
{
$matrix2 = array_pop($matrixIndexes);
/** @var array $matrixValues2 */
@@ -100,10 +92,7 @@ class ArrayArgumentProcessor
return $result;
}
/**
* @param mixed ...$arguments
*/
private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
private static function evaluateMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
{
$matrix2 = array_pop($matrixIndexes);
/** @var array $matrixValues2 */
@@ -130,10 +119,7 @@ class ArrayArgumentProcessor
return $result;
}
/**
* @param mixed ...$arguments
*/
private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array
private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, mixed ...$arguments): array
{
$rowVector = Functions::flattenArray($arguments[$rowIndex]);
$columnVector = Functions::flattenArray($arguments[$columnIndex]);
@@ -155,10 +141,8 @@ class ArrayArgumentProcessor
/**
* Note, offset is from 1 (for the first argument) rather than from 0.
*
* @param mixed ...$arguments
*/
private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array
private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, mixed ...$arguments): array
{
$values = array_slice($arguments, $nthArgument - 1, 1);
/** @var array $values */

View File

@@ -6,69 +6,50 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
class BranchPruner
{
/**
* @var bool
*/
protected $branchPruningEnabled = true;
protected bool $branchPruningEnabled;
/**
* Used to generate unique store keys.
*
* @var int
*/
private $branchStoreKeyCounter = 0;
private int $branchStoreKeyCounter = 0;
/**
* currently pending storeKey (last item of the storeKeysStack.
*
* @var ?string
*/
protected $pendingStoreKey;
protected ?string $pendingStoreKey = null;
/**
* @var string[]
*/
protected $storeKeysStack = [];
protected array $storeKeysStack = [];
/**
* @var bool[]
*/
protected $conditionMap = [];
protected array $conditionMap = [];
/**
* @var bool[]
*/
protected $thenMap = [];
protected array $thenMap = [];
/**
* @var bool[]
*/
protected $elseMap = [];
protected array $elseMap = [];
/**
* @var int[]
*/
protected $braceDepthMap = [];
protected array $braceDepthMap = [];
/**
* @var null|string
*/
protected $currentCondition;
protected ?string $currentCondition = null;
/**
* @var null|string
*/
protected $currentOnlyIf;
protected ?string $currentOnlyIf = null;
/**
* @var null|string
*/
protected $currentOnlyIfNot;
protected ?string $currentOnlyIfNot = null;
/**
* @var null|string
*/
protected $previousStoreKey;
protected ?string $previousStoreKey = null;
public function __construct(bool $branchPruningEnabled)
{
@@ -175,10 +156,7 @@ class BranchPruner
}
}
/**
* @param mixed $value
*/
public function closingBrace($value): void
public function closingBrace(mixed $value): void
{
if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
// we are closing an IF(

View File

@@ -9,34 +9,28 @@ class CyclicReferenceStack
*
* @var mixed[]
*/
private $stack = [];
private array $stack = [];
/**
* Return the number of entries on the stack.
*
* @return int
*/
public function count()
public function count(): int
{
return count($this->stack);
}
/**
* Push a new entry onto the stack.
*
* @param mixed $value
*/
public function push($value): void
public function push(mixed $value): void
{
$this->stack[$value] = $value;
}
/**
* Pop the last entry from the stack.
*
* @return mixed
*/
public function pop()
public function pop(): mixed
{
return array_pop($this->stack);
}
@@ -45,10 +39,8 @@ class CyclicReferenceStack
* Test to see if a specified entry exists on the stack.
*
* @param mixed $value The value to test
*
* @return bool
*/
public function onStack($value)
public function onStack(mixed $value): bool
{
return isset($this->stack[$value]);
}
@@ -66,7 +58,7 @@ class CyclicReferenceStack
*
* @return mixed[]
*/
public function showStack()
public function showStack(): array
{
return $this->stack;
}

View File

@@ -13,6 +13,9 @@ class FormattedNumber
private const STRING_REGEXP_PERCENT = '~^(?:(?: *(?<PrefixedSign>[-+])? *\% *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *\% *))$~i';
// 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'],
@@ -45,7 +48,10 @@ class FormattedNumber
*/
public static function convertToNumberIfNumeric(string &$operand): bool
{
$value = preg_replace(['/(\d),(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
$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 ?? '');
if (is_numeric($value)) {
$operand = (float) $value;
@@ -84,7 +90,10 @@ class FormattedNumber
*/
public static function convertToNumberIfPercent(string &$operand): bool
{
$value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
$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 ?? '');
$match = [];
if ($value !== null && preg_match(self::STRING_REGEXP_PERCENT, $value, $match, PREG_UNMATCHED_AS_NULL)) {
@@ -106,21 +115,33 @@ class FormattedNumber
*/
public static function convertToNumberIfCurrency(string &$operand): bool
{
$quotedCurrencyCode = preg_quote(StringHelper::getCurrencyCode());
$value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
$regExp = '~^(?:(?: *(?<PrefixedSign>[-+])? *' . $quotedCurrencyCode . ' *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *' . $quotedCurrencyCode . ' *))$~ui';
$currencyRegexp = self::currencyMatcherRegexp();
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
$match = [];
if ($value !== null && preg_match($regExp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
//Determine the sign
$sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
$decimalSeparator = StringHelper::getDecimalSeparator();
//Cast to a float
$operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue']));
$intermediate = (string) ($match['PostfixedValue'] ?? $match['PrefixedValue']);
$intermediate = str_replace($decimalSeparator, '.', $intermediate);
if (is_numeric($intermediate)) {
$operand = (float) ($sign . str_replace($decimalSeparator, '.', $intermediate));
return true;
return true;
}
}
return false;
}
public static function currencyMatcherRegexp(): string
{
$currencyCodes = sprintf(self::CURRENCY_CONVERSION_LIST, preg_quote(StringHelper::getCurrencyCode(), '/'));
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
return '~^(?:(?: *(?<PrefixedSign>[-+])? *(?<PrefixedCurrency>[' . $currencyCodes . ']) *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+[' . $decimalSeparator . ']?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+' . $decimalSeparator . '?[0-9]*(?:E[-+]?[0-9]*)?) *(?<PostfixedCurrency>[' . $currencyCodes . ']) *))$~ui';
}
}

View File

@@ -8,34 +8,28 @@ class Logger
* Flag to determine whether a debug log should be generated by the calculation engine
* If true, then a debug log will be generated
* If false, then a debug log will not be generated.
*
* @var bool
*/
private $writeDebugLog = false;
private bool $writeDebugLog = false;
/**
* Flag to determine whether a debug log should be echoed by the calculation engine
* If true, then a debug log will be echoed
* If false, then a debug log will not be echoed
* A debug log can only be echoed if it is generated.
*
* @var bool
*/
private $echoDebugLog = false;
private bool $echoDebugLog = false;
/**
* The debug log generated by the calculation engine.
*
* @var string[]
*/
private $debugLog = [];
private array $debugLog = [];
/**
* The calculation engine cell reference stack.
*
* @var CyclicReferenceStack
*/
private $cellStack;
private CyclicReferenceStack $cellStack;
/**
* Instantiate a Calculation engine logger.
@@ -47,50 +41,40 @@ class Logger
/**
* Enable/Disable Calculation engine logging.
*
* @param bool $writeDebugLog
*/
public function setWriteDebugLog($writeDebugLog): void
public function setWriteDebugLog(bool $writeDebugLog): void
{
$this->writeDebugLog = $writeDebugLog;
}
/**
* Return whether calculation engine logging is enabled or disabled.
*
* @return bool
*/
public function getWriteDebugLog()
public function getWriteDebugLog(): bool
{
return $this->writeDebugLog;
}
/**
* Enable/Disable echoing of debug log information.
*
* @param bool $echoDebugLog
*/
public function setEchoDebugLog($echoDebugLog): void
public function setEchoDebugLog(bool $echoDebugLog): void
{
$this->echoDebugLog = $echoDebugLog;
}
/**
* Return whether echoing of debug log information is enabled or disabled.
*
* @return bool
*/
public function getEchoDebugLog()
public function getEchoDebugLog(): bool
{
return $this->echoDebugLog;
}
/**
* Write an entry to the calculation engine debug log.
*
* @param mixed $args
*/
public function writeDebugLog(string $message, ...$args): void
public function writeDebugLog(string $message, mixed ...$args): void
{
// Only write the debug log if logging is enabled
if ($this->writeDebugLog) {
@@ -102,9 +86,9 @@ class Logger
$message,
PHP_EOL;
}
$this->debugLog[] = $cellReference .
($this->cellStack->count() > 0 ? ' => ' : '') .
$message;
$this->debugLog[] = $cellReference
. ($this->cellStack->count() > 0 ? ' => ' : '')
. $message;
}
}
@@ -135,7 +119,7 @@ class Logger
*
* @return string[]
*/
public function getLog()
public function getLog(): array
{
return $this->debugLog;
}

View File

@@ -2,17 +2,53 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine\Operands;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
use Stringable;
final class StructuredReference implements Operand
final class StructuredReference implements Operand, Stringable
{
public const NAME = 'Structured Reference';
private const OPEN_BRACE = '[';
private const CLOSE_BRACE = ']';
private const ITEM_SPECIFIER_ALL = '#All';
private const ITEM_SPECIFIER_HEADERS = '#Headers';
private const ITEM_SPECIFIER_DATA = '#Data';
private const ITEM_SPECIFIER_TOTALS = '#Totals';
private const ITEM_SPECIFIER_THIS_ROW = '#This Row';
private const ITEM_SPECIFIER_ROWS_SET = [
self::ITEM_SPECIFIER_ALL,
self::ITEM_SPECIFIER_HEADERS,
self::ITEM_SPECIFIER_DATA,
self::ITEM_SPECIFIER_TOTALS,
];
private const TABLE_REFERENCE = '/([\p{L}_\\\\][\p{L}\p{N}\._]+)?(\[(?:[^\]\[]+|(?R))*+\])/miu';
private string $value;
private string $tableName;
private Table $table;
private string $reference;
private ?int $headersRow;
private int $firstDataRow;
private int $lastDataRow;
private ?int $totalsRow;
private array $columns;
public function __construct(string $structuredReference)
{
$this->value = $structuredReference;
@@ -33,7 +69,7 @@ final class StructuredReference implements Operand
}
$srStringRemainder = substr($srStringRemainder, 0, $closingPos + 1);
--$srCount;
if (strpos($srStringRemainder, self::OPEN_BRACE) !== false) {
if (str_contains($srStringRemainder, self::OPEN_BRACE)) {
++$srCount;
}
$val .= $srStringRemainder;
@@ -42,8 +78,278 @@ final class StructuredReference implements Operand
return new self($val);
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function parse(Cell $cell): string
{
$this->getTableStructure($cell);
$cellRange = ($this->isRowReference()) ? $this->getRowReference($cell) : $this->getColumnReference();
$sheetName = '';
$worksheet = $this->table->getWorksheet();
if ($worksheet !== null && $worksheet !== $cell->getWorksheet()) {
$sheetName = "'" . $worksheet->getTitle() . "'!";
}
return $sheetName . $cellRange;
}
private function isRowReference(): bool
{
return str_contains($this->value, '[@')
|| str_contains($this->value, '[' . self::ITEM_SPECIFIER_THIS_ROW . ']');
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getTableStructure(Cell $cell): void
{
preg_match(self::TABLE_REFERENCE, $this->value, $matches);
$this->tableName = $matches[1];
$this->table = ($this->tableName === '')
? $this->getTableForCell($cell)
: $this->getTableByName($cell);
$this->reference = $matches[2];
$tableRange = Coordinate::getRangeBoundaries($this->table->getRange());
$this->headersRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] : null;
$this->firstDataRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] + 1 : $tableRange[0][1];
$this->totalsRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] : null;
$this->lastDataRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] - 1 : $tableRange[1][1];
$cellParam = $cell;
$worksheet = $this->table->getWorksheet();
if ($worksheet !== null && $worksheet !== $cell->getWorksheet()) {
$cellParam = $worksheet->getCell('A1');
}
$this->columns = $this->getColumns($cellParam, $tableRange);
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getTableForCell(Cell $cell): Table
{
$tables = $cell->getWorksheet()->getTableCollection();
foreach ($tables as $table) {
/** @var Table $table */
$range = $table->getRange();
if ($cell->isInRange($range) === true) {
$this->tableName = $table->getName();
return $table;
}
}
throw new Exception('Table for Structured Reference cannot be identified');
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getTableByName(Cell $cell): Table
{
$table = $cell->getWorksheet()->getTableByName($this->tableName);
if ($table === null) {
$spreadsheet = $cell->getWorksheet()->getParent();
if ($spreadsheet !== null) {
$table = $spreadsheet->getTableByName($this->tableName);
}
}
if ($table === null) {
throw new Exception("Table {$this->tableName} for Structured Reference cannot be located");
}
return $table;
}
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) {
$columns[$column] = $worksheet
->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
->getCalculatedValue();
}
$worksheet->getCell($cellReference);
return $columns;
}
private function getRowReference(Cell $cell): string
{
$reference = str_replace("\u{a0}", ' ', $this->reference);
/** @var string $reference */
$reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
foreach ($this->columns as $columnId => $columnName) {
$columnName = str_replace("\u{a0}", ' ', $columnName);
$reference = $this->adjustRowReference($columnName, $reference, $cell, $columnId);
}
return $this->validateParsedReference(trim($reference, '[]@, '));
}
private function adjustRowReference(string $columnName, string $reference, Cell $cell, string $columnId): string
{
if ($columnName !== '') {
$cellReference = $columnId . $cell->getRow();
$pattern1 = '/\[' . preg_quote($columnName, '/') . '\]/miu';
$pattern2 = '/@' . preg_quote($columnName, '/') . '/miu';
if (preg_match($pattern1, $reference) === 1) {
$reference = preg_replace($pattern1, $cellReference, $reference);
} elseif (preg_match($pattern2, $reference) === 1) {
$reference = preg_replace($pattern2, $cellReference, $reference);
}
/** @var string $reference */
}
return $reference;
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getColumnReference(): string
{
$reference = str_replace("\u{a0}", ' ', $this->reference);
$startRow = ($this->totalsRow === null) ? $this->lastDataRow : $this->totalsRow;
$endRow = ($this->headersRow === null) ? $this->firstDataRow : $this->headersRow;
[$startRow, $endRow] = $this->getRowsForColumnReference($reference, $startRow, $endRow);
$reference = $this->getColumnsForColumnReference($reference, $startRow, $endRow);
$reference = trim($reference, '[]@, ');
if (substr_count($reference, ':') > 1) {
$cells = explode(':', $reference);
$firstCell = array_shift($cells);
$lastCell = array_pop($cells);
$reference = "{$firstCell}:{$lastCell}";
}
return $this->validateParsedReference($reference);
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function validateParsedReference(string $reference): string
{
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . ':' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
throw new Exception(
"Invalid Structured Reference {$this->reference} {$reference}",
Exception::CALCULATION_ENGINE_PUSH_TO_STACK
);
}
}
return $reference;
}
private function fullData(int $startRow, int $endRow): string
{
$columns = array_keys($this->columns);
$firstColumn = array_shift($columns);
$lastColumn = (empty($columns)) ? $firstColumn : array_pop($columns);
return "{$firstColumn}{$startRow}:{$lastColumn}{$endRow}";
}
private function getMinimumRow(string $reference): int
{
return match ($reference) {
self::ITEM_SPECIFIER_ALL, self::ITEM_SPECIFIER_HEADERS => $this->headersRow ?? $this->firstDataRow,
self::ITEM_SPECIFIER_DATA => $this->firstDataRow,
self::ITEM_SPECIFIER_TOTALS => $this->totalsRow ?? $this->lastDataRow,
default => $this->headersRow ?? $this->firstDataRow,
};
}
private function getMaximumRow(string $reference): int
{
return match ($reference) {
self::ITEM_SPECIFIER_HEADERS => $this->headersRow ?? $this->firstDataRow,
self::ITEM_SPECIFIER_DATA => $this->lastDataRow,
self::ITEM_SPECIFIER_ALL, self::ITEM_SPECIFIER_TOTALS => $this->totalsRow ?? $this->lastDataRow,
default => $this->totalsRow ?? $this->lastDataRow,
};
}
public function value(): string
{
return $this->value;
}
/**
* @return array<int, int>
*/
private function getRowsForColumnReference(string &$reference, int $startRow, int $endRow): array
{
$rowsSelected = false;
foreach (self::ITEM_SPECIFIER_ROWS_SET as $rowReference) {
$pattern = '/\[' . $rowReference . '\]/mui';
if (preg_match($pattern, $reference) === 1) {
if (($rowReference === self::ITEM_SPECIFIER_HEADERS) && ($this->table->getShowHeaderRow() === false)) {
throw new Exception(
'Table Headers are Hidden, and should not be Referenced',
Exception::CALCULATION_ENGINE_PUSH_TO_STACK
);
}
$rowsSelected = true;
$startRow = min($startRow, $this->getMinimumRow($rowReference));
$endRow = max($endRow, $this->getMaximumRow($rowReference));
$reference = preg_replace($pattern, '', $reference) ?? '';
}
}
if ($rowsSelected === false) {
// If there isn't any Special Item Identifier specified, then the selection defaults to data rows only.
$startRow = $this->firstDataRow;
$endRow = $this->lastDataRow;
}
return [$startRow, $endRow];
}
private function getColumnsForColumnReference(string $reference, int $startRow, int $endRow): string
{
$columnsSelected = false;
foreach ($this->columns as $columnId => $columnName) {
$columnName = str_replace("\u{a0}", ' ', $columnName ?? '');
$cellFrom = "{$columnId}{$startRow}";
$cellTo = "{$columnId}{$endRow}";
$cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
$pattern = '/\[' . preg_quote($columnName, '/') . '\]/mui';
if (preg_match($pattern, $reference) === 1) {
$columnsSelected = true;
$reference = preg_replace($pattern, $cellReference, $reference);
}
/** @var string $reference */
}
if ($columnsSelected === false) {
return $this->fullData($startRow, $endRow);
}
return $reference;
}
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -35,7 +35,7 @@ class BesselI
* 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 BESSELI($x, $ord)
public static function BESSELI(mixed $x, mixed $ord): array|string|float
{
if (is_array($x) || is_array($ord)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -59,15 +59,11 @@ class BesselI
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselI0($x);
case 1:
return self::besselI1($x);
}
return self::besselI2($x, $ord);
return match ($ord) {
0 => self::besselI0($x),
1 => self::besselI1($x),
default => self::besselI2($x, $ord),
};
}
private static function besselI0(float $x): float
@@ -85,8 +81,8 @@ class BesselI
$y = 3.75 / $ax;
return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
+ $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1 +
$y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
+ $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1
+ $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
}
private static function besselI1(float $x): float
@@ -96,31 +92,24 @@ class BesselI
if ($ax < 3.75) {
$y = $x / 3.75;
$y = $y * $y;
$ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1 +
$y * (0.301532e-2 + $y * 0.32411e-3))))));
$ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1
+ $y * (0.301532e-2 + $y * 0.32411e-3))))));
return ($x < 0.0) ? -$ans : $ans;
}
$y = 3.75 / $ax;
$ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
$ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2 +
$y * (-0.1031555e-1 + $y * $ans))));
$ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2
+ $y * (-0.1031555e-1 + $y * $ans))));
$ans *= exp($ax) / sqrt($ax);
return ($x < 0.0) ? -$ans : $ans;
}
/**
* Sop to Scrutinizer.
*
* @var float
*/
private static $zeroPointZero = 0.0;
private static function besselI2(float $x, int $ord): float
{
if ($x === self::$zeroPointZero) {
if ($x === 0.0) {
return 0.0;
}

View File

@@ -34,7 +34,7 @@ class BesselJ
* 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 BESSELJ($x, $ord)
public static function BESSELJ(mixed $x, mixed $ord): array|string|float
{
if (is_array($x) || is_array($ord)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -58,15 +58,11 @@ class BesselJ
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselJ0($x);
case 1:
return self::besselJ1($x);
}
return self::besselJ2($x, $ord);
return match ($ord) {
0 => self::besselJ0($x),
1 => self::besselJ1($x),
default => self::besselJ2($x, $ord),
};
}
private static function besselJ0(float $x): float
@@ -75,10 +71,10 @@ class BesselJ
if ($ax < 8.0) {
$y = $x * $x;
$ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y *
(77392.33017 + $y * (-184.9052456)))));
$ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y *
(267.8532712 + $y * 1.0))));
$ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y
* (77392.33017 + $y * (-184.9052456)))));
$ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y
* (267.8532712 + $y * 1.0))));
return $ans1 / $ans2;
}
@@ -87,8 +83,8 @@ class BesselJ
$y = $z * $z;
$xx = $ax - 0.785398164;
$ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y *
(0.7621095161e-6 - $y * 0.934935152e-7)));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y
* (0.7621095161e-6 - $y * 0.934935152e-7)));
return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
}
@@ -99,10 +95,10 @@ class BesselJ
if ($ax < 8.0) {
$y = $x * $x;
$ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y *
(-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
$ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y *
(376.9991397 + $y * 1.0))));
$ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y
* (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
$ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y
* (376.9991397 + $y * 1.0))));
return $ans1 / $ans2;
}
@@ -112,8 +108,8 @@ class BesselJ
$xx = $ax - 2.356194491;
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
(-0.88228987e-6 + $y * 0.105787412e-6)));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
* (-0.88228987e-6 + $y * 0.105787412e-6)));
$ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
return ($x < 0.0) ? -$ans : $ans;
@@ -133,7 +129,7 @@ class BesselJ
return self::besselj2b($ax, $ord, $x);
}
private static function besselj2a(float $ax, int $ord, float $x)
private static function besselj2a(float $ax, int $ord, float $x): float
{
$tox = 2.0 / $ax;
$bjm = self::besselJ0($ax);
@@ -148,7 +144,7 @@ class BesselJ
return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
}
private static function besselj2b(float $ax, int $ord, float $x)
private static function besselj2b(float $ax, int $ord, float $x): float
{
$tox = 2.0 / $ax;
$jsum = false;

View File

@@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class BesselK
@@ -33,7 +32,7 @@ class BesselK
* 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 BESSELK($x, $ord)
public static function BESSELK(mixed $x, mixed $ord): array|string|float
{
if (is_array($x) || is_array($ord)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -57,15 +56,11 @@ class BesselK
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselK0($x);
case 1:
return self::besselK1($x);
}
return self::besselK2($x, $ord);
return match ($ord) {
0 => self::besselK0($x),
1 => self::besselK1($x),
default => self::besselK2($x, $ord),
};
}
/**
@@ -89,16 +84,16 @@ class BesselK
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2);
return -log($fNum2) * self::callBesselI($x, 0) +
(-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
(0.10750e-3 + $y * 0.74e-5))))));
return -log($fNum2) * self::callBesselI($x, 0)
+ (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y
* (0.10750e-3 + $y * 0.74e-5))))));
}
$y = 2 / $x;
return exp(-$x) / sqrt($x) *
(1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
(0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
return exp(-$x) / sqrt($x)
* (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y
* (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
}
private static function besselK1(float $x): float
@@ -107,16 +102,16 @@ class BesselK
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2);
return log($fNum2) * self::callBesselI($x, 1) +
(1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
(-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
return log($fNum2) * self::callBesselI($x, 1)
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y
* (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
}
$y = 2 / $x;
return exp(-$x) / sqrt($x) *
(1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
(0.325614e-2 + $y * (-0.68245e-3)))))));
return exp(-$x) / sqrt($x)
* (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y
* (0.325614e-2 + $y * (-0.68245e-3)))))));
}
private static function besselK2(float $x, int $ord): float

View File

@@ -31,7 +31,7 @@ class BesselY
* 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 BESSELY($x, $ord)
public static function BESSELY(mixed $x, mixed $ord): array|string|float
{
if (is_array($x) || is_array($ord)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -55,15 +55,11 @@ class BesselY
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselY0($x);
case 1:
return self::besselY1($x);
}
return self::besselY2($x, $ord);
return match ($ord) {
0 => self::besselY0($x),
1 => self::besselY1($x),
default => self::besselY2($x, $ord),
};
}
/**
@@ -85,10 +81,10 @@ class BesselY
{
if ($x < 8.0) {
$y = ($x * $x);
$ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y *
(-86327.92757 + $y * 228.4622733))));
$ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
(47447.26470 + $y * (226.1030244 + $y))));
$ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y
* (-86327.92757 + $y * 228.4622733))));
$ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y
* (47447.26470 + $y * (226.1030244 + $y))));
return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
}
@@ -97,8 +93,8 @@ class BesselY
$y = ($z * $z);
$xx = $x - 0.785398164;
$ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y *
(-0.934945152e-7))));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y
* (-0.934945152e-7))));
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
}
@@ -107,10 +103,10 @@ class BesselY
{
if ($x < 8.0) {
$y = ($x * $x);
$ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y *
(0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
$ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
(0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
$ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y
* (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
$ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y
* (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
}
@@ -119,8 +115,8 @@ class BesselY
$y = $z * $z;
$xx = $x - 2.356194491;
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
(-0.88228987e-6 + $y * 0.105787412e-6)));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
* (-0.88228987e-6 + $y * 0.105787412e-6)));
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
}

View File

@@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class BitWise
@@ -16,11 +15,9 @@ class BitWise
/**
* Split a number into upper and lower portions for full 32-bit support.
*
* @param float|int $number
*
* @return int[]
*/
private static function splitNumber($number): array
private static function splitNumber(float|int $number): array
{
return [(int) floor($number / self::SPLIT_DIVISOR), (int) fmod($number, self::SPLIT_DIVISOR)];
}
@@ -33,16 +30,13 @@ class BitWise
* Excel Function:
* BITAND(number1, number2)
*
* @param array|int $number1
* Or can be an array of values
* @param array|int $number2
* Or can be an array of values
* @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
*
* @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|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($number1, $number2)
public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
{
if (is_array($number1) || is_array($number2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
@@ -57,7 +51,7 @@ class BitWise
$split1 = self::splitNumber($number1);
$split2 = self::splitNumber($number2);
return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
}
/**
@@ -68,16 +62,13 @@ class BitWise
* Excel Function:
* BITOR(number1, number2)
*
* @param array|int $number1
* Or can be an array of values
* @param array|int $number2
* Or can be an array of values
* @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
*
* @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|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($number1, $number2)
public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
{
if (is_array($number1) || is_array($number2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
@@ -93,7 +84,7 @@ class BitWise
$split1 = self::splitNumber($number1);
$split2 = self::splitNumber($number2);
return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
}
/**
@@ -104,16 +95,13 @@ class BitWise
* Excel Function:
* BITXOR(number1, number2)
*
* @param array|int $number1
* Or can be an array of values
* @param array|int $number2
* Or can be an array of values
* @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
*
* @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|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($number1, $number2)
public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
{
if (is_array($number1) || is_array($number2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
@@ -129,7 +117,7 @@ class BitWise
$split1 = self::splitNumber($number1);
$split2 = self::splitNumber($number2);
return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
}
/**
@@ -140,16 +128,13 @@ class BitWise
* Excel Function:
* BITLSHIFT(number, shift_amount)
*
* @param array|int $number
* Or can be an array of values
* @param array|int $shiftAmount
* Or can be an array of values
* @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
*
* @return array|float|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* @return array|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($number, $shiftAmount)
public static function BITLSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
{
if (is_array($number) || is_array($shiftAmount)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
@@ -178,16 +163,13 @@ class BitWise
* Excel Function:
* BITRSHIFT(number, shift_amount)
*
* @param array|int $number
* Or can be an array of values
* @param array|int $shiftAmount
* Or can be an array of values
* @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
*
* @return array|float|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* @return array|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($number, $shiftAmount)
public static function BITRSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
{
if (is_array($number) || is_array($shiftAmount)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
@@ -210,12 +192,8 @@ class BitWise
/**
* Validate arguments passed to the bitwise functions.
*
* @param mixed $value
*
* @return float
*/
private static function validateBitwiseArgument($value)
private static function validateBitwiseArgument(mixed $value): float
{
$value = self::nullFalseTrueToNumber($value);
@@ -237,12 +215,8 @@ class BitWise
/**
* Validate arguments passed to the bitwise functions.
*
* @param mixed $value
*
* @return int
*/
private static function validateShiftAmount($value)
private static function validateShiftAmount(mixed $value): int
{
$value = self::nullFalseTrueToNumber($value);
@@ -259,12 +233,8 @@ class BitWise
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*
* @return mixed
*/
private static function nullFalseTrueToNumber(&$number)
private static function nullFalseTrueToNumber(mixed &$number): mixed
{
if ($number === null) {
$number = 0;

View File

@@ -20,16 +20,16 @@ class Compare
* functions you calculate the count of equal pairs. This function is also known as the
* Kronecker Delta function.
*
* @param array|float $a the first number
* @param array|bool|float|int|string $a the first number
* Or can be an array of values
* @param array|float $b The second number. If omitted, b is assumed to be zero.
* @param array|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)
* 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 DELTA($a, $b = 0.0)
public static function DELTA(array|float|bool|string|int $a, array|float|bool|string|int $b = 0.0): array|string|int
{
if (is_array($a) || is_array($b)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $a, $b);
@@ -55,16 +55,16 @@ 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|float $number the value to test against step
* @param array|bool|float|int|string $number the value to test against step
* Or can be an array of values
* @param null|array|float $step The threshold value. If you omit a value for step, GESTEP uses zero.
* @param null|array|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)
* 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 GESTEP($number, $step = 0.0)
public static function GESTEP(array|float|bool|string|int $number, $step = 0.0): array|string|int
{
if (is_array($number) || is_array($step)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);

View File

@@ -28,11 +28,10 @@ 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|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($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
public static function COMPLEX(mixed $realNumber = 0.0, mixed $imaginary = 0.0, mixed $suffix = 'i'): array|string
{
if (is_array($realNumber) || is_array($imaginary) || is_array($suffix)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $realNumber, $imaginary, $suffix);
@@ -49,7 +48,7 @@ class Complex
return $e->getMessage();
}
if (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) {
if (($suffix === 'i') || ($suffix === 'j') || ($suffix === '')) {
$complex = new ComplexObject($realNumber, $imaginary, $suffix);
return (string) $complex;
@@ -74,7 +73,7 @@ class Complex
* 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 IMAGINARY($complexNumber)
public static function IMAGINARY($complexNumber): array|string|float
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -82,7 +81,7 @@ class Complex
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -104,7 +103,7 @@ class Complex
* 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 IMREAL($complexNumber)
public static function IMREAL($complexNumber): array|string|float
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -112,7 +111,7 @@ class Complex
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}

View File

@@ -22,11 +22,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMABS(array|string $complexNumber): array|float|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -34,7 +33,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -53,11 +52,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMARGUMENT(array|string $complexNumber): array|float|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -65,7 +63,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -87,11 +85,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMCONJUGATE(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -99,7 +96,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -117,11 +114,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the cosine
* 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|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($complexNumber)
public static function IMCOS(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -129,7 +125,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -147,11 +143,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
* 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|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($complexNumber)
public static function IMCOSH(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -159,7 +154,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -177,11 +172,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the cotangent
* 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|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($complexNumber)
public static function IMCOT(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -189,7 +183,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -207,11 +201,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the cosecant
* 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|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($complexNumber)
public static function IMCSC(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -219,7 +212,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -237,11 +230,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
* 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|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($complexNumber)
public static function IMCSCH(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -249,7 +241,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -267,11 +259,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the sine
* 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|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($complexNumber)
public static function IMSIN(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -279,7 +270,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -297,11 +288,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the hyperbolic sine
* 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|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($complexNumber)
public static function IMSINH(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -309,7 +299,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -327,11 +317,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the secant
* 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|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($complexNumber)
public static function IMSEC(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -339,7 +328,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -357,11 +346,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the hyperbolic secant
* 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|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($complexNumber)
public static function IMSECH(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -369,7 +357,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -387,11 +375,10 @@ class ComplexFunctions
* @param array|string $complexNumber the complex number for which you want the tangent
* 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|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($complexNumber)
public static function IMTAN(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -399,7 +386,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -417,11 +404,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMSQRT(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -429,7 +415,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -452,11 +438,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMLN(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -464,7 +449,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -486,11 +471,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMLOG10(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -498,7 +482,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -520,11 +504,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMLOG2(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -532,7 +515,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -554,11 +537,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber)
public static function IMEXP(array|string $complexNumber): array|string
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -566,7 +548,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -586,11 +568,10 @@ class ComplexFunctions
* @param array|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|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($complexNumber, $realNumber)
public static function IMPOWER(array|string $complexNumber, array|float|int|string $realNumber): array|string
{
if (is_array($complexNumber) || is_array($realNumber)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
@@ -598,7 +579,7 @@ class ComplexFunctions
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}

View File

@@ -25,11 +25,10 @@ class ComplexOperations
* @param array|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|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($complexDividend, $complexDivisor)
public static function IMDIV(array|string $complexDividend, array|string $complexDivisor): array|string
{
if (is_array($complexDividend) || is_array($complexDivisor)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexDividend, $complexDivisor);
@@ -37,7 +36,7 @@ class ComplexOperations
try {
return (string) (new ComplexObject($complexDividend))->divideby(new ComplexObject($complexDivisor));
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
}
@@ -55,11 +54,10 @@ class ComplexOperations
* @param array|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|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($complexNumber1, $complexNumber2)
public static function IMSUB(array|string $complexNumber1, array|string $complexNumber2): array|string
{
if (is_array($complexNumber1) || is_array($complexNumber2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
@@ -67,7 +65,7 @@ class ComplexOperations
try {
return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
}
@@ -81,10 +79,8 @@ class ComplexOperations
* IMSUM(complexNumber[,complexNumber[,...]])
*
* @param string ...$complexNumbers Series of complex numbers to add
*
* @return string
*/
public static function IMSUM(...$complexNumbers)
public static function IMSUM(...$complexNumbers): string
{
// Return value
$returnValue = new ComplexObject(0.0);
@@ -95,7 +91,7 @@ class ComplexOperations
foreach ($aArgs as $complex) {
$returnValue = $returnValue->add(new ComplexObject($complex));
}
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}
@@ -111,10 +107,8 @@ class ComplexOperations
* IMPRODUCT(complexNumber[,complexNumber[,...]])
*
* @param string ...$complexNumbers Series of complex numbers to multiply
*
* @return string
*/
public static function IMPRODUCT(...$complexNumbers)
public static function IMPRODUCT(...$complexNumbers): string
{
// Return value
$returnValue = new ComplexObject(1.0);
@@ -125,7 +119,7 @@ class ComplexOperations
foreach ($aArgs as $complex) {
$returnValue = $returnValue->multiply(new ComplexObject($complex));
}
} catch (ComplexException $e) {
} catch (ComplexException) {
return ExcelError::NAN();
}

View File

@@ -11,7 +11,7 @@ abstract class ConvertBase
{
use ArrayEnabled;
protected static function validateValue($value): string
protected static function validateValue(mixed $value): string
{
if (is_bool($value)) {
if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
@@ -29,7 +29,7 @@ abstract class ConvertBase
return strtoupper((string) $value);
}
protected static function validatePlaces($places = null): ?int
protected static function validatePlaces(mixed $places = null): ?int
{
if ($places === null) {
return $places;

View File

@@ -15,7 +15,7 @@ class ConvertBinary extends ConvertBase
* Excel Function:
* BIN2DEC(x)
*
* @param array|string $value The binary number (as a string) that you want to convert. The number
* @param array|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.
@@ -40,7 +40,7 @@ class ConvertBinary extends ConvertBase
return $e->getMessage();
}
if (strlen($value) == 10) {
if (strlen($value) == 10 && $value[0] === '1') {
// Two's Complement
$value = substr($value, -9);
@@ -58,14 +58,14 @@ class ConvertBinary extends ConvertBase
* Excel Function:
* BIN2HEX(x[,places])
*
* @param array|string $value The binary number (as a string) that you want to convert. The number
* @param array|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 array|int $places The number of characters to use. If places is omitted, BIN2HEX uses the
* @param null|array|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.
@@ -77,7 +77,7 @@ class ConvertBinary extends ConvertBase
* 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 toHex($value, $places = null)
public static function toHex($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -91,7 +91,7 @@ class ConvertBinary extends ConvertBase
return $e->getMessage();
}
if (strlen($value) == 10) {
if (strlen($value) == 10 && $value[0] === '1') {
$high2 = substr($value, 0, 2);
$low8 = substr($value, 2);
$xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
@@ -111,14 +111,14 @@ class ConvertBinary extends ConvertBase
* Excel Function:
* BIN2OCT(x[,places])
*
* @param array|string $value The binary number (as a string) that you want to convert. The number
* @param array|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 array|int $places The number of characters to use. If places is omitted, BIN2OCT uses the
* @param null|array|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.
@@ -130,7 +130,7 @@ class ConvertBinary extends ConvertBase
* 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 toOctal($value, $places = null)
public static function toOctal($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -144,7 +144,7 @@ class ConvertBinary extends ConvertBase
return $e->getMessage();
}
if (strlen($value) == 10 && substr($value, 0, 1) === '1') { // Two's Complement
if (strlen($value) == 10 && $value[0] === '1') { // Two's Complement
return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
}
$octVal = (string) decoct((int) bindec($value));

View File

@@ -22,7 +22,7 @@ class ConvertDecimal extends ConvertBase
* Excel Function:
* DEC2BIN(x[,places])
*
* @param array|string $value The decimal integer you want to convert. If number is negative,
* @param array|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 array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
* @param null|array|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.
@@ -45,7 +45,7 @@ class ConvertDecimal extends ConvertBase
* 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 toBinary($value, $places = null)
public static function toBinary($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -79,7 +79,7 @@ class ConvertDecimal extends ConvertBase
* Excel Function:
* DEC2HEX(x[,places])
*
* @param array|string $value The decimal integer you want to convert. If number is negative,
* @param array|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 array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
* @param null|array|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.
@@ -102,7 +102,7 @@ class ConvertDecimal extends ConvertBase
* 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 toHex($value, $places = null)
public static function toHex($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -155,7 +155,7 @@ class ConvertDecimal extends ConvertBase
* Excel Function:
* DEC2OCT(x[,places])
*
* @param array|string $value The decimal integer you want to convert. If number is negative,
* @param array|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
@@ -178,7 +178,7 @@ class ConvertDecimal extends ConvertBase
* 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 toOctal($value, $places = null)
public static function toOctal($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);

View File

@@ -15,7 +15,7 @@ class ConvertHex extends ConvertBase
* Excel Function:
* HEX2BIN(x[,places])
*
* @param array|string $value The hexadecimal number you want to convert.
* @param array|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.
@@ -38,7 +38,7 @@ class ConvertHex extends ConvertBase
* 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 toBinary($value, $places = null)
public static function toBinary($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -65,7 +65,7 @@ class ConvertHex extends ConvertBase
* Excel Function:
* HEX2DEC(x)
*
* @param array|string $value The hexadecimal number you want to convert. This number cannot
* @param array|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
@@ -118,7 +118,7 @@ class ConvertHex extends ConvertBase
* Excel Function:
* HEX2OCT(x[,places])
*
* @param array|string $value The hexadecimal number you want to convert. Number cannot
* @param array|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
@@ -145,7 +145,7 @@ class ConvertHex extends ConvertBase
* 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 toOctal($value, $places = null)
public static function toOctal($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);

View File

@@ -15,7 +15,7 @@ class ConvertOctal extends ConvertBase
* Excel Function:
* OCT2BIN(x[,places])
*
* @param array|string $value The octal number you want to convert. Number may not
* @param array|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
@@ -44,7 +44,7 @@ class ConvertOctal extends ConvertBase
* 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 toBinary($value, $places = null)
public static function toBinary($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -69,7 +69,7 @@ class ConvertOctal extends ConvertBase
* Excel Function:
* OCT2DEC(x)
*
* @param array|string $value The octal number you want to convert. Number may not contain
* @param array|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
@@ -118,7 +118,7 @@ class ConvertOctal extends ConvertBase
* Excel Function:
* OCT2HEX(x[,places])
*
* @param array|string $value The octal number you want to convert. Number may not contain
* @param array|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
@@ -142,7 +142,7 @@ class ConvertOctal extends ConvertBase
* 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 toHex($value, $places = null)
public static function toHex($value, $places = null): array|string
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);

View File

@@ -27,183 +27,183 @@ class ConvertUOM
/**
* Details of the Units of measure that can be used in CONVERTUOM().
*
* @var mixed[]
* @var array<string, array{Group: string, UnitName: string, AllowPrefix: bool}>
*/
private static $conversionUnits = [
private static array $conversionUnits = [
// Weight and Mass
'g' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true],
'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false],
'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false],
'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false],
'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false],
'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
'g' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Gram', 'AllowPrefix' => true],
'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Slug', 'AllowPrefix' => false],
'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'U (atomic mass unit)', 'AllowPrefix' => true],
'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Grain', 'AllowPrefix' => false],
'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial hundredweight', 'AllowPrefix' => false],
'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial hundredweight', 'AllowPrefix' => false],
'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial hundredweight', 'AllowPrefix' => false],
'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Stone', 'AllowPrefix' => false],
'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Ton', 'AllowPrefix' => false],
'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial ton', 'AllowPrefix' => false],
'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial ton', 'AllowPrefix' => false],
'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial ton', 'AllowPrefix' => false],
// Distance
'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true],
'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false],
'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false],
'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false],
'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false],
'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false],
'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false],
'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
'm' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Meter', 'AllowPrefix' => true],
'mi' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Statute mile', 'AllowPrefix' => false],
'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Nautical mile', 'AllowPrefix' => false],
'in' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Inch', 'AllowPrefix' => false],
'ft' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Foot', 'AllowPrefix' => false],
'yd' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Yard', 'AllowPrefix' => false],
'ang' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Angstrom', 'AllowPrefix' => true],
'ell' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Ell', 'AllowPrefix' => false],
'ly' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Light Year', 'AllowPrefix' => false],
'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Parsec', 'AllowPrefix' => false],
'pc' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Parsec', 'AllowPrefix' => false],
'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Pica (1/72 in)', 'AllowPrefix' => false],
'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Pica (1/72 in)', 'AllowPrefix' => false],
'pica' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Pica (1/6 in)', 'AllowPrefix' => false],
'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
// Time
'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false],
'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false],
'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
'yr' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Year', 'AllowPrefix' => false],
'day' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Day', 'AllowPrefix' => false],
'd' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Day', 'AllowPrefix' => false],
'hr' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Hour', 'AllowPrefix' => false],
'mn' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Minute', 'AllowPrefix' => false],
'min' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Minute', 'AllowPrefix' => false],
'sec' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Second', 'AllowPrefix' => true],
's' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Second', 'AllowPrefix' => true],
// Pressure
'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true],
'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true],
'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Pascal', 'AllowPrefix' => true],
'p' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Pascal', 'AllowPrefix' => true],
'atm' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Atmosphere', 'AllowPrefix' => true],
'at' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Atmosphere', 'AllowPrefix' => true],
'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'mm of Mercury', 'AllowPrefix' => true],
'psi' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'PSI', 'AllowPrefix' => true],
'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Torr', 'AllowPrefix' => true],
// Force
'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true],
'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true],
'N' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Newton', 'AllowPrefix' => true],
'dyn' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Dyne', 'AllowPrefix' => true],
'dy' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Dyne', 'AllowPrefix' => true],
'lbf' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Pound force', 'AllowPrefix' => false],
'pond' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Pond', 'AllowPrefix' => true],
// Energy
'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true],
'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true],
'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
'J' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Joule', 'AllowPrefix' => true],
'e' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Erg', 'AllowPrefix' => true],
'c' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Thermodynamic calorie', 'AllowPrefix' => true],
'cal' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'IT calorie', 'AllowPrefix' => true],
'eV' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Electron volt', 'AllowPrefix' => true],
'ev' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Electron volt', 'AllowPrefix' => true],
'HPh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Horsepower-hour', 'AllowPrefix' => false],
'hh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Horsepower-hour', 'AllowPrefix' => false],
'Wh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Watt-hour', 'AllowPrefix' => true],
'wh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Watt-hour', 'AllowPrefix' => true],
'flb' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Foot-pound', 'AllowPrefix' => false],
'BTU' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'BTU', 'AllowPrefix' => false],
'btu' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'BTU', 'AllowPrefix' => false],
// Power
'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false],
'HP' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Horsepower', 'AllowPrefix' => false],
'h' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Horsepower', 'AllowPrefix' => false],
'W' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Watt', 'AllowPrefix' => true],
'w' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Watt', 'AllowPrefix' => true],
'PS' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Pferdestärke', 'AllowPrefix' => false],
// Magnetism
'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
'T' => ['Group' => self::CATEGORY_MAGNETISM, 'UnitName' => 'Tesla', 'AllowPrefix' => true],
'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'UnitName' => 'Gauss', 'AllowPrefix' => true],
// Temperature
'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false],
'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false],
'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Celsius', 'AllowPrefix' => false],
'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Celsius', 'AllowPrefix' => false],
'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Kelvin', 'AllowPrefix' => false],
'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Kelvin', 'AllowPrefix' => false],
'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Rankine', 'AllowPrefix' => false],
'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Réaumur', 'AllowPrefix' => false],
// Volume
'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false],
'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false],
'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false],
'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false],
'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false],
'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
'l' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Litre', 'AllowPrefix' => true],
'L' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Litre', 'AllowPrefix' => true],
'lt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Litre', 'AllowPrefix' => true],
'tsp' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Teaspoon', 'AllowPrefix' => false],
'tspm' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Modern Teaspoon', 'AllowPrefix' => false],
'tbs' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Tablespoon', 'AllowPrefix' => false],
'oz' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Fluid Ounce', 'AllowPrefix' => false],
'cup' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cup', 'AllowPrefix' => false],
'pt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'U.S. Pint', 'AllowPrefix' => false],
'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'U.S. Pint', 'AllowPrefix' => false],
'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'U.K. Pint', 'AllowPrefix' => false],
'qt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Quart', 'AllowPrefix' => false],
'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
'gal' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Gallon', 'AllowPrefix' => false],
'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
'ang3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Angstrom', 'AllowPrefix' => true],
'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Angstrom', 'AllowPrefix' => true],
'barrel' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'US Oil Barrel', 'AllowPrefix' => false],
'bushel' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'US Bushel', 'AllowPrefix' => false],
'in3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Inch', 'AllowPrefix' => false],
'in^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Inch', 'AllowPrefix' => false],
'ft3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Foot', 'AllowPrefix' => false],
'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Foot', 'AllowPrefix' => false],
'ly3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Light Year', 'AllowPrefix' => false],
'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Light Year', 'AllowPrefix' => false],
'm3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Meter', 'AllowPrefix' => true],
'm^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Meter', 'AllowPrefix' => true],
'mi3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Mile', 'AllowPrefix' => false],
'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Mile', 'AllowPrefix' => false],
'yd3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Yard', 'AllowPrefix' => false],
'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Yard', 'AllowPrefix' => false],
'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
'GRT' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Gross Registered Ton', 'AllowPrefix' => false],
'regton' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Gross Registered Ton', 'AllowPrefix' => false],
'MTON' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
// Area
'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true],
'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false],
'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true],
'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false],
'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
'ha' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Hectare', 'AllowPrefix' => true],
'uk_acre' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'International Acre', 'AllowPrefix' => false],
'us_acre' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
'ang2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Angstrom', 'AllowPrefix' => true],
'ang^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Angstrom', 'AllowPrefix' => true],
'ar' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Are', 'AllowPrefix' => true],
'ft2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Feet', 'AllowPrefix' => false],
'ft^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Feet', 'AllowPrefix' => false],
'in2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Inches', 'AllowPrefix' => false],
'in^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Inches', 'AllowPrefix' => false],
'ly2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Light Years', 'AllowPrefix' => false],
'ly^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Light Years', 'AllowPrefix' => false],
'm2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Meters', 'AllowPrefix' => true],
'm^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Meters', 'AllowPrefix' => true],
'Morgen' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Morgen', 'AllowPrefix' => false],
'mi2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Miles', 'AllowPrefix' => false],
'mi^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Miles', 'AllowPrefix' => false],
'Nmi2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Nautical Miles', 'AllowPrefix' => false],
'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Nautical Miles', 'AllowPrefix' => false],
'Pica2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
'Pica^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
'Picapt2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
'yd2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Yards', 'AllowPrefix' => false],
'yd^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Yards', 'AllowPrefix' => false],
// Information
'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true],
'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true],
'byte' => ['Group' => self::CATEGORY_INFORMATION, 'UnitName' => 'Byte', 'AllowPrefix' => true],
'bit' => ['Group' => self::CATEGORY_INFORMATION, 'UnitName' => 'Bit', 'AllowPrefix' => true],
// Speed
'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false],
'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false],
'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false],
'm/s' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per second', 'AllowPrefix' => true],
'm/sec' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per second', 'AllowPrefix' => true],
'm/h' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per hour', 'AllowPrefix' => true],
'm/hr' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per hour', 'AllowPrefix' => true],
'mph' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Miles per hour', 'AllowPrefix' => false],
'admkn' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Admiralty Knot', 'AllowPrefix' => false],
'kn' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Knot', 'AllowPrefix' => false],
];
/**
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
* @var mixed[]
* @var array<string, array{multiplier: float, name: string}>
*/
private static $conversionMultipliers = [
private static array $conversionMultipliers = [
'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
'E' => ['multiplier' => 1E18, 'name' => 'exa'],
@@ -230,9 +230,9 @@ class ConvertUOM
/**
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
* @var mixed[]
** @var array<string, array{multiplier: float|int, name: string}>
*/
private static $binaryConversionMultipliers = [
private static array $binaryConversionMultipliers = [
'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'],
'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'],
@@ -246,9 +246,9 @@ class ConvertUOM
/**
* Details of the Units of measure conversion factors, organised by group.
*
* @var mixed[]
* @var array<string, array<string, float>>
*/
private static $unitConversions = [
private static array $unitConversions = [
// Conversion uses gram (g) as an intermediate unit
self::CATEGORY_WEIGHT_AND_MASS => [
'g' => 1.0,
@@ -435,10 +435,8 @@ class ConvertUOM
/**
* getConversionGroups
* Returns a list of the different conversion groups for UOM conversions.
*
* @return array
*/
public static function getConversionCategories()
public static function getConversionCategories(): array
{
$conversionGroups = [];
foreach (self::$conversionUnits as $conversionUnit) {
@@ -452,11 +450,9 @@ class ConvertUOM
* getConversionGroupUnits
* 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 array
* @param ?string $category The group whose units of measure you want to retrieve
*/
public static function getConversionCategoryUnits($category = null)
public static function getConversionCategoryUnits(?string $category = null): array
{
$conversionGroups = [];
foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
@@ -471,18 +467,16 @@ class ConvertUOM
/**
* getConversionGroupUnitDetails.
*
* @param string $category The group whose units of measure you want to retrieve
*
* @return array
* @param ?string $category The group whose units of measure you want to retrieve
*/
public static function getConversionCategoryUnitDetails($category = null)
public static function getConversionCategoryUnitDetails(?string $category = null): array
{
$conversionGroups = [];
foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
if (($category === null) || ($conversionGroup['Group'] == $category)) {
$conversionGroups[$conversionGroup['Group']][] = [
'unit' => $conversionUnit,
'description' => $conversionGroup['Unit Name'],
'description' => $conversionGroup['UnitName'],
];
}
}
@@ -496,7 +490,7 @@ class ConvertUOM
*
* @return mixed[]
*/
public static function getConversionMultipliers()
public static function getConversionMultipliers(): array
{
return self::$conversionMultipliers;
}
@@ -507,7 +501,7 @@ class ConvertUOM
*
* @return mixed[]
*/
public static function getBinaryConversionMultipliers()
public static function getBinaryConversionMultipliers(): array
{
return self::$binaryConversionMultipliers;
}
@@ -546,7 +540,7 @@ class ConvertUOM
try {
[$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
[$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
} catch (Exception $e) {
} catch (Exception) {
return ExcelError::NA();
}
@@ -564,7 +558,7 @@ class ConvertUOM
} elseif ($fromUOM === $toUOM) {
return $value / $toMultiplier;
} elseif ($fromCategory === self::CATEGORY_TEMPERATURE) {
return self::convertTemperature($fromUOM, $toUOM, /** @scrutinizer ignore-type */ $value);
return self::convertTemperature($fromUOM, $toUOM, $value);
}
$baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]);
@@ -572,7 +566,7 @@ class ConvertUOM
return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
}
private static function getUOMDetails(string $uom)
private static function getUOMDetails(string $uom): array
{
if (isset(self::$conversionUnits[$uom])) {
$unitCategory = self::$conversionUnits[$uom]['Group'];
@@ -621,12 +615,7 @@ class ConvertUOM
throw new Exception('UoM Not Found');
}
/**
* @param float|int $value
*
* @return float|int
*/
protected static function convertTemperature(string $fromUOM, string $toUOM, $value)
protected static function convertTemperature(string $fromUOM, string $toUOM, float|int $value): float|int
{
$fromUOM = self::resolveTemperatureSynonyms($fromUOM);
$toUOM = self::resolveTemperatureSynonyms($toUOM);
@@ -678,17 +667,13 @@ class ConvertUOM
return $value;
}
private static function resolveTemperatureSynonyms(string $uom)
private static function resolveTemperatureSynonyms(string $uom): string
{
switch ($uom) {
case 'fah':
return 'F';
case 'cel':
return 'C';
case 'kel':
return 'K';
}
return $uom;
return match ($uom) {
'fah' => 'F',
'cel' => 'C',
'kel' => 'K',
default => $uom,
};
}
}

View File

@@ -7,10 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class EngineeringValidations
{
/**
* @param mixed $value
*/
public static function validateFloat($value): float
public static function validateFloat(mixed $value): float
{
if (!is_numeric($value)) {
throw new Exception(ExcelError::VALUE());
@@ -19,10 +16,7 @@ class EngineeringValidations
return (float) $value;
}
/**
* @param mixed $value
*/
public static function validateInt($value): int
public static function validateInt(mixed $value): int
{
if (!is_numeric($value)) {
throw new Exception(ExcelError::VALUE());

View File

@@ -10,7 +10,7 @@ class Erf
{
use ArrayEnabled;
private static $twoSqrtPi = 1.128379167095512574;
private const TWO_SQRT_PI = 1.128379167095512574;
/**
* ERF.
@@ -31,11 +31,10 @@ 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|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($lower, $upper = null)
public static function ERF(mixed $lower, mixed $upper = null): array|float|string
{
if (is_array($lower) || is_array($upper)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $lower, $upper);
@@ -64,11 +63,10 @@ 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|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($limit)
public static function ERFPRECISE(mixed $limit)
{
if (is_array($limit)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $limit);
@@ -77,13 +75,19 @@ class Erf
return self::ERF($limit);
}
//
// Private method to calculate the erf value
//
public static function erfValue($value)
private static function makeFloat(mixed $value): float
{
return is_numeric($value) ? ((float) $value) : 0.0;
}
/**
* Method to calculate the erf value.
*/
public static function erfValue(float|int|string $value): float
{
$value = (float) $value;
if (abs($value) > 2.2) {
return 1 - ErfC::ERFC($value);
return 1 - self::makeFloat(ErfC::ERFC($value));
}
$sum = $term = $value;
$xsqr = ($value * $value);
@@ -100,6 +104,6 @@ class Erf
}
} while (abs($term / $sum) > Functions::PRECISION);
return self::$twoSqrtPi * $sum;
return self::TWO_SQRT_PI * $sum;
}
}

View File

@@ -26,11 +26,10 @@ 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|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($value)
public static function ERFC(mixed $value)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -43,13 +42,14 @@ class ErfC
return ExcelError::VALUE();
}
//
// Private method to calculate the erfc value
//
private static $oneSqrtPi = 0.564189583547756287;
private const ONE_SQRT_PI = 0.564189583547756287;
private static function erfcValue($value)
/**
* Method to calculate the erfc value.
*/
private static function erfcValue(float|int|string $value): float|int
{
$value = (float) $value;
if (abs($value) < 2.2) {
return 1 - Erf::erfValue($value);
}
@@ -72,6 +72,6 @@ class ErfC
$q2 = $b / $d;
} while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
return self::$oneSqrtPi * exp(-$value * $value) * $q2;
return self::ONE_SQRT_PI * exp(-$value * $value) * $q2;
}
}

View File

@@ -6,16 +6,12 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class Exception extends PhpSpreadsheetException
{
public const CALCULATION_ENGINE_PUSH_TO_STACK = 1;
/**
* Error handler callback.
*
* @param mixed $code
* @param mixed $string
* @param mixed $file
* @param mixed $line
* @param mixed $context
*/
public static function errorHandlerCallback($code, $string, $file, $line, /** @scrutinizer ignore-unused */ $context): void
public static function errorHandlerCallback(int $code, string $string, string $file, int $line): void
{
$e = new self($string, $code);
$e->line = $line;

View File

@@ -9,7 +9,7 @@ class ExceptionHandler
*/
public function __construct()
{
/** @var callable */
/** @var callable $callable */
$callable = [Exception::class, 'errorHandlerCallback'];
set_error_handler($callable, E_ALL);
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Amortization
{
private const ROUNDING_ADJUSTMENT = (PHP_VERSION_ID < 80400) ? 0 : 1e-14;
/**
* AMORDEGRC.
*
@@ -40,14 +42,14 @@ class Amortization
* @return float|string (string containing the error type if there is an error)
*/
public static function AMORDEGRC(
$cost,
$purchased,
$firstPeriod,
$salvage,
$period,
$rate,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $cost,
mixed $purchased,
mixed $firstPeriod,
mixed $salvage,
mixed $period,
mixed $rate,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|float {
$cost = Functions::flattenSingleValue($cost);
$purchased = Functions::flattenSingleValue($purchased);
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
@@ -74,12 +76,13 @@ class Amortization
if (is_string($yearFracx)) {
return $yearFracx;
}
/** @var float */
/** @var float $yearFrac */
$yearFrac = $yearFracx;
$amortiseCoeff = self::getAmortizationCoefficient($rate);
$rate *= $amortiseCoeff;
$rate += self::ROUNDING_ADJUSTMENT;
$fNRate = round($yearFrac * $rate * $cost, 0);
$cost -= $fNRate;
$fRest = $cost - $salvage;
@@ -89,13 +92,10 @@ class Amortization
$fRest -= $fNRate;
if ($fRest < 0.0) {
switch ($period - $n) {
case 0:
case 1:
return round($cost * 0.5, 0);
default:
return 0.0;
}
return match ($period - $n) {
1 => round($cost * 0.5, 0),
default => 0.0,
};
}
$cost -= $fNRate;
}
@@ -129,14 +129,14 @@ class Amortization
* @return float|string (string containing the error type if there is an error)
*/
public static function AMORLINC(
$cost,
$purchased,
$firstPeriod,
$salvage,
$period,
$rate,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $cost,
mixed $purchased,
mixed $firstPeriod,
mixed $salvage,
mixed $period,
mixed $rate,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|float {
$cost = Functions::flattenSingleValue($cost);
$purchased = Functions::flattenSingleValue($purchased);
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
@@ -167,12 +167,13 @@ class Amortization
if (is_string($yearFracx)) {
return $yearFracx;
}
/** @var float */
/** @var float $yearFrac */
$yearFrac = $yearFracx;
if (
($basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) &&
($yearFrac < 1) && (Functions::scalar(DateTimeExcel\Helpers::isLeapYear($purchasedYear)))
$basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
&& $yearFrac < 1
&& DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear))
) {
$yearFrac *= 365 / 366;
}

View File

@@ -9,25 +9,19 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class CashFlowValidations extends FinancialValidations
{
/**
* @param mixed $rate
*/
public static function validateRate($rate): float
public static function validateRate(mixed $rate): float
{
$rate = self::validateFloat($rate);
return $rate;
}
/**
* @param mixed $type
*/
public static function validatePeriodType($type): int
public static function validatePeriodType(mixed $type): int
{
$rate = self::validateInt($type);
if (
$type !== FinancialConstants::PAYMENT_END_OF_PERIOD &&
$type !== FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD
$type !== FinancialConstants::PAYMENT_END_OF_PERIOD
&& $type !== FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD
) {
throw new Exception(ExcelError::NAN());
}
@@ -35,18 +29,12 @@ class CashFlowValidations extends FinancialValidations
return $rate;
}
/**
* @param mixed $presentValue
*/
public static function validatePresentValue($presentValue): float
public static function validatePresentValue(mixed $presentValue): float
{
return self::validateFloat($presentValue);
}
/**
* @param mixed $futureValue
*/
public static function validateFutureValue($futureValue): float
public static function validateFutureValue(mixed $futureValue): float
{
return self::validateFloat($futureValue);
}

View File

@@ -28,16 +28,14 @@ class Periodic
* @param mixed $type A number 0 or 1 and indicates when payments are due:
* 0 or omitted At the end of the period.
* 1 At the beginning of the period.
*
* @return float|string
*/
public static function futureValue(
$rate,
$numberOfPeriods,
$payment = 0.0,
$presentValue = 0.0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $rate,
mixed $numberOfPeriods,
mixed $payment = 0.0,
mixed $presentValue = 0.0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float {
$rate = Functions::flattenSingleValue($rate);
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
$payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
@@ -71,12 +69,12 @@ class Periodic
* @return float|string Result, or a string containing an error
*/
public static function presentValue(
$rate,
$numberOfPeriods,
$payment = 0.0,
$futureValue = 0.0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $rate,
mixed $numberOfPeriods,
mixed $payment = 0.0,
mixed $futureValue = 0.0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float {
$rate = Functions::flattenSingleValue($rate);
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
$payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
@@ -115,11 +113,11 @@ class Periodic
* @return float|string Result, or a string containing an error
*/
public static function periods(
$rate,
$payment,
$presentValue,
$futureValue = 0.0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
mixed $rate,
mixed $payment,
mixed $presentValue,
mixed $futureValue = 0.0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
$rate = Functions::flattenSingleValue($rate);
$payment = Functions::flattenSingleValue($payment);
@@ -153,8 +151,8 @@ class Periodic
int $type
): float {
if ($rate !== null && $rate != 0) {
return -$presentValue *
(1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
return -$presentValue
* (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
/ $rate;
}
@@ -176,23 +174,20 @@ class Periodic
return -$futureValue - $payment * $numberOfPeriods;
}
/**
* @return float|string
*/
private static function calculatePeriods(
float $rate,
float $payment,
float $presentValue,
float $futureValue,
int $type
) {
): string|float {
if ($rate != 0.0) {
if ($presentValue == 0.0) {
return ExcelError::NAN();
}
return log(($payment * (1 + $rate * $type) / $rate - $futureValue) /
($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
return log(($payment * (1 + $rate * $type) / $rate - $futureValue)
/ ($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
}
return (-$presentValue - $futureValue) / $payment;

View File

@@ -27,17 +27,15 @@ class Cumulative
* @param mixed $type A number 0 or 1 and indicates when payments are due:
* 0 or omitted At the end of the period.
* 1 At the beginning of the period.
*
* @return float|string
*/
public static function interest(
$rate,
$periods,
$presentValue,
$start,
$end,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $rate,
mixed $periods,
mixed $presentValue,
mixed $start,
mixed $end,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float|int {
$rate = Functions::flattenSingleValue($rate);
$periods = Functions::flattenSingleValue($periods);
$presentValue = Functions::flattenSingleValue($presentValue);
@@ -92,17 +90,15 @@ class Cumulative
* @param mixed $type A number 0 or 1 and indicates when payments are due:
* 0 or omitted At the end of the period.
* 1 At the beginning of the period.
*
* @return float|string
*/
public static function principal(
$rate,
$periods,
$presentValue,
$start,
$end,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $rate,
mixed $periods,
mixed $presentValue,
mixed $start,
mixed $end,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float|int {
$rate = Functions::flattenSingleValue($rate);
$periods = Functions::flattenSingleValue($periods);
$presentValue = Functions::flattenSingleValue($presentValue);

View File

@@ -29,17 +29,15 @@ class Interest
* @param mixed $presentValue Present Value
* @param mixed $futureValue Future Value
* @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
*
* @return float|string
*/
public static function payment(
$interestRate,
$period,
$numberOfPeriods,
$presentValue,
$futureValue = 0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $interestRate,
mixed $period,
mixed $numberOfPeriods,
mixed $presentValue,
mixed $futureValue = 0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float {
$interestRate = Functions::flattenSingleValue($interestRate);
$period = Functions::flattenSingleValue($period);
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
@@ -88,10 +86,8 @@ class Interest
* @param mixed $period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
* @param mixed $numberOfPeriods is the number of payments for the annuity
* @param mixed $principleRemaining is the loan amount or present value of the payments
*
* @return float|string
*/
public static function schedulePayment($interestRate, $period, $numberOfPeriods, $principleRemaining)
public static function schedulePayment(mixed $interestRate, mixed $period, mixed $numberOfPeriods, mixed $principleRemaining): string|float
{
$interestRate = Functions::flattenSingleValue($interestRate);
$period = Functions::flattenSingleValue($period);
@@ -152,17 +148,15 @@ class Interest
* 1 At the beginning of the period.
* @param mixed $guess Your guess for what the rate will be.
* If you omit guess, it is assumed to be 10 percent.
*
* @return float|string
*/
public static function rate(
$numberOfPeriods,
$payment,
$presentValue,
$futureValue = 0.0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD,
$guess = 0.1
) {
mixed $numberOfPeriods,
mixed $payment,
mixed $presentValue,
mixed $futureValue = 0.0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD,
mixed $guess = 0.1
): string|float {
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
$payment = Functions::flattenSingleValue($payment);
$presentValue = Functions::flattenSingleValue($presentValue);
@@ -171,7 +165,7 @@ class Interest
$guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
try {
$numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
$numberOfPeriods = CashFlowValidations::validateFloat($numberOfPeriods);
$payment = CashFlowValidations::validateFloat($payment);
$presentValue = CashFlowValidations::validatePresentValue($presentValue);
$futureValue = CashFlowValidations::validateFutureValue($futureValue);
@@ -199,7 +193,7 @@ class Interest
return $close ? $rate : ExcelError::NAN();
}
private static function rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type)
private static function rateNextGuess(float $rate, float $numberOfPeriods, float $payment, float $presentValue, float $futureValue, int $type): string|float
{
if ($rate == 0.0) {
return ExcelError::NAN();

View File

@@ -6,9 +6,9 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstan
class InterestAndPrincipal
{
protected $interest;
protected float $interest;
protected $principal;
protected float $principal;
public function __construct(
float $rate = 0.0,
@@ -24,7 +24,7 @@ class InterestAndPrincipal
$principal = 0.0;
for ($i = 1; $i <= $period; ++$i) {
$interest = ($type === FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD && $i == 1) ? 0 : -$capital * $rate;
$principal = $payment - $interest;
$principal = (float) $payment - $interest;
$capital += $principal;
}

View File

@@ -24,12 +24,12 @@ class Payments
* @return float|string Result, or a string containing an error
*/
public static function annuity(
$interestRate,
$numberOfPeriods,
$presentValue,
$futureValue = 0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $interestRate,
mixed $numberOfPeriods,
mixed $presentValue,
mixed $futureValue = 0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float {
$interestRate = Functions::flattenSingleValue($interestRate);
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
$presentValue = Functions::flattenSingleValue($presentValue);
@@ -48,8 +48,8 @@ class Payments
// Calculate
if ($interestRate != 0.0) {
return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods) /
(1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods)
/ (1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
}
return (-$presentValue - $futureValue) / $numberOfPeriods;
@@ -71,13 +71,13 @@ class Payments
* @return float|string Result, or a string containing an error
*/
public static function interestPayment(
$interestRate,
$period,
$numberOfPeriods,
$presentValue,
$futureValue = 0,
$type = FinancialConstants::PAYMENT_END_OF_PERIOD
) {
mixed $interestRate,
mixed $period,
mixed $numberOfPeriods,
mixed $presentValue,
mixed $futureValue = 0,
mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
): string|float {
$interestRate = Functions::flattenSingleValue($interestRate);
$period = Functions::flattenSingleValue($period);
$numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);

View File

@@ -19,10 +19,8 @@ class Single
*
* @param mixed $principal the present value
* @param float[] $schedule an array of interest rates to apply
*
* @return float|string
*/
public static function futureValue($principal, $schedule)
public static function futureValue(mixed $principal, array $schedule): string|float
{
$principal = Functions::flattenSingleValue($principal);
$schedule = Functions::flattenArray($schedule);
@@ -52,7 +50,7 @@ class Single
*
* @return float|string Result, or a string containing an error
*/
public static function periods($rate, $presentValue, $futureValue)
public static function periods(mixed $rate, mixed $presentValue, mixed $futureValue): string|float
{
$rate = Functions::flattenSingleValue($rate);
$presentValue = Functions::flattenSingleValue($presentValue);
@@ -79,13 +77,13 @@ class Single
*
* Calculates the interest rate required for an investment to grow to a specified future value .
*
* @param float $periods The number of periods over which the investment is made
* @param float $presentValue Present Value
* @param float $futureValue Future Value
* @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
*
* @return float|string Result, or a string containing an error
*/
public static function interestRate($periods = 0.0, $presentValue = 0.0, $futureValue = 0.0)
public static function interestRate(array|float $periods = 0.0, array|float $presentValue = 0.0, array|float $futureValue = 0.0): string|float
{
$periods = Functions::flattenSingleValue($periods);
$presentValue = Functions::flattenSingleValue($presentValue);

View File

@@ -29,10 +29,8 @@ class NonPeriodic
* 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
*
* @return float|string
*/
public static function rate($values, $dates, $guess = self::DEFAULT_GUESS)
public static function rate(array $values, array $dates, mixed $guess = self::DEFAULT_GUESS): float|string
{
$rslt = self::xirrPart1($values, $dates);
if ($rslt !== '') {
@@ -51,8 +49,11 @@ class NonPeriodic
$f2 = self::xnpvOrdered($x2, $values, $dates, false);
$found = false;
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
if (!is_numeric($f1) || !is_numeric($f2)) {
break;
if (!is_numeric($f1)) {
return $f1;
}
if (!is_numeric($f2)) {
return $f2;
}
$f1 = (float) $f1;
$f2 = (float) $f2;
@@ -68,11 +69,32 @@ class NonPeriodic
$f2 = self::xnpvOrdered($x2, $values, $dates, false);
}
}
if (!$found) {
return ExcelError::NAN();
if ($found) {
return self::xirrPart3($values, $dates, $x1, $x2);
}
return self::xirrPart3($values, $dates, $x1, $x2);
// Newton-Raphson didn't work - try bisection
$x1 = $guess - 0.5;
$x2 = $guess + 0.5;
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
$f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
$f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
if (!is_numeric($f1) || !is_numeric($f2)) {
break;
}
if ($f1 * $f2 <= 0) {
$found = true;
break;
}
$x1 -= 0.5;
$x2 += 0.5;
}
if ($found) {
return self::xirrBisection($values, $dates, $x1, $x2);
}
return ExcelError::NAN();
}
/**
@@ -84,7 +106,7 @@ class NonPeriodic
* Excel Function:
* =XNPV(rate,values,dates)
*
* @param float $rate the discount rate to apply to the cash flows
* @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.
* The first payment is optional and corresponds to a cost or payment that occurs
* at the beginning of the investment.
@@ -94,10 +116,8 @@ class NonPeriodic
* @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
* The first payment date indicates the beginning of the schedule of payments.
* All other dates must be later than this date, but they may occur in any order.
*
* @return float|string
*/
public static function presentValue($rate, $values, $dates)
public static function presentValue(array|float $rate, array $values, array $dates): float|string
{
return self::xnpvOrdered($rate, $values, $dates, true);
}
@@ -107,11 +127,7 @@ class NonPeriodic
return $neg && $pos;
}
/**
* @param mixed $values
* @param mixed $dates
*/
private static function xirrPart1(&$values, &$dates): string
private static function xirrPart1(mixed &$values, mixed &$dates): string
{
$values = Functions::flattenArray($values);
$dates = Functions::flattenArray($dates);
@@ -158,10 +174,7 @@ class NonPeriodic
return '';
}
/**
* @return float|string
*/
private static function xirrPart3(array $values, array $dates, float $x1, float $x2)
private static function xirrPart3(array $values, array $dates, float $x1, float $x2): float|string
{
$f = self::xnpvOrdered($x1, $values, $dates, false);
if ($f < 0.0) {
@@ -190,14 +203,43 @@ class NonPeriodic
return $rslt;
}
/**
* @param mixed $rate
* @param mixed $values
* @param mixed $dates
*
* @return float|string
*/
private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true)
private static function xirrBisection(array $values, array $dates, float $x1, float $x2): string|float
{
$rslt = ExcelError::NAN();
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
$rslt = ExcelError::NAN();
$f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
$f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
if (!is_numeric($f1) || !is_numeric($f2)) {
break;
}
$f1 = (float) $f1;
$f2 = (float) $f2;
if (abs($f1) < self::FINANCIAL_PRECISION && abs($f2) < self::FINANCIAL_PRECISION) {
break;
}
if ($f1 * $f2 > 0) {
break;
}
$rslt = ($x1 + $x2) / 2;
$f3 = self::xnpvOrdered($rslt, $values, $dates, false, true);
if (!is_float($f3)) {
break;
}
if ($f3 * $f1 < 0) {
$x2 = $rslt;
} else {
$x1 = $rslt;
}
if (abs($f3) < self::FINANCIAL_PRECISION) {
break;
}
}
return $rslt;
}
private static function xnpvOrdered(mixed $rate, mixed $values, mixed $dates, bool $ordered = true, bool $capAtNegative1 = false): float|string
{
$rate = Functions::flattenSingleValue($rate);
$values = Functions::flattenArray($values);
@@ -206,6 +248,9 @@ class NonPeriodic
try {
self::validateXnpv($rate, $values, $dates);
if ($capAtNegative1 && $rate <= -1) {
$rate = -1.0 + 1.0E-10;
}
$date0 = DateTimeExcel\Helpers::getDateValue($dates[0]);
} catch (Exception $e) {
return $e->getMessage();
@@ -225,7 +270,7 @@ class NonPeriodic
if ($date0 > $datei) {
$dif = $ordered ? ExcelError::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
} else {
$dif = DateTimeExcel\Difference::interval($date0, $datei, 'd');
$dif = Functions::scalar(DateTimeExcel\Difference::interval($date0, $datei, 'd'));
}
if (!is_numeric($dif)) {
return $dif;
@@ -240,10 +285,7 @@ class NonPeriodic
return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
}
/**
* @param mixed $rate
*/
private static function validateXnpv($rate, array $values, array $dates): void
private static function validateXnpv(mixed $rate, array $values, array $dates): void
{
if (!is_numeric($rate)) {
throw new Exception(ExcelError::VALUE());

View File

@@ -28,10 +28,8 @@ class Periodic
* Values must contain at least one positive value and one negative value to
* calculate the internal rate of return.
* @param mixed $guess A number that you guess is close to the result of IRR
*
* @return float|string
*/
public static function rate($values, $guess = 0.1)
public static function rate(mixed $values, mixed $guess = 0.1): string|float
{
if (!is_array($values)) {
return ExcelError::VALUE();
@@ -99,7 +97,7 @@ class Periodic
*
* @return float|string Result, or a string containing an error
*/
public static function modifiedRate($values, $financeRate, $reinvestmentRate)
public static function modifiedRate(mixed $values, mixed $financeRate, mixed $reinvestmentRate): string|float
{
if (!is_array($values)) {
return ExcelError::DIV0();
@@ -112,7 +110,7 @@ class Periodic
$rr = 1.0 + $reinvestmentRate;
$fr = 1.0 + $financeRate;
$npvPos = $npvNeg = self::$zeroPointZero;
$npvPos = $npvNeg = 0.0;
foreach ($values as $i => $v) {
if ($v >= 0) {
$npvPos += $v / $rr ** $i;
@@ -121,7 +119,7 @@ class Periodic
}
}
if ($npvNeg === self::$zeroPointZero || $npvPos === self::$zeroPointZero) {
if ($npvNeg === 0.0 || $npvPos === 0.0) {
return ExcelError::DIV0();
}
@@ -131,23 +129,14 @@ class Periodic
return is_finite($mirr) ? $mirr : ExcelError::NAN();
}
/**
* Sop to Scrutinizer.
*
* @var float
*/
private static $zeroPointZero = 0.0;
/**
* NPV.
*
* Returns the Net Present Value of a cash flow series given a discount rate.
*
* @param mixed $rate
*
* @return float
* @param array $args
*/
public static function presentValue($rate, ...$args)
public static function presentValue(mixed $rate, ...$args): int|float
{
$returnValue = 0;

View File

@@ -39,15 +39,13 @@ class Coupons
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function COUPDAYBS(
$settlement,
$maturity,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|int|float {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$frequency = Functions::flattenSingleValue($frequency);
@@ -102,15 +100,13 @@ class Coupons
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function COUPDAYS(
$settlement,
$maturity,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|int|float {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$frequency = Functions::flattenSingleValue($frequency);
@@ -173,15 +169,13 @@ class Coupons
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function COUPDAYSNC(
$settlement,
$maturity,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|float {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$frequency = Functions::flattenSingleValue($frequency);
@@ -199,8 +193,8 @@ class Coupons
return $e->getMessage();
}
/** @var int */
$daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
/** @var int $daysPerYear */
$daysPerYear = Helpers::daysPerYear(Functions::Scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
$next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) {
@@ -239,15 +233,14 @@ class Coupons
* 3 Actual/365
* 4 European 30/360
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* @return float|string Excel date/time serial value or error message
*/
public static function COUPNCD(
$settlement,
$maturity,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|float {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$frequency = Functions::flattenSingleValue($frequency);
@@ -260,8 +253,7 @@ class Coupons
$maturity = FinancialValidations::validateMaturityDate($maturity);
self::validateCouponPeriod($settlement, $maturity);
$frequency = FinancialValidations::validateFrequency($frequency);
$basis = FinancialValidations::validateBasis($basis);
self::doNothing($basis);
FinancialValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
@@ -294,15 +286,13 @@ class Coupons
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return int|string
*/
public static function COUPNUM(
$settlement,
$maturity,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|int {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$frequency = Functions::flattenSingleValue($frequency);
@@ -315,8 +305,7 @@ class Coupons
$maturity = FinancialValidations::validateMaturityDate($maturity);
self::validateCouponPeriod($settlement, $maturity);
$frequency = FinancialValidations::validateFrequency($frequency);
$basis = FinancialValidations::validateBasis($basis);
self::doNothing($basis);
FinancialValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
@@ -355,15 +344,14 @@ class Coupons
* 3 Actual/365
* 4 European 30/360
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* @return float|string Excel date/time serial value or error message
*/
public static function COUPPCD(
$settlement,
$maturity,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|float {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$frequency = Functions::flattenSingleValue($frequency);
@@ -376,8 +364,7 @@ class Coupons
$maturity = FinancialValidations::validateMaturityDate($maturity);
self::validateCouponPeriod($settlement, $maturity);
$frequency = FinancialValidations::validateFrequency($frequency);
$basis = FinancialValidations::validateBasis($basis);
self::doNothing($basis);
FinancialValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
@@ -417,10 +404,4 @@ class Coupons
throw new Exception(ExcelError::NAN());
}
}
/** @param mixed $basis */
private static function doNothing($basis): bool
{
return $basis;
}
}

View File

@@ -8,8 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class Depreciation
{
/** @var float */
private static $zeroPointZero = 0.0;
private static float $zeroPointZero = 0.0;
/**
* DB.
@@ -33,10 +32,8 @@ class Depreciation
* depreciation. Period must use the same units as life.
* @param mixed $month Number of months in the first year. If month is omitted,
* it defaults to 12.
*
* @return float|string
*/
public static function DB($cost, $salvage, $life, $period, $month = 12)
public static function DB(mixed $cost, mixed $salvage, mixed $life, mixed $period, mixed $month = 12): string|float|int
{
$cost = Functions::flattenSingleValue($cost);
$salvage = Functions::flattenSingleValue($salvage);
@@ -99,10 +96,8 @@ class Depreciation
* @param mixed $factor The rate at which the balance declines.
* If factor is omitted, it is assumed to be 2 (the
* double-declining balance method).
*
* @return float|string
*/
public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
public static function DDB(mixed $cost, mixed $salvage, mixed $life, mixed $period, mixed $factor = 2.0): float|string
{
$cost = Functions::flattenSingleValue($cost);
$salvage = Functions::flattenSingleValue($salvage);
@@ -150,7 +145,7 @@ class Depreciation
*
* @return float|string Result, or a string containing an error
*/
public static function SLN($cost, $salvage, $life)
public static function SLN(mixed $cost, mixed $salvage, mixed $life): string|float
{
$cost = Functions::flattenSingleValue($cost);
$salvage = Functions::flattenSingleValue($salvage);
@@ -183,7 +178,7 @@ class Depreciation
*
* @return float|string Result, or a string containing an error
*/
public static function SYD($cost, $salvage, $life, $period)
public static function SYD(mixed $cost, mixed $salvage, mixed $life, mixed $period): string|float
{
$cost = Functions::flattenSingleValue($cost);
$salvage = Functions::flattenSingleValue($salvage);
@@ -208,7 +203,7 @@ class Depreciation
return $syd;
}
private static function validateCost($cost, bool $negativeValueAllowed = false): float
private static function validateCost(mixed $cost, bool $negativeValueAllowed = false): float
{
$cost = FinancialValidations::validateFloat($cost);
if ($cost < 0.0 && $negativeValueAllowed === false) {
@@ -218,7 +213,7 @@ class Depreciation
return $cost;
}
private static function validateSalvage($salvage, bool $negativeValueAllowed = false): float
private static function validateSalvage(mixed $salvage, bool $negativeValueAllowed = false): float
{
$salvage = FinancialValidations::validateFloat($salvage);
if ($salvage < 0.0 && $negativeValueAllowed === false) {
@@ -228,7 +223,7 @@ class Depreciation
return $salvage;
}
private static function validateLife($life, bool $negativeValueAllowed = false): float
private static function validateLife(mixed $life, bool $negativeValueAllowed = false): float
{
$life = FinancialValidations::validateFloat($life);
if ($life < 0.0 && $negativeValueAllowed === false) {
@@ -238,7 +233,7 @@ class Depreciation
return $life;
}
private static function validatePeriod($period, bool $negativeValueAllowed = false): float
private static function validatePeriod(mixed $period, bool $negativeValueAllowed = false): float
{
$period = FinancialValidations::validateFloat($period);
if ($period <= 0.0 && $negativeValueAllowed === false) {
@@ -248,7 +243,7 @@ class Depreciation
return $period;
}
private static function validateMonth($month): int
private static function validateMonth(mixed $month): int
{
$month = FinancialValidations::validateInt($month);
if ($month < 1) {
@@ -258,7 +253,7 @@ class Depreciation
return $month;
}
private static function validateFactor($factor): float
private static function validateFactor(mixed $factor): float
{
$factor = FinancialValidations::validateFloat($factor);
if ($factor <= 0.0) {

View File

@@ -25,11 +25,10 @@ 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|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($number, $precision = 2)
public static function format(mixed $number, mixed $precision = 2)
{
return Format::DOLLAR($number, $precision);
}
@@ -48,10 +47,8 @@ class Dollar
* Or can be an array of values
* @param mixed $fraction Fraction
* Or can be an array of values
*
* @return array|float|string
*/
public static function decimal($fractionalDollar = null, $fraction = 0)
public static function decimal(mixed $fractionalDollar = null, mixed $fraction = 0): array|string|float
{
if (is_array($fractionalDollar) || is_array($fraction)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $fractionalDollar, $fraction);
@@ -96,10 +93,8 @@ class Dollar
* Or can be an array of values
* @param mixed $fraction Fraction
* Or can be an array of values
*
* @return array|float|string
*/
public static function fractional($decimalDollar = null, $fraction = 0)
public static function fractional(mixed $decimalDollar = null, mixed $fraction = 0): array|string|float
{
if (is_array($decimalDollar) || is_array($fraction)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $decimalDollar, $fraction);

View File

@@ -9,34 +9,22 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class FinancialValidations
{
/**
* @param mixed $date
*/
public static function validateDate($date): float
public static function validateDate(mixed $date): float
{
return DateTimeExcel\Helpers::getDateValue($date);
}
/**
* @param mixed $settlement
*/
public static function validateSettlementDate($settlement): float
public static function validateSettlementDate(mixed $settlement): float
{
return self::validateDate($settlement);
}
/**
* @param mixed $maturity
*/
public static function validateMaturityDate($maturity): float
public static function validateMaturityDate(mixed $maturity): float
{
return self::validateDate($maturity);
}
/**
* @param mixed $value
*/
public static function validateFloat($value): float
public static function validateFloat(mixed $value): float
{
if (!is_numeric($value)) {
throw new Exception(ExcelError::VALUE());
@@ -45,10 +33,7 @@ class FinancialValidations
return (float) $value;
}
/**
* @param mixed $value
*/
public static function validateInt($value): int
public static function validateInt(mixed $value): int
{
if (!is_numeric($value)) {
throw new Exception(ExcelError::VALUE());
@@ -57,10 +42,7 @@ class FinancialValidations
return (int) floor((float) $value);
}
/**
* @param mixed $rate
*/
public static function validateRate($rate): float
public static function validateRate(mixed $rate): float
{
$rate = self::validateFloat($rate);
if ($rate < 0.0) {
@@ -70,16 +52,13 @@ class FinancialValidations
return $rate;
}
/**
* @param mixed $frequency
*/
public static function validateFrequency($frequency): int
public static function validateFrequency(mixed $frequency): int
{
$frequency = self::validateInt($frequency);
if (
($frequency !== FinancialConstants::FREQUENCY_ANNUAL) &&
($frequency !== FinancialConstants::FREQUENCY_SEMI_ANNUAL) &&
($frequency !== FinancialConstants::FREQUENCY_QUARTERLY)
($frequency !== FinancialConstants::FREQUENCY_ANNUAL)
&& ($frequency !== FinancialConstants::FREQUENCY_SEMI_ANNUAL)
&& ($frequency !== FinancialConstants::FREQUENCY_QUARTERLY)
) {
throw new Exception(ExcelError::NAN());
}
@@ -87,10 +66,7 @@ class FinancialValidations
return $frequency;
}
/**
* @param mixed $basis
*/
public static function validateBasis($basis): int
public static function validateBasis(mixed $basis): int
{
if (!is_numeric($basis)) {
throw new Exception(ExcelError::VALUE());
@@ -104,10 +80,7 @@ class FinancialValidations
return $basis;
}
/**
* @param mixed $price
*/
public static function validatePrice($price): float
public static function validatePrice(mixed $price): float
{
$price = self::validateFloat($price);
if ($price < 0.0) {
@@ -117,10 +90,7 @@ class FinancialValidations
return $price;
}
/**
* @param mixed $parValue
*/
public static function validateParValue($parValue): float
public static function validateParValue(mixed $parValue): float
{
$parValue = self::validateFloat($parValue);
if ($parValue < 0.0) {
@@ -130,10 +100,7 @@ class FinancialValidations
return $parValue;
}
/**
* @param mixed $yield
*/
public static function validateYield($yield): float
public static function validateYield(mixed $yield): float
{
$yield = self::validateFloat($yield);
if ($yield < 0.0) {
@@ -143,10 +110,7 @@ class FinancialValidations
return $yield;
}
/**
* @param mixed $discount
*/
public static function validateDiscount($discount): float
public static function validateDiscount(mixed $discount): float
{
$discount = self::validateFloat($discount);
if ($discount <= 0.0) {

View File

@@ -24,7 +24,7 @@ class Helpers
*
* @return int|string Result, or a string containing an error
*/
public static function daysPerYear($year, $basis = 0)
public static function daysPerYear($year, $basis = 0): string|int
{
if (!is_numeric($basis)) {
return ExcelError::NAN();

View File

@@ -19,10 +19,8 @@ class InterestRate
*
* @param mixed $nominalRate Nominal interest rate as a float
* @param mixed $periodsPerYear Integer number of compounding payments per year
*
* @return float|string
*/
public static function effective($nominalRate = 0, $periodsPerYear = 0)
public static function effective(mixed $nominalRate = 0, mixed $periodsPerYear = 0): string|float
{
$nominalRate = Functions::flattenSingleValue($nominalRate);
$periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
@@ -51,7 +49,7 @@ class InterestRate
*
* @return float|string Result, or a string containing an error
*/
public static function nominal($effectiveRate = 0, $periodsPerYear = 0)
public static function nominal(mixed $effectiveRate = 0, mixed $periodsPerYear = 0): string|float
{
$effectiveRate = Functions::flattenSingleValue($effectiveRate);
$periodsPerYear = Functions::flattenSingleValue($periodsPerYear);

View File

@@ -40,21 +40,20 @@ class AccruedInterest
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
* @param mixed $calcMethod
* @param mixed $calcMethod Unused by PhpSpreadsheet, and apparently by Excel (https://exceljet.net/functions/accrint-function)
*
* @return float|string Result, or a string containing an error
*/
public static function periodic(
$issue,
$firstInterest,
$settlement,
$rate,
$parValue = 1000,
$frequency = FinancialConstants::FREQUENCY_ANNUAL,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD,
$calcMethod = self::ACCRINT_CALCMODE_ISSUE_TO_SETTLEMENT
mixed $issue,
mixed $firstInterest,
mixed $settlement,
mixed $rate,
mixed $parValue = 1000,
mixed $frequency = FinancialConstants::FREQUENCY_ANNUAL,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD,
mixed $calcMethod = self::ACCRINT_CALCMODE_ISSUE_TO_SETTLEMENT
) {
self::doNothing($calcMethod);
$issue = Functions::flattenSingleValue($issue);
$firstInterest = Functions::flattenSingleValue($firstInterest);
$settlement = Functions::flattenSingleValue($settlement);
@@ -73,8 +72,7 @@ class AccruedInterest
SecurityValidations::validateSecurityPeriod($issue, $settlement);
$rate = SecurityValidations::validateRate($rate);
$parValue = SecurityValidations::validateParValue($parValue);
$frequency = SecurityValidations::validateFrequency($frequency);
self::doNothing($frequency);
SecurityValidations::validateFrequency($frequency);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
@@ -117,11 +115,11 @@ class AccruedInterest
* @return float|string Result, or a string containing an error
*/
public static function atMaturity(
$issue,
$settlement,
$rate,
$parValue = 1000,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
mixed $issue,
mixed $settlement,
mixed $rate,
mixed $parValue = 1000,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$issue = Functions::flattenSingleValue($issue);
$settlement = Functions::flattenSingleValue($settlement);
@@ -150,10 +148,4 @@ class AccruedInterest
return $parValue * $rate * $daysBetweenIssueAndSettlement;
}
/** @param mixed $arg */
private static function doNothing($arg): bool
{
return (bool) $arg;
}
}

View File

@@ -28,7 +28,6 @@ class Price
* For annual payments, frequency = 1;
* for semiannual, frequency = 2;
* for quarterly, frequency = 4.
* @param mixed $frequency
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
@@ -39,14 +38,14 @@ class Price
* @return float|string Result, or a string containing an error
*/
public static function price(
$settlement,
$maturity,
$rate,
$yield,
$redemption,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $rate,
mixed $yield,
mixed $redemption,
mixed $frequency,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): string|float {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$rate = Functions::flattenSingleValue($rate);
@@ -70,10 +69,10 @@ class Price
return $e->getMessage();
}
$dsc = Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
$e = Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
$n = Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
$a = Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
$dsc = (float) Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
$e = (float) Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
$n = (int) Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
$a = (float) Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
$baseYF = 1.0 + ($yield / $frequency);
$rfp = 100 * ($rate / $frequency);
@@ -110,11 +109,11 @@ class Price
* @return float|string Result, or a string containing an error
*/
public static function priceDiscounted(
$settlement,
$maturity,
$discount,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
mixed $settlement,
mixed $maturity,
mixed $discount,
mixed $redemption,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -167,12 +166,12 @@ class Price
* @return float|string Result, or a string containing an error
*/
public static function priceAtMaturity(
$settlement,
$maturity,
$issue,
$rate,
$yield,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
mixed $settlement,
mixed $maturity,
mixed $issue,
mixed $rate,
mixed $yield,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -195,7 +194,7 @@ class Price
return $e->getMessage();
}
$daysPerYear = Functions::scalar(Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis));
$daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
if (!is_numeric($daysPerYear)) {
return $daysPerYear;
}
@@ -218,9 +217,9 @@ class Price
}
$daysBetweenSettlementAndMaturity *= $daysPerYear;
return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
(1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
(($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100))
/ (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield))
- (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
}
/**
@@ -245,11 +244,11 @@ class Price
* @return float|string Result, or a string containing an error
*/
public static function received(
$settlement,
$maturity,
$investment,
$discount,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
mixed $settlement,
mixed $maturity,
mixed $investment,
mixed $discount,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -276,7 +275,7 @@ class Price
$daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
return Functions::scalar($daysBetweenSettlementAndMaturity);
}
return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));

View File

@@ -31,16 +31,14 @@ class Rates
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function discount(
$settlement,
$maturity,
$price,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $price,
mixed $redemption,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): float|string {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$price = Functions::flattenSingleValue($price);
@@ -94,16 +92,14 @@ class Rates
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function interest(
$settlement,
$maturity,
$investment,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
mixed $settlement,
mixed $maturity,
mixed $investment,
mixed $redemption,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
): float|string {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$investment = Functions::flattenSingleValue($investment);

View File

@@ -8,29 +8,19 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
class SecurityValidations extends FinancialValidations
{
/**
* @param mixed $issue
*/
public static function validateIssueDate($issue): float
public static function validateIssueDate(mixed $issue): float
{
return self::validateDate($issue);
}
/**
* @param mixed $settlement
* @param mixed $maturity
*/
public static function validateSecurityPeriod($settlement, $maturity): void
public static function validateSecurityPeriod(mixed $settlement, mixed $maturity): void
{
if ($settlement >= $maturity) {
throw new Exception(ExcelError::NAN());
}
}
/**
* @param mixed $redemption
*/
public static function validateRedemption($redemption): float
public static function validateRedemption(mixed $redemption): float
{
$redemption = self::validateFloat($redemption);
if ($redemption <= 0.0) {

View File

@@ -32,11 +32,11 @@ class Yields
* @return float|string Result, or a string containing an error
*/
public static function yieldDiscounted(
$settlement,
$maturity,
$price,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
mixed $settlement,
mixed $maturity,
mixed $price,
mixed $redemption,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -57,7 +57,7 @@ class Yields
return $e->getMessage();
}
$daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
$daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
if (!is_numeric($daysPerYear)) {
return $daysPerYear;
}
@@ -94,12 +94,12 @@ class Yields
* @return float|string Result, or a string containing an error
*/
public static function yieldAtMaturity(
$settlement,
$maturity,
$issue,
$rate,
$price,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
mixed $settlement,
mixed $maturity,
mixed $issue,
mixed $rate,
mixed $price,
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -122,7 +122,7 @@ class Yields
return $e->getMessage();
}
$daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
$daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
if (!is_numeric($daysPerYear)) {
return $daysPerYear;
}
@@ -145,9 +145,9 @@ class Yields
}
$daysBetweenSettlementAndMaturity *= $daysPerYear;
return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) -
(($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
(($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
($daysPerYear / $daysBetweenSettlementAndMaturity);
return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate)
- (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate)))
/ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate)))
* ($daysPerYear / $daysBetweenSettlementAndMaturity);
}
}

View File

@@ -24,7 +24,7 @@ class TreasuryBill
*
* @return float|string Result, or a string containing an error
*/
public static function bondEquivalentYield($settlement, $maturity, $discount)
public static function bondEquivalentYield(mixed $settlement, mixed $maturity, mixed $discount): string|float
{
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -69,7 +69,7 @@ class TreasuryBill
*
* @return float|string Result, or a string containing an error
*/
public static function price($settlement, $maturity, $discount)
public static function price(mixed $settlement, mixed $maturity, mixed $discount): string|float
{
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
@@ -115,11 +115,9 @@ class TreasuryBill
* the Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param mixed $price The Treasury bill's price per $100 face value
*
* @return float|string
* @param float|string $price The Treasury bill's price per $100 face value
*/
public static function yield($settlement, $maturity, $price)
public static function yield(mixed $settlement, mixed $maturity, $price): string|float
{
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);

View File

@@ -46,24 +46,22 @@ class FormulaParser
/**
* Formula.
*
* @var string
*/
private $formula;
private string $formula;
/**
* Tokens.
*
* @var FormulaToken[]
*/
private $tokens = [];
private array $tokens = [];
/**
* Create a new FormulaParser.
*
* @param ?string $formula Formula to parse
*/
public function __construct($formula = '')
public function __construct(?string $formula = '')
{
// Check parameters
if ($formula === null) {
@@ -78,10 +76,8 @@ class FormulaParser
/**
* Get Formula.
*
* @return string
*/
public function getFormula()
public function getFormula(): string
{
return $this->formula;
}
@@ -102,10 +98,8 @@ class FormulaParser
/**
* Get Token count.
*
* @return int
*/
public function getTokenCount()
public function getTokenCount(): int
{
return count($this->tokens);
}
@@ -115,7 +109,7 @@ class FormulaParser
*
* @return FormulaToken[]
*/
public function getTokens()
public function getTokens(): array
{
return $this->tokens;
}
@@ -217,7 +211,7 @@ class FormulaParser
}
// scientific notation check
if (strpos(self::OPERATORS_SN, $this->formula[$index]) !== false) {
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) {
$value .= $this->formula[$index];
@@ -232,7 +226,7 @@ class FormulaParser
// establish state-dependent character evaluations
if ($this->formula[$index] == self::QUOTE_DOUBLE) {
if (strlen($value) > 0) {
if ($value !== '') {
// unexpected
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
$value = '';
@@ -244,7 +238,7 @@ class FormulaParser
}
if ($this->formula[$index] == self::QUOTE_SINGLE) {
if (strlen($value) > 0) {
if ($value !== '') {
// unexpected
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
$value = '';
@@ -264,7 +258,7 @@ class FormulaParser
}
if ($this->formula[$index] == self::ERROR_START) {
if (strlen($value) > 0) {
if ($value !== '') {
// unexpected
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
$value = '';
@@ -278,7 +272,7 @@ class FormulaParser
// mark start and end of arrays and array rows
if ($this->formula[$index] == self::BRACE_OPEN) {
if (strlen($value) > 0) {
if ($value !== '') {
// unexpected
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
$value = '';
@@ -298,11 +292,12 @@ class FormulaParser
}
if ($this->formula[$index] == self::SEMICOLON) {
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
/** @var FormulaToken $tmp */
$tmp = array_pop($stack);
$tmp->setValue('');
$tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -321,16 +316,18 @@ class FormulaParser
}
if ($this->formula[$index] == self::BRACE_CLOSE) {
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
/** @var FormulaToken $tmp */
$tmp = array_pop($stack);
$tmp->setValue('');
$tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
$tokens1[] = $tmp;
/** @var FormulaToken $tmp */
$tmp = array_pop($stack);
$tmp->setValue('');
$tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -343,7 +340,7 @@ class FormulaParser
// trim white-space
if ($this->formula[$index] == self::WHITESPACE) {
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
@@ -359,7 +356,7 @@ class FormulaParser
// multi-character comparators
if (($index + 2) <= $formulaLength) {
if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
@@ -371,8 +368,8 @@ class FormulaParser
}
// standard infix operators
if (strpos(self::OPERATORS_INFIX, $this->formula[$index]) !== false) {
if (strlen($value) > 0) {
if (str_contains(self::OPERATORS_INFIX, $this->formula[$index])) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
@@ -383,8 +380,8 @@ class FormulaParser
}
// standard postfix operators (only one)
if (strpos(self::OPERATORS_POSTFIX, $this->formula[$index]) !== false) {
if (strlen($value) > 0) {
if (str_contains(self::OPERATORS_POSTFIX, $this->formula[$index])) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
@@ -396,7 +393,7 @@ class FormulaParser
// start subexpression or function
if ($this->formula[$index] == self::PAREN_OPEN) {
if (strlen($value) > 0) {
if ($value !== '') {
$tmp = new FormulaToken($value, FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
$tokens1[] = $tmp;
$stack[] = clone $tmp;
@@ -413,11 +410,12 @@ class FormulaParser
// function, subexpression, or array parameters, or operand unions
if ($this->formula[$index] == self::COMMA) {
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
/** @var FormulaToken $tmp */
$tmp = array_pop($stack);
$tmp->setValue('');
$tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -435,11 +433,12 @@ class FormulaParser
// stop subexpression
if ($this->formula[$index] == self::PAREN_CLOSE) {
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
$value = '';
}
/** @var FormulaToken $tmp */
$tmp = array_pop($stack);
$tmp->setValue('');
$tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -456,7 +455,7 @@ class FormulaParser
}
// dump remaining accumulation
if (strlen($value) > 0) {
if ($value !== '') {
$tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
}
@@ -475,10 +474,6 @@ class FormulaParser
$nextToken = null;
}
if ($token === null) {
continue;
}
if ($token->getTokenType() != FormulaToken::TOKEN_TYPE_WHITESPACE) {
$tokens2[] = $token;
@@ -491,9 +486,9 @@ class FormulaParser
if (
!(
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
|| (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
|| ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
)
) {
continue;
@@ -505,9 +500,9 @@ class FormulaParser
if (
!(
(($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
(($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
(($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START))
|| (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START))
|| ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
)
) {
continue;
@@ -528,26 +523,17 @@ class FormulaParser
} else {
$previousToken = null;
}
//if (isset($tokens2[$i + 1])) {
// $nextToken = $tokens2[$i + 1];
//} else {
// $nextToken = null;
//}
if ($token === null) {
continue;
}
if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') {
if ($i == 0) {
$token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
} elseif (
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
(($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION)
&& ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
|| (($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION)
&& ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
|| ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX)
|| ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
) {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
} else {
@@ -563,12 +549,12 @@ class FormulaParser
if ($i == 0) {
continue;
} elseif (
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
(($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
(($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION)
&& ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
|| (($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION)
&& ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
|| ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX)
|| ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
) {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
} else {
@@ -581,10 +567,10 @@ class FormulaParser
}
if (
$token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
$token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
$token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX
&& $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
) {
if (strpos('<>=', substr($token->getValue(), 0, 1)) !== false) {
if (str_contains('<>=', substr($token->getValue(), 0, 1))) {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
} elseif ($token->getValue() == '&') {
$token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
@@ -598,8 +584,8 @@ class FormulaParser
}
if (
$token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
$token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
$token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND
&& $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
) {
if (!is_numeric($token->getValue())) {
if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') {
@@ -617,8 +603,8 @@ class FormulaParser
}
if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
if (strlen($token->getValue()) > 0) {
if (substr($token->getValue(), 0, 1) == '@') {
if ($token->getValue() !== '') {
if (str_starts_with($token->getValue(), '@')) {
$token->setValue(substr($token->getValue(), 1));
}
}

View File

@@ -54,33 +54,26 @@ class FormulaToken
/**
* Value.
*
* @var string
*/
private $value;
private string $value;
/**
* Token Type (represented by TOKEN_TYPE_*).
*
* @var string
*/
private $tokenType;
private string $tokenType;
/**
* Token SubType (represented by TOKEN_SUBTYPE_*).
*
* @var string
*/
private $tokenSubType;
private string $tokenSubType;
/**
* Create a new FormulaToken.
*
* @param string $value
* @param string $tokenType Token type (represented by TOKEN_TYPE_*)
* @param string $tokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
*/
public function __construct($value, $tokenType = self::TOKEN_TYPE_UNKNOWN, $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
public function __construct(string $value, string $tokenType = self::TOKEN_TYPE_UNKNOWN, string $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
{
// Initialise values
$this->value = $value;
@@ -90,60 +83,48 @@ class FormulaToken
/**
* Get Value.
*
* @return string
*/
public function getValue()
public function getValue(): string
{
return $this->value;
}
/**
* Set Value.
*
* @param string $value
*/
public function setValue($value): void
public function setValue(string $value): void
{
$this->value = $value;
}
/**
* Get Token Type (represented by TOKEN_TYPE_*).
*
* @return string
*/
public function getTokenType()
public function getTokenType(): string
{
return $this->tokenType;
}
/**
* Set Token Type (represented by TOKEN_TYPE_*).
*
* @param string $value
*/
public function setTokenType($value): void
public function setTokenType(string $value): void
{
$this->tokenType = $value;
}
/**
* Get Token SubType (represented by TOKEN_SUBTYPE_*).
*
* @return string
*/
public function getTokenSubType()
public function getTokenSubType(): string
{
return $this->tokenSubType;
}
/**
* Set Token SubType (represented by TOKEN_SUBTYPE_*).
*
* @param string $value
*/
public function setTokenSubType($value): void
public function setTokenSubType(string $value): void
{
$this->tokenSubType = $value;
}

View File

@@ -26,19 +26,17 @@ class Functions
const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
const RETURNDATE_EXCEL = 'E';
public const NOT_YET_IMPLEMENTED = '#Not Yet Implemented';
/**
* Compatibility mode to use for error checking and responses.
*
* @var string
*/
protected static $compatibilityMode = self::COMPATIBILITY_EXCEL;
protected static string $compatibilityMode = self::COMPATIBILITY_EXCEL;
/**
* Data Type to use when returning date values.
*
* @var string
*/
protected static $returnDateType = self::RETURNDATE_EXCEL;
protected static string $returnDateType = self::RETURNDATE_EXCEL;
/**
* Set the Compatibility Mode.
@@ -51,12 +49,12 @@ class Functions
*
* @return bool (Success or Failure)
*/
public static function setCompatibilityMode($compatibilityMode)
public static function setCompatibilityMode(string $compatibilityMode): bool
{
if (
($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
($compatibilityMode == self::COMPATIBILITY_EXCEL)
|| ($compatibilityMode == self::COMPATIBILITY_GNUMERIC)
|| ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
) {
self::$compatibilityMode = $compatibilityMode;
@@ -75,7 +73,7 @@ class Functions
* Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
* Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
*/
public static function getCompatibilityMode()
public static function getCompatibilityMode(): string
{
return self::$compatibilityMode;
}
@@ -91,12 +89,12 @@ class Functions
*
* @return bool Success or failure
*/
public static function setReturnDateType($returnDateType)
public static function setReturnDateType(string $returnDateType): bool
{
if (
($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
($returnDateType == self::RETURNDATE_EXCEL)
($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP)
|| ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT)
|| ($returnDateType == self::RETURNDATE_EXCEL)
) {
self::$returnDateType = $returnDateType;
@@ -115,7 +113,7 @@ class Functions
* Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
* Functions::RETURNDATE_EXCEL ' 'E'
*/
public static function getReturnDateType()
public static function getReturnDateType(): string
{
return self::$returnDateType;
}
@@ -125,31 +123,31 @@ class Functions
*
* @return string #Not Yet Implemented
*/
public static function DUMMY()
public static function DUMMY(): string
{
return '#Not Yet Implemented';
return self::NOT_YET_IMPLEMENTED;
}
public static function isMatrixValue($idx)
public static function isMatrixValue(mixed $idx): bool
{
return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
}
public static function isValue($idx)
public static function isValue(mixed $idx): bool
{
return substr_count($idx, '.') === 0;
}
public static function isCellValue($idx)
public static function isCellValue(mixed $idx): bool
{
return substr_count($idx, '.') > 1;
}
public static function ifCondition($condition)
public static function ifCondition(mixed $condition): string
{
$condition = self::flattenSingleValue($condition);
if ($condition === '') {
if ($condition === '' || $condition === null) {
return '=""';
}
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
@@ -180,7 +178,7 @@ class Functions
return str_replace('""""', '""', $operator . $operand);
}
private static function operandSpecialHandling($operand)
private static function operandSpecialHandling(mixed $operand): mixed
{
if (is_numeric($operand) || is_bool($operand)) {
return $operand;
@@ -201,333 +199,14 @@ class Functions
return $operand;
}
/**
* NULL.
*
* Returns the error value #NULL!
*
* @deprecated 1.23.0 Use the null() method in the Information\ExcelError class instead
* @see Information\ExcelError::null()
*
* @return string #NULL!
*/
public static function null()
{
return Information\ExcelError::null();
}
/**
* NaN.
*
* Returns the error value #NUM!
*
* @deprecated 1.23.0 Use the NAN() method in the Information\Error class instead
* @see Information\ExcelError::NAN()
*
* @return string #NUM!
*/
public static function NAN()
{
return Information\ExcelError::NAN();
}
/**
* REF.
*
* Returns the error value #REF!
*
* @deprecated 1.23.0 Use the REF() method in the Information\ExcelError class instead
* @see Information\ExcelError::REF()
*
* @return string #REF!
*/
public static function REF()
{
return Information\ExcelError::REF();
}
/**
* NA.
*
* Excel Function:
* =NA()
*
* Returns the error value #N/A
* #N/A is the error value that means "no value is available."
*
* @deprecated 1.23.0 Use the NA() method in the Information\ExcelError class instead
* @see Information\ExcelError::NA()
*
* @return string #N/A!
*/
public static function NA()
{
return Information\ExcelError::NA();
}
/**
* VALUE.
*
* Returns the error value #VALUE!
*
* @deprecated 1.23.0 Use the VALUE() method in the Information\ExcelError class instead
* @see Information\ExcelError::VALUE()
*
* @return string #VALUE!
*/
public static function VALUE()
{
return Information\ExcelError::VALUE();
}
/**
* NAME.
*
* Returns the error value #NAME?
*
* @deprecated 1.23.0 Use the NAME() method in the Information\ExcelError class instead
* @see Information\ExcelError::NAME()
*
* @return string #NAME?
*/
public static function NAME()
{
return Information\ExcelError::NAME();
}
/**
* DIV0.
*
* @deprecated 1.23.0 Use the DIV0() method in the Information\ExcelError class instead
* @see Information\ExcelError::DIV0()
*
* @return string #Not Yet Implemented
*/
public static function DIV0()
{
return Information\ExcelError::DIV0();
}
/**
* ERROR_TYPE.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the type() method in the Information\ExcelError class instead
* @see Information\ExcelError::type()
*
* @return array|int|string
*/
public static function errorType($value = '')
{
return Information\ExcelError::type($value);
}
/**
* IS_BLANK.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isBlank() method in the Information\Value class instead
* @see Information\Value::isBlank()
*
* @return array|bool
*/
public static function isBlank($value = null)
{
return Information\Value::isBlank($value);
}
/**
* IS_ERR.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isErr() method in the Information\ErrorValue class instead
* @see Information\ErrorValue::isErr()
*
* @return array|bool
*/
public static function isErr($value = '')
{
return Information\ErrorValue::isErr($value);
}
/**
* IS_ERROR.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isError() method in the Information\ErrorValue class instead
* @see Information\ErrorValue::isError()
*
* @return array|bool
*/
public static function isError($value = '')
{
return Information\ErrorValue::isError($value);
}
/**
* IS_NA.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isNa() method in the Information\ErrorValue class instead
* @see Information\ErrorValue::isNa()
*
* @return array|bool
*/
public static function isNa($value = '')
{
return Information\ErrorValue::isNa($value);
}
/**
* IS_EVEN.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isEven() method in the Information\Value class instead
* @see Information\Value::isEven()
*
* @return array|bool|string
*/
public static function isEven($value = null)
{
return Information\Value::isEven($value);
}
/**
* IS_ODD.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isOdd() method in the Information\Value class instead
* @see Information\Value::isOdd()
*
* @return array|bool|string
*/
public static function isOdd($value = null)
{
return Information\Value::isOdd($value);
}
/**
* IS_NUMBER.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isNumber() method in the Information\Value class instead
* @see Information\Value::isNumber()
*
* @return array|bool
*/
public static function isNumber($value = null)
{
return Information\Value::isNumber($value);
}
/**
* IS_LOGICAL.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isLogical() method in the Information\Value class instead
* @see Information\Value::isLogical()
*
* @return array|bool
*/
public static function isLogical($value = null)
{
return Information\Value::isLogical($value);
}
/**
* IS_TEXT.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isText() method in the Information\Value class instead
* @see Information\Value::isText()
*
* @return array|bool
*/
public static function isText($value = null)
{
return Information\Value::isText($value);
}
/**
* IS_NONTEXT.
*
* @param mixed $value Value to check
*
* @deprecated 1.23.0 Use the isNonText() method in the Information\Value class instead
* @see Information\Value::isNonText()
*
* @return array|bool
*/
public static function isNonText($value = null)
{
return Information\Value::isNonText($value);
}
/**
* N.
*
* Returns a value converted to a number
*
* @deprecated 1.23.0 Use the asNumber() method in the Information\Value class instead
* @see Information\Value::asNumber()
*
* @param null|mixed $value The value you want converted
*
* @return number|string N converts values listed in the following table
* If value is or refers to N returns
* A number That number
* A date The serial number of that date
* TRUE 1
* FALSE 0
* An error value The error value
* Anything else 0
*/
public static function n($value = null)
{
return Information\Value::asNumber($value);
}
/**
* TYPE.
*
* Returns a number that identifies the type of a value
*
* @deprecated 1.23.0 Use the type() method in the Information\Value class instead
* @see Information\Value::type()
*
* @param null|mixed $value The value you want tested
*
* @return number N converts values listed in the following table
* If value is or refers to N returns
* A number 1
* Text 2
* Logical Value 4
* An error value 16
* Array or Matrix 64
*/
public static function TYPE($value = null)
{
return Information\Value::type($value);
}
/**
* Convert a multi-dimensional array to a simple 1-dimensional array.
*
* @param array|mixed $array Array to be flattened
* @param mixed $array Array to be flattened
*
* @return array Flattened array
*/
public static function flattenArray($array)
public static function flattenArray(mixed $array): array
{
if (!is_array($array)) {
return (array) $array;
@@ -549,12 +228,7 @@ class Functions
return $flattened;
}
/**
* @param mixed $value
*
* @return null|mixed
*/
public static function scalar($value)
public static function scalar(mixed $value): mixed
{
if (!is_array($value)) {
return $value;
@@ -574,7 +248,7 @@ class Functions
*
* @return array Flattened array
*/
public static function flattenArrayIndexed($array)
public static function flattenArrayIndexed($array): array
{
if (!is_array($array)) {
return (array) $array;
@@ -604,10 +278,8 @@ class Functions
* Convert an array to a single scalar value by extracting the first element.
*
* @param mixed $value Array or scalar value
*
* @return mixed
*/
public static function flattenSingleValue($value = '')
public static function flattenSingleValue(mixed $value): mixed
{
while (is_array($value)) {
$value = array_shift($value);
@@ -616,26 +288,10 @@ class Functions
return $value;
}
/**
* ISFORMULA.
*
* @deprecated 1.23.0 Use the isFormula() method in the Information\Value class instead
* @see Information\Value::isFormula()
*
* @param mixed $cellReference The cell to check
* @param ?Cell $cell The current cell (containing this formula)
*
* @return array|bool|string
*/
public static function isFormula($cellReference = '', ?Cell $cell = null)
{
return Information\Value::isFormula($cellReference, $cell);
}
public static function expandDefinedName(string $coordinate, Cell $cell): string
{
$worksheet = $cell->getWorksheet();
$spreadsheet = $worksheet->getParent();
$spreadsheet = $worksheet->getParentOrThrow();
// Uppercase coordinate
$pCoordinatex = strtoupper($coordinate);
// Eliminate leading equal sign
@@ -644,8 +300,8 @@ class Functions
if ($defined !== null) {
$worksheet2 = $defined->getWorkSheet();
if (!$defined->isFormula() && $worksheet2 !== null) {
$coordinate = "'" . $worksheet2->getTitle() . "'!" .
(string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
$coordinate = "'" . $worksheet2->getTitle() . "'!"
. (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
}
}
@@ -659,7 +315,7 @@ class Functions
public static function trimSheetFromCellReference(string $coordinate): string
{
if (strpos($coordinate, '!') !== false) {
if (str_contains($coordinate, '!')) {
$coordinate = substr($coordinate, strrpos($coordinate, '!') + 1);
}

View File

@@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class ErrorValue
{
@@ -14,11 +15,10 @@ 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|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($value = '')
public static function isErr(mixed $value = ''): array|bool
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -33,11 +33,10 @@ 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|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($value = '')
public static function isError(mixed $value = '', bool $tryNotImplemented = false): array|bool
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -46,6 +45,9 @@ class ErrorValue
if (!is_string($value)) {
return false;
}
if ($tryNotImplemented && $value === Functions::NOT_YET_IMPLEMENTED) {
return true;
}
return in_array($value, ExcelError::ERROR_CODES, true);
}
@@ -56,11 +58,10 @@ 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|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($value = '')
public static function isNa(mixed $value = ''): array|bool
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);

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