Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into
develop
This commit is contained in:
parent
764ce03d10
commit
2663c8f2f2
5
composer.json
Normal file
5
composer.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"phpoffice/phpspreadsheet": "^1.12"
|
||||
}
|
||||
}
|
||||
339
composer.lock
generated
Normal file
339
composer.lock
generated
Normal file
@ -0,0 +1,339 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a68ce168cd747e78b793f2d2d0bb89b9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "markbaker/complex",
|
||||
"version": "1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPComplex.git",
|
||||
"reference": "c3131244e29c08d44fefb49e0dd35021e9e39dd2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/c3131244e29c08d44fefb49e0dd35021e9e39dd2",
|
||||
"reference": "c3131244e29c08d44fefb49e0dd35021e9e39dd2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
|
||||
"phpcompatibility/php-compatibility": "^9.0",
|
||||
"phpdocumentor/phpdocumentor": "2.*",
|
||||
"phploc/phploc": "^4.0|^5.0|^6.0|^7.0",
|
||||
"phpmd/phpmd": "2.*",
|
||||
"phpunit/phpunit": "^4.8.35|^5.0|^6.0|^7.0",
|
||||
"sebastian/phpcpd": "2.*",
|
||||
"squizlabs/php_codesniffer": "^3.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"classes/src/functions/abs.php",
|
||||
"classes/src/functions/acos.php",
|
||||
"classes/src/functions/acosh.php",
|
||||
"classes/src/functions/acot.php",
|
||||
"classes/src/functions/acoth.php",
|
||||
"classes/src/functions/acsc.php",
|
||||
"classes/src/functions/acsch.php",
|
||||
"classes/src/functions/argument.php",
|
||||
"classes/src/functions/asec.php",
|
||||
"classes/src/functions/asech.php",
|
||||
"classes/src/functions/asin.php",
|
||||
"classes/src/functions/asinh.php",
|
||||
"classes/src/functions/atan.php",
|
||||
"classes/src/functions/atanh.php",
|
||||
"classes/src/functions/conjugate.php",
|
||||
"classes/src/functions/cos.php",
|
||||
"classes/src/functions/cosh.php",
|
||||
"classes/src/functions/cot.php",
|
||||
"classes/src/functions/coth.php",
|
||||
"classes/src/functions/csc.php",
|
||||
"classes/src/functions/csch.php",
|
||||
"classes/src/functions/exp.php",
|
||||
"classes/src/functions/inverse.php",
|
||||
"classes/src/functions/ln.php",
|
||||
"classes/src/functions/log2.php",
|
||||
"classes/src/functions/log10.php",
|
||||
"classes/src/functions/negative.php",
|
||||
"classes/src/functions/pow.php",
|
||||
"classes/src/functions/rho.php",
|
||||
"classes/src/functions/sec.php",
|
||||
"classes/src/functions/sech.php",
|
||||
"classes/src/functions/sin.php",
|
||||
"classes/src/functions/sinh.php",
|
||||
"classes/src/functions/sqrt.php",
|
||||
"classes/src/functions/tan.php",
|
||||
"classes/src/functions/tanh.php",
|
||||
"classes/src/functions/theta.php",
|
||||
"classes/src/operations/add.php",
|
||||
"classes/src/operations/subtract.php",
|
||||
"classes/src/operations/multiply.php",
|
||||
"classes/src/operations/divideby.php",
|
||||
"classes/src/operations/divideinto.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Complex\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@lange.demon.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with complex numbers",
|
||||
"homepage": "https://github.com/MarkBaker/PHPComplex",
|
||||
"keywords": [
|
||||
"complex",
|
||||
"mathematics"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPComplex/tree/1.5.0"
|
||||
},
|
||||
"time": "2020-08-26T19:47:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/matrix",
|
||||
"version": "1.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPMatrix.git",
|
||||
"reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/44bb1ab01811116f01fe216ab37d921dccc6c10d",
|
||||
"reference": "44bb1ab01811116f01fe216ab37d921dccc6c10d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6.0|^7.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "dev-master",
|
||||
"phploc/phploc": "^4",
|
||||
"phpmd/phpmd": "dev-master",
|
||||
"phpunit/phpunit": "^5.7|^6.0|7.0",
|
||||
"sebastian/phpcpd": "^3.0",
|
||||
"squizlabs/php_codesniffer": "^3.0@dev"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"classes/src/Functions/adjoint.php",
|
||||
"classes/src/Functions/antidiagonal.php",
|
||||
"classes/src/Functions/cofactors.php",
|
||||
"classes/src/Functions/determinant.php",
|
||||
"classes/src/Functions/diagonal.php",
|
||||
"classes/src/Functions/identity.php",
|
||||
"classes/src/Functions/inverse.php",
|
||||
"classes/src/Functions/minors.php",
|
||||
"classes/src/Functions/trace.php",
|
||||
"classes/src/Functions/transpose.php",
|
||||
"classes/src/Operations/add.php",
|
||||
"classes/src/Operations/directsum.php",
|
||||
"classes/src/Operations/subtract.php",
|
||||
"classes/src/Operations/multiply.php",
|
||||
"classes/src/Operations/divideby.php",
|
||||
"classes/src/Operations/divideinto.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Matrix\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@lange.demon.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with matrices",
|
||||
"homepage": "https://github.com/MarkBaker/PHPMatrix",
|
||||
"keywords": [
|
||||
"mathematics",
|
||||
"matrix",
|
||||
"vector"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPMatrix/tree/1.2.3"
|
||||
},
|
||||
"time": "2021-01-26T14:36:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||
"reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
|
||||
"reference": "f79611d6dc1f6b7e8e30b738fc371b392001dbfd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"markbaker/complex": "^1.4",
|
||||
"markbaker/matrix": "^1.2",
|
||||
"php": "^7.1",
|
||||
"psr/simple-cache": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dompdf/dompdf": "^0.8.3",
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"jpgraph/jpgraph": "^4.0",
|
||||
"mpdf/mpdf": "^8.0",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpunit/phpunit": "^7.5",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"tecnickcom/tcpdf": "^6.3"
|
||||
},
|
||||
"suggest": {
|
||||
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||
"jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "https://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"homepage": "https://markbakeruk.net"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "https://rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
},
|
||||
{
|
||||
"name": "Adrien Crivelli"
|
||||
}
|
||||
],
|
||||
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||
"keywords": [
|
||||
"OpenXML",
|
||||
"excel",
|
||||
"gnumeric",
|
||||
"ods",
|
||||
"php",
|
||||
"spreadsheet",
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.12.0"
|
||||
},
|
||||
"time": "2020-04-27T08:12:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/simple-cache",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/simple-cache.git",
|
||||
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
|
||||
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\SimpleCache\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for simple caching",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"caching",
|
||||
"psr",
|
||||
"psr-16",
|
||||
"simple-cache"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/simple-cache/tree/master"
|
||||
},
|
||||
"time": "2017-10-23T01:57:42+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
@ -92,7 +92,7 @@ class ExportExcel2007 extends ModeleExports
|
||||
if (empty($this->disabled)) {
|
||||
require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
|
||||
$this->label_lib = 'PhpSpreadSheet';
|
||||
$this->version_lib = '1.6.0'; // No way to get info from library
|
||||
$this->version_lib = '1.12.0'; // No way to get info from library
|
||||
}
|
||||
|
||||
$this->row = 0;
|
||||
|
||||
@ -41,7 +41,7 @@ class modFournisseur extends DolibarrModules
|
||||
*/
|
||||
public function __construct($db)
|
||||
{
|
||||
global $conf, $user;
|
||||
global $conf, $langs, $user;
|
||||
|
||||
$this->db = $db;
|
||||
$this->numero = 40;
|
||||
@ -309,6 +309,8 @@ class modFournisseur extends DolibarrModules
|
||||
//--------
|
||||
$r = 0;
|
||||
|
||||
$langs->loadLangs(array("suppliers", "multicurrency"));
|
||||
|
||||
$r++;
|
||||
$this->export_code[$r] = $this->rights_class.'_'.$r;
|
||||
$this->export_label[$r] = 'Vendor invoices and lines of invoices';
|
||||
|
||||
@ -765,7 +765,7 @@ class modProduct extends DolibarrModules
|
||||
'sp.quantity' => "QtyMin*",
|
||||
'sp.tva_tx' => 'VATRate',
|
||||
'sp.default_vat_code' => 'VATCode',
|
||||
'sp.delivery_time_days' => 'DeliveryDelay',
|
||||
'sp.delivery_time_days' => 'NbDaysToDelivery',
|
||||
'sp.supplier_reputation' => 'SupplierReputation',
|
||||
'sp.status' => 'Status'
|
||||
);
|
||||
|
||||
@ -722,7 +722,7 @@ class modService extends DolibarrModules
|
||||
'sp.quantity' => "QtyMin*",
|
||||
'sp.tva_tx' => 'VATRate',
|
||||
'sp.default_vat_code' => 'VATCode',
|
||||
'sp.delivery_time_days' => 'DeliveryDelay',
|
||||
'sp.delivery_time_days' => 'NbDaysToDelivery',
|
||||
'sp.supplier_reputation' => 'SupplierReputation'
|
||||
);
|
||||
if (is_object($mysoc) && $usenpr) {
|
||||
|
||||
@ -36,7 +36,6 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
|
||||
*/
|
||||
class modSupplierProposal extends DolibarrModules
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructor. Define names, constants, directories, boxes, permissions
|
||||
*
|
||||
|
||||
@ -1180,7 +1180,8 @@ if ($step == 5 && $datatoexport) {
|
||||
print '<br>';
|
||||
|
||||
// List of available export formats
|
||||
$htmltabloflibs = '<table class="noborder centpercent">';
|
||||
$htmltabloflibs = '<!-- Table with available export formats --><br>';
|
||||
$htmltabloflibs .= '<table class="noborder centpercent nomarginbottom">';
|
||||
$htmltabloflibs .= '<tr class="liste_titre">';
|
||||
$htmltabloflibs .= '<td>'.$langs->trans("AvailableFormats").'</td>';
|
||||
$htmltabloflibs .= '<td>'.$langs->trans("LibraryUsed").'</td>';
|
||||
@ -1204,7 +1205,7 @@ if ($step == 5 && $datatoexport) {
|
||||
$htmltabloflibs .= '<td class="right">'.$objmodelexport->getLibVersionForKey($key).'</td>';
|
||||
$htmltabloflibs .= '</tr>'."\n";
|
||||
}
|
||||
$htmltabloflibs .= '</table>';
|
||||
$htmltabloflibs .= '</table><br>';
|
||||
|
||||
print '<span class="opacitymedium">'.$form->textwithpicto($langs->trans("NowClickToGenerateToBuildExportFile"), $htmltabloflibs, 1, 'help', '', 0, 2, 'helphonformat').'</span>';
|
||||
//print $htmltabloflibs;
|
||||
|
||||
4
htdocs/includes/phpoffice/phpspreadsheet/.gitattributes
vendored
Normal file
4
htdocs/includes/phpoffice/phpspreadsheet/.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/tests export-ignore
|
||||
README.md export-ignore
|
||||
*.min.js binary
|
||||
/.github export-ignore
|
||||
10
htdocs/includes/phpoffice/phpspreadsheet/.gitignore
vendored
Normal file
10
htdocs/includes/phpoffice/phpspreadsheet/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/tests/codeCoverage
|
||||
/analysis
|
||||
/vendor/
|
||||
/phpunit.xml
|
||||
|
||||
## IDE support
|
||||
*.buildpath
|
||||
*.project
|
||||
/.settings
|
||||
/.idea
|
||||
183
htdocs/includes/phpoffice/phpspreadsheet/.php_cs.dist
Normal file
183
htdocs/includes/phpoffice/phpspreadsheet/.php_cs.dist
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->exclude(['vendor', 'tests/data/Calculation'])
|
||||
->in('samples')
|
||||
->in('src')
|
||||
->in('tests/PhpSpreadsheetTests')
|
||||
;
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder)
|
||||
->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__))
|
||||
->setRules([
|
||||
'align_multiline_comment' => true,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'backtick_to_shell_exec' => true,
|
||||
'binary_operator_spaces' => true,
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_statement' => true,
|
||||
'braces' => true,
|
||||
'cast_spaces' => true,
|
||||
'class_attributes_separation' => ['elements' => ['method', 'property']], // const are often grouped with other related const
|
||||
'class_definition' => true,
|
||||
'class_keyword_remove' => false, // ::class keyword gives us beter support in IDE
|
||||
'combine_consecutive_issets' => true,
|
||||
'combine_consecutive_unsets' => true,
|
||||
'compact_nullable_typehint' => true,
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'declare_equal_normalize' => true,
|
||||
'declare_strict_types' => false, // Too early to adopt strict types
|
||||
'dir_constant' => true,
|
||||
'doctrine_annotation_array_assignment' => true,
|
||||
'doctrine_annotation_braces' => true,
|
||||
'doctrine_annotation_indentation' => true,
|
||||
'doctrine_annotation_spaces' => true,
|
||||
'elseif' => true,
|
||||
'encoding' => true,
|
||||
'ereg_to_preg' => true,
|
||||
'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_internal_class' => true,
|
||||
'full_opening_tag' => true,
|
||||
'function_declaration' => true,
|
||||
'function_to_constant' => true,
|
||||
'function_typehint_space' => true,
|
||||
'general_phpdoc_annotation_remove' => false, // No use for that
|
||||
'hash_to_slash_comment' => true,
|
||||
'header_comment' => false, // We don't use common header in all our files
|
||||
'heredoc_to_nowdoc' => false, // Not sure about this one
|
||||
'include' => true,
|
||||
'increment_style' => true,
|
||||
'indentation_type' => true,
|
||||
'is_null' => ['use_yoda_style' => false],
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'line_ending' => true,
|
||||
'list_syntax' => ['syntax' => 'short'],
|
||||
'lowercase_cast' => true,
|
||||
'lowercase_constants' => true,
|
||||
'lowercase_keywords' => true,
|
||||
'magic_constant_casing' => true,
|
||||
'mb_str_functions' => false, // No, too dangerous to change that
|
||||
'method_argument_space' => true,
|
||||
'method_chaining_indentation' => true,
|
||||
'method_separation' => true,
|
||||
'modernize_types_casting' => true,
|
||||
'multiline_comment_opening_closing' => true,
|
||||
'native_function_casing' => true,
|
||||
'native_function_invocation' => false, // This is risky and seems to be micro-optimization that make code uglier so not worth it, at least for now
|
||||
'new_with_braces' => true,
|
||||
'no_alias_functions' => true,
|
||||
'no_blank_lines_after_class_opening' => true,
|
||||
'no_blank_lines_after_phpdoc' => true,
|
||||
'no_blank_lines_before_namespace' => false, // we want 1 blank line before namespace
|
||||
'no_break_comment' => true,
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_comment' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_empty_statement' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
'no_homoglyph_names' => true,
|
||||
'no_leading_import_slash' => true,
|
||||
'no_leading_namespace_whitespace' => true,
|
||||
'no_mixed_echo_print' => true,
|
||||
'no_multiline_whitespace_around_double_arrow' => true,
|
||||
'no_multiline_whitespace_before_semicolons' => true,
|
||||
'non_printable_character' => true,
|
||||
'no_null_property_initialization' => true,
|
||||
'no_php4_constructor' => true,
|
||||
'normalize_index_brace' => true,
|
||||
'no_short_bool_cast' => true,
|
||||
'no_short_echo_tag' => true,
|
||||
'no_singleline_whitespace_before_semicolons' => true,
|
||||
'no_spaces_after_function_name' => true,
|
||||
'no_spaces_around_offset' => true,
|
||||
'no_spaces_inside_parenthesis' => true,
|
||||
'no_superfluous_elseif' => false, // Might be risky on a huge code base
|
||||
'not_operator_with_space' => false, // No we prefer to keep '!' without spaces
|
||||
'not_operator_with_successor_space' => false, // idem
|
||||
'no_trailing_comma_in_list_call' => true,
|
||||
'no_trailing_comma_in_singleline_array' => true,
|
||||
'no_trailing_whitespace_in_comment' => true,
|
||||
'no_trailing_whitespace' => true,
|
||||
'no_unneeded_control_parentheses' => true,
|
||||
'no_unneeded_curly_braces' => true,
|
||||
'no_unneeded_final_method' => true,
|
||||
'no_unreachable_default_argument_value' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_useless_return' => true,
|
||||
'no_whitespace_before_comma_in_array' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'object_operator_without_whitespace' => true,
|
||||
'ordered_class_elements' => false, // We prefer to keep some freedom
|
||||
'ordered_imports' => true,
|
||||
'phpdoc_add_missing_param_annotation' => true,
|
||||
'phpdoc_align' => false, // Waste of time
|
||||
'phpdoc_annotation_without_dot' => true,
|
||||
'phpdoc_indent' => true,
|
||||
'phpdoc_inline_tag' => true,
|
||||
'phpdoc_no_access' => true,
|
||||
'phpdoc_no_alias_tag' => true,
|
||||
'phpdoc_no_empty_return' => true,
|
||||
'phpdoc_no_package' => true,
|
||||
'phpdoc_no_useless_inheritdoc' => true,
|
||||
'phpdoc_order' => true,
|
||||
'phpdoc_return_self_reference' => true,
|
||||
'phpdoc_scalar' => true,
|
||||
'phpdoc_separation' => true,
|
||||
'phpdoc_single_line_var_spacing' => true,
|
||||
'phpdoc_summary' => true,
|
||||
'phpdoc_to_comment' => true,
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_types_order' => true,
|
||||
'phpdoc_types' => true,
|
||||
'phpdoc_var_without_name' => true,
|
||||
'php_unit_construct' => true,
|
||||
'php_unit_dedicate_assert' => true,
|
||||
'php_unit_expectation' => true,
|
||||
'php_unit_fqcn_annotation' => true,
|
||||
'php_unit_mock' => true,
|
||||
'php_unit_namespaced' => true,
|
||||
'php_unit_no_expectation_annotation' => true,
|
||||
'php_unit_strict' => false, // We sometime actually need assertEquals
|
||||
'php_unit_test_annotation' => true,
|
||||
'php_unit_test_class_requires_covers' => false, // We don't care as much as we should about coverage
|
||||
'pow_to_exponentiation' => false,
|
||||
'protected_to_private' => true,
|
||||
'psr0' => true,
|
||||
'psr4' => true,
|
||||
'random_api_migration' => false, // This breaks our unit tests
|
||||
'return_type_declaration' => true,
|
||||
'self_accessor' => true,
|
||||
'semicolon_after_instruction' => false, // Buggy in `samples/index.php`
|
||||
'short_scalar_cast' => true,
|
||||
'silenced_deprecation_error' => true,
|
||||
'simplified_null_return' => false, // While technically correct we prefer to be explicit when returning null
|
||||
'single_blank_line_at_eof' => true,
|
||||
'single_blank_line_before_namespace' => true,
|
||||
'single_class_element_per_statement' => true,
|
||||
'single_import_per_statement' => true,
|
||||
'single_line_after_imports' => true,
|
||||
'single_line_comment_style' => true,
|
||||
'single_quote' => true,
|
||||
'space_after_semicolon' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'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
|
||||
'switch_case_semicolon_to_colon' => true,
|
||||
'switch_case_space' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'ternary_to_null_coalescing' => true,
|
||||
'trailing_comma_in_multiline_array' => true,
|
||||
'trim_array_spaces' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'visibility_required' => true,
|
||||
'void_return' => false, // Cannot use that with PHP 5.6
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
'yoda_style' => false,
|
||||
]);
|
||||
27
htdocs/includes/phpoffice/phpspreadsheet/.scrutinizer.yml
Normal file
27
htdocs/includes/phpoffice/phpspreadsheet/.scrutinizer.yml
Normal file
@ -0,0 +1,27 @@
|
||||
checks:
|
||||
php: true
|
||||
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
before_parentheses:
|
||||
closure_definition: true
|
||||
around_operators:
|
||||
concatenation: true
|
||||
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
|
||||
tools:
|
||||
external_code_coverage:
|
||||
timeout: 3600
|
||||
|
||||
build_failure_conditions:
|
||||
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
|
||||
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
|
||||
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
|
||||
- 'patches.label("Unused Use Statements").new.exists' # No new unused imports patches allowed
|
||||
57
htdocs/includes/phpoffice/phpspreadsheet/.travis.yml
Normal file
57
htdocs/includes/phpoffice/phpspreadsheet/.travis.yml
Normal file
@ -0,0 +1,57 @@
|
||||
language: php
|
||||
dist: bionic
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- $HOME/.composer/cache
|
||||
|
||||
before_script:
|
||||
# Deactivate xdebug
|
||||
- phpenv config-rm xdebug.ini
|
||||
- composer install --ignore-platform-reqs
|
||||
|
||||
script:
|
||||
- ./vendor/bin/phpunit
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
- stage: Code style
|
||||
php: 7.2
|
||||
script:
|
||||
- ./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run
|
||||
- ./vendor/bin/phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PSR2 -n
|
||||
|
||||
- stage: Coverage
|
||||
php: 7.2
|
||||
script:
|
||||
- pecl install pcov
|
||||
- composer require pcov/clobber --dev
|
||||
- ./vendor/bin/pcov clobber
|
||||
- ./vendor/bin/phpunit --coverage-clover coverage-clover.xml
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover tests/coverage-clover.xml
|
||||
|
||||
- stage: API documentations
|
||||
if: tag is present
|
||||
php: 7.4
|
||||
before_script:
|
||||
- curl -O https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.0.0-rc/phpDocumentor.phar
|
||||
script:
|
||||
- php phpDocumentor.phar --directory src/ --target docs/api
|
||||
deploy:
|
||||
provider: pages
|
||||
skip-cleanup: true
|
||||
local-dir: docs/api
|
||||
github-token: $GITHUB_TOKEN
|
||||
on:
|
||||
all_branches: true
|
||||
condition: $TRAVIS_BRANCH =~ ^master$
|
||||
1593
htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md
Normal file
1593
htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md
Normal file
File diff suppressed because it is too large
Load Diff
471
htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.md
Normal file
471
htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.md
Normal file
@ -0,0 +1,471 @@
|
||||
# Changelog
|
||||
|
||||
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).
|
||||
|
||||
## [1.12.0] - 2020-04-27
|
||||
|
||||
### Added
|
||||
|
||||
- Improved the ARABIC function to also handle short-hand roman numerals
|
||||
- Added support for the FLOOR.MATH and FLOOR.PRECISE functions [#1351](https://github.com/PHPOffice/PhpSpreadsheet/pull/1351)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix ROUNDUP and ROUNDDOWN for floating-point rounding error [#1404](https://github.com/PHPOffice/PhpSpreadsheet/pull/1404)
|
||||
- Fix ROUNDUP and ROUNDDOWN for negative number [#1417](https://github.com/PHPOffice/PhpSpreadsheet/pull/1417)
|
||||
- Fix loading styles from vmlDrawings when containing whitespace [#1347](https://github.com/PHPOffice/PhpSpreadsheet/issues/1347)
|
||||
- Fix incorrect behavior when removing last row [#1365](https://github.com/PHPOffice/PhpSpreadsheet/pull/1365)
|
||||
- MATCH with a static array should return the position of the found value based on the values submitted [#1332](https://github.com/PHPOffice/PhpSpreadsheet/pull/1332)
|
||||
- Fix Xlsx Reader's handling of undefined fill color [#1353](https://github.com/PHPOffice/PhpSpreadsheet/pull/1353)
|
||||
|
||||
## [1.11.0] - 2020-03-02
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for the BASE function
|
||||
- Added support for the ARABIC function
|
||||
- Conditionals - Extend Support for (NOT)CONTAINSBLANKS [#1278](https://github.com/PHPOffice/PhpSpreadsheet/pull/1278)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle Error in Formula Processing Better for Xls [#1267](https://github.com/PHPOffice/PhpSpreadsheet/pull/1267)
|
||||
- Handle ConditionalStyle NumberFormat When Reading Xlsx File [#1296](https://github.com/PHPOffice/PhpSpreadsheet/pull/1296)
|
||||
- Fix Xlsx Writer's handling of decimal commas [#1282](https://github.com/PHPOffice/PhpSpreadsheet/pull/1282)
|
||||
- Fix for issue by removing test code mistakenly left in [#1328](https://github.com/PHPOffice/PhpSpreadsheet/pull/1328)
|
||||
- Fix for Xls writer wrong selected cells and active sheet [#1256](https://github.com/PHPOffice/PhpSpreadsheet/pull/1256)
|
||||
- Fix active cell when freeze pane is used [#1323](https://github.com/PHPOffice/PhpSpreadsheet/pull/1323)
|
||||
- Fix XLSX file loading with autofilter containing '$' [#1326](https://github.com/PHPOffice/PhpSpreadsheet/pull/1326)
|
||||
- PHPDoc - Use `@return $this` for fluent methods [#1362](https://github.com/PHPOffice/PhpSpreadsheet/pull/1362)
|
||||
|
||||
## [1.10.1] - 2019-12-02
|
||||
|
||||
### Changed
|
||||
|
||||
- PHP 7.4 compatibility
|
||||
|
||||
### Fixed
|
||||
|
||||
- FLOOR() function accept negative number and negative significance [#1245](https://github.com/PHPOffice/PhpSpreadsheet/pull/1245)
|
||||
- Correct column style even when using rowspan [#1249](https://github.com/PHPOffice/PhpSpreadsheet/pull/1249)
|
||||
- Do not confuse defined names and cell refs [#1263](https://github.com/PHPOffice/PhpSpreadsheet/pull/1263)
|
||||
- XLSX reader/writer keep decimal for floats with a zero decimal part [#1262](https://github.com/PHPOffice/PhpSpreadsheet/pull/1262)
|
||||
- ODS writer prevent invalid numeric value if locale decimal separator is comma [#1268](https://github.com/PHPOffice/PhpSpreadsheet/pull/1268)
|
||||
- Xlsx writer actually writes plotVisOnly and dispBlanksAs from chart properties [#1266](https://github.com/PHPOffice/PhpSpreadsheet/pull/1266)
|
||||
|
||||
## [1.10.0] - 2019-11-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Change license from LGPL 2.1 to MIT [#140](https://github.com/PHPOffice/PhpSpreadsheet/issues/140)
|
||||
|
||||
### Added
|
||||
|
||||
- Implementation of IFNA() logical function
|
||||
- Support "showZeros" worksheet option to change how Excel shows and handles "null" values returned from a calculation
|
||||
- Allow HTML Reader to accept HTML as a string into an existing spreadsheet [#1212](https://github.com/PHPOffice/PhpSpreadsheet/pull/1212)
|
||||
|
||||
### Fixed
|
||||
|
||||
- IF implementation properly handles the value `#N/A` [#1165](https://github.com/PHPOffice/PhpSpreadsheet/pull/1165)
|
||||
- Formula Parser: Wrong line count for stuff like "MyOtherSheet!A:D" [#1215](https://github.com/PHPOffice/PhpSpreadsheet/issues/1215)
|
||||
- Call garbage collector after removing a column to prevent stale cached values
|
||||
- Trying to remove a column that doesn't exist deletes the latest column
|
||||
- Keep big integer as integer instead of lossely casting to float [#874](https://github.com/PHPOffice/PhpSpreadsheet/pull/874)
|
||||
- Fix branch pruning handling of non boolean conditions [#1167](https://github.com/PHPOffice/PhpSpreadsheet/pull/1167)
|
||||
- Fix ODS Reader when no DC namespace are defined [#1182](https://github.com/PHPOffice/PhpSpreadsheet/pull/1182)
|
||||
- Fixed Functions->ifCondition for allowing <> and empty condition [#1206](https://github.com/PHPOffice/PhpSpreadsheet/pull/1206)
|
||||
- Validate XIRR inputs and return correct error values [#1120](https://github.com/PHPOffice/PhpSpreadsheet/issues/1120)
|
||||
- Allow to read xlsx files with exotic workbook names like "workbook2.xml" [#1183](https://github.com/PHPOffice/PhpSpreadsheet/pull/1183)
|
||||
|
||||
## [1.9.0] - 2019-08-17
|
||||
|
||||
### Changed
|
||||
|
||||
- Drop support for PHP 5.6 and 7.0, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support
|
||||
|
||||
### Added
|
||||
|
||||
- When <br> appears in a table cell, set the cell to wrap [#1071](https://github.com/PHPOffice/PhpSpreadsheet/issues/1071) and [#1070](https://github.com/PHPOffice/PhpSpreadsheet/pull/1070)
|
||||
- Add MAXIFS, MINIFS, COUNTIFS and Remove MINIF, MAXIF [#1056](https://github.com/PHPOffice/PhpSpreadsheet/issues/1056)
|
||||
- HLookup needs an ordered list even if range_lookup is set to false [#1055](https://github.com/PHPOffice/PhpSpreadsheet/issues/1055) and [#1076](https://github.com/PHPOffice/PhpSpreadsheet/pull/1076)
|
||||
- Improve performance of IF function calls via ranch pruning to avoid resolution of every branches [#844](https://github.com/PHPOffice/PhpSpreadsheet/pull/844)
|
||||
- MATCH function supports `*?~` Excel functionality, when match_type=0 [#1116](https://github.com/PHPOffice/PhpSpreadsheet/issues/1116)
|
||||
- Allow HTML Reader to accept HTML as a string [#1136](https://github.com/PHPOffice/PhpSpreadsheet/pull/1136)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix to AVERAGEIF() function when called with a third argument
|
||||
- Eliminate duplicate fill none style entries [#1066](https://github.com/PHPOffice/PhpSpreadsheet/issues/1066)
|
||||
- Fix number format masks containing literal (non-decimal point) dots [#1079](https://github.com/PHPOffice/PhpSpreadsheet/issues/1079)
|
||||
- Fix number format masks containing named colours that were being misinterpreted as date formats; and add support for masks that fully replace the value with a full text string [#1009](https://github.com/PHPOffice/PhpSpreadsheet/issues/1009)
|
||||
- Stricter-typed comparison testing in COUNTIF() and COUNTIFS() evaluation [#1046](https://github.com/PHPOffice/PhpSpreadsheet/issues/1046)
|
||||
- COUPNUM should not return zero when settlement is in the last period [#1020](https://github.com/PHPOffice/PhpSpreadsheet/issues/1020) and [#1021](https://github.com/PHPOffice/PhpSpreadsheet/pull/1021)
|
||||
- Fix handling of named ranges referencing sheets with spaces or "!" in their title
|
||||
- Cover `getSheetByName()` with tests for name with quote and spaces [#739](https://github.com/PHPOffice/PhpSpreadsheet/issues/739)
|
||||
- Best effort to support invalid colspan values in HTML reader - [#878](https://github.com/PHPOffice/PhpSpreadsheet/pull/878)
|
||||
- Fixes incorrect rows deletion [#868](https://github.com/PHPOffice/PhpSpreadsheet/issues/868)
|
||||
- MATCH function fix (value search by type, stop search when match_type=-1 and unordered element encountered) [#1116](https://github.com/PHPOffice/PhpSpreadsheet/issues/1116)
|
||||
- Fix `getCalculatedValue()` error with more than two INDIRECT [#1115](https://github.com/PHPOffice/PhpSpreadsheet/pull/1115)
|
||||
- Writer\Html did not hide columns [#985](https://github.com/PHPOffice/PhpSpreadsheet/pull/985)
|
||||
|
||||
## [1.8.2] - 2019-07-08
|
||||
|
||||
### Fixed
|
||||
|
||||
- Uncaught error when opening ods file and properties aren't defined [#1047](https://github.com/PHPOffice/PhpSpreadsheet/issues/1047)
|
||||
- Xlsx Reader Cell datavalidations bug [#1052](https://github.com/PHPOffice/PhpSpreadsheet/pull/1052)
|
||||
|
||||
## [1.8.1] - 2019-07-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Allow nullable theme for Xlsx Style Reader class [#1043](https://github.com/PHPOffice/PhpSpreadsheet/issues/1043)
|
||||
|
||||
## [1.8.0] - 2019-07-01
|
||||
|
||||
### Security Fix (CVE-2019-12331)
|
||||
|
||||
- Detect double-encoded xml in the Security scanner, and reject as suspicious.
|
||||
- This change also broadens the scope of the `libxml_disable_entity_loader` setting when reading XML-based formats, so that it is enabled while the xml is being parsed and not simply while it is loaded.
|
||||
On some versions of PHP, this can cause problems because it is not thread-safe, and can affect other PHP scripts running on the same server. This flag is set to true when instantiating a loader, and back to its original setting when the Reader is no longer in scope, or manually unset.
|
||||
- Provide a check to identify whether libxml_disable_entity_loader is thread-safe or not.
|
||||
|
||||
`XmlScanner::threadSafeLibxmlDisableEntityLoaderAvailability()`
|
||||
- Provide an option to disable the libxml_disable_entity_loader call through settings. This is not recommended as it reduces the security of the XML-based readers, and should only be used if you understand the consequences and have no other choice.
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for the SWITCH function [#963](https://github.com/PHPOffice/PhpSpreadsheet/issues/963) and [#983](https://github.com/PHPOffice/PhpSpreadsheet/pull/983)
|
||||
- Add accounting number format style [#974](https://github.com/PHPOffice/PhpSpreadsheet/pull/974)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Whitelist `tsv` extension when opening CSV files [#429](https://github.com/PHPOffice/PhpSpreadsheet/issues/429)
|
||||
- Fix a SUMIF warning with some versions of PHP when having different length of arrays provided as input [#873](https://github.com/PHPOffice/PhpSpreadsheet/pull/873)
|
||||
- Fix incorrectly handled backslash-escaped space characters in number format
|
||||
|
||||
## [1.7.0] - 2019-05-26
|
||||
|
||||
- Added support for inline styles in Html reader (borders, alignment, width, height)
|
||||
- QuotedText cells no longer treated as formulae if the content begins with a `=`
|
||||
- Clean handling for DDE in formulae
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix handling for escaped enclosures and new lines in CSV Separator Inference
|
||||
- Fix MATCH an error was appearing when comparing strings against 0 (always true)
|
||||
- Fix wrong calculation of highest column with specified row [#700](https://github.com/PHPOffice/PhpSpreadsheet/issues/700)
|
||||
- Fix VLOOKUP
|
||||
- Fix return type hint
|
||||
|
||||
## [1.6.0] - 2019-01-02
|
||||
|
||||
### Added
|
||||
|
||||
- Refactored Matrix Functions to use external Matrix library
|
||||
- Possibility to specify custom colors of values for pie and donut charts [#768](https://github.com/PHPOffice/PhpSpreadsheet/pull/768)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve XLSX parsing speed if no readFilter is applied [#772](https://github.com/PHPOffice/PhpSpreadsheet/issues/772)
|
||||
- Fix column names if read filter calls in XLSX reader skip columns [#777](https://github.com/PHPOffice/PhpSpreadsheet/pull/777)
|
||||
- XLSX reader can now ignore blank cells, using the setReadEmptyCells(false) method. [#810](https://github.com/PHPOffice/PhpSpreadsheet/issues/810)
|
||||
- Fix LOOKUP function which was breaking on edge cases [#796](https://github.com/PHPOffice/PhpSpreadsheet/issues/796)
|
||||
- Fix VLOOKUP with exact matches [#809](https://github.com/PHPOffice/PhpSpreadsheet/pull/809)
|
||||
- Support COUNTIFS multiple arguments [#830](https://github.com/PHPOffice/PhpSpreadsheet/pull/830)
|
||||
- Change `libxml_disable_entity_loader()` as shortly as possible [#819](https://github.com/PHPOffice/PhpSpreadsheet/pull/819)
|
||||
- Improved memory usage and performance when loading large spreadsheets [#822](https://github.com/PHPOffice/PhpSpreadsheet/pull/822)
|
||||
- Improved performance when loading large spreadsheets [#825](https://github.com/PHPOffice/PhpSpreadsheet/pull/825)
|
||||
- Improved performance when loading large spreadsheets [#824](https://github.com/PHPOffice/PhpSpreadsheet/pull/824)
|
||||
- Fix color from CSS when reading from HTML [#831](https://github.com/PHPOffice/PhpSpreadsheet/pull/831)
|
||||
- Fix infinite loop when reading invalid ODS files [#832](https://github.com/PHPOffice/PhpSpreadsheet/pull/832)
|
||||
- Fix time format for duration is incorrect [#666](https://github.com/PHPOffice/PhpSpreadsheet/pull/666)
|
||||
- Fix iconv unsupported `//IGNORE//TRANSLIT` on IBM i [#791](https://github.com/PHPOffice/PhpSpreadsheet/issues/791)
|
||||
|
||||
### Changed
|
||||
|
||||
- `master` is the new default branch, `develop` does not exist anymore
|
||||
|
||||
## [1.5.2] - 2018-11-25
|
||||
|
||||
### Security
|
||||
|
||||
- Improvements to the design of the XML Security Scanner [#771](https://github.com/PHPOffice/PhpSpreadsheet/issues/771)
|
||||
|
||||
## [1.5.1] - 2018-11-20
|
||||
|
||||
### Security
|
||||
|
||||
- Fix and improve XXE security scanning for XML-based and HTML Readers [#771](https://github.com/PHPOffice/PhpSpreadsheet/issues/771)
|
||||
|
||||
### Added
|
||||
|
||||
- Support page margin in mPDF [#750](https://github.com/PHPOffice/PhpSpreadsheet/issues/750)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Support numeric condition in SUMIF, SUMIFS, AVERAGEIF, COUNTIF, MAXIF and MINIF [#683](https://github.com/PHPOffice/PhpSpreadsheet/issues/683)
|
||||
- SUMIFS containing multiple conditions [#704](https://github.com/PHPOffice/PhpSpreadsheet/issues/704)
|
||||
- Csv reader avoid notice when the file is empty [#743](https://github.com/PHPOffice/PhpSpreadsheet/pull/743)
|
||||
- Fix print area parser for XLSX reader [#734](https://github.com/PHPOffice/PhpSpreadsheet/pull/734)
|
||||
- Support overriding `DefaultValueBinder::dataTypeForValue()` without overriding `DefaultValueBinder::bindValue()` [#735](https://github.com/PHPOffice/PhpSpreadsheet/pull/735)
|
||||
- Mpdf export can exceed pcre.backtrack_limit [#637](https://github.com/PHPOffice/PhpSpreadsheet/issues/637)
|
||||
- Fix index overflow on data values array [#748](https://github.com/PHPOffice/PhpSpreadsheet/pull/748)
|
||||
|
||||
## [1.5.0] - 2018-10-21
|
||||
|
||||
### Added
|
||||
|
||||
- PHP 7.3 support
|
||||
- Add the DAYS() function [#594](https://github.com/PHPOffice/PhpSpreadsheet/pull/594)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Sheet title can contain exclamation mark [#325](https://github.com/PHPOffice/PhpSpreadsheet/issues/325)
|
||||
- Xls file cause the exception during open by Xls reader [#402](https://github.com/PHPOffice/PhpSpreadsheet/issues/402)
|
||||
- Skip non numeric value in SUMIF [#618](https://github.com/PHPOffice/PhpSpreadsheet/pull/618)
|
||||
- OFFSET should allow omitted height and width [#561](https://github.com/PHPOffice/PhpSpreadsheet/issues/561)
|
||||
- Correctly determine delimiter when CSV contains line breaks inside enclosures [#716](https://github.com/PHPOffice/PhpSpreadsheet/issues/716)
|
||||
|
||||
## [1.4.1] - 2018-09-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove locale from formatting string [#644](https://github.com/PHPOffice/PhpSpreadsheet/pull/644)
|
||||
- Allow iterators to go out of bounds with prev [#587](https://github.com/PHPOffice/PhpSpreadsheet/issues/587)
|
||||
- Fix warning when reading xlsx without styles [#631](https://github.com/PHPOffice/PhpSpreadsheet/pull/631)
|
||||
- Fix broken sample links on windows due to $baseDir having backslash [#653](https://github.com/PHPOffice/PhpSpreadsheet/pull/653)
|
||||
|
||||
## [1.4.0] - 2018-08-06
|
||||
|
||||
### Added
|
||||
|
||||
- Add excel function EXACT(value1, value2) support [#595](https://github.com/PHPOffice/PhpSpreadsheet/pull/595)
|
||||
- Support workbook view attributes for Xlsx format [#523](https://github.com/PHPOffice/PhpSpreadsheet/issues/523)
|
||||
- Read and write hyperlink for drawing image [#490](https://github.com/PHPOffice/PhpSpreadsheet/pull/490)
|
||||
- Added calculation engine support for the new bitwise functions that were added in MS Excel 2013
|
||||
- BITAND() Returns a Bitwise 'And' of two numbers
|
||||
- BITOR() Returns a Bitwise 'Or' of two number
|
||||
- BITXOR() Returns a Bitwise 'Exclusive Or' of two numbers
|
||||
- BITLSHIFT() Returns a number shifted left by a specified number of bits
|
||||
- BITRSHIFT() Returns a number shifted right by a specified number of bits
|
||||
- Added calculation engine support for other new functions that were added in MS Excel 2013 and MS Excel 2016
|
||||
- Text Functions
|
||||
- CONCAT() Synonym for CONCATENATE()
|
||||
- NUMBERVALUE() Converts text to a number, in a locale-independent way
|
||||
- UNICHAR() Synonym for CHAR() in PHPSpreadsheet, which has always used UTF-8 internally
|
||||
- UNIORD() Synonym for ORD() in PHPSpreadsheet, which has always used UTF-8 internally
|
||||
- TEXTJOIN() Joins together two or more text strings, separated by a delimiter
|
||||
- Logical Functions
|
||||
- XOR() Returns a logical Exclusive Or of all arguments
|
||||
- Date/Time Functions
|
||||
- ISOWEEKNUM() Returns the ISO 8601 week number of the year for a given date
|
||||
- Lookup and Reference Functions
|
||||
- FORMULATEXT() Returns a formula as a string
|
||||
- Financial Functions
|
||||
- PDURATION() Calculates the number of periods required for an investment to reach a specified value
|
||||
- RRI() Calculates the interest rate required for an investment to grow to a specified future value
|
||||
- Engineering Functions
|
||||
- ERF.PRECISE() Returns the error function integrated between 0 and a supplied limit
|
||||
- ERFC.PRECISE() Synonym for ERFC
|
||||
- Math and Trig Functions
|
||||
- SEC() Returns the secant of an angle
|
||||
- SECH() Returns the hyperbolic secant of an angle
|
||||
- CSC() Returns the cosecant of an angle
|
||||
- CSCH() Returns the hyperbolic cosecant of an angle
|
||||
- COT() Returns the cotangent of an angle
|
||||
- COTH() Returns the hyperbolic cotangent of an angle
|
||||
- ACOT() Returns the cotangent of an angle
|
||||
- ACOTH() Returns the hyperbolic cotangent of an angle
|
||||
- Refactored Complex Engineering Functions to use external complex number library
|
||||
- Added calculation engine support for the new complex number functions that were added in MS Excel 2013
|
||||
- IMCOSH() Returns the hyperbolic cosine of a complex number
|
||||
- IMCOT() Returns the cotangent of a complex number
|
||||
- IMCSC() Returns the cosecant of a complex number
|
||||
- IMCSCH() Returns the hyperbolic cosecant of a complex number
|
||||
- IMSEC() Returns the secant of a complex number
|
||||
- IMSECH() Returns the hyperbolic secant of a complex number
|
||||
- IMSINH() Returns the hyperbolic sine of a complex number
|
||||
- IMTAN() Returns the tangent of a complex number
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix ISFORMULA() function to work with a cell reference to another worksheet
|
||||
- Xlsx reader crashed when reading a file with workbook protection [#553](https://github.com/PHPOffice/PhpSpreadsheet/pull/553)
|
||||
- Cell formats with escaped spaces were causing incorrect date formatting [#557](https://github.com/PHPOffice/PhpSpreadsheet/issues/557)
|
||||
- Could not open CSV file containing HTML fragment [#564](https://github.com/PHPOffice/PhpSpreadsheet/issues/564)
|
||||
- Exclude the vendor folder in migration [#481](https://github.com/PHPOffice/PhpSpreadsheet/issues/481)
|
||||
- Chained operations on cell ranges involving borders operated on last cell only [#428](https://github.com/PHPOffice/PhpSpreadsheet/issues/428)
|
||||
- Avoid memory exhaustion when cloning worksheet with a drawing [#437](https://github.com/PHPOffice/PhpSpreadsheet/issues/437)
|
||||
- Migration tool keep variables containing $PHPExcel untouched [#598](https://github.com/PHPOffice/PhpSpreadsheet/issues/598)
|
||||
- Rowspans/colspans were incorrect when adding worksheet using loadIntoExisting [#619](https://github.com/PHPOffice/PhpSpreadsheet/issues/619)
|
||||
|
||||
## [1.3.1] - 2018-06-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ranges across Z and AA columns incorrectly threw an exception [#545](https://github.com/PHPOffice/PhpSpreadsheet/issues/545)
|
||||
|
||||
## [1.3.0] - 2018-06-10
|
||||
|
||||
### Added
|
||||
|
||||
- Support to read Xlsm templates with form elements, macros, printer settings, protected elements and back compatibility drawing, and save result without losing important elements of document [#435](https://github.com/PHPOffice/PhpSpreadsheet/issues/435)
|
||||
- Expose sheet title maximum length as `Worksheet::SHEET_TITLE_MAXIMUM_LENGTH` [#482](https://github.com/PHPOffice/PhpSpreadsheet/issues/482)
|
||||
- Allow escape character to be set in CSV reader [#492](https://github.com/PHPOffice/PhpSpreadsheet/issues/492)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Subtotal 9 in a group that has other subtotals 9 exclude the totals of the other subtotals in the range [#332](https://github.com/PHPOffice/PhpSpreadsheet/issues/332)
|
||||
- `Helper\Html` support UTF-8 HTML input [#444](https://github.com/PHPOffice/PhpSpreadsheet/issues/444)
|
||||
- Xlsx loaded an extra empty comment for each real comment [#375](https://github.com/PHPOffice/PhpSpreadsheet/issues/375)
|
||||
- Xlsx reader do not read rows and columns filtered out in readFilter at all [#370](https://github.com/PHPOffice/PhpSpreadsheet/issues/370)
|
||||
- Make newer Excel versions properly recalculate formulas on document open [#456](https://github.com/PHPOffice/PhpSpreadsheet/issues/456)
|
||||
- `Coordinate::extractAllCellReferencesInRange()` throws an exception for an invalid range [#519](https://github.com/PHPOffice/PhpSpreadsheet/issues/519)
|
||||
- Fixed parsing of conditionals in COUNTIF functions [#526](https://github.com/PHPOffice/PhpSpreadsheet/issues/526)
|
||||
- Corruption errors for saved Xlsx docs with frozen panes [#532](https://github.com/PHPOffice/PhpSpreadsheet/issues/532)
|
||||
|
||||
## [1.2.1] - 2018-04-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Plain text and richtext mixed in same cell can be read [#442](https://github.com/PHPOffice/PhpSpreadsheet/issues/442)
|
||||
|
||||
## [1.2.0] - 2018-03-04
|
||||
|
||||
### Added
|
||||
|
||||
- HTML writer creates a generator meta tag [#312](https://github.com/PHPOffice/PhpSpreadsheet/issues/312)
|
||||
- Support invalid zoom value in XLSX format [#350](https://github.com/PHPOffice/PhpSpreadsheet/pull/350)
|
||||
- Support for `_xlfn.` prefixed functions and `ISFORMULA`, `MODE.SNGL`, `STDEV.S`, `STDEV.P` [#390](https://github.com/PHPOffice/PhpSpreadsheet/pull/390)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Avoid potentially unsupported PSR-16 cache keys [#354](https://github.com/PHPOffice/PhpSpreadsheet/issues/354)
|
||||
- Check for MIME type to know if CSV reader can read a file [#167](https://github.com/PHPOffice/PhpSpreadsheet/issues/167)
|
||||
- Use proper € symbol for currency format [#379](https://github.com/PHPOffice/PhpSpreadsheet/pull/379)
|
||||
- Read printing area correctly when skipping some sheets [#371](https://github.com/PHPOffice/PhpSpreadsheet/issues/371)
|
||||
- Avoid incorrectly overwriting calculated value type [#394](https://github.com/PHPOffice/PhpSpreadsheet/issues/394)
|
||||
- Select correct cell when calling freezePane [#389](https://github.com/PHPOffice/PhpSpreadsheet/issues/389)
|
||||
- `setStrikethrough()` did not set the font [#403](https://github.com/PHPOffice/PhpSpreadsheet/issues/403)
|
||||
|
||||
## [1.1.0] - 2018-01-28
|
||||
|
||||
### Added
|
||||
|
||||
- Support for PHP 7.2
|
||||
- Support cell comments in HTML writer and reader [#308](https://github.com/PHPOffice/PhpSpreadsheet/issues/308)
|
||||
- Option to stop at a conditional styling, if it matches (only XLSX format) [#292](https://github.com/PHPOffice/PhpSpreadsheet/pull/292)
|
||||
- Support for line width for data series when rendering Xlsx [#329](https://github.com/PHPOffice/PhpSpreadsheet/pull/329)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Better auto-detection of CSV separators [#305](https://github.com/PHPOffice/PhpSpreadsheet/issues/305)
|
||||
- Support for shape style ending with `;` [#304](https://github.com/PHPOffice/PhpSpreadsheet/issues/304)
|
||||
- Freeze Panes takes wrong coordinates for XLSX [#322](https://github.com/PHPOffice/PhpSpreadsheet/issues/322)
|
||||
- `COLUMNS` and `ROWS` functions crashed in some cases [#336](https://github.com/PHPOffice/PhpSpreadsheet/issues/336)
|
||||
- Support XML file without styles [#331](https://github.com/PHPOffice/PhpSpreadsheet/pull/331)
|
||||
- Cell coordinates which are already a range cause an exception [#319](https://github.com/PHPOffice/PhpSpreadsheet/issues/319)
|
||||
|
||||
## [1.0.0] - 2017-12-25
|
||||
|
||||
### Added
|
||||
|
||||
- Support to write merged cells in ODS format [#287](https://github.com/PHPOffice/PhpSpreadsheet/issues/287)
|
||||
- Able to set the `topLeftCell` in freeze panes [#261](https://github.com/PHPOffice/PhpSpreadsheet/pull/261)
|
||||
- Support `DateTimeImmutable` as cell value
|
||||
- Support migration of prefixed classes
|
||||
|
||||
### Fixed
|
||||
|
||||
- Can read very small HTML files [#194](https://github.com/PHPOffice/PhpSpreadsheet/issues/194)
|
||||
- Written DataValidation was corrupted [#290](https://github.com/PHPOffice/PhpSpreadsheet/issues/290)
|
||||
- Date format compatible with both LibreOffice and Excel [#298](https://github.com/PHPOffice/PhpSpreadsheet/issues/298)
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
- Constant `TYPE_DOUGHTNUTCHART` is now `TYPE_DOUGHNUTCHART`.
|
||||
|
||||
## [1.0.0-beta2] - 2017-11-26
|
||||
|
||||
### Added
|
||||
|
||||
- Support for chart fill color - @CrazyBite [#158](https://github.com/PHPOffice/PhpSpreadsheet/pull/158)
|
||||
- Support for read Hyperlink for xml - @GreatHumorist [#223](https://github.com/PHPOffice/PhpSpreadsheet/pull/223)
|
||||
- Support for cell value validation according to data validation rules - @SailorMax [#257](https://github.com/PHPOffice/PhpSpreadsheet/pull/257)
|
||||
- Support for custom implementation, or configuration, of PDF libraries - @SailorMax [#266](https://github.com/PHPOffice/PhpSpreadsheet/pull/266)
|
||||
|
||||
### Changed
|
||||
|
||||
- Merge data-validations to reduce written worksheet size - @billblume [#131](https://github.com/PHPOffice/PhpSpreadSheet/issues/131)
|
||||
- Throws exception if a XML file is invalid - @GreatHumorist [#222](https://github.com/PHPOffice/PhpSpreadsheet/pull/222)
|
||||
- Upgrade to mPDF 7.0+ [#144](https://github.com/PHPOffice/PhpSpreadsheet/issues/144)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Control characters in cell values are automatically escaped [#212](https://github.com/PHPOffice/PhpSpreadsheet/issues/212)
|
||||
- Prevent color changing when copy/pasting xls files written by PhpSpreadsheet to another file - @al-lala [#218](https://github.com/PHPOffice/PhpSpreadsheet/issues/218)
|
||||
- Add cell reference automatic when there is no cell reference('r' attribute) in Xlsx file. - @GreatHumorist [#225](https://github.com/PHPOffice/PhpSpreadsheet/pull/225) Refer to [#201](https://github.com/PHPOffice/PhpSpreadsheet/issues/201)
|
||||
- `Reader\Xlsx::getFromZipArchive()` function return false if the zip entry could not be located. - @anton-harvey [#268](https://github.com/PHPOffice/PhpSpreadsheet/pull/268)
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
- Extracted coordinate method to dedicate class [migration guide](./docs/topics/migration-from-PHPExcel.md).
|
||||
- Column indexes are based on 1, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
|
||||
- Standardization of array keys used for style, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
|
||||
- Easier usage of PDF writers, and other custom readers and writers, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
|
||||
- Easier usage of chart renderers, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
|
||||
- Rename a few more classes to keep them in their related namespaces:
|
||||
- `CalcEngine` => `Calculation\Engine`
|
||||
- `PhpSpreadsheet\Calculation` => `PhpSpreadsheet\Calculation\Calculation`
|
||||
- `PhpSpreadsheet\Cell` => `PhpSpreadsheet\Cell\Cell`
|
||||
- `PhpSpreadsheet\Chart` => `PhpSpreadsheet\Chart\Chart`
|
||||
- `PhpSpreadsheet\RichText` => `PhpSpreadsheet\RichText\RichText`
|
||||
- `PhpSpreadsheet\Style` => `PhpSpreadsheet\Style\Style`
|
||||
- `PhpSpreadsheet\Worksheet` => `PhpSpreadsheet\Worksheet\Worksheet`
|
||||
|
||||
## [1.0.0-beta] - 2017-08-17
|
||||
|
||||
### Added
|
||||
|
||||
- Initial implementation of SUMIFS() function
|
||||
- Additional codepages
|
||||
- MemoryDrawing not working in HTML writer [#808](https://github.com/PHPOffice/PHPExcel/issues/808)
|
||||
- CSV Reader can auto-detect the separator used in file [#141](https://github.com/PHPOffice/PhpSpreadsheet/pull/141)
|
||||
- HTML Reader supports some basic inline styles [#180](https://github.com/PHPOffice/PhpSpreadsheet/pull/180)
|
||||
|
||||
### Changed
|
||||
|
||||
- Start following [SemVer](https://semver.org) properly.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix to getCell() method when cell reference includes a worksheet reference - @MarkBaker
|
||||
- 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)
|
||||
|
||||
### General
|
||||
|
||||
- Whitespace after toRichTextObject() - @MarkBaker [#554](https://github.com/PHPOffice/PHPExcel/issues/554)
|
||||
- Optimize vlookup() sort - @umpirsky [#548](https://github.com/PHPOffice/PHPExcel/issues/548)
|
||||
- c:max and c:min elements shall NOT be inside c:orientation elements - @vitalyrepin [#869](https://github.com/PHPOffice/PHPExcel/pull/869)
|
||||
- Implement actual timezone adjustment into PHPExcel_Shared_Date::PHPToExcel - @sim642 [#489](https://github.com/PHPOffice/PHPExcel/pull/489)
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
- Introduction of namespaces for all classes, eg: `PHPExcel_Calculation_Functions` becomes `PhpOffice\PhpSpreadsheet\Calculation\Functions`
|
||||
- Some classes were renamed for clarity and/or consistency:
|
||||
|
||||
For a comprehensive list of all class changes, and a semi-automated migration path, read the [migration guide](./docs/topics/migration-from-PHPExcel.md).
|
||||
|
||||
- Dropped `PHPExcel_Calculation_Functions::VERSION()`. Composer or git should be used to know the version.
|
||||
- Dropped `PHPExcel_Settings::setPdfRenderer()` and `PHPExcel_Settings::setPdfRenderer()`. Composer should be used to autoload PDF libs.
|
||||
- Dropped support for HHVM
|
||||
|
||||
## Previous versions of PHPExcel
|
||||
|
||||
The changelog for the project when it was called PHPExcel is [still available](./CHANGELOG.PHPExcel.md).
|
||||
11
htdocs/includes/phpoffice/phpspreadsheet/CONTRIBUTING.md
Normal file
11
htdocs/includes/phpoffice/phpspreadsheet/CONTRIBUTING.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Want to contribute?
|
||||
|
||||
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`
|
||||
- [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")
|
||||
21
htdocs/includes/phpoffice/phpspreadsheet/LICENSE
Normal file
21
htdocs/includes/phpoffice/phpspreadsheet/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 PhpSpreadsheet Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
86
htdocs/includes/phpoffice/phpspreadsheet/composer.json
Normal file
86
htdocs/includes/phpoffice/phpspreadsheet/composer.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"keywords": ["PHP", "OpenXML", "Excel", "xlsx", "xls", "ods", "gnumeric", "spreadsheet"],
|
||||
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "https://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"homepage": "https://markbakeruk.net"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "https://rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
},
|
||||
{
|
||||
"name": "Adrien Crivelli"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"check": [
|
||||
"php-cs-fixer fix --ansi --dry-run --diff",
|
||||
"phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PSR2 -n",
|
||||
"phpunit --color=always"
|
||||
],
|
||||
"fix": [
|
||||
"php-cs-fixer fix --ansi"
|
||||
],
|
||||
"versions": [
|
||||
"phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.1- -n"
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-SimpleXML": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"markbaker/complex": "^1.4",
|
||||
"markbaker/matrix": "^1.2",
|
||||
"psr/simple-cache": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dompdf/dompdf": "^0.8.3",
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"jpgraph/jpgraph": "^4.0",
|
||||
"mpdf/mpdf": "^8.0",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpunit/phpunit": "^7.5",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"tecnickcom/tcpdf": "^6.3"
|
||||
},
|
||||
"suggest": {
|
||||
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer",
|
||||
"jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\PhpSpreadsheetTests\\": "tests/PhpSpreadsheetTests"
|
||||
}
|
||||
}
|
||||
}
|
||||
3503
htdocs/includes/phpoffice/phpspreadsheet/composer.lock
generated
Normal file
3503
htdocs/includes/phpoffice/phpspreadsheet/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
htdocs/includes/phpoffice/phpspreadsheet/mkdocs.yml
Normal file
7
htdocs/includes/phpoffice/phpspreadsheet/mkdocs.yml
Normal file
@ -0,0 +1,7 @@
|
||||
site_name: PhpSpreadsheet Documentation
|
||||
repo_url: https://github.com/PHPOffice/phpspreadsheet
|
||||
edit_uri: edit/master/docs/
|
||||
|
||||
theme: readthedocs
|
||||
extra_css:
|
||||
- extra/extra.css
|
||||
23
htdocs/includes/phpoffice/phpspreadsheet/phpunit.xml.dist
Normal file
23
htdocs/includes/phpoffice/phpspreadsheet/phpunit.xml.dist
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
|
||||
bootstrap="./tests/bootstrap.php"
|
||||
backupGlobals="true"
|
||||
colors="true">
|
||||
<php>
|
||||
<ini name="memory_limit" value="2048M"/>
|
||||
</php>
|
||||
<testsuite name="PhpSpreadsheet Unit Test Suite">
|
||||
<directory suffix="Test.php">./tests/PhpSpreadsheetTests</directory>
|
||||
</testsuite>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./src</directory>
|
||||
<exclude>
|
||||
<directory>./src/PhpSpreadsheet/Shared/JAMA</directory>
|
||||
<directory>./src/PhpSpreadsheet/Writer/PDF</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
22
htdocs/includes/phpoffice/phpspreadsheet/src/Bootstrap.php
Normal file
22
htdocs/includes/phpoffice/phpspreadsheet/src/Bootstrap.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Bootstrap for PhpSpreadsheet classes.
|
||||
*/
|
||||
|
||||
// This sucks, but we have to try to find the composer autoloader
|
||||
|
||||
$paths = [
|
||||
__DIR__ . '/../vendor/autoload.php', // In case PhpSpreadsheet is cloned directly
|
||||
__DIR__ . '/../../../autoload.php', // In case PhpSpreadsheet is a composer dependency.
|
||||
];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
require_once $path;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('Composer autoloader could not be found. Install dependencies with `composer install` and try again.');
|
||||
@ -25,7 +25,7 @@ class Calculation
|
||||
// Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
|
||||
const CALCULATION_REGEXP_FUNCTION = '@?(?:_xlfn\.)?([A-Z][A-Z0-9\.]*)[\s]*\(';
|
||||
// Cell reference (cell or range of cells, with or without a sheet reference)
|
||||
const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})';
|
||||
const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
|
||||
// Named Range of cells
|
||||
const CALCULATION_REGEXP_NAMEDRANGE = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)';
|
||||
// Error
|
||||
@ -66,6 +66,15 @@ class Calculation
|
||||
*/
|
||||
private $calculationCacheEnabled = true;
|
||||
|
||||
/**
|
||||
* Used to generate unique store keys.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $branchStoreKeyCounter = 0;
|
||||
|
||||
private $branchPruningEnabled = true;
|
||||
|
||||
/**
|
||||
* List of operators that can be used within formulae
|
||||
* The true/false value indicates whether it is a binary operator or a unary operator.
|
||||
@ -254,6 +263,11 @@ class Calculation
|
||||
'functionCall' => [Logical::class, 'logicalAnd'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'ARABIC' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
'functionCall' => [MathTrig::class, 'ARABIC'],
|
||||
'argumentCount' => '1',
|
||||
],
|
||||
'AREAS' => [
|
||||
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
@ -319,6 +333,11 @@ class Calculation
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'argumentCount' => '1',
|
||||
],
|
||||
'BASE' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
'functionCall' => [MathTrig::class, 'BASE'],
|
||||
'argumentCount' => '2,3',
|
||||
],
|
||||
'BESSELI' => [
|
||||
'category' => Category::CATEGORY_ENGINEERING,
|
||||
'functionCall' => [Engineering::class, 'BESSELI'],
|
||||
@ -527,7 +546,7 @@ class Calculation
|
||||
],
|
||||
'COUNTIFS' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'functionCall' => [Statistical::class, 'COUNTIFS'],
|
||||
'argumentCount' => '2+',
|
||||
],
|
||||
'COUPDAYBS' => [
|
||||
@ -895,6 +914,16 @@ class Calculation
|
||||
'functionCall' => [MathTrig::class, 'FLOOR'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'FLOOR.MATH' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
'functionCall' => [MathTrig::class, 'FLOORMATH'],
|
||||
'argumentCount' => '3',
|
||||
],
|
||||
'FLOOR.PRECISE' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
'functionCall' => [MathTrig::class, 'FLOORPRECISE'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'FORECAST' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'FORECAST'],
|
||||
@ -1018,6 +1047,16 @@ class Calculation
|
||||
'functionCall' => [Logical::class, 'IFERROR'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'IFNA' => [
|
||||
'category' => Category::CATEGORY_LOGICAL,
|
||||
'functionCall' => [Logical::class, 'IFNA'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'IFS' => [
|
||||
'category' => Category::CATEGORY_LOGICAL,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'argumentCount' => '2+',
|
||||
],
|
||||
'IMABS' => [
|
||||
'category' => Category::CATEGORY_ENGINEERING,
|
||||
'functionCall' => [Engineering::class, 'IMABS'],
|
||||
@ -1356,10 +1395,10 @@ class Calculation
|
||||
'functionCall' => [Statistical::class, 'MAXA'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'MAXIF' => [
|
||||
'MAXIFS' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'MAXIF'],
|
||||
'argumentCount' => '2+',
|
||||
'functionCall' => [Statistical::class, 'MAXIFS'],
|
||||
'argumentCount' => '3+',
|
||||
],
|
||||
'MDETERM' => [
|
||||
'category' => Category::CATEGORY_MATH_AND_TRIG,
|
||||
@ -1401,10 +1440,10 @@ class Calculation
|
||||
'functionCall' => [Statistical::class, 'MINA'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'MINIF' => [
|
||||
'MINIFS' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'MINIF'],
|
||||
'argumentCount' => '2+',
|
||||
'functionCall' => [Statistical::class, 'MINIFS'],
|
||||
'argumentCount' => '3+',
|
||||
],
|
||||
'MINUTE' => [
|
||||
'category' => Category::CATEGORY_DATE_AND_TIME,
|
||||
@ -1474,7 +1513,12 @@ class Calculation
|
||||
'NETWORKDAYS' => [
|
||||
'category' => Category::CATEGORY_DATE_AND_TIME,
|
||||
'functionCall' => [DateTime::class, 'NETWORKDAYS'],
|
||||
'argumentCount' => '2+',
|
||||
'argumentCount' => '2-3',
|
||||
],
|
||||
'NETWORKDAYS.INTL' => [
|
||||
'category' => Category::CATEGORY_DATE_AND_TIME,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'argumentCount' => '2-4',
|
||||
],
|
||||
'NOMINAL' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
@ -2143,7 +2187,12 @@ class Calculation
|
||||
'WORKDAY' => [
|
||||
'category' => Category::CATEGORY_DATE_AND_TIME,
|
||||
'functionCall' => [DateTime::class, 'WORKDAY'],
|
||||
'argumentCount' => '2+',
|
||||
'argumentCount' => '2-3',
|
||||
],
|
||||
'WORKDAY.INTL' => [
|
||||
'category' => Category::CATEGORY_DATE_AND_TIME,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'argumentCount' => '2-4',
|
||||
],
|
||||
'XIRR' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
@ -2196,7 +2245,7 @@ class Calculation
|
||||
private static $controlFunctions = [
|
||||
'MKMATRIX' => [
|
||||
'argumentCount' => '*',
|
||||
'functionCall' => 'self::mkMatrix',
|
||||
'functionCall' => [__CLASS__, 'mkMatrix'],
|
||||
],
|
||||
];
|
||||
|
||||
@ -2251,6 +2300,7 @@ class Calculation
|
||||
public function flushInstance()
|
||||
{
|
||||
$this->clearCalculationCache();
|
||||
$this->clearBranchStore();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2394,6 +2444,32 @@ class Calculation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable calculation cache.
|
||||
*
|
||||
* @param bool $pValue
|
||||
* @param mixed $enabled
|
||||
*/
|
||||
public function setBranchPruningEnabled($enabled)
|
||||
{
|
||||
$this->branchPruningEnabled = $enabled;
|
||||
}
|
||||
|
||||
public function enableBranchPruning()
|
||||
{
|
||||
$this->setBranchPruningEnabled(true);
|
||||
}
|
||||
|
||||
public function disableBranchPruning()
|
||||
{
|
||||
$this->setBranchPruningEnabled(false);
|
||||
}
|
||||
|
||||
public function clearBranchStore()
|
||||
{
|
||||
$this->branchStoreKeyCounter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently defined locale code.
|
||||
*
|
||||
@ -2416,7 +2492,7 @@ class Calculation
|
||||
// Identify our locale and language
|
||||
$language = $locale = strtolower($locale);
|
||||
if (strpos($locale, '_') !== false) {
|
||||
list($language) = explode('_', $locale);
|
||||
[$language] = explode('_', $locale);
|
||||
}
|
||||
if (count(self::$validLocaleLanguages) == 1) {
|
||||
self::loadLocales();
|
||||
@ -2441,9 +2517,9 @@ class Calculation
|
||||
// Retrieve the list of locale or language specific function names
|
||||
$localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($localeFunctions as $localeFunction) {
|
||||
list($localeFunction) = explode('##', $localeFunction); // Strip out comments
|
||||
[$localeFunction] = explode('##', $localeFunction); // Strip out comments
|
||||
if (strpos($localeFunction, '=') !== false) {
|
||||
list($fName, $lfName) = explode('=', $localeFunction);
|
||||
[$fName, $lfName] = explode('=', $localeFunction);
|
||||
$fName = trim($fName);
|
||||
$lfName = trim($lfName);
|
||||
if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
|
||||
@ -2466,9 +2542,9 @@ class Calculation
|
||||
if (file_exists($configFile)) {
|
||||
$localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($localeSettings as $localeSetting) {
|
||||
list($localeSetting) = explode('##', $localeSetting); // Strip out comments
|
||||
[$localeSetting] = explode('##', $localeSetting); // Strip out comments
|
||||
if (strpos($localeSetting, '=') !== false) {
|
||||
list($settingName, $settingValue) = explode('=', $localeSetting);
|
||||
[$settingName, $settingValue] = explode('=', $localeSetting);
|
||||
$settingName = strtoupper(trim($settingName));
|
||||
switch ($settingName) {
|
||||
case 'ARGUMENTSEPARATOR':
|
||||
@ -2772,7 +2848,7 @@ class Calculation
|
||||
}
|
||||
self::$returnArrayAsType = $returnArrayAsType;
|
||||
|
||||
if ($result === null) {
|
||||
if ($result === null && $pCell->getWorksheet()->getSheetView()->getShowZeros()) {
|
||||
return 0;
|
||||
} elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
|
||||
return Functions::NAN();
|
||||
@ -2823,13 +2899,13 @@ class Calculation
|
||||
$this->debugLog->clearLog();
|
||||
$this->cyclicReferenceStack->clear();
|
||||
|
||||
$resetCache = $this->getCalculationCacheEnabled();
|
||||
if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
|
||||
$cellID = 'A1';
|
||||
$pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
|
||||
} else {
|
||||
// Disable calculation cacheing because it only applies to cell calculations, not straight formulae
|
||||
// But don't actually flush any cache
|
||||
$resetCache = $this->getCalculationCacheEnabled();
|
||||
$this->calculationCacheEnabled = false;
|
||||
}
|
||||
|
||||
@ -2862,6 +2938,7 @@ class Calculation
|
||||
if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
|
||||
$this->debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
|
||||
// Return the cached result
|
||||
|
||||
$cellValue = $this->calculationCache[$cellReference];
|
||||
|
||||
return true;
|
||||
@ -2977,17 +3054,17 @@ class Calculation
|
||||
// Examine each of the two operands, and turn them into an array if they aren't one already
|
||||
// Note that this function should only be called if one or both of the operand is already an array
|
||||
if (!is_array($operand1)) {
|
||||
list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2);
|
||||
[$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand2);
|
||||
$operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
|
||||
$resize = 0;
|
||||
} elseif (!is_array($operand2)) {
|
||||
list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1);
|
||||
[$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand1);
|
||||
$operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
|
||||
$resize = 0;
|
||||
}
|
||||
|
||||
list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1);
|
||||
list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2);
|
||||
[$matrix1Rows, $matrix1Columns] = self::getMatrixDimensions($operand1);
|
||||
[$matrix2Rows, $matrix2Columns] = self::getMatrixDimensions($operand2);
|
||||
if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
|
||||
$resize = 1;
|
||||
}
|
||||
@ -3197,7 +3274,7 @@ class Calculation
|
||||
/**
|
||||
* @param string $formula
|
||||
*
|
||||
* @return string
|
||||
* @return false|string False indicates an error
|
||||
*/
|
||||
private function convertMatrixReferences($formula)
|
||||
{
|
||||
@ -3321,9 +3398,53 @@ class Calculation
|
||||
// - is a negation or + is a positive operator rather than an operation
|
||||
$expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand
|
||||
// should be null in a function call
|
||||
|
||||
// IF branch pruning
|
||||
// currently pending storeKey (last item of the storeKeysStack
|
||||
$pendingStoreKey = null;
|
||||
// stores a list of storeKeys (string[])
|
||||
$pendingStoreKeysStack = [];
|
||||
$expectingConditionMap = []; // ['storeKey' => true, ...]
|
||||
$expectingThenMap = []; // ['storeKey' => true, ...]
|
||||
$expectingElseMap = []; // ['storeKey' => true, ...]
|
||||
$parenthesisDepthMap = []; // ['storeKey' => 4, ...]
|
||||
|
||||
// The guts of the lexical parser
|
||||
// Loop through the formula extracting each operator and operand in turn
|
||||
while (true) {
|
||||
// Branch pruning: we adapt the output item to the context (it will
|
||||
// be used to limit its computation)
|
||||
$currentCondition = null;
|
||||
$currentOnlyIf = null;
|
||||
$currentOnlyIfNot = null;
|
||||
$previousStoreKey = null;
|
||||
$pendingStoreKey = end($pendingStoreKeysStack);
|
||||
|
||||
if ($this->branchPruningEnabled) {
|
||||
// this is a condition ?
|
||||
if (isset($expectingConditionMap[$pendingStoreKey]) && $expectingConditionMap[$pendingStoreKey]) {
|
||||
$currentCondition = $pendingStoreKey;
|
||||
$stackDepth = count($pendingStoreKeysStack);
|
||||
if ($stackDepth > 1) { // nested if
|
||||
$previousStoreKey = $pendingStoreKeysStack[$stackDepth - 2];
|
||||
}
|
||||
}
|
||||
if (isset($expectingThenMap[$pendingStoreKey]) && $expectingThenMap[$pendingStoreKey]) {
|
||||
$currentOnlyIf = $pendingStoreKey;
|
||||
} elseif (isset($previousStoreKey)) {
|
||||
if (isset($expectingThenMap[$previousStoreKey]) && $expectingThenMap[$previousStoreKey]) {
|
||||
$currentOnlyIf = $previousStoreKey;
|
||||
}
|
||||
}
|
||||
if (isset($expectingElseMap[$pendingStoreKey]) && $expectingElseMap[$pendingStoreKey]) {
|
||||
$currentOnlyIfNot = $pendingStoreKey;
|
||||
} elseif (isset($previousStoreKey)) {
|
||||
if (isset($expectingElseMap[$previousStoreKey]) && $expectingElseMap[$previousStoreKey]) {
|
||||
$currentOnlyIfNot = $previousStoreKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$opCharacter = $formula[$index]; // Get the first character of the value at the current index position
|
||||
if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
|
||||
$opCharacter .= $formula[++$index];
|
||||
@ -3333,10 +3454,12 @@ class Calculation
|
||||
$isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
|
||||
|
||||
if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
|
||||
$stack->push('Unary Operator', '~'); // Put a negation on the stack
|
||||
// Put a negation on the stack
|
||||
$stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
++$index; // and drop the negation symbol
|
||||
} elseif ($opCharacter == '%' && $expectingOperator) {
|
||||
$stack->push('Unary Operator', '%'); // Put a percentage on the stack
|
||||
// Put a percentage on the stack
|
||||
$stack->push('Unary Operator', '%', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
++$index;
|
||||
} elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
|
||||
++$index; // Drop the redundant plus symbol
|
||||
@ -3349,7 +3472,10 @@ class Calculation
|
||||
@(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
|
||||
$output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
|
||||
}
|
||||
$stack->push('Binary Operator', $opCharacter); // Finally put our current operator onto the stack
|
||||
|
||||
// Finally put our current operator onto the stack
|
||||
$stack->push('Binary Operator', $opCharacter, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
|
||||
++$index;
|
||||
$expectingOperator = false;
|
||||
} elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
|
||||
@ -3361,7 +3487,29 @@ class Calculation
|
||||
$output[] = $o2;
|
||||
}
|
||||
$d = $stack->last(2);
|
||||
if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
|
||||
|
||||
// Branch pruning we decrease the depth whether is it a function
|
||||
// call or a parenthesis
|
||||
if (!empty($pendingStoreKey)) {
|
||||
$parenthesisDepthMap[$pendingStoreKey] -= 1;
|
||||
}
|
||||
|
||||
if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
|
||||
if (!empty($pendingStoreKey) && $parenthesisDepthMap[$pendingStoreKey] == -1) {
|
||||
// we are closing an IF(
|
||||
if ($d['value'] != 'IF(') {
|
||||
return $this->raiseFormulaError('Parser bug we should be in an "IF("');
|
||||
}
|
||||
if ($expectingConditionMap[$pendingStoreKey]) {
|
||||
return $this->raiseFormulaError('We should not be expecting a condition');
|
||||
}
|
||||
$expectingThenMap[$pendingStoreKey] = false;
|
||||
$expectingElseMap[$pendingStoreKey] = false;
|
||||
$parenthesisDepthMap[$pendingStoreKey] -= 1;
|
||||
array_pop($pendingStoreKeysStack);
|
||||
unset($pendingStoreKey);
|
||||
}
|
||||
|
||||
$functionName = $matches[1]; // Get the function name
|
||||
$d = $stack->pop();
|
||||
$argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack)
|
||||
@ -3422,6 +3570,20 @@ class Calculation
|
||||
}
|
||||
++$index;
|
||||
} elseif ($opCharacter == ',') { // Is this the separator for function arguments?
|
||||
if (!empty($pendingStoreKey) &&
|
||||
$parenthesisDepthMap[$pendingStoreKey] == 0
|
||||
) {
|
||||
// We must go to the IF next argument
|
||||
if ($expectingConditionMap[$pendingStoreKey]) {
|
||||
$expectingConditionMap[$pendingStoreKey] = false;
|
||||
$expectingThenMap[$pendingStoreKey] = true;
|
||||
} elseif ($expectingThenMap[$pendingStoreKey]) {
|
||||
$expectingThenMap[$pendingStoreKey] = false;
|
||||
$expectingElseMap[$pendingStoreKey] = true;
|
||||
} elseif ($expectingElseMap[$pendingStoreKey]) {
|
||||
return $this->raiseFormulaError('Reaching fourth argument of an IF');
|
||||
}
|
||||
}
|
||||
while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
|
||||
if ($o2 === null) {
|
||||
return $this->raiseFormulaError('Formula Error: Unexpected ,');
|
||||
@ -3439,13 +3601,19 @@ class Calculation
|
||||
return $this->raiseFormulaError('Formula Error: Unexpected ,');
|
||||
}
|
||||
$d = $stack->pop();
|
||||
$stack->push($d['type'], ++$d['value'], $d['reference']); // increment the argument count
|
||||
$stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
|
||||
$itemStoreKey = $d['storeKey'] ?? null;
|
||||
$itemOnlyIf = $d['onlyIf'] ?? null;
|
||||
$itemOnlyIfNot = $d['onlyIfNot'] ?? null;
|
||||
$stack->push($d['type'], ++$d['value'], $d['reference'], $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // increment the argument count
|
||||
$stack->push('Brace', '(', null, $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // put the ( back on, we'll need to pop back to it again
|
||||
$expectingOperator = false;
|
||||
$expectingOperand = true;
|
||||
++$index;
|
||||
} elseif ($opCharacter == '(' && !$expectingOperator) {
|
||||
$stack->push('Brace', '(');
|
||||
if (!empty($pendingStoreKey)) { // Branch pruning: we go deeper
|
||||
$parenthesisDepthMap[$pendingStoreKey] += 1;
|
||||
}
|
||||
$stack->push('Brace', '(', null, $currentCondition, $currentOnlyIf, $currentOnlyIf);
|
||||
++$index;
|
||||
} elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
|
||||
$expectingOperator = true;
|
||||
@ -3456,13 +3624,28 @@ class Calculation
|
||||
if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) {
|
||||
$val = preg_replace('/\s/u', '', $val);
|
||||
if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
|
||||
$stack->push('Function', strtoupper($val));
|
||||
$valToUpper = strtoupper($val);
|
||||
// here $matches[1] will contain values like "IF"
|
||||
// and $val "IF("
|
||||
if ($this->branchPruningEnabled && ($valToUpper == 'IF(')) { // we handle a new if
|
||||
$pendingStoreKey = $this->getUnusedBranchStoreKey();
|
||||
$pendingStoreKeysStack[] = $pendingStoreKey;
|
||||
$expectingConditionMap[$pendingStoreKey] = true;
|
||||
$parenthesisDepthMap[$pendingStoreKey] = 0;
|
||||
} else { // this is not a if but we good deeper
|
||||
if (!empty($pendingStoreKey) && array_key_exists($pendingStoreKey, $parenthesisDepthMap)) {
|
||||
$parenthesisDepthMap[$pendingStoreKey] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
$stack->push('Function', $valToUpper, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
// tests if the function is closed right after opening
|
||||
$ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index + $length), $amatch);
|
||||
if ($ax) {
|
||||
$stack->push('Operand Count for Function ' . strtoupper($val) . ')', 0);
|
||||
$stack->push('Operand Count for Function ' . $valToUpper . ')', 0, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
$expectingOperator = true;
|
||||
} else {
|
||||
$stack->push('Operand Count for Function ' . strtoupper($val) . ')', 1);
|
||||
$stack->push('Operand Count for Function ' . $valToUpper . ')', 1, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
$expectingOperator = false;
|
||||
}
|
||||
$stack->push('Brace', '(');
|
||||
@ -3475,7 +3658,7 @@ class Calculation
|
||||
|
||||
// If the last entry on the stack was a : operator, then we have a cell range reference
|
||||
$testPrevOp = $stack->last(1);
|
||||
if ($testPrevOp['value'] == ':') {
|
||||
if ($testPrevOp !== null && $testPrevOp['value'] == ':') {
|
||||
// If we have a worksheet reference, then we're playing with a 3D reference
|
||||
if ($matches[2] == '') {
|
||||
// Otherwise, we 'inherit' the worksheet reference from the start cell reference
|
||||
@ -3490,32 +3673,39 @@ class Calculation
|
||||
}
|
||||
}
|
||||
|
||||
$output[] = ['type' => 'Cell Reference', 'value' => $val, 'reference' => $val];
|
||||
$outputItem = $stack->getStackItem('Cell Reference', $val, $val, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
|
||||
$output[] = $outputItem;
|
||||
} else { // it's a variable, constant, string, number or boolean
|
||||
// If the last entry on the stack was a : operator, then we may have a row or column range reference
|
||||
$testPrevOp = $stack->last(1);
|
||||
if ($testPrevOp['value'] == ':') {
|
||||
if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
|
||||
$startRowColRef = $output[count($output) - 1]['value'];
|
||||
list($rangeWS1, $startRowColRef) = Worksheet::extractSheetTitle($startRowColRef, true);
|
||||
[$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
|
||||
$rangeSheetRef = $rangeWS1;
|
||||
if ($rangeWS1 != '') {
|
||||
$rangeWS1 .= '!';
|
||||
}
|
||||
list($rangeWS2, $val) = Worksheet::extractSheetTitle($val, true);
|
||||
[$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
|
||||
if ($rangeWS2 != '') {
|
||||
$rangeWS2 .= '!';
|
||||
} else {
|
||||
$rangeWS2 = $rangeWS1;
|
||||
}
|
||||
$refSheet = $pCellParent;
|
||||
if ($pCellParent !== null && $rangeSheetRef !== $pCellParent->getTitle()) {
|
||||
$refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef);
|
||||
}
|
||||
if ((is_int($startRowColRef)) && (ctype_digit($val)) &&
|
||||
($startRowColRef <= 1048576) && ($val <= 1048576)) {
|
||||
// Row range
|
||||
$endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
|
||||
$endRowColRef = ($refSheet !== null) ? $refSheet->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
|
||||
$output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef;
|
||||
$val = $rangeWS2 . $endRowColRef . $val;
|
||||
} elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
|
||||
(strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
|
||||
// Column range
|
||||
$endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
|
||||
$endRowColRef = ($refSheet !== null) ? $refSheet->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
|
||||
$output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1';
|
||||
$val = $rangeWS2 . $val . $endRowColRef;
|
||||
}
|
||||
@ -3537,7 +3727,7 @@ class Calculation
|
||||
} elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
|
||||
$val = self::$excelConstants[$localeConstant];
|
||||
}
|
||||
$details = ['type' => 'Value', 'value' => $val, 'reference' => null];
|
||||
$details = $stack->getStackItem('Value', $val, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
|
||||
if ($localeConstant) {
|
||||
$details['localeValue'] = $localeConstant;
|
||||
}
|
||||
@ -3557,7 +3747,7 @@ class Calculation
|
||||
} elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
|
||||
return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
|
||||
} else { // I don't even want to know what you did to get here
|
||||
return $this->raiseFormulaError('Formula Error: An unexpected error occured');
|
||||
return $this->raiseFormulaError('Formula Error: An unexpected error occurred');
|
||||
}
|
||||
// Test for end of formula string
|
||||
if ($index == strlen($formula)) {
|
||||
@ -3640,9 +3830,85 @@ class Calculation
|
||||
$pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
|
||||
$stack = new Stack();
|
||||
|
||||
// Stores branches that have been pruned
|
||||
$fakedForBranchPruning = [];
|
||||
// help us to know when pruning ['branchTestId' => true/false]
|
||||
$branchStore = [];
|
||||
|
||||
// Loop through each token in turn
|
||||
foreach ($tokens as $tokenData) {
|
||||
$token = $tokenData['value'];
|
||||
|
||||
// Branch pruning: skip useless resolutions
|
||||
$storeKey = $tokenData['storeKey'] ?? null;
|
||||
if ($this->branchPruningEnabled && isset($tokenData['onlyIf'])) {
|
||||
$onlyIfStoreKey = $tokenData['onlyIf'];
|
||||
$storeValue = $branchStore[$onlyIfStoreKey] ?? null;
|
||||
$storeValueAsBool = ($storeValue === null) ?
|
||||
true : (bool) Functions::flattenSingleValue($storeValue);
|
||||
if (is_array($storeValue)) {
|
||||
$wrappedItem = end($storeValue);
|
||||
$storeValue = end($wrappedItem);
|
||||
}
|
||||
|
||||
if (isset($storeValue)
|
||||
&& (
|
||||
!$storeValueAsBool
|
||||
|| Functions::isError($storeValue)
|
||||
|| ($storeValue === 'Pruned branch')
|
||||
)
|
||||
) {
|
||||
// If branching value is not true, we don't need to compute
|
||||
if (!isset($fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey])) {
|
||||
$stack->push('Value', 'Pruned branch (only if ' . $onlyIfStoreKey . ') ' . $token);
|
||||
$fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey] = true;
|
||||
}
|
||||
|
||||
if (isset($storeKey)) {
|
||||
// We are processing an if condition
|
||||
// We cascade the pruning to the depending branches
|
||||
$branchStore[$storeKey] = 'Pruned branch';
|
||||
$fakedForBranchPruning['onlyIfNot-' . $storeKey] = true;
|
||||
$fakedForBranchPruning['onlyIf-' . $storeKey] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->branchPruningEnabled && isset($tokenData['onlyIfNot'])) {
|
||||
$onlyIfNotStoreKey = $tokenData['onlyIfNot'];
|
||||
$storeValue = $branchStore[$onlyIfNotStoreKey] ?? null;
|
||||
$storeValueAsBool = ($storeValue === null) ?
|
||||
true : (bool) Functions::flattenSingleValue($storeValue);
|
||||
if (is_array($storeValue)) {
|
||||
$wrappedItem = end($storeValue);
|
||||
$storeValue = end($wrappedItem);
|
||||
}
|
||||
if (isset($storeValue)
|
||||
&& (
|
||||
$storeValueAsBool
|
||||
|| Functions::isError($storeValue)
|
||||
|| ($storeValue === 'Pruned branch'))
|
||||
) {
|
||||
// If branching value is true, we don't need to compute
|
||||
if (!isset($fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey])) {
|
||||
$stack->push('Value', 'Pruned branch (only if not ' . $onlyIfNotStoreKey . ') ' . $token);
|
||||
$fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey] = true;
|
||||
}
|
||||
|
||||
if (isset($storeKey)) {
|
||||
// We are processing an if condition
|
||||
// We cascade the pruning to the depending branches
|
||||
$branchStore[$storeKey] = 'Pruned branch';
|
||||
$fakedForBranchPruning['onlyIfNot-' . $storeKey] = true;
|
||||
$fakedForBranchPruning['onlyIf-' . $storeKey] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
|
||||
if (isset(self::$binaryOperators[$token])) {
|
||||
// We must have two operands, error if we don't
|
||||
@ -3672,18 +3938,21 @@ class Calculation
|
||||
case '<=': // Less than or Equal to
|
||||
case '=': // Equality
|
||||
case '<>': // Inequality
|
||||
$this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
|
||||
$result = $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
// Binary Operators
|
||||
case ':': // Range
|
||||
if (strpos($operand1Data['reference'], '!') !== false) {
|
||||
list($sheet1, $operand1Data['reference']) = Worksheet::extractSheetTitle($operand1Data['reference'], true);
|
||||
[$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
|
||||
} else {
|
||||
$sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
|
||||
}
|
||||
|
||||
list($sheet2, $operand2Data['reference']) = Worksheet::extractSheetTitle($operand2Data['reference'], true);
|
||||
[$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
|
||||
if (empty($sheet2)) {
|
||||
$sheet2 = $sheet1;
|
||||
}
|
||||
@ -3728,23 +3997,38 @@ class Calculation
|
||||
|
||||
break;
|
||||
case '+': // Addition
|
||||
$this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack);
|
||||
$result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
case '-': // Subtraction
|
||||
$this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack);
|
||||
$result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
case '*': // Multiplication
|
||||
$this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack);
|
||||
$result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
case '/': // Division
|
||||
$this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack);
|
||||
$result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
case '^': // Exponential
|
||||
$this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack);
|
||||
$result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
case '&': // Concatenation
|
||||
@ -3777,6 +4061,10 @@ class Calculation
|
||||
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
|
||||
$stack->push('Value', $result);
|
||||
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
|
||||
break;
|
||||
case '|': // Intersect
|
||||
$rowIntersect = array_intersect_key($operand1, $operand2);
|
||||
@ -3821,6 +4109,9 @@ class Calculation
|
||||
}
|
||||
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
|
||||
$stack->push('Value', $result);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
} else {
|
||||
$this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack);
|
||||
}
|
||||
@ -3894,9 +4185,16 @@ class Calculation
|
||||
}
|
||||
}
|
||||
$stack->push('Value', $cellValue, $cellRef);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $cellValue;
|
||||
}
|
||||
|
||||
// if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
|
||||
// if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
|
||||
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $token, $matches)) {
|
||||
if ($pCellParent) {
|
||||
$pCell->attach($pCellParent);
|
||||
}
|
||||
|
||||
$functionName = $matches[1];
|
||||
$argCount = $stack->pop();
|
||||
$argCount = $argCount['value'];
|
||||
@ -3939,6 +4237,7 @@ class Calculation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the order of the arguments
|
||||
krsort($args);
|
||||
|
||||
@ -3963,22 +4262,32 @@ class Calculation
|
||||
}
|
||||
unset($arg);
|
||||
}
|
||||
|
||||
$result = call_user_func_array($functionCall, $args);
|
||||
|
||||
if ($functionName != 'MKMATRIX') {
|
||||
$this->debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
|
||||
}
|
||||
$stack->push('Value', self::wrapResult($result));
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if the token is a number, boolean, string or an Excel error, push it onto the stack
|
||||
if (isset(self::$excelConstants[strtoupper($token)])) {
|
||||
$excelConstant = strtoupper($token);
|
||||
$stack->push('Constant Value', self::$excelConstants[$excelConstant]);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = self::$excelConstants[$excelConstant];
|
||||
}
|
||||
$this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
|
||||
} elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == '"') || ($token[0] == '#')) {
|
||||
$stack->push('Value', $token);
|
||||
// if the token is a named range, push the named range name onto the stack
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $token;
|
||||
}
|
||||
// if the token is a named range, push the named range name onto the stack
|
||||
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) {
|
||||
$namedRange = $matches[6];
|
||||
$this->debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
|
||||
@ -3987,6 +4296,9 @@ class Calculation
|
||||
$pCell->attach($pCellParent);
|
||||
$this->debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
|
||||
$stack->push('Named Range', $cellValue, $namedRange);
|
||||
if (isset($storeKey)) {
|
||||
$branchStore[$storeKey] = $cellValue;
|
||||
}
|
||||
} else {
|
||||
return $this->raiseFormulaError("undefined variable '$token'");
|
||||
}
|
||||
@ -4048,7 +4360,7 @@ class Calculation
|
||||
* @param Stack $stack
|
||||
* @param bool $recursingArrays
|
||||
*
|
||||
* @return bool
|
||||
* @return mixed
|
||||
*/
|
||||
private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false)
|
||||
{
|
||||
@ -4085,7 +4397,7 @@ class Calculation
|
||||
// And push the result onto the stack
|
||||
$stack->push('Array', $result);
|
||||
|
||||
return true;
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Simple validate the two operands if they are string values
|
||||
@ -4175,7 +4487,7 @@ class Calculation
|
||||
// And push the result onto the stack
|
||||
$stack->push('Value', $result);
|
||||
|
||||
return true;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4201,7 +4513,7 @@ class Calculation
|
||||
* @param string $matrixFunction
|
||||
* @param mixed $stack
|
||||
*
|
||||
* @return bool
|
||||
* @return bool|mixed
|
||||
*/
|
||||
private function executeNumericBinaryOperation($operand1, $operand2, $operation, $matrixFunction, &$stack)
|
||||
{
|
||||
@ -4279,7 +4591,7 @@ class Calculation
|
||||
// And push the result onto the stack
|
||||
$stack->push('Value', $result);
|
||||
|
||||
return true;
|
||||
return $result;
|
||||
}
|
||||
|
||||
// trigger an error, but nicely, if need be
|
||||
@ -4312,7 +4624,7 @@ class Calculation
|
||||
if ($pSheet !== null) {
|
||||
$pSheetName = $pSheet->getTitle();
|
||||
if (strpos($pRange, '!') !== false) {
|
||||
list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
|
||||
[$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
|
||||
$pSheet = $this->spreadsheet->getSheetByName($pSheetName);
|
||||
}
|
||||
|
||||
@ -4365,7 +4677,7 @@ class Calculation
|
||||
if ($pSheet !== null) {
|
||||
$pSheetName = $pSheet->getTitle();
|
||||
if (strpos($pRange, '!') !== false) {
|
||||
list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
|
||||
[$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
|
||||
$pSheet = $this->spreadsheet->getSheetByName($pSheetName);
|
||||
}
|
||||
|
||||
@ -4389,7 +4701,7 @@ class Calculation
|
||||
$aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
|
||||
if (!isset($aReferences[1])) {
|
||||
// Single cell (or single column or row) in range
|
||||
list($currentCol, $currentRow) = Coordinate::coordinateFromString($aReferences[0]);
|
||||
[$currentCol, $currentRow] = Coordinate::coordinateFromString($aReferences[0]);
|
||||
if ($pSheet->cellExists($aReferences[0])) {
|
||||
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
|
||||
} else {
|
||||
@ -4399,7 +4711,7 @@ class Calculation
|
||||
// Extract cell data for all cells in the range
|
||||
foreach ($aReferences as $reference) {
|
||||
// Extract range
|
||||
list($currentCol, $currentRow) = Coordinate::coordinateFromString($reference);
|
||||
[$currentCol, $currentRow] = Coordinate::coordinateFromString($reference);
|
||||
if ($pSheet->cellExists($reference)) {
|
||||
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
|
||||
} else {
|
||||
@ -4483,4 +4795,26 @@ class Calculation
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function getUnusedBranchStoreKey()
|
||||
{
|
||||
$storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
|
||||
++$this->branchStoreKeyCounter;
|
||||
|
||||
return $storeKeyValue;
|
||||
}
|
||||
|
||||
private function getTokensAsString($tokens)
|
||||
{
|
||||
$tokensStr = array_map(function ($token) {
|
||||
$value = $token['value'] ?? 'no value';
|
||||
while (is_array($value)) {
|
||||
$value = array_pop($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $tokens);
|
||||
|
||||
return '[ ' . implode(' | ', $tokensStr) . ' ]';
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ class Database
|
||||
* the column label in which you specify a condition for the
|
||||
* column.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string
|
||||
*/
|
||||
public static function DAVERAGE($database, $field, $criteria)
|
||||
{
|
||||
@ -452,7 +452,7 @@ class Database
|
||||
* the column label in which you specify a condition for the
|
||||
* column.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string
|
||||
*/
|
||||
public static function DSTDEV($database, $field, $criteria)
|
||||
{
|
||||
@ -493,7 +493,7 @@ class Database
|
||||
* the column label in which you specify a condition for the
|
||||
* column.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string
|
||||
*/
|
||||
public static function DSTDEVP($database, $field, $criteria)
|
||||
{
|
||||
|
||||
@ -16,7 +16,7 @@ class DateTime
|
||||
*/
|
||||
public static function isLeapYear($year)
|
||||
{
|
||||
return (($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0);
|
||||
return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,11 +156,11 @@ class DateTime
|
||||
$retValue = (float) Date::PHPToExcel(time());
|
||||
|
||||
break;
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
$retValue = (int) time();
|
||||
|
||||
break;
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
$retValue = new \DateTime();
|
||||
|
||||
break;
|
||||
@ -200,11 +200,11 @@ class DateTime
|
||||
$retValue = (float) $excelDateTime;
|
||||
|
||||
break;
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
$retValue = (int) Date::excelToTimestamp($excelDateTime);
|
||||
|
||||
break;
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
$retValue = Date::excelToDateTimeObject($excelDateTime);
|
||||
|
||||
break;
|
||||
@ -325,9 +325,9 @@ class DateTime
|
||||
switch (Functions::getReturnDateType()) {
|
||||
case Functions::RETURNDATE_EXCEL:
|
||||
return (float) $excelDateValue;
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) Date::excelToTimestamp($excelDateValue);
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
return Date::excelToDateTimeObject($excelDateValue);
|
||||
}
|
||||
}
|
||||
@ -420,9 +420,9 @@ class DateTime
|
||||
}
|
||||
|
||||
return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
$dayAdjust = 0;
|
||||
if ($hour < 0) {
|
||||
$dayAdjust = floor($hour / 24);
|
||||
@ -472,7 +472,6 @@ class DateTime
|
||||
*/
|
||||
public static function DATEVALUE($dateValue = 1)
|
||||
{
|
||||
$dateValueOrig = $dateValue;
|
||||
$dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
|
||||
// Strip any ordinals because they're allowed in Excel (English only)
|
||||
$dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
|
||||
@ -492,7 +491,7 @@ class DateTime
|
||||
$yearFound = true;
|
||||
}
|
||||
}
|
||||
if ((count($t1) == 1) && (strpos($t, ':') != false)) {
|
||||
if ((count($t1) == 1) && (strpos($t, ':') !== false)) {
|
||||
// We've been fed a time value without any date
|
||||
return 0.0;
|
||||
} elseif (count($t1) == 2) {
|
||||
@ -569,9 +568,9 @@ class DateTime
|
||||
switch (Functions::getReturnDateType()) {
|
||||
case Functions::RETURNDATE_EXCEL:
|
||||
return (float) $excelDateValue;
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) Date::excelToTimestamp($excelDateValue);
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
|
||||
}
|
||||
}
|
||||
@ -631,9 +630,9 @@ class DateTime
|
||||
switch (Functions::getReturnDateType()) {
|
||||
case Functions::RETURNDATE_EXCEL:
|
||||
return (float) $excelDateValue;
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
|
||||
}
|
||||
}
|
||||
@ -683,7 +682,6 @@ class DateTime
|
||||
$endMonths = $PHPEndDateObject->format('n');
|
||||
$endYears = $PHPEndDateObject->format('Y');
|
||||
|
||||
$retVal = Functions::NAN();
|
||||
switch ($unit) {
|
||||
case 'D':
|
||||
$retVal = (int) $difference;
|
||||
@ -880,6 +878,8 @@ class DateTime
|
||||
*
|
||||
* Excel Function:
|
||||
* YEARFRAC(startDate,endDate[,method])
|
||||
* See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
|
||||
* for description of algorithm used in Excel
|
||||
*
|
||||
* @category Date/Time Functions
|
||||
*
|
||||
@ -894,7 +894,7 @@ class DateTime
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float fraction of the year
|
||||
* @return float|string fraction of the year, or a string containing an error
|
||||
*/
|
||||
public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
|
||||
{
|
||||
@ -908,6 +908,11 @@ class DateTime
|
||||
if (is_string($endDate = self::getDateValue($endDate))) {
|
||||
return Functions::VALUE();
|
||||
}
|
||||
if ($startDate > $endDate) {
|
||||
$temp = $startDate;
|
||||
$startDate = $endDate;
|
||||
$endDate = $temp;
|
||||
}
|
||||
|
||||
if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
|
||||
switch ($method) {
|
||||
@ -918,46 +923,43 @@ class DateTime
|
||||
$startYear = self::YEAR($startDate);
|
||||
$endYear = self::YEAR($endDate);
|
||||
$years = $endYear - $startYear + 1;
|
||||
$leapDays = 0;
|
||||
$startMonth = self::MONTHOFYEAR($startDate);
|
||||
$startDay = self::DAYOFMONTH($startDate);
|
||||
$endMonth = self::MONTHOFYEAR($endDate);
|
||||
$endDay = self::DAYOFMONTH($endDate);
|
||||
$startMonthDay = 100 * $startMonth + $startDay;
|
||||
$endMonthDay = 100 * $endMonth + $endDay;
|
||||
if ($years == 1) {
|
||||
if (self::isLeapYear($endYear)) {
|
||||
$startMonth = self::MONTHOFYEAR($startDate);
|
||||
$endMonth = self::MONTHOFYEAR($endDate);
|
||||
$endDay = self::DAYOFMONTH($endDate);
|
||||
if (($startMonth < 3) ||
|
||||
(($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
|
||||
$leapDays += 1;
|
||||
$tmpCalcAnnualBasis = 366;
|
||||
} else {
|
||||
$tmpCalcAnnualBasis = 365;
|
||||
}
|
||||
} elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
|
||||
if (self::isLeapYear($startYear)) {
|
||||
if ($startMonthDay <= 229) {
|
||||
$tmpCalcAnnualBasis = 366;
|
||||
} else {
|
||||
$tmpCalcAnnualBasis = 365;
|
||||
}
|
||||
} elseif (self::isLeapYear($endYear)) {
|
||||
if ($endMonthDay >= 229) {
|
||||
$tmpCalcAnnualBasis = 366;
|
||||
} else {
|
||||
$tmpCalcAnnualBasis = 365;
|
||||
}
|
||||
} else {
|
||||
$tmpCalcAnnualBasis = 365;
|
||||
}
|
||||
} else {
|
||||
$tmpCalcAnnualBasis = 0;
|
||||
for ($year = $startYear; $year <= $endYear; ++$year) {
|
||||
if ($year == $startYear) {
|
||||
$startMonth = self::MONTHOFYEAR($startDate);
|
||||
$startDay = self::DAYOFMONTH($startDate);
|
||||
if ($startMonth < 3) {
|
||||
$leapDays += (self::isLeapYear($year)) ? 1 : 0;
|
||||
}
|
||||
} elseif ($year == $endYear) {
|
||||
$endMonth = self::MONTHOFYEAR($endDate);
|
||||
$endDay = self::DAYOFMONTH($endDate);
|
||||
if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
|
||||
$leapDays += (self::isLeapYear($year)) ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
$leapDays += (self::isLeapYear($year)) ? 1 : 0;
|
||||
}
|
||||
$tmpCalcAnnualBasis += self::isLeapYear($year) ? 366 : 365;
|
||||
}
|
||||
if ($years == 2) {
|
||||
if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
|
||||
$leapDays = 1;
|
||||
} elseif ($days < 366) {
|
||||
$years = 1;
|
||||
}
|
||||
}
|
||||
$leapDays /= $years;
|
||||
$tmpCalcAnnualBasis /= $years;
|
||||
}
|
||||
|
||||
return $days / (365 + $leapDays);
|
||||
return $days / $tmpCalcAnnualBasis;
|
||||
case 2:
|
||||
return self::DATEDIF($startDate, $endDate) / 360;
|
||||
case 3:
|
||||
@ -1154,9 +1156,9 @@ class DateTime
|
||||
switch (Functions::getReturnDateType()) {
|
||||
case Functions::RETURNDATE_EXCEL:
|
||||
return (float) $endDate;
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) Date::excelToTimestamp($endDate);
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
return Date::excelToDateTimeObject($endDate);
|
||||
}
|
||||
}
|
||||
@ -1239,7 +1241,7 @@ class DateTime
|
||||
|
||||
// Execute function
|
||||
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
|
||||
$DoW = $PHPDateObject->format('w');
|
||||
$DoW = (int) $PHPDateObject->format('w');
|
||||
|
||||
$firstDay = 1;
|
||||
switch ($style) {
|
||||
@ -1248,13 +1250,13 @@ class DateTime
|
||||
|
||||
break;
|
||||
case 2:
|
||||
if ($DoW == 0) {
|
||||
if ($DoW === 0) {
|
||||
$DoW = 7;
|
||||
}
|
||||
|
||||
break;
|
||||
case 3:
|
||||
if ($DoW == 0) {
|
||||
if ($DoW === 0) {
|
||||
$DoW = 7;
|
||||
}
|
||||
$firstDay = 0;
|
||||
@ -1272,9 +1274,39 @@ class DateTime
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $DoW;
|
||||
return $DoW;
|
||||
}
|
||||
|
||||
const STARTWEEK_SUNDAY = 1;
|
||||
const STARTWEEK_MONDAY = 2;
|
||||
const STARTWEEK_MONDAY_ALT = 11;
|
||||
const STARTWEEK_TUESDAY = 12;
|
||||
const STARTWEEK_WEDNESDAY = 13;
|
||||
const STARTWEEK_THURSDAY = 14;
|
||||
const STARTWEEK_FRIDAY = 15;
|
||||
const STARTWEEK_SATURDAY = 16;
|
||||
const STARTWEEK_SUNDAY_ALT = 17;
|
||||
const DOW_SUNDAY = 1;
|
||||
const DOW_MONDAY = 2;
|
||||
const DOW_TUESDAY = 3;
|
||||
const DOW_WEDNESDAY = 4;
|
||||
const DOW_THURSDAY = 5;
|
||||
const DOW_FRIDAY = 6;
|
||||
const DOW_SATURDAY = 7;
|
||||
const STARTWEEK_MONDAY_ISO = 21;
|
||||
const METHODARR = [
|
||||
self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
|
||||
self::DOW_MONDAY,
|
||||
self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
|
||||
self::DOW_TUESDAY,
|
||||
self::DOW_WEDNESDAY,
|
||||
self::DOW_THURSDAY,
|
||||
self::DOW_FRIDAY,
|
||||
self::DOW_SATURDAY,
|
||||
self::DOW_SUNDAY,
|
||||
self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
|
||||
];
|
||||
|
||||
/**
|
||||
* WEEKNUM.
|
||||
*
|
||||
@ -1293,41 +1325,51 @@ class DateTime
|
||||
* @param int $method Week begins on Sunday or Monday
|
||||
* 1 or omitted Week begins on Sunday.
|
||||
* 2 Week begins on Monday.
|
||||
* 11 Week begins on Monday.
|
||||
* 12 Week begins on Tuesday.
|
||||
* 13 Week begins on Wednesday.
|
||||
* 14 Week begins on Thursday.
|
||||
* 15 Week begins on Friday.
|
||||
* 16 Week begins on Saturday.
|
||||
* 17 Week begins on Sunday.
|
||||
* 21 ISO (Jan. 4 is week 1, begins on Monday).
|
||||
*
|
||||
* @return int|string Week Number
|
||||
*/
|
||||
public static function WEEKNUM($dateValue = 1, $method = 1)
|
||||
public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY)
|
||||
{
|
||||
$dateValue = Functions::flattenSingleValue($dateValue);
|
||||
$method = Functions::flattenSingleValue($method);
|
||||
|
||||
if (!is_numeric($method)) {
|
||||
return Functions::VALUE();
|
||||
} elseif (($method < 1) || ($method > 2)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$method = floor($method);
|
||||
$method = (int) $method;
|
||||
if (!array_key_exists($method, self::METHODARR)) {
|
||||
return Functions::NaN();
|
||||
}
|
||||
$method = self::METHODARR[$method];
|
||||
|
||||
if ($dateValue === null) {
|
||||
$dateValue = 1;
|
||||
} elseif (is_string($dateValue = self::getDateValue($dateValue))) {
|
||||
$dateValue = self::getDateValue($dateValue);
|
||||
if (is_string($dateValue)) {
|
||||
return Functions::VALUE();
|
||||
} elseif ($dateValue < 0.0) {
|
||||
}
|
||||
if ($dateValue < 0.0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
// Execute function
|
||||
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
|
||||
if ($method == self::STARTWEEK_MONDAY_ISO) {
|
||||
return (int) $PHPDateObject->format('W');
|
||||
}
|
||||
$dayOfYear = $PHPDateObject->format('z');
|
||||
$PHPDateObject->modify('-' . $dayOfYear . ' days');
|
||||
$firstDayOfFirstWeek = $PHPDateObject->format('w');
|
||||
$daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
|
||||
$interval = $dayOfYear - $daysInFirstWeek;
|
||||
$weekOfYear = floor($interval / 7) + 1;
|
||||
|
||||
if ($daysInFirstWeek) {
|
||||
++$weekOfYear;
|
||||
}
|
||||
$daysInFirstWeek += 7 * !$daysInFirstWeek;
|
||||
$endFirstWeek = $daysInFirstWeek - 1;
|
||||
$weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
|
||||
|
||||
return (int) $weekOfYear;
|
||||
}
|
||||
@ -1591,9 +1633,9 @@ class DateTime
|
||||
switch (Functions::getReturnDateType()) {
|
||||
case Functions::RETURNDATE_EXCEL:
|
||||
return (float) Date::PHPToExcel($PHPDateObject);
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
return $PHPDateObject;
|
||||
}
|
||||
}
|
||||
@ -1640,9 +1682,9 @@ class DateTime
|
||||
switch (Functions::getReturnDateType()) {
|
||||
case Functions::RETURNDATE_EXCEL:
|
||||
return (float) Date::PHPToExcel($PHPDateObject);
|
||||
case Functions::RETURNDATE_PHP_NUMERIC:
|
||||
case Functions::RETURNDATE_UNIX_TIMESTAMP:
|
||||
return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
|
||||
case Functions::RETURNDATE_PHP_OBJECT:
|
||||
case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
|
||||
return $PHPDateObject;
|
||||
}
|
||||
}
|
||||
|
||||
@ -785,7 +785,7 @@ class Engineering
|
||||
* If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELI returns the #NUM! error value.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function BESSELI($x, $ord)
|
||||
{
|
||||
@ -839,7 +839,7 @@ class Engineering
|
||||
* If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELJ returns the #NUM! error value.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function BESSELJ($x, $ord)
|
||||
{
|
||||
@ -932,7 +932,7 @@ class Engineering
|
||||
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELK returns the #NUM! error value.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function BESSELK($x, $ord)
|
||||
{
|
||||
@ -1021,7 +1021,7 @@ class Engineering
|
||||
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELK returns the #NUM! error value.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function BESSELY($x, $ord)
|
||||
{
|
||||
@ -2713,7 +2713,7 @@ class Engineering
|
||||
* @param string $fromUOM the units for value
|
||||
* @param string $toUOM the units for the result
|
||||
*
|
||||
* @return float
|
||||
* @return float|string
|
||||
*/
|
||||
public static function CONVERTUOM($value, $fromUOM, $toUOM)
|
||||
{
|
||||
|
||||
@ -71,7 +71,7 @@ class Financial
|
||||
* 3 365
|
||||
* 4 European 360
|
||||
*
|
||||
* @return int
|
||||
* @return int|string Result, or a string containing an error
|
||||
*/
|
||||
private static function daysPerYear($year, $basis = 0)
|
||||
{
|
||||
@ -144,7 +144,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
|
||||
{
|
||||
@ -197,7 +197,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
|
||||
{
|
||||
@ -417,7 +417,7 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (($settlement > $maturity) ||
|
||||
if (($settlement >= $maturity) ||
|
||||
(!self::isValidFrequency($frequency)) ||
|
||||
(($basis < 0) || ($basis > 4))) {
|
||||
return Functions::NAN();
|
||||
@ -476,7 +476,7 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (($settlement > $maturity) ||
|
||||
if (($settlement >= $maturity) ||
|
||||
(!self::isValidFrequency($frequency)) ||
|
||||
(($basis < 0) || ($basis > 4))) {
|
||||
return Functions::NAN();
|
||||
@ -550,7 +550,7 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (($settlement > $maturity) ||
|
||||
if (($settlement >= $maturity) ||
|
||||
(!self::isValidFrequency($frequency)) ||
|
||||
(($basis < 0) || ($basis > 4))) {
|
||||
return Functions::NAN();
|
||||
@ -610,7 +610,7 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (($settlement > $maturity) ||
|
||||
if (($settlement >= $maturity) ||
|
||||
(!self::isValidFrequency($frequency)) ||
|
||||
(($basis < 0) || ($basis > 4))) {
|
||||
return Functions::NAN();
|
||||
@ -667,26 +667,22 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (($settlement > $maturity) ||
|
||||
if (($settlement >= $maturity) ||
|
||||
(!self::isValidFrequency($frequency)) ||
|
||||
(($basis < 0) || ($basis > 4))) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$settlement = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
|
||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis) * 365;
|
||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis) * $daysPerYear;
|
||||
|
||||
switch ($frequency) {
|
||||
case 1: // annual payments
|
||||
return ceil($daysBetweenSettlementAndMaturity / 360);
|
||||
case 2: // half-yearly
|
||||
return ceil($daysBetweenSettlementAndMaturity / 180);
|
||||
case 4: // quarterly
|
||||
return ceil($daysBetweenSettlementAndMaturity / 90);
|
||||
case 6: // bimonthly
|
||||
return ceil($daysBetweenSettlementAndMaturity / 60);
|
||||
case 12: // monthly
|
||||
return ceil($daysBetweenSettlementAndMaturity / 30);
|
||||
return ceil($daysBetweenSettlementAndMaturity / $daysPerYear * $frequency);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
@ -740,7 +736,7 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
if (($settlement > $maturity) ||
|
||||
if (($settlement >= $maturity) ||
|
||||
(!self::isValidFrequency($frequency)) ||
|
||||
(($basis < 0) || ($basis > 4))) {
|
||||
return Functions::NAN();
|
||||
@ -1428,7 +1424,7 @@ class Financial
|
||||
* @param float $finance_rate The interest rate you pay on the money used in the cash flows
|
||||
* @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function MIRR($values, $finance_rate, $reinvestment_rate)
|
||||
{
|
||||
@ -1470,7 +1466,7 @@ class Financial
|
||||
* @param float $effect_rate Effective interest rate
|
||||
* @param int $npery Number of compounding payments per year
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function NOMINAL($effect_rate = 0, $npery = 0)
|
||||
{
|
||||
@ -1497,7 +1493,7 @@ class Financial
|
||||
* @param float $fv Future Value
|
||||
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
|
||||
{
|
||||
@ -1565,7 +1561,7 @@ class Financial
|
||||
* @param float $pv Present Value
|
||||
* @param float $fv Future Value
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function PDURATION($rate = 0, $pv = 0, $fv = 0)
|
||||
{
|
||||
@ -1594,7 +1590,7 @@ class Financial
|
||||
* @param float $fv Future Value
|
||||
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
|
||||
{
|
||||
@ -1629,7 +1625,7 @@ class Financial
|
||||
* @param float $fv Future Value
|
||||
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
|
||||
{
|
||||
@ -1713,7 +1709,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
|
||||
{
|
||||
@ -1759,7 +1755,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
|
||||
{
|
||||
@ -1817,7 +1813,7 @@ class Financial
|
||||
* @param float $fv Future Value
|
||||
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
|
||||
{
|
||||
@ -1933,7 +1929,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
|
||||
{
|
||||
@ -1969,7 +1965,7 @@ class Financial
|
||||
* @param float $pv Present Value
|
||||
* @param float $fv Future Value
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function RRI($nper = 0, $pv = 0, $fv = 0)
|
||||
{
|
||||
@ -1996,7 +1992,7 @@ class Financial
|
||||
* @param mixed $salvage Value at the end of the depreciation
|
||||
* @param mixed $life Number of periods over which the asset is depreciated
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function SLN($cost, $salvage, $life)
|
||||
{
|
||||
@ -2026,7 +2022,7 @@ class Financial
|
||||
* @param mixed $life Number of periods over which the asset is depreciated
|
||||
* @param mixed $period Period
|
||||
*
|
||||
* @return float|string
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function SYD($cost, $salvage, $life, $period)
|
||||
{
|
||||
@ -2058,7 +2054,7 @@ class Financial
|
||||
* The maturity date is the date when the Treasury bill expires.
|
||||
* @param int $discount The Treasury bill's discount rate
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function TBILLEQ($settlement, $maturity, $discount)
|
||||
{
|
||||
@ -2097,7 +2093,7 @@ class Financial
|
||||
* The maturity date is the date when the Treasury bill expires.
|
||||
* @param int $discount The Treasury bill's discount rate
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function TBILLPRICE($settlement, $maturity, $discount)
|
||||
{
|
||||
@ -2152,7 +2148,7 @@ class Financial
|
||||
* The maturity date is the date when the Treasury bill expires.
|
||||
* @param int $price The Treasury bill's price per $100 face value
|
||||
*
|
||||
* @return float
|
||||
* @return float|mixed|string
|
||||
*/
|
||||
public static function TBILLYIELD($settlement, $maturity, $price)
|
||||
{
|
||||
@ -2187,6 +2183,23 @@ class Financial
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* XIRR.
|
||||
*
|
||||
* Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic.
|
||||
*
|
||||
* Excel Function:
|
||||
* =XIRR(values,dates,guess)
|
||||
*
|
||||
* @param float[] $values A series of cash flow payments
|
||||
* The series of values must contain at least one positive value & one negative value
|
||||
* @param mixed[] $dates A series of payment dates
|
||||
* The first payment date indicates the beginning of the schedule of payments
|
||||
* All other dates must be later than this date, but they may occur in any order
|
||||
* @param float $guess An optional guess at the expected answer
|
||||
*
|
||||
* @return float|mixed|string
|
||||
*/
|
||||
public static function XIRR($values, $dates, $guess = 0.1)
|
||||
{
|
||||
if ((!is_array($values)) && (!is_array($dates))) {
|
||||
@ -2199,11 +2212,28 @@ class Financial
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$datesCount = count($dates);
|
||||
for ($i = 0; $i < $datesCount; ++$i) {
|
||||
$dates[$i] = DateTime::getDateValue($dates[$i]);
|
||||
if (!is_numeric($dates[$i])) {
|
||||
return Functions::VALUE();
|
||||
}
|
||||
}
|
||||
if (min($dates) != $dates[0]) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
// create an initial range, with a root somewhere between 0 and guess
|
||||
$x1 = 0.0;
|
||||
$x2 = $guess;
|
||||
$f1 = self::XNPV($x1, $values, $dates);
|
||||
if (!is_numeric($f1)) {
|
||||
return $f1;
|
||||
}
|
||||
$f2 = self::XNPV($x2, $values, $dates);
|
||||
if (!is_numeric($f2)) {
|
||||
return $f2;
|
||||
}
|
||||
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
|
||||
if (($f1 * $f2) < 0.0) {
|
||||
break;
|
||||
@ -2214,7 +2244,7 @@ class Financial
|
||||
}
|
||||
}
|
||||
if (($f1 * $f2) > 0.0) {
|
||||
return Functions::VALUE();
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$f = self::XNPV($x1, $values, $dates);
|
||||
@ -2251,15 +2281,15 @@ class Financial
|
||||
* =XNPV(rate,values,dates)
|
||||
*
|
||||
* @param float $rate the discount rate to apply to the cash flows
|
||||
* @param array of float $values A series of cash flows that corresponds to a schedule of payments in dates.
|
||||
* @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
|
||||
* The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
|
||||
* If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
|
||||
* The series of values must contain at least one positive value and one negative value.
|
||||
* @param array of mixed $dates A schedule of payment dates that corresponds to the cash flow payments.
|
||||
* @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
|
||||
* 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
|
||||
* @return float|mixed|string
|
||||
*/
|
||||
public static function XNPV($rate, $values, $dates)
|
||||
{
|
||||
@ -2277,7 +2307,7 @@ class Financial
|
||||
return Functions::NAN();
|
||||
}
|
||||
if ((min($values) > 0) || (max($values) < 0)) {
|
||||
return Functions::VALUE();
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$xnpv = 0.0;
|
||||
@ -2309,7 +2339,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
|
||||
{
|
||||
@ -2360,7 +2390,7 @@ class Financial
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
|
||||
{
|
||||
|
||||
@ -17,8 +17,11 @@ class Functions
|
||||
const COMPATIBILITY_EXCEL = 'Excel';
|
||||
const COMPATIBILITY_GNUMERIC = 'Gnumeric';
|
||||
const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
|
||||
|
||||
const RETURNDATE_PHP_NUMERIC = 'P';
|
||||
const RETURNDATE_UNIX_TIMESTAMP = 'P';
|
||||
const RETURNDATE_PHP_OBJECT = 'O';
|
||||
const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
|
||||
const RETURNDATE_EXCEL = 'E';
|
||||
|
||||
/**
|
||||
@ -101,16 +104,16 @@ class Functions
|
||||
*
|
||||
* @param string $returnDateType Return Date Format
|
||||
* Permitted values are:
|
||||
* Functions::RETURNDATE_PHP_NUMERIC 'P'
|
||||
* Functions::RETURNDATE_PHP_OBJECT 'O'
|
||||
* Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
|
||||
* Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
|
||||
* Functions::RETURNDATE_EXCEL 'E'
|
||||
*
|
||||
* @return bool Success or failure
|
||||
*/
|
||||
public static function setReturnDateType($returnDateType)
|
||||
{
|
||||
if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
|
||||
($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
|
||||
if (($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
|
||||
($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
|
||||
($returnDateType == self::RETURNDATE_EXCEL)
|
||||
) {
|
||||
self::$returnDateType = $returnDateType;
|
||||
@ -128,8 +131,8 @@ class Functions
|
||||
*
|
||||
* @return string Return Date Format
|
||||
* Possible Return values are:
|
||||
* Functions::RETURNDATE_PHP_NUMERIC 'P'
|
||||
* Functions::RETURNDATE_PHP_OBJECT 'O'
|
||||
* Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
|
||||
* Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
|
||||
* Functions::RETURNDATE_EXCEL 'E'
|
||||
*/
|
||||
public static function getReturnDateType()
|
||||
@ -267,25 +270,29 @@ class Functions
|
||||
public static function ifCondition($condition)
|
||||
{
|
||||
$condition = self::flattenSingleValue($condition);
|
||||
if (!isset($condition[0]) && !is_numeric($condition)) {
|
||||
|
||||
if ($condition === '') {
|
||||
$condition = '=""';
|
||||
}
|
||||
if (!in_array($condition[0], ['>', '<', '='])) {
|
||||
|
||||
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
|
||||
if (!is_numeric($condition)) {
|
||||
$condition = Calculation::wrapResult(strtoupper($condition));
|
||||
}
|
||||
|
||||
return '=' . $condition;
|
||||
return str_replace('""""', '""', '=' . $condition);
|
||||
}
|
||||
preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
|
||||
list(, $operator, $operand) = $matches;
|
||||
[, $operator, $operand] = $matches;
|
||||
|
||||
if (!is_numeric($operand)) {
|
||||
if (is_numeric(trim($operand, '"'))) {
|
||||
$operand = trim($operand, '"');
|
||||
} elseif (!is_numeric($operand)) {
|
||||
$operand = str_replace('"', '""', $operand);
|
||||
$operand = Calculation::wrapResult(strtoupper($operand));
|
||||
}
|
||||
|
||||
return $operator . $operand;
|
||||
return str_replace('""""', '""', $operator . $operand);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -639,7 +646,7 @@ class Functions
|
||||
public static function flattenSingleValue($value = '')
|
||||
{
|
||||
while (is_array($value)) {
|
||||
$value = array_pop($value);
|
||||
$value = array_shift($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
@ -266,6 +266,10 @@ class Logical
|
||||
*/
|
||||
public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
|
||||
{
|
||||
if (Functions::isError($condition)) {
|
||||
return $condition;
|
||||
}
|
||||
|
||||
$condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition);
|
||||
$returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue);
|
||||
$returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse);
|
||||
@ -347,4 +351,25 @@ class Logical
|
||||
|
||||
return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* IFNA.
|
||||
*
|
||||
* Excel Function:
|
||||
* =IFNA(testValue,napart)
|
||||
*
|
||||
* @category Logical Functions
|
||||
*
|
||||
* @param mixed $testValue Value to check, is also the value returned when not an NA
|
||||
* @param mixed $napart Value to return when testValue is an NA condition
|
||||
*
|
||||
* @return mixed The value of errorpart or testValue determined by error condition
|
||||
*/
|
||||
public static function IFNA($testValue = '', $napart = '')
|
||||
{
|
||||
$testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
|
||||
$napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
|
||||
|
||||
return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,9 +98,9 @@ class LookupRef
|
||||
return (int) Coordinate::columnIndexFromString($columnKey);
|
||||
}
|
||||
} else {
|
||||
list($sheet, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
[$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
if (strpos($cellAddress, ':') !== false) {
|
||||
list($startAddress, $endAddress) = explode(':', $cellAddress);
|
||||
[$startAddress, $endAddress] = explode(':', $cellAddress);
|
||||
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
|
||||
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
|
||||
$returnValue = [];
|
||||
@ -138,7 +138,7 @@ class LookupRef
|
||||
|
||||
reset($cellAddress);
|
||||
$isMatrix = (is_numeric(key($cellAddress)));
|
||||
list($columns, $rows) = Calculation::getMatrixDimensions($cellAddress);
|
||||
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
|
||||
|
||||
if ($isMatrix) {
|
||||
return $rows;
|
||||
@ -175,9 +175,9 @@ class LookupRef
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list($sheet, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
[$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
if (strpos($cellAddress, ':') !== false) {
|
||||
list($startAddress, $endAddress) = explode(':', $cellAddress);
|
||||
[$startAddress, $endAddress] = explode(':', $cellAddress);
|
||||
$startAddress = preg_replace('/\D/', '', $startAddress);
|
||||
$endAddress = preg_replace('/\D/', '', $endAddress);
|
||||
$returnValue = [];
|
||||
@ -187,7 +187,7 @@ class LookupRef
|
||||
|
||||
return $returnValue;
|
||||
}
|
||||
list($cellAddress) = explode(':', $cellAddress);
|
||||
[$cellAddress] = explode(':', $cellAddress);
|
||||
|
||||
return (int) preg_replace('/\D/', '', $cellAddress);
|
||||
}
|
||||
@ -215,7 +215,7 @@ class LookupRef
|
||||
|
||||
reset($cellAddress);
|
||||
$isMatrix = (is_numeric(key($cellAddress)));
|
||||
list($columns, $rows) = Calculation::getMatrixDimensions($cellAddress);
|
||||
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
|
||||
|
||||
if ($isMatrix) {
|
||||
return $columns;
|
||||
@ -285,7 +285,7 @@ class LookupRef
|
||||
$cellAddress1 = $cellAddress;
|
||||
$cellAddress2 = null;
|
||||
if (strpos($cellAddress, ':') !== false) {
|
||||
list($cellAddress1, $cellAddress2) = explode(':', $cellAddress);
|
||||
[$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
|
||||
}
|
||||
|
||||
if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
|
||||
@ -295,7 +295,7 @@ class LookupRef
|
||||
}
|
||||
|
||||
if (strpos($cellAddress, '!') !== false) {
|
||||
list($sheetName, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
$pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
|
||||
} else {
|
||||
@ -306,7 +306,7 @@ class LookupRef
|
||||
}
|
||||
|
||||
if (strpos($cellAddress, '!') !== false) {
|
||||
list($sheetName, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
$pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
|
||||
} else {
|
||||
@ -359,16 +359,16 @@ class LookupRef
|
||||
|
||||
$sheetName = null;
|
||||
if (strpos($cellAddress, '!')) {
|
||||
list($sheetName, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
|
||||
$sheetName = trim($sheetName, "'");
|
||||
}
|
||||
if (strpos($cellAddress, ':')) {
|
||||
list($startCell, $endCell) = explode(':', $cellAddress);
|
||||
[$startCell, $endCell] = explode(':', $cellAddress);
|
||||
} else {
|
||||
$startCell = $endCell = $cellAddress;
|
||||
}
|
||||
list($startCellColumn, $startCellRow) = Coordinate::coordinateFromString($startCell);
|
||||
list($endCellColumn, $endCellRow) = Coordinate::coordinateFromString($endCell);
|
||||
[$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
|
||||
[$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
|
||||
|
||||
$startCellRow += $rows;
|
||||
$startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
|
||||
@ -464,9 +464,10 @@ class LookupRef
|
||||
*
|
||||
* @param mixed $lookupValue The value that you want to match in lookup_array
|
||||
* @param mixed $lookupArray The range of cells being searched
|
||||
* @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered.
|
||||
* @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
|
||||
* If match_type is 1 or -1, the list has to be ordered.
|
||||
*
|
||||
* @return int The relative position of the found item
|
||||
* @return int|string The relative position of the found item
|
||||
*/
|
||||
public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
|
||||
{
|
||||
@ -474,9 +475,10 @@ class LookupRef
|
||||
$lookupValue = Functions::flattenSingleValue($lookupValue);
|
||||
$matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
|
||||
|
||||
$initialLookupValue = $lookupValue;
|
||||
// MATCH is not case sensitive
|
||||
$lookupValue = StringHelper::strToLower($lookupValue);
|
||||
// MATCH is not case sensitive, so we convert lookup value to be lower cased in case it's string type.
|
||||
if (is_string($lookupValue)) {
|
||||
$lookupValue = StringHelper::strToLower($lookupValue);
|
||||
}
|
||||
|
||||
// Lookup_value type has to be number, text, or logical values
|
||||
if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
|
||||
@ -522,16 +524,54 @@ class LookupRef
|
||||
// find the match
|
||||
// **
|
||||
|
||||
if ($matchType == 0 || $matchType == 1) {
|
||||
if ($matchType === 0 || $matchType === 1) {
|
||||
foreach ($lookupArray as $i => $lookupArrayValue) {
|
||||
$onlyNumeric = is_numeric($lookupArrayValue) && is_numeric($lookupValue);
|
||||
$onlyNumericExactMatch = $onlyNumeric && $lookupArrayValue == $lookupValue;
|
||||
$nonOnlyNumericExactMatch = !$onlyNumeric && $lookupArrayValue === $lookupValue;
|
||||
$exactMatch = $onlyNumericExactMatch || $nonOnlyNumericExactMatch;
|
||||
if (($matchType == 0) && $exactMatch) {
|
||||
// exact match
|
||||
return $i + 1;
|
||||
} elseif (($matchType == 1) && ($lookupArrayValue <= $lookupValue)) {
|
||||
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
|
||||
$exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
|
||||
$nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
|
||||
$exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
|
||||
|
||||
if ($matchType === 0) {
|
||||
if ($typeMatch && is_string($lookupValue) && (bool) preg_match('/([\?\*])/', $lookupValue)) {
|
||||
$splitString = $lookupValue;
|
||||
$chars = array_map(function ($i) use ($splitString) {
|
||||
return mb_substr($splitString, $i, 1);
|
||||
}, range(0, mb_strlen($splitString) - 1));
|
||||
|
||||
$length = count($chars);
|
||||
$pattern = '/^';
|
||||
for ($j = 0; $j < $length; ++$j) {
|
||||
if ($chars[$j] === '~') {
|
||||
if (isset($chars[$j + 1])) {
|
||||
if ($chars[$j + 1] === '*') {
|
||||
$pattern .= preg_quote($chars[$j + 1], '/');
|
||||
++$j;
|
||||
} elseif ($chars[$j + 1] === '?') {
|
||||
$pattern .= preg_quote($chars[$j + 1], '/');
|
||||
++$j;
|
||||
}
|
||||
} else {
|
||||
$pattern .= preg_quote($chars[$j], '/');
|
||||
}
|
||||
} elseif ($chars[$j] === '*') {
|
||||
$pattern .= '.*';
|
||||
} elseif ($chars[$j] === '?') {
|
||||
$pattern .= '.{1}';
|
||||
} else {
|
||||
$pattern .= preg_quote($chars[$j], '/');
|
||||
}
|
||||
}
|
||||
|
||||
$pattern .= '$/';
|
||||
if ((bool) preg_match($pattern, $lookupArrayValue)) {
|
||||
// exact match
|
||||
return $i + 1;
|
||||
}
|
||||
} elseif ($exactMatch) {
|
||||
// exact match
|
||||
return $i + 1;
|
||||
}
|
||||
} elseif (($matchType === 1) && $typeMatch && ($lookupArrayValue <= $lookupValue)) {
|
||||
$i = array_search($i, $keySet);
|
||||
|
||||
// The current value is the (first) match
|
||||
@ -539,26 +579,26 @@ class LookupRef
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// matchType = -1
|
||||
|
||||
// "Special" case: since the array it's supposed to be ordered in descending order, the
|
||||
// Excel algorithm gives up immediately if the first element is smaller than the searched value
|
||||
if ($lookupArray[0] < $lookupValue) {
|
||||
return Functions::NA();
|
||||
}
|
||||
|
||||
$maxValueKey = null;
|
||||
|
||||
// The basic algorithm is:
|
||||
// Iterate and keep the highest match until the next element is smaller than the searched value.
|
||||
// Return immediately if perfect match is found
|
||||
foreach ($lookupArray as $i => $lookupArrayValue) {
|
||||
if ($lookupArrayValue == $lookupValue) {
|
||||
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
|
||||
$exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
|
||||
$nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
|
||||
$exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
|
||||
|
||||
if ($exactMatch) {
|
||||
// Another "special" case. If a perfect match is found,
|
||||
// the algorithm gives up immediately
|
||||
return $i + 1;
|
||||
} elseif ($lookupArrayValue >= $lookupValue) {
|
||||
} elseif ($typeMatch & $lookupArrayValue >= $lookupValue) {
|
||||
$maxValueKey = $i + 1;
|
||||
} elseif ($typeMatch & $lookupArrayValue < $lookupValue) {
|
||||
//Excel algorithm gives up immediately if the first element is smaller than the searched value
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -794,8 +834,10 @@ class LookupRef
|
||||
$lookupLower = StringHelper::strToLower($lookup_value);
|
||||
$rowDataLower = StringHelper::strToLower($rowData);
|
||||
|
||||
if (($bothNumeric && $rowData > $lookup_value) ||
|
||||
($bothNotNumeric && $rowDataLower > $lookupLower)) {
|
||||
if ($not_exact_match && (
|
||||
($bothNumeric && $rowData > $lookup_value) ||
|
||||
($bothNotNumeric && $rowDataLower > $lookupLower)
|
||||
)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,88 @@ class MathTrig
|
||||
return ($num - ($num % $n)) / $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* ARABIC.
|
||||
*
|
||||
* Converts a Roman numeral to an Arabic numeral.
|
||||
*
|
||||
* Excel Function:
|
||||
* ARABIC(text)
|
||||
*
|
||||
* @category Mathematical and Trigonometric Functions
|
||||
*
|
||||
* @param string $roman
|
||||
*
|
||||
* @return int|string the arabic numberal contrived from the roman numeral
|
||||
*/
|
||||
public static function ARABIC($roman)
|
||||
{
|
||||
// An empty string should return 0
|
||||
$roman = substr(trim(strtoupper((string) Functions::flattenSingleValue($roman))), 0, 255);
|
||||
if ($roman === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert the roman numeral to an arabic number
|
||||
$negativeNumber = $roman[0] === '-';
|
||||
if ($negativeNumber) {
|
||||
$roman = substr($roman, 1);
|
||||
}
|
||||
|
||||
try {
|
||||
$arabic = self::calculateArabic(str_split($roman));
|
||||
} catch (\Exception $e) {
|
||||
return Functions::VALUE(); // Invalid character detected
|
||||
}
|
||||
|
||||
if ($negativeNumber) {
|
||||
$arabic *= -1; // The number should be negative
|
||||
}
|
||||
|
||||
return $arabic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively calculate the arabic value of a roman numeral.
|
||||
*
|
||||
* @param array $roman
|
||||
* @param int $sum
|
||||
* @param int $subtract
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
|
||||
{
|
||||
$lookup = [
|
||||
'M' => 1000,
|
||||
'D' => 500,
|
||||
'C' => 100,
|
||||
'L' => 50,
|
||||
'X' => 10,
|
||||
'V' => 5,
|
||||
'I' => 1,
|
||||
];
|
||||
|
||||
$numeral = array_shift($roman);
|
||||
if (!isset($lookup[$numeral])) {
|
||||
throw new \Exception('Invalid character detected');
|
||||
}
|
||||
|
||||
$arabic = $lookup[$numeral];
|
||||
if (count($roman) > 0 && isset($lookup[$roman[0]]) && $arabic < $lookup[$roman[0]]) {
|
||||
$subtract += $arabic;
|
||||
} else {
|
||||
$sum += ($arabic - $subtract);
|
||||
$subtract = 0;
|
||||
}
|
||||
|
||||
if (count($roman) > 0) {
|
||||
self::calculateArabic($roman, $sum, $subtract);
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* ATAN2.
|
||||
*
|
||||
@ -59,7 +141,7 @@ class MathTrig
|
||||
* @param float $xCoordinate the x-coordinate of the point
|
||||
* @param float $yCoordinate the y-coordinate of the point
|
||||
*
|
||||
* @return float the inverse tangent of the specified x- and y-coordinates
|
||||
* @return float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
|
||||
*/
|
||||
public static function ATAN2($xCoordinate = null, $yCoordinate = null)
|
||||
{
|
||||
@ -84,6 +166,49 @@ class MathTrig
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* BASE.
|
||||
*
|
||||
* Converts a number into a text representation with the given radix (base).
|
||||
*
|
||||
* Excel Function:
|
||||
* BASE(Number, Radix [Min_length])
|
||||
*
|
||||
* @category Mathematical and Trigonometric Functions
|
||||
*
|
||||
* @param float $number
|
||||
* @param float $radix
|
||||
* @param int $minLength
|
||||
*
|
||||
* @return string the text representation with the given radix (base)
|
||||
*/
|
||||
public static function BASE($number, $radix, $minLength = null)
|
||||
{
|
||||
$number = Functions::flattenSingleValue($number);
|
||||
$radix = Functions::flattenSingleValue($radix);
|
||||
$minLength = Functions::flattenSingleValue($minLength);
|
||||
|
||||
if (is_numeric($number) && is_numeric($radix) && ($minLength === null || is_numeric($minLength))) {
|
||||
// Truncate to an integer
|
||||
$number = (int) $number;
|
||||
$radix = (int) $radix;
|
||||
$minLength = (int) $minLength;
|
||||
|
||||
if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
|
||||
return Functions::NAN(); // Numeric range constraints
|
||||
}
|
||||
|
||||
$outcome = strtoupper((string) base_convert($number, 10, $radix));
|
||||
if ($minLength !== null) {
|
||||
$outcome = str_pad($outcome, $minLength, '0', STR_PAD_LEFT); // String padding
|
||||
}
|
||||
|
||||
return $outcome;
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* CEILING.
|
||||
*
|
||||
@ -100,7 +225,7 @@ class MathTrig
|
||||
* @param float $number the number you want to round
|
||||
* @param float $significance the multiple to which you want to round
|
||||
*
|
||||
* @return float Rounded Number
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function CEILING($number, $significance = null)
|
||||
{
|
||||
@ -139,7 +264,7 @@ class MathTrig
|
||||
* @param int $numObjs Number of different objects
|
||||
* @param int $numInSet Number of objects in each combination
|
||||
*
|
||||
* @return int Number of combinations
|
||||
* @return int|string Number of combinations, or a string containing an error
|
||||
*/
|
||||
public static function COMBIN($numObjs, $numInSet)
|
||||
{
|
||||
@ -175,7 +300,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $number Number to round
|
||||
*
|
||||
* @return int Rounded Number
|
||||
* @return int|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function EVEN($number)
|
||||
{
|
||||
@ -209,7 +334,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $factVal Factorial Value
|
||||
*
|
||||
* @return int Factorial
|
||||
* @return int|string Factorial, or a string containing an error
|
||||
*/
|
||||
public static function FACT($factVal)
|
||||
{
|
||||
@ -220,10 +345,9 @@ class MathTrig
|
||||
return Functions::NAN();
|
||||
}
|
||||
$factLoop = floor($factVal);
|
||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
|
||||
if ($factVal > $factLoop) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
if ((Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) &&
|
||||
($factVal > $factLoop)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$factorial = 1;
|
||||
@ -249,7 +373,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $factVal Factorial Value
|
||||
*
|
||||
* @return int Double Factorial
|
||||
* @return int|string Double Factorial, or a string containing an error
|
||||
*/
|
||||
public static function FACTDOUBLE($factVal)
|
||||
{
|
||||
@ -285,7 +409,7 @@ class MathTrig
|
||||
* @param float $number Number to round
|
||||
* @param float $significance Significance
|
||||
*
|
||||
* @return float Rounded Number
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function FLOOR($number, $significance = null)
|
||||
{
|
||||
@ -302,7 +426,9 @@ class MathTrig
|
||||
return Functions::DIV0();
|
||||
} elseif ($number == 0.0) {
|
||||
return 0.0;
|
||||
} elseif (self::SIGN($number) == self::SIGN($significance)) {
|
||||
} elseif (self::SIGN($significance) == 1) {
|
||||
return floor($number / $significance) * $significance;
|
||||
} elseif (self::SIGN($number) == -1 && self::SIGN($significance) == -1) {
|
||||
return floor($number / $significance) * $significance;
|
||||
}
|
||||
|
||||
@ -312,6 +438,80 @@ class MathTrig
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* FLOOR.MATH.
|
||||
*
|
||||
* Round a number down to the nearest integer or to the nearest multiple of significance.
|
||||
*
|
||||
* Excel Function:
|
||||
* FLOOR.MATH(number[,significance[,mode]])
|
||||
*
|
||||
* @category Mathematical and Trigonometric Functions
|
||||
*
|
||||
* @param float $number Number to round
|
||||
* @param float $significance Significance
|
||||
* @param int $mode direction to round negative numbers
|
||||
*
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function FLOORMATH($number, $significance = null, $mode = 0)
|
||||
{
|
||||
$number = Functions::flattenSingleValue($number);
|
||||
$significance = Functions::flattenSingleValue($significance);
|
||||
$mode = Functions::flattenSingleValue($mode);
|
||||
|
||||
if (is_numeric($number) && $significance === null) {
|
||||
$significance = $number / abs($number);
|
||||
}
|
||||
|
||||
if (is_numeric($number) && is_numeric($significance) && is_numeric($mode)) {
|
||||
if ($significance == 0.0) {
|
||||
return Functions::DIV0();
|
||||
} elseif ($number == 0.0) {
|
||||
return 0.0;
|
||||
} elseif (self::SIGN($significance) == -1 || (self::SIGN($number) == -1 && !empty($mode))) {
|
||||
return ceil($number / $significance) * $significance;
|
||||
}
|
||||
|
||||
return floor($number / $significance) * $significance;
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* FLOOR.PRECISE.
|
||||
*
|
||||
* Rounds number down, toward zero, to the nearest multiple of significance.
|
||||
*
|
||||
* Excel Function:
|
||||
* FLOOR.PRECISE(number[,significance])
|
||||
*
|
||||
* @category Mathematical and Trigonometric Functions
|
||||
*
|
||||
* @param float $number Number to round
|
||||
* @param float $significance Significance
|
||||
*
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function FLOORPRECISE($number, $significance = 1)
|
||||
{
|
||||
$number = Functions::flattenSingleValue($number);
|
||||
$significance = Functions::flattenSingleValue($significance);
|
||||
|
||||
if ((is_numeric($number)) && (is_numeric($significance))) {
|
||||
if ($significance == 0.0) {
|
||||
return Functions::DIV0();
|
||||
} elseif ($number == 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return floor($number / abs($significance)) * abs($significance);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
private static function evaluateGCD($a, $b)
|
||||
{
|
||||
return $b ? self::evaluateGCD($b, $a % $b) : $a;
|
||||
@ -331,7 +531,7 @@ class MathTrig
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return int Greatest Common Divisor
|
||||
* @return int|mixed|string Greatest Common Divisor, or a string containing an error
|
||||
*/
|
||||
public static function GCD(...$args)
|
||||
{
|
||||
@ -365,7 +565,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $number Number to cast to an integer
|
||||
*
|
||||
* @return int Integer value
|
||||
* @return int|string Integer value, or a string containing an error
|
||||
*/
|
||||
public static function INT($number)
|
||||
{
|
||||
@ -398,7 +598,7 @@ class MathTrig
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return int Lowest Common Multiplier
|
||||
* @return int|string Lowest Common Multiplier, or a string containing an error
|
||||
*/
|
||||
public static function LCM(...$args)
|
||||
{
|
||||
@ -450,7 +650,7 @@ class MathTrig
|
||||
* @param float $number The positive real number for which you want the logarithm
|
||||
* @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
|
||||
*
|
||||
* @return float
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function logBase($number = null, $base = 10)
|
||||
{
|
||||
@ -479,7 +679,7 @@ class MathTrig
|
||||
*
|
||||
* @param array $matrixValues A matrix of values
|
||||
*
|
||||
* @return float
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function MDETERM($matrixValues)
|
||||
{
|
||||
@ -531,7 +731,7 @@ class MathTrig
|
||||
*
|
||||
* @param array $matrixValues A matrix of values
|
||||
*
|
||||
* @return array
|
||||
* @return array|string The result, or a string containing an error
|
||||
*/
|
||||
public static function MINVERSE($matrixValues)
|
||||
{
|
||||
@ -581,7 +781,7 @@ class MathTrig
|
||||
* @param array $matrixData1 A matrix of values
|
||||
* @param array $matrixData2 A matrix of values
|
||||
*
|
||||
* @return array
|
||||
* @return array|string The result, or a string containing an error
|
||||
*/
|
||||
public static function MMULT($matrixData1, $matrixData2)
|
||||
{
|
||||
@ -643,7 +843,7 @@ class MathTrig
|
||||
* @param int $a Dividend
|
||||
* @param int $b Divisor
|
||||
*
|
||||
* @return int Remainder
|
||||
* @return int|string Remainder, or a string containing an error
|
||||
*/
|
||||
public static function MOD($a = 1, $b = 1)
|
||||
{
|
||||
@ -669,7 +869,7 @@ class MathTrig
|
||||
* @param float $number Number to round
|
||||
* @param int $multiple Multiple to which you want to round $number
|
||||
*
|
||||
* @return float Rounded Number
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function MROUND($number, $multiple)
|
||||
{
|
||||
@ -699,7 +899,7 @@ class MathTrig
|
||||
*
|
||||
* @param array of mixed Data Series
|
||||
*
|
||||
* @return float
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function MULTINOMIAL(...$args)
|
||||
{
|
||||
@ -736,7 +936,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $number Number to round
|
||||
*
|
||||
* @return int Rounded Number
|
||||
* @return int|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function ODD($number)
|
||||
{
|
||||
@ -771,7 +971,7 @@ class MathTrig
|
||||
* @param float $x
|
||||
* @param float $y
|
||||
*
|
||||
* @return float
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function POWER($x = 0, $y = 2)
|
||||
{
|
||||
@ -930,7 +1130,7 @@ class MathTrig
|
||||
* @param float $number Number to round
|
||||
* @param int $digits Number of digits to which you want to round $number
|
||||
*
|
||||
* @return float Rounded Number
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function ROUNDUP($number, $digits)
|
||||
{
|
||||
@ -938,12 +1138,11 @@ class MathTrig
|
||||
$digits = Functions::flattenSingleValue($digits);
|
||||
|
||||
if ((is_numeric($number)) && (is_numeric($digits))) {
|
||||
$significance = pow(10, (int) $digits);
|
||||
if ($number < 0.0) {
|
||||
return floor($number * $significance) / $significance;
|
||||
return round($number - 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_DOWN);
|
||||
}
|
||||
|
||||
return ceil($number * $significance) / $significance;
|
||||
return round($number + 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_DOWN);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
@ -957,7 +1156,7 @@ class MathTrig
|
||||
* @param float $number Number to round
|
||||
* @param int $digits Number of digits to which you want to round $number
|
||||
*
|
||||
* @return float Rounded Number
|
||||
* @return float|string Rounded Number, or a string containing an error
|
||||
*/
|
||||
public static function ROUNDDOWN($number, $digits)
|
||||
{
|
||||
@ -965,12 +1164,11 @@ class MathTrig
|
||||
$digits = Functions::flattenSingleValue($digits);
|
||||
|
||||
if ((is_numeric($number)) && (is_numeric($digits))) {
|
||||
$significance = pow(10, (int) $digits);
|
||||
if ($number < 0.0) {
|
||||
return ceil($number * $significance) / $significance;
|
||||
return round($number + 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
return floor($number * $significance) / $significance;
|
||||
return round($number - 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
@ -986,7 +1184,7 @@ class MathTrig
|
||||
* @param float $m Step by which to increase $n for each term in the series
|
||||
* @param array of mixed Data Series
|
||||
*
|
||||
* @return float
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function SERIESSUM(...$args)
|
||||
{
|
||||
@ -1025,7 +1223,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $number Number to round
|
||||
*
|
||||
* @return int sign value
|
||||
* @return int|string sign value, or a string containing an error
|
||||
*/
|
||||
public static function SIGN($number)
|
||||
{
|
||||
@ -1052,7 +1250,7 @@ class MathTrig
|
||||
*
|
||||
* @param float $number Number
|
||||
*
|
||||
* @return float Square Root of Number * Pi
|
||||
* @return float|string Square Root of Number * Pi, or a string containing an error
|
||||
*/
|
||||
public static function SQRTPI($number)
|
||||
{
|
||||
@ -1074,7 +1272,7 @@ class MathTrig
|
||||
return array_filter(
|
||||
$args,
|
||||
function ($index) use ($cellReference) {
|
||||
list(, $row, $column) = explode('.', $index);
|
||||
[, $row, $column] = explode('.', $index);
|
||||
|
||||
return $cellReference->getWorksheet()->getRowDimension($row)->getVisible() &&
|
||||
$cellReference->getWorksheet()->getColumnDimension($column)->getVisible();
|
||||
@ -1088,7 +1286,7 @@ class MathTrig
|
||||
return array_filter(
|
||||
$args,
|
||||
function ($index) use ($cellReference) {
|
||||
list(, $row, $column) = explode('.', $index);
|
||||
[, $row, $column] = explode('.', $index);
|
||||
if ($cellReference->getWorksheet()->cellExists($column . $row)) {
|
||||
//take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
|
||||
$isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
|
||||
@ -1116,7 +1314,7 @@ class MathTrig
|
||||
* in hidden rows or columns
|
||||
* @param array of mixed Data Series
|
||||
*
|
||||
* @return float
|
||||
* @return float|string
|
||||
*/
|
||||
public static function SUBTOTAL(...$args)
|
||||
{
|
||||
@ -1306,7 +1504,7 @@ class MathTrig
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function SUMPRODUCT(...$args)
|
||||
{
|
||||
@ -1451,7 +1649,7 @@ class MathTrig
|
||||
* @param float $value
|
||||
* @param int $digits
|
||||
*
|
||||
* @return float Truncated value
|
||||
* @return float|string Truncated value, or a string containing an error
|
||||
*/
|
||||
public static function TRUNC($value = 0, $digits = 0)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,7 @@ class TextData
|
||||
return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
|
||||
}
|
||||
|
||||
if (self::$invalidChars == null) {
|
||||
if (self::$invalidChars === null) {
|
||||
self::$invalidChars = range(chr(0), chr(31));
|
||||
}
|
||||
|
||||
@ -84,6 +84,15 @@ class TextData
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function convertBooleanValue($value)
|
||||
{
|
||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
|
||||
}
|
||||
|
||||
/**
|
||||
* ASCIICODE.
|
||||
*
|
||||
@ -98,11 +107,7 @@ class TextData
|
||||
}
|
||||
$characters = Functions::flattenSingleValue($characters);
|
||||
if (is_bool($characters)) {
|
||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
|
||||
$characters = (int) $characters;
|
||||
} else {
|
||||
$characters = ($characters) ? Calculation::getTRUE() : Calculation::getFALSE();
|
||||
}
|
||||
$characters = self::convertBooleanValue($characters);
|
||||
}
|
||||
|
||||
$character = $characters;
|
||||
@ -126,11 +131,7 @@ class TextData
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
foreach ($aArgs as $arg) {
|
||||
if (is_bool($arg)) {
|
||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
|
||||
$arg = (int) $arg;
|
||||
} else {
|
||||
$arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
|
||||
}
|
||||
$arg = self::convertBooleanValue($arg);
|
||||
}
|
||||
$returnValue .= $arg;
|
||||
}
|
||||
@ -197,7 +198,7 @@ class TextData
|
||||
}
|
||||
|
||||
if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
|
||||
if (StringHelper::countCharacters($needle) == 0) {
|
||||
if (StringHelper::countCharacters($needle) === 0) {
|
||||
return $offset;
|
||||
}
|
||||
|
||||
@ -232,7 +233,7 @@ class TextData
|
||||
}
|
||||
|
||||
if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
|
||||
if (StringHelper::countCharacters($needle) == 0) {
|
||||
if (StringHelper::countCharacters($needle) === 0) {
|
||||
return $offset;
|
||||
}
|
||||
|
||||
@ -265,14 +266,19 @@ class TextData
|
||||
if (!is_numeric($value) || !is_numeric($decimals)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$decimals = floor($decimals);
|
||||
$decimals = (int) floor($decimals);
|
||||
|
||||
$valueResult = round($value, $decimals);
|
||||
if ($decimals < 0) {
|
||||
$decimals = 0;
|
||||
}
|
||||
if (!$no_commas) {
|
||||
$valueResult = number_format($valueResult, $decimals);
|
||||
$valueResult = number_format(
|
||||
$valueResult,
|
||||
$decimals,
|
||||
StringHelper::getDecimalSeparator(),
|
||||
StringHelper::getThousandsSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
return (string) $valueResult;
|
||||
@ -659,11 +665,7 @@ class TextData
|
||||
if ($ignoreEmpty && trim($arg) == '') {
|
||||
unset($aArgs[$key]);
|
||||
} elseif (is_bool($arg)) {
|
||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
|
||||
$arg = (int) $arg;
|
||||
} else {
|
||||
$arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
|
||||
}
|
||||
$arg = self::convertBooleanValue($arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,14 +36,24 @@ class Stack
|
||||
* @param mixed $type
|
||||
* @param mixed $value
|
||||
* @param mixed $reference
|
||||
* @param null|string $storeKey will store the result under this alias
|
||||
* @param null|string $onlyIf will only run computation if the matching
|
||||
* store key is true
|
||||
* @param null|string $onlyIfNot will only run computation if the matching
|
||||
* store key is false
|
||||
*/
|
||||
public function push($type, $value, $reference = null)
|
||||
{
|
||||
$this->stack[$this->count++] = [
|
||||
'type' => $type,
|
||||
'value' => $value,
|
||||
'reference' => $reference,
|
||||
];
|
||||
public function push(
|
||||
$type,
|
||||
$value,
|
||||
$reference = null,
|
||||
$storeKey = null,
|
||||
$onlyIf = null,
|
||||
$onlyIfNot = null
|
||||
) {
|
||||
$stackItem = $this->getStackItem($type, $value, $reference, $storeKey, $onlyIf, $onlyIfNot);
|
||||
|
||||
$this->stack[$this->count++] = $stackItem;
|
||||
|
||||
if ($type == 'Function') {
|
||||
$localeFunction = Calculation::localeFunc($value);
|
||||
if ($localeFunction != $value) {
|
||||
@ -52,6 +62,35 @@ class Stack
|
||||
}
|
||||
}
|
||||
|
||||
public function getStackItem(
|
||||
$type,
|
||||
$value,
|
||||
$reference = null,
|
||||
$storeKey = null,
|
||||
$onlyIf = null,
|
||||
$onlyIfNot = null
|
||||
) {
|
||||
$stackItem = [
|
||||
'type' => $type,
|
||||
'value' => $value,
|
||||
'reference' => $reference,
|
||||
];
|
||||
|
||||
if (isset($storeKey)) {
|
||||
$stackItem['storeKey'] = $storeKey;
|
||||
}
|
||||
|
||||
if (isset($onlyIf)) {
|
||||
$stackItem['onlyIf'] = $onlyIf;
|
||||
}
|
||||
|
||||
if (isset($onlyIfNot)) {
|
||||
$stackItem['onlyIfNot'] = $onlyIfNot;
|
||||
}
|
||||
|
||||
return $stackItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the last entry from the stack.
|
||||
*
|
||||
@ -90,4 +129,21 @@ class Stack
|
||||
$this->stack = [];
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$str = 'Stack: ';
|
||||
foreach ($this->stack as $index => $item) {
|
||||
if ($index > $this->count - 1) {
|
||||
break;
|
||||
}
|
||||
$value = $item['value'] ?? 'no value';
|
||||
while (is_array($value)) {
|
||||
$value = array_pop($value);
|
||||
}
|
||||
$str .= $value . ' |> ';
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ ADDRESS
|
||||
AMORDEGRC
|
||||
AMORLINC
|
||||
AND
|
||||
ARABIC
|
||||
AREAS
|
||||
ASC
|
||||
ASIN
|
||||
@ -22,6 +23,7 @@ AVERAGEA
|
||||
AVERAGEIF
|
||||
AVERAGEIFS
|
||||
BAHTTEXT
|
||||
BASE
|
||||
BESSELI
|
||||
BESSELJ
|
||||
BESSELK
|
||||
@ -137,6 +139,8 @@ FISHER
|
||||
FISHERINV
|
||||
FIXED
|
||||
FLOOR
|
||||
FLOOR.MATH
|
||||
FLOOR.PRECISE
|
||||
FORECAST
|
||||
FREQUENCY
|
||||
FTEST
|
||||
@ -224,6 +228,7 @@ LOWER
|
||||
MATCH
|
||||
MAX
|
||||
MAXA
|
||||
MAXIFS
|
||||
MDETERM
|
||||
MDURATION
|
||||
MEDIAN
|
||||
@ -231,6 +236,7 @@ MID
|
||||
MIDB
|
||||
MIN
|
||||
MINA
|
||||
MINIFS
|
||||
MINUTE
|
||||
MINVERSE
|
||||
MIRR
|
||||
|
||||
@ -118,7 +118,7 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
|
||||
// Check for time without seconds e.g. '9:45', '09:45'
|
||||
if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
|
||||
// Convert value to number
|
||||
list($h, $m) = explode(':', $value);
|
||||
[$h, $m] = explode(':', $value);
|
||||
$days = $h / 24 + $m / 1440;
|
||||
$cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
|
||||
// Set style
|
||||
@ -131,7 +131,7 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
|
||||
// Check for time with seconds '9:45:59', '09:45:59'
|
||||
if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
|
||||
// Convert value to number
|
||||
list($h, $m, $s) = explode(':', $value);
|
||||
[$h, $m, $s] = explode(':', $value);
|
||||
$days = $h / 24 + $m / 1440 + $s / 86400;
|
||||
// Convert value to number
|
||||
$cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
|
||||
|
||||
@ -67,7 +67,7 @@ class Cell
|
||||
/**
|
||||
* Update the cell into the cell collection.
|
||||
*
|
||||
* @return self
|
||||
* @return $this
|
||||
*/
|
||||
public function updateInCollection()
|
||||
{
|
||||
@ -177,7 +177,7 @@ class Cell
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return Cell
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($pValue)
|
||||
{
|
||||
@ -217,7 +217,10 @@ class Cell
|
||||
|
||||
break;
|
||||
case DataType::TYPE_NUMERIC:
|
||||
$this->value = (float) $pValue;
|
||||
if (is_string($pValue) && !is_numeric($pValue)) {
|
||||
throw new Exception('Invalid numeric value for datatype Numeric');
|
||||
}
|
||||
$this->value = 0 + $pValue;
|
||||
|
||||
break;
|
||||
case DataType::TYPE_FORMULA:
|
||||
@ -263,7 +266,7 @@ class Cell
|
||||
// We don't yet handle array returns
|
||||
if (is_array($result)) {
|
||||
while (is_array($result)) {
|
||||
$result = array_pop($result);
|
||||
$result = array_shift($result);
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
@ -511,7 +514,7 @@ class Cell
|
||||
{
|
||||
if ($mergeRange = $this->getMergeRange()) {
|
||||
$mergeRange = Coordinate::splitRange($mergeRange);
|
||||
list($startCell) = $mergeRange[0];
|
||||
[$startCell] = $mergeRange[0];
|
||||
if ($this->getCoordinate() === $startCell) {
|
||||
return true;
|
||||
}
|
||||
@ -569,7 +572,7 @@ class Cell
|
||||
*/
|
||||
public function isInRange($pRange)
|
||||
{
|
||||
list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange);
|
||||
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange);
|
||||
|
||||
// Translate properties
|
||||
$myColumn = Coordinate::columnIndexFromString($this->getColumn());
|
||||
@ -669,7 +672,7 @@ class Cell
|
||||
*
|
||||
* @param mixed $pAttributes
|
||||
*
|
||||
* @return Cell
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormulaAttributes($pAttributes)
|
||||
{
|
||||
|
||||
@ -71,7 +71,7 @@ abstract class Coordinate
|
||||
}
|
||||
|
||||
// Split out any worksheet name from the reference
|
||||
list($worksheet, $pCoordinateString) = Worksheet::extractSheetTitle($pCoordinateString, true);
|
||||
[$worksheet, $pCoordinateString] = Worksheet::extractSheetTitle($pCoordinateString, true);
|
||||
if ($worksheet > '') {
|
||||
$worksheet .= '!';
|
||||
}
|
||||
@ -102,13 +102,13 @@ abstract class Coordinate
|
||||
}
|
||||
|
||||
// Split out any worksheet name from the coordinate
|
||||
list($worksheet, $pCoordinateString) = Worksheet::extractSheetTitle($pCoordinateString, true);
|
||||
[$worksheet, $pCoordinateString] = Worksheet::extractSheetTitle($pCoordinateString, true);
|
||||
if ($worksheet > '') {
|
||||
$worksheet .= '!';
|
||||
}
|
||||
|
||||
// Create absolute coordinate
|
||||
list($column, $row) = self::coordinateFromString($pCoordinateString);
|
||||
[$column, $row] = self::coordinateFromString($pCoordinateString);
|
||||
$column = ltrim($column, '$');
|
||||
$row = ltrim($row, '$');
|
||||
|
||||
@ -187,7 +187,7 @@ abstract class Coordinate
|
||||
if (strpos($pRange, ':') === false) {
|
||||
$rangeA = $rangeB = $pRange;
|
||||
} else {
|
||||
list($rangeA, $rangeB) = explode(':', $pRange);
|
||||
[$rangeA, $rangeB] = explode(':', $pRange);
|
||||
}
|
||||
|
||||
// Calculate range outer borders
|
||||
@ -211,7 +211,7 @@ abstract class Coordinate
|
||||
public static function rangeDimension($pRange)
|
||||
{
|
||||
// Calculate range outer borders
|
||||
list($rangeStart, $rangeEnd) = self::rangeBoundaries($pRange);
|
||||
[$rangeStart, $rangeEnd] = self::rangeBoundaries($pRange);
|
||||
|
||||
return [($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1)];
|
||||
}
|
||||
@ -238,7 +238,7 @@ abstract class Coordinate
|
||||
if (strpos($pRange, ':') === false) {
|
||||
$rangeA = $rangeB = $pRange;
|
||||
} else {
|
||||
list($rangeA, $rangeB) = explode(':', $pRange);
|
||||
[$rangeA, $rangeB] = explode(':', $pRange);
|
||||
}
|
||||
|
||||
return [self::coordinateFromString($rangeA), self::coordinateFromString($rangeB)];
|
||||
@ -376,9 +376,9 @@ abstract class Coordinate
|
||||
}
|
||||
|
||||
// Range...
|
||||
list($rangeStart, $rangeEnd) = $range;
|
||||
list($startColumn, $startRow) = self::coordinateFromString($rangeStart);
|
||||
list($endColumn, $endRow) = self::coordinateFromString($rangeEnd);
|
||||
[$rangeStart, $rangeEnd] = $range;
|
||||
[$startColumn, $startRow] = self::coordinateFromString($rangeStart);
|
||||
[$endColumn, $endRow] = self::coordinateFromString($rangeEnd);
|
||||
$startColumnIndex = self::columnIndexFromString($startColumn);
|
||||
$endColumnIndex = self::columnIndexFromString($endColumn);
|
||||
++$endColumnIndex;
|
||||
@ -432,7 +432,7 @@ abstract class Coordinate
|
||||
continue;
|
||||
}
|
||||
|
||||
list($column, $row) = self::coordinateFromString($coord);
|
||||
[$column, $row] = self::coordinateFromString($coord);
|
||||
$row = (int) (ltrim($row, '$'));
|
||||
$hashCode = $column . '-' . (is_object($value) ? $value->getHashCode() : $value);
|
||||
|
||||
|
||||
@ -142,7 +142,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormula1($value)
|
||||
{
|
||||
@ -166,7 +166,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormula2($value)
|
||||
{
|
||||
@ -190,7 +190,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($value)
|
||||
{
|
||||
@ -214,7 +214,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value see self::STYLE_*
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setErrorStyle($value)
|
||||
{
|
||||
@ -238,7 +238,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setOperator($value)
|
||||
{
|
||||
@ -262,7 +262,7 @@ class DataValidation
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setAllowBlank($value)
|
||||
{
|
||||
@ -286,7 +286,7 @@ class DataValidation
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowDropDown($value)
|
||||
{
|
||||
@ -310,7 +310,7 @@ class DataValidation
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowInputMessage($value)
|
||||
{
|
||||
@ -334,7 +334,7 @@ class DataValidation
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowErrorMessage($value)
|
||||
{
|
||||
@ -358,7 +358,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setErrorTitle($value)
|
||||
{
|
||||
@ -382,7 +382,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setError($value)
|
||||
{
|
||||
@ -406,7 +406,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setPromptTitle($value)
|
||||
{
|
||||
@ -430,7 +430,7 @@ class DataValidation
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return DataValidation
|
||||
* @return $this
|
||||
*/
|
||||
public function setPrompt($value)
|
||||
{
|
||||
|
||||
@ -51,16 +51,16 @@ class DefaultValueBinder implements IValueBinder
|
||||
// Match the value against a few data types
|
||||
if ($pValue === null) {
|
||||
return DataType::TYPE_NULL;
|
||||
} elseif (is_float($pValue) || is_int($pValue)) {
|
||||
return DataType::TYPE_NUMERIC;
|
||||
} elseif (is_bool($pValue)) {
|
||||
return DataType::TYPE_BOOL;
|
||||
} elseif ($pValue === '') {
|
||||
return DataType::TYPE_STRING;
|
||||
} elseif ($pValue instanceof RichText) {
|
||||
return DataType::TYPE_INLINE;
|
||||
} elseif ($pValue[0] === '=' && strlen($pValue) > 1) {
|
||||
} elseif (is_string($pValue) && $pValue[0] === '=' && strlen($pValue) > 1) {
|
||||
return DataType::TYPE_FORMULA;
|
||||
} elseif (is_bool($pValue)) {
|
||||
return DataType::TYPE_BOOL;
|
||||
} elseif (is_float($pValue) || is_int($pValue)) {
|
||||
return DataType::TYPE_NUMERIC;
|
||||
} elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $pValue)) {
|
||||
$tValue = ltrim($pValue, '+-');
|
||||
if (is_string($pValue) && $tValue[0] === '0' && strlen($tValue) > 1 && $tValue[1] !== '.') {
|
||||
|
||||
@ -46,7 +46,7 @@ class Hyperlink
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Hyperlink
|
||||
* @return $this
|
||||
*/
|
||||
public function setUrl($value)
|
||||
{
|
||||
@ -70,7 +70,7 @@ class Hyperlink
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Hyperlink
|
||||
* @return $this
|
||||
*/
|
||||
public function setTooltip($value)
|
||||
{
|
||||
|
||||
@ -354,7 +354,7 @@ class Axis extends Properties
|
||||
*
|
||||
* @param int $shadow_presets
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowPresetsProperties($shadow_presets)
|
||||
{
|
||||
@ -370,7 +370,7 @@ class Axis extends Properties
|
||||
* @param array $properties_map
|
||||
* @param mixed &$reference
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowProperiesMapValues(array $properties_map, &$reference = null)
|
||||
{
|
||||
@ -402,7 +402,7 @@ class Axis extends Properties
|
||||
* @param int $alpha
|
||||
* @param string $type
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowColor($color, $alpha, $type)
|
||||
{
|
||||
@ -416,7 +416,7 @@ class Axis extends Properties
|
||||
*
|
||||
* @param float $blur
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowBlur($blur)
|
||||
{
|
||||
@ -432,7 +432,7 @@ class Axis extends Properties
|
||||
*
|
||||
* @param int $angle
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowAngle($angle)
|
||||
{
|
||||
@ -448,7 +448,7 @@ class Axis extends Properties
|
||||
*
|
||||
* @param float $distance
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowDistance($distance)
|
||||
{
|
||||
@ -506,7 +506,7 @@ class Axis extends Properties
|
||||
*
|
||||
* @param float $size
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setGlowSize($size)
|
||||
{
|
||||
@ -524,7 +524,7 @@ class Axis extends Properties
|
||||
* @param int $alpha
|
||||
* @param string $type
|
||||
*
|
||||
* @return Axis
|
||||
* @return $this
|
||||
*/
|
||||
private function setGlowColor($color, $alpha, $type)
|
||||
{
|
||||
|
||||
@ -156,7 +156,7 @@ class Chart
|
||||
* @param null|GridLines $majorGridlines
|
||||
* @param null|GridLines $minorGridlines
|
||||
*/
|
||||
public function __construct($name, Title $title = null, Legend $legend = null, PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = '0', Title $xAxisLabel = null, Title $yAxisLabel = null, Axis $xAxis = null, Axis $yAxis = null, GridLines $majorGridlines = null, GridLines $minorGridlines = null)
|
||||
public function __construct($name, Title $title = null, Legend $legend = null, PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = 'gap', Title $xAxisLabel = null, Title $yAxisLabel = null, Axis $xAxis = null, Axis $yAxis = null, GridLines $majorGridlines = null, GridLines $minorGridlines = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->title = $title;
|
||||
@ -197,7 +197,7 @@ class Chart
|
||||
*
|
||||
* @param Worksheet $pValue
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setWorksheet(Worksheet $pValue = null)
|
||||
{
|
||||
@ -221,7 +221,7 @@ class Chart
|
||||
*
|
||||
* @param Title $title
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle(Title $title)
|
||||
{
|
||||
@ -245,7 +245,7 @@ class Chart
|
||||
*
|
||||
* @param Legend $legend
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setLegend(Legend $legend)
|
||||
{
|
||||
@ -269,7 +269,7 @@ class Chart
|
||||
*
|
||||
* @param Title $label
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setXAxisLabel(Title $label)
|
||||
{
|
||||
@ -293,7 +293,7 @@ class Chart
|
||||
*
|
||||
* @param Title $label
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setYAxisLabel(Title $label)
|
||||
{
|
||||
@ -327,7 +327,7 @@ class Chart
|
||||
*
|
||||
* @param bool $plotVisibleOnly
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlotVisibleOnly($plotVisibleOnly)
|
||||
{
|
||||
@ -351,7 +351,7 @@ class Chart
|
||||
*
|
||||
* @param string $displayBlanksAs
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setDisplayBlanksAs($displayBlanksAs)
|
||||
{
|
||||
@ -423,7 +423,7 @@ class Chart
|
||||
* @param int $xOffset
|
||||
* @param int $yOffset
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setTopLeftPosition($cell, $xOffset = null, $yOffset = null)
|
||||
{
|
||||
@ -467,7 +467,7 @@ class Chart
|
||||
*
|
||||
* @param string $cell
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setTopLeftCell($cell)
|
||||
{
|
||||
@ -482,7 +482,7 @@ class Chart
|
||||
* @param int $xOffset
|
||||
* @param int $yOffset
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setTopLeftOffset($xOffset, $yOffset)
|
||||
{
|
||||
@ -541,7 +541,7 @@ class Chart
|
||||
* @param int $xOffset
|
||||
* @param int $yOffset
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setBottomRightPosition($cell, $xOffset = null, $yOffset = null)
|
||||
{
|
||||
@ -593,7 +593,7 @@ class Chart
|
||||
* @param int $xOffset
|
||||
* @param int $yOffset
|
||||
*
|
||||
* @return Chart
|
||||
* @return $this
|
||||
*/
|
||||
public function setBottomRightOffset($xOffset, $yOffset)
|
||||
{
|
||||
|
||||
@ -157,7 +157,7 @@ class DataSeries
|
||||
*
|
||||
* @param string $plotType
|
||||
*
|
||||
* @return DataSeries
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlotType($plotType)
|
||||
{
|
||||
@ -181,7 +181,7 @@ class DataSeries
|
||||
*
|
||||
* @param string $groupingType
|
||||
*
|
||||
* @return DataSeries
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlotGrouping($groupingType)
|
||||
{
|
||||
@ -205,7 +205,7 @@ class DataSeries
|
||||
*
|
||||
* @param string $plotDirection
|
||||
*
|
||||
* @return DataSeries
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlotDirection($plotDirection)
|
||||
{
|
||||
@ -297,7 +297,7 @@ class DataSeries
|
||||
*
|
||||
* @param null|string $plotStyle
|
||||
*
|
||||
* @return DataSeries
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlotStyle($plotStyle)
|
||||
{
|
||||
@ -360,7 +360,7 @@ class DataSeries
|
||||
*
|
||||
* @param bool $smoothLine
|
||||
*
|
||||
* @return DataSeries
|
||||
* @return $this
|
||||
*/
|
||||
public function setSmoothLine($smoothLine)
|
||||
{
|
||||
|
||||
@ -117,7 +117,7 @@ class DataSeriesValues
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return DataSeriesValues
|
||||
* @return $this
|
||||
*/
|
||||
public function setDataType($dataType)
|
||||
{
|
||||
@ -144,7 +144,7 @@ class DataSeriesValues
|
||||
*
|
||||
* @param string $dataSource
|
||||
*
|
||||
* @return DataSeriesValues
|
||||
* @return $this
|
||||
*/
|
||||
public function setDataSource($dataSource)
|
||||
{
|
||||
@ -168,7 +168,7 @@ class DataSeriesValues
|
||||
*
|
||||
* @param string $marker
|
||||
*
|
||||
* @return DataSeriesValues
|
||||
* @return $this
|
||||
*/
|
||||
public function setPointMarker($marker)
|
||||
{
|
||||
@ -192,7 +192,7 @@ class DataSeriesValues
|
||||
*
|
||||
* @param string $formatCode
|
||||
*
|
||||
* @return DataSeriesValues
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormatCode($formatCode)
|
||||
{
|
||||
@ -275,7 +275,7 @@ class DataSeriesValues
|
||||
*
|
||||
* @param int $width
|
||||
*
|
||||
* @return DataSeriesValues
|
||||
* @return $this
|
||||
*/
|
||||
public function setLineWidth($width)
|
||||
{
|
||||
@ -346,7 +346,7 @@ class DataSeriesValues
|
||||
*
|
||||
* @param array $dataValues
|
||||
*
|
||||
* @return DataSeriesValues
|
||||
* @return $this
|
||||
*/
|
||||
public function setDataValues($dataValues)
|
||||
{
|
||||
@ -370,13 +370,13 @@ class DataSeriesValues
|
||||
if ($flatten) {
|
||||
$this->dataValues = Functions::flattenArray($newDataValues);
|
||||
foreach ($this->dataValues as &$dataValue) {
|
||||
if ((!empty($dataValue)) && ($dataValue[0] == '#')) {
|
||||
if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
|
||||
$dataValue = 0.0;
|
||||
}
|
||||
}
|
||||
unset($dataValue);
|
||||
} else {
|
||||
list($worksheet, $cellRange) = Worksheet::extractSheetTitle($this->dataSource, true);
|
||||
[$worksheet, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
|
||||
$dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange));
|
||||
if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
|
||||
$this->dataValues = Functions::flattenArray($newDataValues);
|
||||
|
||||
@ -91,7 +91,7 @@ class GridLines extends Properties
|
||||
/**
|
||||
* Change Object State to True.
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function activateObject()
|
||||
{
|
||||
@ -229,7 +229,7 @@ class GridLines extends Properties
|
||||
*
|
||||
* @param float $size
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setGlowSize($size)
|
||||
{
|
||||
@ -245,7 +245,7 @@ class GridLines extends Properties
|
||||
* @param int $alpha
|
||||
* @param string $type
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setGlowColor($color, $alpha, $type)
|
||||
{
|
||||
@ -305,7 +305,7 @@ class GridLines extends Properties
|
||||
*
|
||||
* @param int $shadow_presets
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowPresetsProperties($shadow_presets)
|
||||
{
|
||||
@ -321,7 +321,7 @@ class GridLines extends Properties
|
||||
* @param array $properties_map
|
||||
* @param mixed &$reference
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowProperiesMapValues(array $properties_map, &$reference = null)
|
||||
{
|
||||
@ -353,7 +353,7 @@ class GridLines extends Properties
|
||||
* @param int $alpha
|
||||
* @param string $type
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowColor($color, $alpha, $type)
|
||||
{
|
||||
@ -375,7 +375,7 @@ class GridLines extends Properties
|
||||
*
|
||||
* @param float $blur
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowBlur($blur)
|
||||
{
|
||||
@ -391,7 +391,7 @@ class GridLines extends Properties
|
||||
*
|
||||
* @param int $angle
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowAngle($angle)
|
||||
{
|
||||
@ -407,7 +407,7 @@ class GridLines extends Properties
|
||||
*
|
||||
* @param float $distance
|
||||
*
|
||||
* @return GridLines
|
||||
* @return $this
|
||||
*/
|
||||
private function setShadowDistance($distance)
|
||||
{
|
||||
|
||||
@ -153,7 +153,7 @@ class Layout
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setLayoutTarget($value)
|
||||
{
|
||||
@ -177,7 +177,7 @@ class Layout
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setXMode($value)
|
||||
{
|
||||
@ -201,7 +201,7 @@ class Layout
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setYMode($value)
|
||||
{
|
||||
@ -225,7 +225,7 @@ class Layout
|
||||
*
|
||||
* @param float $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setXPosition($value)
|
||||
{
|
||||
@ -249,7 +249,7 @@ class Layout
|
||||
*
|
||||
* @param float $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setYPosition($value)
|
||||
{
|
||||
@ -273,7 +273,7 @@ class Layout
|
||||
*
|
||||
* @param float $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidth($value)
|
||||
{
|
||||
@ -297,7 +297,7 @@ class Layout
|
||||
*
|
||||
* @param float $value
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeight($value)
|
||||
{
|
||||
@ -322,7 +322,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show legend key
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowLegendKey($value)
|
||||
{
|
||||
@ -347,7 +347,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show val
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowVal($value)
|
||||
{
|
||||
@ -372,7 +372,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show cat name
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowCatName($value)
|
||||
{
|
||||
@ -397,7 +397,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show series name
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowSerName($value)
|
||||
{
|
||||
@ -422,7 +422,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show percentage
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowPercent($value)
|
||||
{
|
||||
@ -447,7 +447,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show bubble size
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowBubbleSize($value)
|
||||
{
|
||||
@ -472,7 +472,7 @@ class Layout
|
||||
*
|
||||
* @param bool $value Show leader lines
|
||||
*
|
||||
* @return Layout
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowLeaderLines($value)
|
||||
{
|
||||
|
||||
@ -94,7 +94,7 @@ class PlotArea
|
||||
*
|
||||
* @param DataSeries[] $plotSeries
|
||||
*
|
||||
* @return PlotArea
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlotSeries(array $plotSeries)
|
||||
{
|
||||
|
||||
@ -442,7 +442,7 @@ class JpGraph implements IRenderer
|
||||
$seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
|
||||
} elseif ($scatterStyle == 'smoothMarker') {
|
||||
$spline = new \Spline($dataValuesY, $dataValuesX);
|
||||
list($splineDataY, $splineDataX) = $spline->Get(count($dataValuesX) * self::$width / 20);
|
||||
[$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
|
||||
$lplot = new \LinePlot($splineDataX, $splineDataY);
|
||||
$lplot->SetColor(self::$colourSet[self::$plotColour]);
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ class Title
|
||||
*
|
||||
* @param string $caption
|
||||
*
|
||||
* @return Title
|
||||
* @return $this
|
||||
*/
|
||||
public function setCaption($caption)
|
||||
{
|
||||
|
||||
@ -16,8 +16,6 @@ abstract class CellsFactory
|
||||
* */
|
||||
public static function getInstance(Worksheet $parent)
|
||||
{
|
||||
$instance = new Cells($parent, Settings::getCache());
|
||||
|
||||
return $instance;
|
||||
return new Cells($parent, Settings::getCache());
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param string $author
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthor($author)
|
||||
{
|
||||
@ -120,7 +120,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param RichText $pValue
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setText(RichText $pValue)
|
||||
{
|
||||
@ -144,7 +144,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param string $width
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidth($width)
|
||||
{
|
||||
@ -168,7 +168,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeight($value)
|
||||
{
|
||||
@ -192,7 +192,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setMarginLeft($value)
|
||||
{
|
||||
@ -216,7 +216,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setMarginTop($value)
|
||||
{
|
||||
@ -240,7 +240,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setVisible($value)
|
||||
{
|
||||
@ -264,7 +264,7 @@ class Comment implements IComparable
|
||||
*
|
||||
* @param string $alignment see Style\Alignment::HORIZONTAL_*
|
||||
*
|
||||
* @return Comment
|
||||
* @return $this
|
||||
*/
|
||||
public function setAlignment($alignment)
|
||||
{
|
||||
|
||||
@ -122,7 +122,7 @@ class Properties
|
||||
*
|
||||
* @param string $creator
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreator($creator)
|
||||
{
|
||||
@ -146,7 +146,7 @@ class Properties
|
||||
*
|
||||
* @param string $pValue
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastModifiedBy($pValue)
|
||||
{
|
||||
@ -170,7 +170,7 @@ class Properties
|
||||
*
|
||||
* @param int|string $time
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreated($time)
|
||||
{
|
||||
@ -204,7 +204,7 @@ class Properties
|
||||
*
|
||||
* @param int|string $time
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setModified($time)
|
||||
{
|
||||
@ -238,7 +238,7 @@ class Properties
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
@ -262,7 +262,7 @@ class Properties
|
||||
*
|
||||
* @param string $description
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
@ -286,7 +286,7 @@ class Properties
|
||||
*
|
||||
* @param string $subject
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setSubject($subject)
|
||||
{
|
||||
@ -310,7 +310,7 @@ class Properties
|
||||
*
|
||||
* @param string $keywords
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setKeywords($keywords)
|
||||
{
|
||||
@ -334,7 +334,7 @@ class Properties
|
||||
*
|
||||
* @param string $category
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setCategory($category)
|
||||
{
|
||||
@ -358,7 +358,7 @@ class Properties
|
||||
*
|
||||
* @param string $company
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setCompany($company)
|
||||
{
|
||||
@ -382,7 +382,7 @@ class Properties
|
||||
*
|
||||
* @param string $manager
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setManager($manager)
|
||||
{
|
||||
@ -453,7 +453,7 @@ class Properties
|
||||
* 'd' : Date/Time
|
||||
* 'b' : Boolean
|
||||
*
|
||||
* @return Properties
|
||||
* @return $this
|
||||
*/
|
||||
public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null)
|
||||
{
|
||||
|
||||
@ -75,7 +75,7 @@ class Security
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Security
|
||||
* @return $this
|
||||
*/
|
||||
public function setLockRevision($pValue)
|
||||
{
|
||||
@ -99,7 +99,7 @@ class Security
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Security
|
||||
* @return $this
|
||||
*/
|
||||
public function setLockStructure($pValue)
|
||||
{
|
||||
@ -123,7 +123,7 @@ class Security
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Security
|
||||
* @return $this
|
||||
*/
|
||||
public function setLockWindows($pValue)
|
||||
{
|
||||
@ -148,7 +148,7 @@ class Security
|
||||
* @param string $pValue
|
||||
* @param bool $pAlreadyHashed If the password has already been hashed, set this to true
|
||||
*
|
||||
* @return Security
|
||||
* @return $this
|
||||
*/
|
||||
public function setRevisionsPassword($pValue, $pAlreadyHashed = false)
|
||||
{
|
||||
@ -176,7 +176,7 @@ class Security
|
||||
* @param string $pValue
|
||||
* @param bool $pAlreadyHashed If the password has already been hashed, set this to true
|
||||
*
|
||||
* @return Security
|
||||
* @return $this
|
||||
*/
|
||||
public function setWorkbookPassword($pValue, $pAlreadyHashed = false)
|
||||
{
|
||||
|
||||
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Category;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class DocumentGenerator
|
||||
{
|
||||
/**
|
||||
* @param array[] $phpSpreadsheetFunctions
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateFunctionListByCategory(array $phpSpreadsheetFunctions): string
|
||||
{
|
||||
$result = "# Function list by category\n";
|
||||
foreach (self::getCategories() as $categoryConstant => $category) {
|
||||
$result .= "\n";
|
||||
$result .= "## {$categoryConstant}\n";
|
||||
$result .= "\n";
|
||||
$lengths = [20, 42];
|
||||
$result .= self::tableRow($lengths, ['Excel Function', 'PhpSpreadsheet Function']) . "\n";
|
||||
$result .= self::tableRow($lengths, null) . "\n";
|
||||
foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) {
|
||||
if ($category === $functionInfo['category']) {
|
||||
$phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']);
|
||||
$result .= self::tableRow($lengths, [$excelFunction, $phpFunction]) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function getCategories(): array
|
||||
{
|
||||
return (new ReflectionClass(Category::class))->getConstants();
|
||||
}
|
||||
|
||||
private static function tableRow(array $lengths, array $values = null): string
|
||||
{
|
||||
$result = '';
|
||||
foreach (array_map(null, $lengths, $values ?? []) as $i => [$length, $value]) {
|
||||
$pad = $value === null ? '-' : ' ';
|
||||
if ($i > 0) {
|
||||
$result .= '|' . $pad;
|
||||
}
|
||||
$result .= str_pad($value ?? '', $length, $pad);
|
||||
}
|
||||
|
||||
return rtrim($result, ' ');
|
||||
}
|
||||
|
||||
private static function getPhpSpreadsheetFunctionText($functionCall): string
|
||||
{
|
||||
if (is_string($functionCall)) {
|
||||
return $functionCall;
|
||||
}
|
||||
if ($functionCall === [Functions::class, 'DUMMY']) {
|
||||
return '**Not yet Implemented**';
|
||||
}
|
||||
if (is_array($functionCall)) {
|
||||
return "\\{$functionCall[0]}::{$functionCall[1]}";
|
||||
}
|
||||
|
||||
throw new UnexpectedValueException(
|
||||
'$functionCall is of type ' . gettype($functionCall) . '. string or array expected'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array[] $phpSpreadsheetFunctions
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateFunctionListByName(array $phpSpreadsheetFunctions): string
|
||||
{
|
||||
$categoryConstants = array_flip(self::getCategories());
|
||||
$result = "# Function list by name\n";
|
||||
$lastAlphabet = null;
|
||||
foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) {
|
||||
$lengths = [20, 31, 42];
|
||||
if ($lastAlphabet !== $excelFunction[0]) {
|
||||
$lastAlphabet = $excelFunction[0];
|
||||
$result .= "\n";
|
||||
$result .= "## {$lastAlphabet}\n";
|
||||
$result .= "\n";
|
||||
$result .= self::tableRow($lengths, ['Excel Function', 'Category', 'PhpSpreadsheet Function']) . "\n";
|
||||
$result .= self::tableRow($lengths, null) . "\n";
|
||||
}
|
||||
$category = $categoryConstants[$functionInfo['category']];
|
||||
$phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']);
|
||||
$result .= self::tableRow($lengths, [$excelFunction, $category, $phpFunction]) . "\n";
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -52,9 +52,8 @@ abstract class IOFactory
|
||||
|
||||
// Instantiate writer
|
||||
$className = self::$writers[$writerType];
|
||||
$writer = new $className($spreadsheet);
|
||||
|
||||
return $writer;
|
||||
return new $className($spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,9 +73,8 @@ abstract class IOFactory
|
||||
|
||||
// Instantiate reader
|
||||
$className = self::$readers[$readerType];
|
||||
$reader = new $className();
|
||||
|
||||
return $reader;
|
||||
return new $className();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -82,7 +82,7 @@ class NamedRange
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return NamedRange
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($value)
|
||||
{
|
||||
@ -123,7 +123,7 @@ class NamedRange
|
||||
*
|
||||
* @param Worksheet $value
|
||||
*
|
||||
* @return NamedRange
|
||||
* @return $this
|
||||
*/
|
||||
public function setWorksheet(Worksheet $value = null)
|
||||
{
|
||||
@ -149,7 +149,7 @@ class NamedRange
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return NamedRange
|
||||
* @return $this
|
||||
*/
|
||||
public function setRange($value)
|
||||
{
|
||||
@ -175,7 +175,7 @@ class NamedRange
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return NamedRange
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocalOnly($value)
|
||||
{
|
||||
@ -200,7 +200,7 @@ class NamedRange
|
||||
*
|
||||
* @param null|Worksheet $value
|
||||
*
|
||||
* @return NamedRange
|
||||
* @return $this
|
||||
*/
|
||||
public function setScope(Worksheet $value = null)
|
||||
{
|
||||
|
||||
@ -70,7 +70,7 @@ class Csv extends BaseReader
|
||||
*
|
||||
* @param string $pValue Input encoding, eg: 'UTF-8'
|
||||
*
|
||||
* @return Csv
|
||||
* @return $this
|
||||
*/
|
||||
public function setInputEncoding($pValue)
|
||||
{
|
||||
@ -175,9 +175,8 @@ class Csv extends BaseReader
|
||||
}
|
||||
}
|
||||
foreach ($potentialDelimiters as $delimiter) {
|
||||
$counts[$delimiter][] = isset($countLine[$delimiter])
|
||||
? $countLine[$delimiter]
|
||||
: 0;
|
||||
$counts[$delimiter][] = $countLine[$delimiter]
|
||||
?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,7 +415,7 @@ class Csv extends BaseReader
|
||||
*
|
||||
* @param string $delimiter Delimiter, eg: ','
|
||||
*
|
||||
* @return CSV
|
||||
* @return $this
|
||||
*/
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
@ -440,7 +439,7 @@ class Csv extends BaseReader
|
||||
*
|
||||
* @param string $enclosure Enclosure, defaults to "
|
||||
*
|
||||
* @return CSV
|
||||
* @return $this
|
||||
*/
|
||||
public function setEnclosure($enclosure)
|
||||
{
|
||||
@ -467,7 +466,7 @@ class Csv extends BaseReader
|
||||
*
|
||||
* @param int $pValue Sheet index
|
||||
*
|
||||
* @return CSV
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetIndex($pValue)
|
||||
{
|
||||
@ -481,7 +480,7 @@ class Csv extends BaseReader
|
||||
*
|
||||
* @param bool $contiguous
|
||||
*
|
||||
* @return Csv
|
||||
* @return $this
|
||||
*/
|
||||
public function setContiguous($contiguous)
|
||||
{
|
||||
|
||||
@ -267,7 +267,7 @@ class Gnumeric extends BaseReader
|
||||
|
||||
break;
|
||||
case 'user-defined':
|
||||
list(, $attrName) = explode(':', $attributes['name']);
|
||||
[, $attrName] = explode(':', $attributes['name']);
|
||||
switch ($attrName) {
|
||||
case 'publisher':
|
||||
$docProps->setCompany(trim($propertyValue));
|
||||
@ -879,7 +879,7 @@ class Gnumeric extends BaseReader
|
||||
|
||||
private static function parseGnumericColour($gnmColour)
|
||||
{
|
||||
list($gnmR, $gnmG, $gnmB) = explode(':', $gnmColour);
|
||||
[$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
|
||||
$gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
|
||||
$gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
|
||||
$gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
|
||||
|
||||
@ -223,7 +223,7 @@ class Html extends BaseReader
|
||||
*
|
||||
* @param string $pValue Input encoding, eg: 'ANSI'
|
||||
*
|
||||
* @return Html
|
||||
* @return $this
|
||||
*/
|
||||
public function setInputEncoding($pValue)
|
||||
{
|
||||
@ -374,8 +374,9 @@ class Html extends BaseReader
|
||||
// no break
|
||||
case 'br':
|
||||
if ($this->tableLevel > 0) {
|
||||
// If we're inside a table, replace with a \n
|
||||
// If we're inside a table, replace with a \n and set the cell to wrap
|
||||
$cellContent .= "\n";
|
||||
$sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
|
||||
} else {
|
||||
// Otherwise flush our existing content and move the row cursor on
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
@ -489,22 +490,22 @@ class Html extends BaseReader
|
||||
case 'td':
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
// apply inline style
|
||||
$this->applyInlineStyle($sheet, $row, $column, $attributeArray);
|
||||
|
||||
while (isset($this->rowspan[$column . $row])) {
|
||||
++$column;
|
||||
}
|
||||
|
||||
// apply inline style
|
||||
$this->applyInlineStyle($sheet, $row, $column, $attributeArray);
|
||||
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
|
||||
if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
|
||||
//create merging rowspan and colspan
|
||||
$columnTo = $column;
|
||||
for ($i = 0; $i < $attributeArray['colspan'] - 1; ++$i) {
|
||||
for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
|
||||
++$columnTo;
|
||||
}
|
||||
$range = $column . $row . ':' . $columnTo . ($row + $attributeArray['rowspan'] - 1);
|
||||
$range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
|
||||
$this->rowspan[$value] = true;
|
||||
}
|
||||
@ -512,7 +513,7 @@ class Html extends BaseReader
|
||||
$column = $columnTo;
|
||||
} elseif (isset($attributeArray['rowspan'])) {
|
||||
//create merging rowspan
|
||||
$range = $column . $row . ':' . $column . ($row + $attributeArray['rowspan'] - 1);
|
||||
$range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
|
||||
$this->rowspan[$value] = true;
|
||||
}
|
||||
@ -520,7 +521,7 @@ class Html extends BaseReader
|
||||
} elseif (isset($attributeArray['colspan'])) {
|
||||
//create merging colspan
|
||||
$columnTo = $column;
|
||||
for ($i = 0; $i < $attributeArray['colspan'] - 1; ++$i) {
|
||||
for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
|
||||
++$columnTo;
|
||||
}
|
||||
$sheet->mergeCells($column . $row . ':' . $columnTo . $row);
|
||||
@ -591,28 +592,63 @@ class Html extends BaseReader
|
||||
throw new Exception($pFilename . ' is an Invalid HTML file.');
|
||||
}
|
||||
|
||||
// Create new sheet
|
||||
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
|
||||
$spreadsheet->createSheet();
|
||||
}
|
||||
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
|
||||
|
||||
// Create a new DOM object
|
||||
// Create a new DOM object
|
||||
$dom = new DOMDocument();
|
||||
// Reload the HTML file into the DOM object
|
||||
// Reload the HTML file into the DOM object
|
||||
$loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
|
||||
if ($loaded === false) {
|
||||
throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document');
|
||||
}
|
||||
|
||||
// Discard white space
|
||||
$dom->preserveWhiteSpace = false;
|
||||
return $this->loadDocument($dom, $spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spreadsheet from content.
|
||||
*
|
||||
* @param string $content
|
||||
* @param null|Spreadsheet $spreadsheet
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
|
||||
{
|
||||
// Create a new DOM object
|
||||
$dom = new DOMDocument();
|
||||
// Reload the HTML file into the DOM object
|
||||
$loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'));
|
||||
if ($loaded === false) {
|
||||
throw new Exception('Failed to load content as a DOM Document');
|
||||
}
|
||||
|
||||
return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
|
||||
*
|
||||
* @param DOMDocument $document
|
||||
* @param Spreadsheet $spreadsheet
|
||||
*
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet
|
||||
{
|
||||
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
|
||||
$spreadsheet->createSheet();
|
||||
}
|
||||
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
|
||||
|
||||
// Discard white space
|
||||
$document->preserveWhiteSpace = false;
|
||||
|
||||
$row = 0;
|
||||
$column = 'A';
|
||||
$content = '';
|
||||
$this->rowspan = [];
|
||||
$this->processDomElement($dom, $spreadsheet->getActiveSheet(), $row, $column, $content);
|
||||
$this->processDomElement($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
@ -633,7 +669,7 @@ class Html extends BaseReader
|
||||
*
|
||||
* @param int $pValue Sheet index
|
||||
*
|
||||
* @return HTML
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetIndex($pValue)
|
||||
{
|
||||
@ -919,7 +955,7 @@ class Html extends BaseReader
|
||||
*/
|
||||
private function setBorderStyle(Style $cellStyle, $styleValue, $type)
|
||||
{
|
||||
list(, $borderStyle, $color) = explode(' ', $styleValue);
|
||||
[, $borderStyle, $color] = explode(' ', $styleValue);
|
||||
|
||||
$cellStyle->applyFromArray([
|
||||
'borders' => [
|
||||
|
||||
@ -490,7 +490,7 @@ class Ods extends BaseReader
|
||||
|
||||
$dateObj = new DateTime($value, $GMT);
|
||||
$dateObj->setTimeZone($timezoneObj);
|
||||
list($year, $month, $day, $hour, $minute, $second) = explode(
|
||||
[$year, $month, $day, $hour, $minute, $second] = explode(
|
||||
' ',
|
||||
$dateObj->format('Y m d H i s')
|
||||
);
|
||||
|
||||
@ -19,12 +19,11 @@ class Properties
|
||||
$docProps = $this->spreadsheet->getProperties();
|
||||
$officeProperty = $xml->children($namespacesMeta['office']);
|
||||
foreach ($officeProperty as $officePropertyData) {
|
||||
/** @var \SimpleXMLElement $officePropertyData */
|
||||
$officePropertiesDC = (object) [];
|
||||
// @var \SimpleXMLElement $officePropertyData
|
||||
if (isset($namespacesMeta['dc'])) {
|
||||
$officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
|
||||
$this->setCoreProperties($docProps, $officePropertiesDC);
|
||||
}
|
||||
$this->setCoreProperties($docProps, $officePropertiesDC);
|
||||
|
||||
$officePropertyMeta = (object) [];
|
||||
if (isset($namespacesMeta['dc'])) {
|
||||
|
||||
@ -83,7 +83,7 @@ class Slk extends BaseReader
|
||||
*
|
||||
* @param string $pValue Input encoding, eg: 'ANSI'
|
||||
*
|
||||
* @return Slk
|
||||
* @return $this
|
||||
*/
|
||||
public function setInputEncoding($pValue)
|
||||
{
|
||||
@ -384,7 +384,7 @@ class Slk extends BaseReader
|
||||
|
||||
break;
|
||||
case 'W':
|
||||
list($startCol, $endCol, $columnWidth) = explode(' ', substr($rowDatum, 1));
|
||||
[$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1));
|
||||
|
||||
break;
|
||||
case 'S':
|
||||
@ -485,7 +485,7 @@ class Slk extends BaseReader
|
||||
*
|
||||
* @param int $pValue Sheet index
|
||||
*
|
||||
* @return Slk
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetIndex($pValue)
|
||||
{
|
||||
|
||||
@ -1089,8 +1089,8 @@ class Xls extends BaseReader
|
||||
}
|
||||
|
||||
// calculate the width and height of the shape
|
||||
list($startColumn, $startRow) = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
|
||||
list($endColumn, $endRow) = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
|
||||
[$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
|
||||
[$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
|
||||
|
||||
$startOffsetX = $spContainer->getStartOffsetX();
|
||||
$startOffsetY = $spContainer->getStartOffsetY();
|
||||
@ -1175,7 +1175,7 @@ class Xls extends BaseReader
|
||||
// treat SHAREDFMLA records
|
||||
if ($this->version == self::XLS_BIFF8) {
|
||||
foreach ($this->sharedFormulaParts as $cell => $baseCell) {
|
||||
list($column, $row) = Coordinate::coordinateFromString($cell);
|
||||
[$column, $row] = Coordinate::coordinateFromString($cell);
|
||||
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
|
||||
$formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
|
||||
$this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
|
||||
@ -1251,8 +1251,8 @@ class Xls extends BaseReader
|
||||
|
||||
$coordinateStrings = explode(':', $extractedRange);
|
||||
if (count($coordinateStrings) == 2) {
|
||||
list($firstColumn, $firstRow) = Coordinate::coordinateFromString($coordinateStrings[0]);
|
||||
list($lastColumn, $lastRow) = Coordinate::coordinateFromString($coordinateStrings[1]);
|
||||
[$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
|
||||
[$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
|
||||
|
||||
if ($firstColumn == 'A' and $lastColumn == 'IV') {
|
||||
// then we have repeating rows
|
||||
@ -3825,7 +3825,7 @@ class Xls extends BaseReader
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->readDataOnly && !$emptyCell) {
|
||||
if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[$xfIndex])) {
|
||||
// add style information
|
||||
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
|
||||
}
|
||||
@ -5279,12 +5279,10 @@ class Xls extends BaseReader
|
||||
$nextIdentifier = self::getUInt2d($this->data, $this->pos);
|
||||
} while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
|
||||
|
||||
$splicedData = [
|
||||
return [
|
||||
'recordData' => $data,
|
||||
'spliceOffsets' => $spliceOffsets,
|
||||
];
|
||||
|
||||
return $splicedData;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5355,12 +5353,12 @@ class Xls extends BaseReader
|
||||
$formulaStrings = [];
|
||||
foreach ($tokens as $token) {
|
||||
// initialize spaces
|
||||
$space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
|
||||
$space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
|
||||
$space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
|
||||
$space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
|
||||
$space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
|
||||
$space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
|
||||
$space0 = $space0 ?? ''; // spaces before next token, not tParen
|
||||
$space1 = $space1 ?? ''; // carriage returns before next token, not tParen
|
||||
$space2 = $space2 ?? ''; // spaces before opening parenthesis
|
||||
$space3 = $space3 ?? ''; // carriage returns before opening parenthesis
|
||||
$space4 = $space4 ?? ''; // spaces before closing parenthesis
|
||||
$space5 = $space5 ?? ''; // carriage returns before closing parenthesis
|
||||
|
||||
switch ($token['name']) {
|
||||
case 'tAdd': // addition
|
||||
@ -7145,7 +7143,7 @@ class Xls extends BaseReader
|
||||
*/
|
||||
private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
|
||||
{
|
||||
list($baseCol, $baseRow) = Coordinate::coordinateFromString($baseCell);
|
||||
[$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
|
||||
$baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
|
||||
|
||||
// offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
|
||||
@ -7328,7 +7326,7 @@ class Xls extends BaseReader
|
||||
*/
|
||||
private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
|
||||
{
|
||||
list($baseCol, $baseRow) = Coordinate::coordinateFromString($baseCell);
|
||||
[$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
|
||||
$baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
|
||||
|
||||
// TODO: if cell range is just a single cell, should this funciton
|
||||
|
||||
@ -77,34 +77,17 @@ class Xlsx extends BaseReader
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$xl = false;
|
||||
// Load file
|
||||
$result = false;
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($pFilename) === true) {
|
||||
// check if it is an OOXML archive
|
||||
$rels = simplexml_load_string(
|
||||
$this->securityScanner->scan(
|
||||
$this->getFromZipArchive($zip, '_rels/.rels')
|
||||
),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if ($rels !== false) {
|
||||
foreach ($rels->Relationship as $rel) {
|
||||
switch ($rel['Type']) {
|
||||
case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
|
||||
if (basename($rel['Target']) == 'workbook.xml') {
|
||||
$xl = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($zip->open($pFilename) === true) {
|
||||
$workbookBasename = $this->getWorkbookBaseName($zip);
|
||||
$result = !empty($workbookBasename);
|
||||
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
return $xl;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -357,8 +340,9 @@ class Xlsx extends BaseReader
|
||||
|
||||
// Read the theme first, because we need the colour scheme when reading the styles
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$workbookBasename = $this->getWorkbookBaseName($zip);
|
||||
$wbRels = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, 'xl/_rels/workbook.xml.rels')),
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, "xl/_rels/${workbookBasename}.rels")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
@ -445,18 +429,20 @@ class Xlsx extends BaseReader
|
||||
|
||||
$sharedStrings = [];
|
||||
$xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$xmlStrings = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if (isset($xmlStrings, $xmlStrings->si)) {
|
||||
foreach ($xmlStrings->si as $val) {
|
||||
if (isset($val->t)) {
|
||||
$sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
|
||||
} elseif (isset($val->r)) {
|
||||
$sharedStrings[] = $this->parseRichText($val);
|
||||
if ($xpath) {
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$xmlStrings = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if (isset($xmlStrings, $xmlStrings->si)) {
|
||||
foreach ($xmlStrings->si as $val) {
|
||||
if (isset($val->t)) {
|
||||
$sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
|
||||
} elseif (isset($val->r)) {
|
||||
$sharedStrings[] = $this->parseRichText($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,15 +729,6 @@ class Xlsx extends BaseReader
|
||||
|
||||
// read empty cells or the cells are not empty
|
||||
if ($this->readEmptyCells || ($value !== null && $value !== '')) {
|
||||
// Check for numeric values
|
||||
if (is_numeric($value) && $cellDataType != 's') {
|
||||
if ($value == (int) $value) {
|
||||
$value = (int) $value;
|
||||
} elseif ($value == (float) $value) {
|
||||
$value = (float) $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Rich text?
|
||||
if ($value instanceof RichText && $this->readDataOnly) {
|
||||
$value = $value->getPlainText();
|
||||
@ -1352,7 +1329,7 @@ class Xlsx extends BaseReader
|
||||
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
$newRangeSets = [];
|
||||
foreach ($rangeSets as $rangeSet) {
|
||||
list($sheetName, $rangeSet) = Worksheet::extractSheetTitle($rangeSet, true);
|
||||
[$sheetName, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
|
||||
if (strpos($rangeSet, ':') === false) {
|
||||
$rangeSet = $rangeSet . ':' . $rangeSet;
|
||||
}
|
||||
@ -1426,7 +1403,7 @@ class Xlsx extends BaseReader
|
||||
$locatedSheet = $excel->getSheetByName($extractedSheetName);
|
||||
|
||||
// Modify range
|
||||
list($worksheetName, $extractedRange) = Worksheet::extractSheetTitle($extractedRange, true);
|
||||
[$worksheetName, $extractedRange] = Worksheet::extractSheetTitle($extractedRange, true);
|
||||
}
|
||||
|
||||
if ($locatedSheet !== null) {
|
||||
@ -1437,7 +1414,7 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) {
|
||||
if ((!$this->readDataOnly || !empty($this->loadSheetsOnly)) && isset($xmlWorkbook->bookViews->workbookView)) {
|
||||
$workbookView = $xmlWorkbook->bookViews->workbookView;
|
||||
|
||||
// active sheet index
|
||||
@ -1643,8 +1620,6 @@ class Xlsx extends BaseReader
|
||||
$docStyle->getFill()->setFillType($patternType);
|
||||
if ($style->fill->patternFill->fgColor) {
|
||||
$docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true));
|
||||
} else {
|
||||
$docStyle->getFill()->getStartColor()->setARGB('FF000000');
|
||||
}
|
||||
if ($style->fill->patternFill->bgColor) {
|
||||
$docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true));
|
||||
@ -1841,7 +1816,7 @@ class Xlsx extends BaseReader
|
||||
|
||||
private static function getArrayItem($array, $key = 0)
|
||||
{
|
||||
return isset($array[$key]) ? $array[$key] : null;
|
||||
return $array[$key] ?? null;
|
||||
}
|
||||
|
||||
private static function dirAdd($base, $add)
|
||||
@ -1851,7 +1826,7 @@ class Xlsx extends BaseReader
|
||||
|
||||
private static function toCSSArray($style)
|
||||
{
|
||||
$style = trim(str_replace(["\r", "\n"], '', $style), ';');
|
||||
$style = self::stripWhiteSpaceFromStyleString($style);
|
||||
|
||||
$temp = explode(';', $style);
|
||||
$style = [];
|
||||
@ -1880,6 +1855,11 @@ class Xlsx extends BaseReader
|
||||
return $style;
|
||||
}
|
||||
|
||||
public static function stripWhiteSpaceFromStyleString($string)
|
||||
{
|
||||
return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
|
||||
}
|
||||
|
||||
private static function boolean($value)
|
||||
{
|
||||
if (is_object($value)) {
|
||||
@ -2026,4 +2006,38 @@ class Xlsx extends BaseReader
|
||||
|
||||
return (bool) $xsdBoolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipArchive $zip Opened zip archive
|
||||
*
|
||||
* @return string basename of the used excel workbook
|
||||
*/
|
||||
private function getWorkbookBaseName(ZipArchive $zip)
|
||||
{
|
||||
$workbookBasename = '';
|
||||
|
||||
// check if it is an OOXML archive
|
||||
$rels = simplexml_load_string(
|
||||
$this->securityScanner->scan(
|
||||
$this->getFromZipArchive($zip, '_rels/.rels')
|
||||
),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if ($rels !== false) {
|
||||
foreach ($rels->Relationship as $rel) {
|
||||
switch ($rel['Type']) {
|
||||
case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
|
||||
$basename = basename($rel['Target']);
|
||||
if (preg_match('/workbook.*\.xml/', $basename)) {
|
||||
$workbookBasename = $basename;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $workbookBasename;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,8 @@ class AutoFilter
|
||||
|
||||
public function load()
|
||||
{
|
||||
$autoFilterRange = (string) $this->worksheetXml->autoFilter['ref'];
|
||||
// Remove all "$" in the auto filter range
|
||||
$autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref']);
|
||||
if (strpos($autoFilterRange, ':') !== false) {
|
||||
$this->readAutoFilter($autoFilterRange, $this->worksheetXml);
|
||||
}
|
||||
|
||||
@ -36,6 +36,8 @@ class ConditionalStyles
|
||||
if (((string) $cfRule['type'] == Conditional::CONDITION_NONE
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
|
||||
&& isset($this->dxfs[(int) ($cfRule['dxfId'])])) {
|
||||
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
|
||||
|
||||
@ -86,6 +86,6 @@ class Properties
|
||||
|
||||
private static function getArrayItem(array $array, $key = 0)
|
||||
{
|
||||
return isset($array[$key]) ? $array[$key] : null;
|
||||
return $array[$key] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ class SheetViews extends BaseParserClass
|
||||
$this->gridLines();
|
||||
$this->headers();
|
||||
$this->direction();
|
||||
$this->showZeros();
|
||||
|
||||
if (isset($this->sheetViewXml->pane)) {
|
||||
$this->pane();
|
||||
@ -92,6 +93,15 @@ class SheetViews extends BaseParserClass
|
||||
}
|
||||
}
|
||||
|
||||
private function showZeros()
|
||||
{
|
||||
if (isset($this->sheetViewXml['showZeros'])) {
|
||||
$this->worksheet->getSheetView()->setShowZeros(
|
||||
self::boolean((string) $this->sheetViewXml['showZeros'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function pane()
|
||||
{
|
||||
$xSplit = 0;
|
||||
|
||||
@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\Borders;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Color;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
|
||||
@ -71,6 +72,17 @@ class Styles extends BaseParserClass
|
||||
}
|
||||
}
|
||||
|
||||
private static function readNumberFormat(NumberFormat $numfmtStyle, \SimpleXMLElement $numfmtStyleXml)
|
||||
{
|
||||
if ($numfmtStyleXml->count() === 0) {
|
||||
return;
|
||||
}
|
||||
$numfmt = $numfmtStyleXml->attributes();
|
||||
if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) {
|
||||
$numfmtStyle->setFormatCode((string) $numfmt['formatCode']);
|
||||
}
|
||||
}
|
||||
|
||||
private static function readFillStyle(Fill $fillStyle, \SimpleXMLElement $fillStyleXml)
|
||||
{
|
||||
if ($fillStyleXml->gradientFill) {
|
||||
@ -149,7 +161,11 @@ class Styles extends BaseParserClass
|
||||
|
||||
private function readStyle(Style $docStyle, $style)
|
||||
{
|
||||
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
|
||||
if ($style->numFmt instanceof \SimpleXMLElement) {
|
||||
self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
|
||||
} else {
|
||||
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
|
||||
}
|
||||
|
||||
if (isset($style->font)) {
|
||||
self::readFontStyle($docStyle->getFont(), $style->font);
|
||||
@ -163,7 +179,7 @@ class Styles extends BaseParserClass
|
||||
self::readBorderStyle($docStyle->getBorders(), $style->border);
|
||||
}
|
||||
|
||||
if (isset($style->alignment)) {
|
||||
if (isset($style->alignment->alignment)) {
|
||||
self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
|
||||
}
|
||||
|
||||
@ -260,6 +276,6 @@ class Styles extends BaseParserClass
|
||||
|
||||
private static function getArrayItem($array, $key = 0)
|
||||
{
|
||||
return isset($array[$key]) ? $array[$key] : null;
|
||||
return $array[$key] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,9 +285,8 @@ class Xml extends BaseReader
|
||||
{
|
||||
$pixels = ($widthUnits / 256) * 7;
|
||||
$offsetWidthUnits = $widthUnits % 256;
|
||||
$pixels += round($offsetWidthUnits / (256 / 7));
|
||||
|
||||
return $pixels;
|
||||
return $pixels + round($offsetWidthUnits / (256 / 7));
|
||||
}
|
||||
|
||||
protected static function hex2str($hex)
|
||||
|
||||
@ -82,11 +82,8 @@ class ReferenceHelper
|
||||
*/
|
||||
public static function cellSort($a, $b)
|
||||
{
|
||||
// TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
|
||||
// [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
|
||||
// Switch when we drop support for 5.6
|
||||
list($ac, $ar) = sscanf($a, '%[A-Z]%d');
|
||||
list($bc, $br) = sscanf($b, '%[A-Z]%d');
|
||||
[$ac, $ar] = sscanf($a, '%[A-Z]%d');
|
||||
[$bc, $br] = sscanf($b, '%[A-Z]%d');
|
||||
|
||||
if ($ar === $br) {
|
||||
return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
|
||||
@ -106,11 +103,8 @@ class ReferenceHelper
|
||||
*/
|
||||
public static function cellReverseSort($a, $b)
|
||||
{
|
||||
// TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
|
||||
// [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
|
||||
// Switch when we drop support for 5.6
|
||||
list($ac, $ar) = sscanf($a, '%[A-Z]%d');
|
||||
list($bc, $br) = sscanf($b, '%[A-Z]%d');
|
||||
[$ac, $ar] = sscanf($a, '%[A-Z]%d');
|
||||
[$bc, $br] = sscanf($b, '%[A-Z]%d');
|
||||
|
||||
if ($ar === $br) {
|
||||
return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
|
||||
@ -132,7 +126,7 @@ class ReferenceHelper
|
||||
*/
|
||||
private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)
|
||||
{
|
||||
list($cellColumn, $cellRow) = Coordinate::coordinateFromString($cellAddress);
|
||||
[$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
|
||||
$cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
|
||||
// Is cell within the range of rows/columns if we're deleting
|
||||
if ($pNumRows < 0 &&
|
||||
@ -319,7 +313,7 @@ class ReferenceHelper
|
||||
if (!empty($aColumnDimensions)) {
|
||||
foreach ($aColumnDimensions as $objColumnDimension) {
|
||||
$newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
|
||||
list($newReference) = Coordinate::coordinateFromString($newReference);
|
||||
[$newReference] = Coordinate::coordinateFromString($newReference);
|
||||
if ($objColumnDimension->getColumnIndex() != $newReference) {
|
||||
$objColumnDimension->setColumnIndex($newReference);
|
||||
}
|
||||
@ -344,7 +338,7 @@ class ReferenceHelper
|
||||
if (!empty($aRowDimensions)) {
|
||||
foreach ($aRowDimensions as $objRowDimension) {
|
||||
$newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
|
||||
list(, $newReference) = Coordinate::coordinateFromString($newReference);
|
||||
[, $newReference] = Coordinate::coordinateFromString($newReference);
|
||||
if ($objRowDimension->getRowIndex() != $newReference) {
|
||||
$objRowDimension->setRowIndex($newReference);
|
||||
}
|
||||
@ -378,7 +372,7 @@ class ReferenceHelper
|
||||
$allCoordinates = $pSheet->getCoordinates();
|
||||
|
||||
// Get coordinate of $pBefore
|
||||
list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);
|
||||
[$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($pBefore);
|
||||
$beforeColumnIndex = Coordinate::columnIndexFromString($beforeColumn);
|
||||
|
||||
// Clear cells if we are removing columns or rows
|
||||
@ -539,7 +533,7 @@ class ReferenceHelper
|
||||
$row = 0;
|
||||
sscanf($pBefore, '%[A-Z]%d', $column, $row);
|
||||
$columnIndex = Coordinate::columnIndexFromString($column);
|
||||
list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($autoFilterRange);
|
||||
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($autoFilterRange);
|
||||
if ($columnIndex <= $rangeEnd[0]) {
|
||||
if ($pNumCols < 0) {
|
||||
// If we're actually deleting any columns that fall within the autofilter range,
|
||||
@ -707,7 +701,7 @@ class ReferenceHelper
|
||||
if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
|
||||
$toString = ($match[2] > '') ? $match[2] . '!' : '';
|
||||
$toString .= $modified3 . ':' . $modified4;
|
||||
list($column, $row) = Coordinate::coordinateFromString($match[3]);
|
||||
[$column, $row] = Coordinate::coordinateFromString($match[3]);
|
||||
// Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
|
||||
$column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
|
||||
$row = trim($row, '$') + 10000000;
|
||||
@ -733,7 +727,7 @@ class ReferenceHelper
|
||||
if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
|
||||
$toString = ($match[2] > '') ? $match[2] . '!' : '';
|
||||
$toString .= $modified3;
|
||||
list($column, $row) = Coordinate::coordinateFromString($match[3]);
|
||||
[$column, $row] = Coordinate::coordinateFromString($match[3]);
|
||||
// Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
|
||||
$column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
|
||||
$row = trim($row, '$') + 10000000;
|
||||
@ -881,10 +875,10 @@ class ReferenceHelper
|
||||
}
|
||||
|
||||
// Get coordinate of $pBefore
|
||||
list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);
|
||||
[$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($pBefore);
|
||||
|
||||
// Get coordinate of $pCellReference
|
||||
list($newColumn, $newRow) = Coordinate::coordinateFromString($pCellReference);
|
||||
[$newColumn, $newRow] = Coordinate::coordinateFromString($pCellReference);
|
||||
|
||||
// Verify which parts should be updated
|
||||
$updateColumn = (($newColumn[0] != '$') && ($beforeColumn[0] != '$') && (Coordinate::columnIndexFromString($newColumn) >= Coordinate::columnIndexFromString($beforeColumn)));
|
||||
|
||||
@ -47,7 +47,7 @@ class RichText implements IComparable
|
||||
*
|
||||
* @param ITextElement $pText Rich text element
|
||||
*
|
||||
* @return RichText
|
||||
* @return $this
|
||||
*/
|
||||
public function addText(ITextElement $pText)
|
||||
{
|
||||
@ -133,7 +133,7 @@ class RichText implements IComparable
|
||||
*
|
||||
* @param ITextElement[] $textElements Array of elements
|
||||
*
|
||||
* @return RichText
|
||||
* @return $this
|
||||
*/
|
||||
public function setRichTextElements(array $textElements)
|
||||
{
|
||||
|
||||
@ -40,7 +40,7 @@ class Run extends TextElement implements ITextElement
|
||||
*
|
||||
* @param Font $pFont Font
|
||||
*
|
||||
* @return ITextElement
|
||||
* @return $this
|
||||
*/
|
||||
public function setFont(Font $pFont = null)
|
||||
{
|
||||
|
||||
@ -37,7 +37,7 @@ class TextElement implements ITextElement
|
||||
*
|
||||
* @param $text string Text
|
||||
*
|
||||
* @return ITextElement
|
||||
* @return $this
|
||||
*/
|
||||
public function setText($text)
|
||||
{
|
||||
|
||||
@ -30,7 +30,6 @@ class Settings
|
||||
* 7.2 < 7.2.1
|
||||
* 7.1 < 7.1.13
|
||||
* 7.0 < 7.0.27
|
||||
* 5.6 ANY
|
||||
* then you may need to disable this check to prevent unwanted behaviour in other threads
|
||||
* SECURITY WARNING: Changing this flag is not recommended.
|
||||
*
|
||||
@ -122,7 +121,6 @@ class Settings
|
||||
* 7.2 < 7.2.1
|
||||
* 7.1 < 7.1.13
|
||||
* 7.0 < 7.0.27
|
||||
* 5.6 ANY
|
||||
* then you may need to disable this check to prevent unwanted behaviour in other threads
|
||||
* SECURITY WARNING: Changing this flag to false is not recommended.
|
||||
*
|
||||
|
||||
@ -298,9 +298,7 @@ class Font
|
||||
$upperLeftCornerX = $textBox[6];
|
||||
|
||||
// Consider the rotation when calculating the width
|
||||
$textWidth = max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
|
||||
|
||||
return $textWidth;
|
||||
return max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -174,7 +174,7 @@ class Matrix
|
||||
switch ($match) {
|
||||
//A($i0...; $j0...)
|
||||
case 'integer,integer':
|
||||
list($i0, $j0) = $args;
|
||||
[$i0, $j0] = $args;
|
||||
if ($i0 >= 0) {
|
||||
$m = $this->m - $i0;
|
||||
} else {
|
||||
@ -197,7 +197,7 @@ class Matrix
|
||||
break;
|
||||
//A($i0...$iF; $j0...$jF)
|
||||
case 'integer,integer,integer,integer':
|
||||
list($i0, $iF, $j0, $jF) = $args;
|
||||
[$i0, $iF, $j0, $jF] = $args;
|
||||
if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
|
||||
$m = $iF - $i0;
|
||||
} else {
|
||||
@ -220,7 +220,7 @@ class Matrix
|
||||
break;
|
||||
//$R = array of row indices; $C = array of column indices
|
||||
case 'array,array':
|
||||
list($RL, $CL) = $args;
|
||||
[$RL, $CL] = $args;
|
||||
if (count($RL) > 0) {
|
||||
$m = count($RL);
|
||||
} else {
|
||||
@ -243,7 +243,7 @@ class Matrix
|
||||
break;
|
||||
//A($i0...$iF); $CL = array of column indices
|
||||
case 'integer,integer,array':
|
||||
list($i0, $iF, $CL) = $args;
|
||||
[$i0, $iF, $CL] = $args;
|
||||
if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
|
||||
$m = $iF - $i0;
|
||||
} else {
|
||||
@ -266,7 +266,7 @@ class Matrix
|
||||
break;
|
||||
//$RL = array of row indices
|
||||
case 'array,integer,integer':
|
||||
list($RL, $j0, $jF) = $args;
|
||||
[$RL, $j0, $jF] = $args;
|
||||
if (count($RL) > 0) {
|
||||
$m = count($RL);
|
||||
} else {
|
||||
@ -524,7 +524,7 @@ class Matrix
|
||||
*
|
||||
* @param mixed $B Matrix/Array
|
||||
*
|
||||
* @return Matrix Sum
|
||||
* @return $this
|
||||
*/
|
||||
public function plusEquals(...$args)
|
||||
{
|
||||
@ -628,7 +628,7 @@ class Matrix
|
||||
*
|
||||
* @param mixed $B Matrix/Array
|
||||
*
|
||||
* @return Matrix Sum
|
||||
* @return $this
|
||||
*/
|
||||
public function minusEquals(...$args)
|
||||
{
|
||||
@ -734,7 +734,7 @@ class Matrix
|
||||
*
|
||||
* @param mixed $B Matrix/Array
|
||||
*
|
||||
* @return Matrix Matrix Aij
|
||||
* @return $this
|
||||
*/
|
||||
public function arrayTimesEquals(...$args)
|
||||
{
|
||||
@ -1091,7 +1091,7 @@ class Matrix
|
||||
*
|
||||
* @param mixed $B Matrix/Array
|
||||
*
|
||||
* @return Matrix Sum
|
||||
* @return $this
|
||||
*/
|
||||
public function power(...$args)
|
||||
{
|
||||
@ -1150,7 +1150,7 @@ class Matrix
|
||||
*
|
||||
* @param mixed $B Matrix/Array
|
||||
*
|
||||
* @return Matrix Sum
|
||||
* @return $this
|
||||
*/
|
||||
public function concat(...$args)
|
||||
{
|
||||
|
||||
@ -60,7 +60,7 @@ class QRDecomposition
|
||||
{
|
||||
if ($A instanceof Matrix) {
|
||||
// Initialize.
|
||||
$this->QR = $A->getArrayCopy();
|
||||
$this->QR = $A->getArray();
|
||||
$this->m = $A->getRowDimension();
|
||||
$this->n = $A->getColumnDimension();
|
||||
// Main loop.
|
||||
|
||||
@ -253,7 +253,7 @@ class OLE
|
||||
*/
|
||||
private static function _readInt1($fh)
|
||||
{
|
||||
list(, $tmp) = unpack('c', fread($fh, 1));
|
||||
[, $tmp] = unpack('c', fread($fh, 1));
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
@ -267,7 +267,7 @@ class OLE
|
||||
*/
|
||||
private static function _readInt2($fh)
|
||||
{
|
||||
list(, $tmp) = unpack('v', fread($fh, 2));
|
||||
[, $tmp] = unpack('v', fread($fh, 2));
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
@ -281,7 +281,7 @@ class OLE
|
||||
*/
|
||||
private static function _readInt4($fh)
|
||||
{
|
||||
list(, $tmp) = unpack('V', fread($fh, 4));
|
||||
[, $tmp] = unpack('V', fread($fh, 4));
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ class Root extends PPS
|
||||
$aList = [];
|
||||
PPS::_savePpsSetPnt($aList, [$this]);
|
||||
// calculate values for header
|
||||
list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo);
|
||||
[$iSBDcnt, $iBBcnt, $iPPScnt] = $this->_calcSize($aList); //, $rhInfo);
|
||||
// Save Header
|
||||
$this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
|
||||
|
||||
@ -149,7 +149,7 @@ class Root extends PPS
|
||||
public function _calcSize(&$raList)
|
||||
{
|
||||
// Calculate Basic Setting
|
||||
list($iSBDcnt, $iBBcnt, $iPPScnt) = [0, 0, 0];
|
||||
[$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
|
||||
$iSmallLen = 0;
|
||||
$iSBcnt = 0;
|
||||
$iCount = count($raList);
|
||||
|
||||
@ -430,9 +430,7 @@ class StringHelper
|
||||
// characters
|
||||
$chars = self::convertEncoding($value, 'UTF-16LE', 'UTF-8');
|
||||
|
||||
$data = pack('vC', $ln, 0x0001) . $chars;
|
||||
|
||||
return $data;
|
||||
return pack('vC', $ln, 0x0001) . $chars;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -211,7 +211,7 @@ class Xls
|
||||
*/
|
||||
public static function oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height)
|
||||
{
|
||||
list($column, $row) = Coordinate::coordinateFromString($coordinates);
|
||||
[$column, $row] = Coordinate::coordinateFromString($coordinates);
|
||||
$col_start = Coordinate::columnIndexFromString($column);
|
||||
$row_start = $row - 1;
|
||||
|
||||
@ -269,7 +269,7 @@ class Xls
|
||||
$startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1);
|
||||
$endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1);
|
||||
|
||||
$twoAnchor = [
|
||||
return [
|
||||
'startCoordinates' => $startCoordinates,
|
||||
'startOffsetX' => $x1,
|
||||
'startOffsetY' => $y1,
|
||||
@ -277,7 +277,5 @@ class Xls
|
||||
'endOffsetX' => $x2,
|
||||
'endOffsetY' => $y2,
|
||||
];
|
||||
|
||||
return $twoAnchor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,7 +721,7 @@ class Spreadsheet
|
||||
{
|
||||
$worksheetCount = count($this->workSheetCollection);
|
||||
for ($i = 0; $i < $worksheetCount; ++$i) {
|
||||
if ($this->workSheetCollection[$i]->getTitle() === $pName) {
|
||||
if ($this->workSheetCollection[$i]->getTitle() === trim($pName, "'")) {
|
||||
return $this->workSheetCollection[$i];
|
||||
}
|
||||
}
|
||||
@ -955,7 +955,7 @@ class Spreadsheet
|
||||
* @param string $namedRange
|
||||
* @param null|Worksheet $pSheet scope: use null for global scope
|
||||
*
|
||||
* @return Spreadsheet
|
||||
* @return $this
|
||||
*/
|
||||
public function removeNamedRange($namedRange, Worksheet $pSheet = null)
|
||||
{
|
||||
|
||||
@ -140,7 +140,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -193,7 +193,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @param string $pValue see self::HORIZONTAL_*
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setHorizontal($pValue)
|
||||
{
|
||||
@ -230,7 +230,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @param string $pValue see self::VERTICAL_*
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setVertical($pValue)
|
||||
{
|
||||
@ -269,7 +269,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setTextRotation($pValue)
|
||||
{
|
||||
@ -312,7 +312,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setWrapText($pValue)
|
||||
{
|
||||
@ -348,7 +348,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setShrinkToFit($pValue)
|
||||
{
|
||||
@ -384,7 +384,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setIndent($pValue)
|
||||
{
|
||||
@ -424,7 +424,7 @@ class Alignment extends Supervisor
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return Alignment
|
||||
* @return $this
|
||||
*/
|
||||
public function setReadOrder($pValue)
|
||||
{
|
||||
|
||||
@ -127,7 +127,7 @@ class Border extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Border
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -166,7 +166,7 @@ class Border extends Supervisor
|
||||
* When passing a boolean, FALSE equates Border::BORDER_NONE
|
||||
* and TRUE to Border::BORDER_MEDIUM
|
||||
*
|
||||
* @return Border
|
||||
* @return $this
|
||||
*/
|
||||
public function setBorderStyle($pValue)
|
||||
{
|
||||
@ -202,7 +202,7 @@ class Border extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Border
|
||||
* @return $this
|
||||
*/
|
||||
public function setColor(Color $pValue)
|
||||
{
|
||||
|
||||
@ -197,7 +197,7 @@ class Borders extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Borders
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -382,7 +382,7 @@ class Borders extends Supervisor
|
||||
*
|
||||
* @param int $pValue see self::DIAGONAL_*
|
||||
*
|
||||
* @return Borders
|
||||
* @return $this
|
||||
*/
|
||||
public function setDiagonalDirection($pValue)
|
||||
{
|
||||
|
||||
@ -6,6 +6,17 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||
|
||||
class Color extends Supervisor
|
||||
{
|
||||
const NAMED_COLORS = [
|
||||
'Black',
|
||||
'White',
|
||||
'Red',
|
||||
'Green',
|
||||
'Blue',
|
||||
'Yellow',
|
||||
'Magenta',
|
||||
'Cyan',
|
||||
];
|
||||
|
||||
// Colors
|
||||
const COLOR_BLACK = 'FF000000';
|
||||
const COLOR_WHITE = 'FFFFFFFF';
|
||||
@ -95,7 +106,7 @@ class Color extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Color
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -132,7 +143,7 @@ class Color extends Supervisor
|
||||
*
|
||||
* @param string $pValue see self::COLOR_*
|
||||
*
|
||||
* @return Color
|
||||
* @return $this
|
||||
*/
|
||||
public function setARGB($pValue)
|
||||
{
|
||||
@ -168,7 +179,7 @@ class Color extends Supervisor
|
||||
*
|
||||
* @param string $pValue RGB value
|
||||
*
|
||||
* @return Color
|
||||
* @return $this
|
||||
*/
|
||||
public function setRGB($pValue)
|
||||
{
|
||||
@ -198,11 +209,8 @@ class Color extends Supervisor
|
||||
private static function getColourComponent($RGB, $offset, $hex = true)
|
||||
{
|
||||
$colour = substr($RGB, $offset, 2);
|
||||
if (!$hex) {
|
||||
$colour = hexdec($colour);
|
||||
}
|
||||
|
||||
return $colour;
|
||||
return ($hex) ? $colour : hexdec($colour);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -257,7 +265,7 @@ class Color extends Supervisor
|
||||
*/
|
||||
public static function changeBrightness($hex, $adjustPercentage)
|
||||
{
|
||||
$rgba = (strlen($hex) == 8);
|
||||
$rgba = (strlen($hex) === 8);
|
||||
|
||||
$red = self::getRed($hex, false);
|
||||
$green = self::getGreen($hex, false);
|
||||
@ -289,9 +297,9 @@ class Color extends Supervisor
|
||||
}
|
||||
|
||||
$rgb = strtoupper(
|
||||
str_pad(dechex($red), 2, '0', 0) .
|
||||
str_pad(dechex($green), 2, '0', 0) .
|
||||
str_pad(dechex($blue), 2, '0', 0)
|
||||
str_pad(dechex((int) $red), 2, '0', 0) .
|
||||
str_pad(dechex((int) $green), 2, '0', 0) .
|
||||
str_pad(dechex((int) $blue), 2, '0', 0)
|
||||
);
|
||||
|
||||
return (($rgba) ? 'FF' : '') . $rgb;
|
||||
@ -304,7 +312,7 @@ class Color extends Supervisor
|
||||
* @param bool $background Flag to indicate whether default background or foreground colour
|
||||
* should be returned if the indexed colour doesn't exist
|
||||
*
|
||||
* @return Color
|
||||
* @return self
|
||||
*/
|
||||
public static function indexedColor($pIndex, $background = false)
|
||||
{
|
||||
|
||||
@ -12,6 +12,7 @@ class Conditional implements IComparable
|
||||
const CONDITION_CONTAINSTEXT = 'containsText';
|
||||
const CONDITION_EXPRESSION = 'expression';
|
||||
const CONDITION_CONTAINSBLANKS = 'containsBlanks';
|
||||
const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks';
|
||||
|
||||
// Operator types
|
||||
const OPERATOR_NONE = '';
|
||||
@ -93,7 +94,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param string $pValue Condition type, see self::CONDITION_*
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function setConditionType($pValue)
|
||||
{
|
||||
@ -117,7 +118,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param string $pValue Conditional operator type, see self::OPERATOR_*
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function setOperatorType($pValue)
|
||||
{
|
||||
@ -141,7 +142,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function setText($value)
|
||||
{
|
||||
@ -165,7 +166,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param bool $value
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function setStopIfTrue($value)
|
||||
{
|
||||
@ -189,7 +190,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param string[] $pValue Condition
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function setConditions($pValue)
|
||||
{
|
||||
@ -206,7 +207,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param string $pValue Condition
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function addCondition($pValue)
|
||||
{
|
||||
@ -230,7 +231,7 @@ class Conditional implements IComparable
|
||||
*
|
||||
* @param Style $pValue
|
||||
*
|
||||
* @return Conditional
|
||||
* @return $this
|
||||
*/
|
||||
public function setStyle(Style $pValue = null)
|
||||
{
|
||||
|
||||
@ -141,7 +141,7 @@ class Fill extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Fill
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -188,7 +188,7 @@ class Fill extends Supervisor
|
||||
*
|
||||
* @param string $pValue Fill type, see self::FILL_*
|
||||
*
|
||||
* @return Fill
|
||||
* @return $this
|
||||
*/
|
||||
public function setFillType($pValue)
|
||||
{
|
||||
@ -221,7 +221,7 @@ class Fill extends Supervisor
|
||||
*
|
||||
* @param float $pValue
|
||||
*
|
||||
* @return Fill
|
||||
* @return $this
|
||||
*/
|
||||
public function setRotation($pValue)
|
||||
{
|
||||
@ -252,7 +252,7 @@ class Fill extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Fill
|
||||
* @return $this
|
||||
*/
|
||||
public function setStartColor(Color $pValue)
|
||||
{
|
||||
@ -286,7 +286,7 @@ class Fill extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Fill
|
||||
* @return $this
|
||||
*/
|
||||
public function setEndColor(Color $pValue)
|
||||
{
|
||||
@ -313,12 +313,13 @@ class Fill extends Supervisor
|
||||
if ($this->isSupervisor) {
|
||||
return $this->getSharedComponent()->getHashCode();
|
||||
}
|
||||
|
||||
// Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
|
||||
// different hashes if we don't explicitly prevent this
|
||||
return md5(
|
||||
$this->getFillType() .
|
||||
$this->getRotation() .
|
||||
$this->getStartColor()->getHashCode() .
|
||||
$this->getEndColor()->getHashCode() .
|
||||
($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
|
||||
($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
|
||||
__CLASS__
|
||||
);
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -219,7 +219,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param string $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($pValue)
|
||||
{
|
||||
@ -255,7 +255,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param float $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setSize($pValue)
|
||||
{
|
||||
@ -291,7 +291,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setBold($pValue)
|
||||
{
|
||||
@ -327,7 +327,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setItalic($pValue)
|
||||
{
|
||||
@ -363,7 +363,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setSuperscript($pValue)
|
||||
{
|
||||
@ -400,7 +400,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setSubscript($pValue)
|
||||
{
|
||||
@ -439,7 +439,7 @@ class Font extends Supervisor
|
||||
* If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
|
||||
* false equates to UNDERLINE_NONE
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setUnderline($pValue)
|
||||
{
|
||||
@ -477,7 +477,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setStrikethrough($pValue)
|
||||
{
|
||||
@ -512,7 +512,7 @@ class Font extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Font
|
||||
* @return $this
|
||||
*/
|
||||
public function setColor(Color $pValue)
|
||||
{
|
||||
|
||||
@ -23,8 +23,8 @@ class NumberFormat extends Supervisor
|
||||
const FORMAT_PERCENTAGE_00 = '0.00%';
|
||||
|
||||
const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
|
||||
const FORMAT_DATE_YYYYMMDD = 'yy-mm-dd';
|
||||
const FORMAT_DATE_DDMMYYYY = 'dd/mm/yy';
|
||||
const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
|
||||
const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
|
||||
const FORMAT_DATE_DMYSLASH = 'd/m/yy';
|
||||
const FORMAT_DATE_DMYMINUS = 'd-m-yy';
|
||||
const FORMAT_DATE_DMMINUS = 'd-m';
|
||||
@ -43,7 +43,7 @@ class NumberFormat extends Supervisor
|
||||
const FORMAT_DATE_TIME6 = 'h:mm:ss';
|
||||
const FORMAT_DATE_TIME7 = 'i:s.S';
|
||||
const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
|
||||
const FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd;@';
|
||||
const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
|
||||
|
||||
const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
|
||||
const FORMAT_CURRENCY_USD = '$#,##0_-';
|
||||
@ -139,7 +139,7 @@ class NumberFormat extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return NumberFormat
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -176,7 +176,7 @@ class NumberFormat extends Supervisor
|
||||
*
|
||||
* @param string $pValue see self::FORMAT_*
|
||||
*
|
||||
* @return NumberFormat
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormatCode($pValue)
|
||||
{
|
||||
@ -213,7 +213,7 @@ class NumberFormat extends Supervisor
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return NumberFormat
|
||||
* @return $this
|
||||
*/
|
||||
public function setBuiltInFormatCode($pValue)
|
||||
{
|
||||
@ -367,7 +367,7 @@ class NumberFormat extends Supervisor
|
||||
self::fillBuiltInFormatCodes();
|
||||
|
||||
// Lookup format code
|
||||
if (isset(self::$flippedBuiltInFormats[$formatCode])) {
|
||||
if (array_key_exists($formatCode, self::$flippedBuiltInFormats)) {
|
||||
return self::$flippedBuiltInFormats[$formatCode];
|
||||
}
|
||||
|
||||
@ -551,24 +551,32 @@ class NumberFormat extends Supervisor
|
||||
}
|
||||
}
|
||||
|
||||
private static function complexNumberFormatMask($number, $mask)
|
||||
private static function mergeComplexNumberFormatMasks($numbers, $masks)
|
||||
{
|
||||
$sign = ($number < 0.0);
|
||||
$number = abs($number);
|
||||
if (strpos($mask, '.') !== false) {
|
||||
$numbers = explode('.', $number . '.0');
|
||||
$masks = explode('.', $mask . '.0');
|
||||
$result1 = self::complexNumberFormatMask($numbers[0], $masks[0]);
|
||||
$result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1])));
|
||||
$decimalCount = strlen($numbers[1]);
|
||||
$postDecimalMasks = [];
|
||||
|
||||
return (($sign) ? '-' : '') . $result1 . '.' . $result2;
|
||||
}
|
||||
do {
|
||||
$tempMask = array_pop($masks);
|
||||
$postDecimalMasks[] = $tempMask;
|
||||
$decimalCount -= strlen($tempMask);
|
||||
} while ($decimalCount > 0);
|
||||
|
||||
$r = preg_match_all('/0+/', $mask, $result, PREG_OFFSET_CAPTURE);
|
||||
if ($r > 1) {
|
||||
$result = array_reverse($result[0]);
|
||||
return [
|
||||
implode('.', $masks),
|
||||
implode('.', array_reverse($postDecimalMasks)),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($result as $block) {
|
||||
private static function processComplexNumberFormatMask($number, $mask)
|
||||
{
|
||||
$result = $number;
|
||||
$maskingBlockCount = preg_match_all('/0+/', $mask, $maskingBlocks, PREG_OFFSET_CAPTURE);
|
||||
|
||||
if ($maskingBlockCount > 1) {
|
||||
$maskingBlocks = array_reverse($maskingBlocks[0]);
|
||||
|
||||
foreach ($maskingBlocks as $block) {
|
||||
$divisor = 1 . $block[0];
|
||||
$size = strlen($block[0]);
|
||||
$offset = $block[1];
|
||||
@ -584,13 +592,134 @@ class NumberFormat extends Supervisor
|
||||
$mask = substr_replace($mask, $number, $offset, 0);
|
||||
}
|
||||
$result = $mask;
|
||||
} else {
|
||||
$result = $number;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function complexNumberFormatMask($number, $mask, $splitOnPoint = true)
|
||||
{
|
||||
$sign = ($number < 0.0);
|
||||
$number = abs($number);
|
||||
|
||||
if ($splitOnPoint && strpos($mask, '.') !== false && strpos($number, '.') !== false) {
|
||||
$numbers = explode('.', $number);
|
||||
$masks = explode('.', $mask);
|
||||
if (count($masks) > 2) {
|
||||
$masks = self::mergeComplexNumberFormatMasks($numbers, $masks);
|
||||
}
|
||||
$result1 = self::complexNumberFormatMask($numbers[0], $masks[0], false);
|
||||
$result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1]), false));
|
||||
|
||||
return (($sign) ? '-' : '') . $result1 . '.' . $result2;
|
||||
}
|
||||
|
||||
$result = self::processComplexNumberFormatMask($number, $mask);
|
||||
|
||||
return (($sign) ? '-' : '') . $result;
|
||||
}
|
||||
|
||||
private static function formatStraightNumericValue($value, $format, array $matches, $useThousands, $number_regex)
|
||||
{
|
||||
$left = $matches[1];
|
||||
$dec = $matches[2];
|
||||
$right = $matches[3];
|
||||
|
||||
// minimun width of formatted number (including dot)
|
||||
$minWidth = strlen($left) + strlen($dec) + strlen($right);
|
||||
if ($useThousands) {
|
||||
$value = number_format(
|
||||
$value,
|
||||
strlen($right),
|
||||
StringHelper::getDecimalSeparator(),
|
||||
StringHelper::getThousandsSeparator()
|
||||
);
|
||||
$value = preg_replace($number_regex, $value, $format);
|
||||
} else {
|
||||
if (preg_match('/[0#]E[+-]0/i', $format)) {
|
||||
// Scientific format
|
||||
$value = sprintf('%5.2E', $value);
|
||||
} elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) {
|
||||
if ($value == (int) $value && substr_count($format, '.') === 1) {
|
||||
$value *= 10 ** strlen(explode('.', $format)[1]);
|
||||
}
|
||||
$value = self::complexNumberFormatMask($value, $format);
|
||||
} else {
|
||||
$sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
|
||||
$value = sprintf($sprintf_pattern, $value);
|
||||
$value = preg_replace($number_regex, $value, $format);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private static function formatAsNumber($value, $format)
|
||||
{
|
||||
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
||||
return 'EUR ' . sprintf('%1.2f', $value);
|
||||
}
|
||||
|
||||
// Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
|
||||
$format = str_replace(['"', '*'], '', $format);
|
||||
|
||||
// Find out if we need thousands separator
|
||||
// This is indicated by a comma enclosed by a digit placeholder:
|
||||
// #,# or 0,0
|
||||
$useThousands = preg_match('/(#,#|0,0)/', $format);
|
||||
if ($useThousands) {
|
||||
$format = preg_replace('/0,0/', '00', $format);
|
||||
$format = preg_replace('/#,#/', '##', $format);
|
||||
}
|
||||
|
||||
// Scale thousands, millions,...
|
||||
// This is indicated by a number of commas after a digit placeholder:
|
||||
// #, or 0.0,,
|
||||
$scale = 1; // same as no scale
|
||||
$matches = [];
|
||||
if (preg_match('/(#|0)(,+)/', $format, $matches)) {
|
||||
$scale = pow(1000, strlen($matches[2]));
|
||||
|
||||
// strip the commas
|
||||
$format = preg_replace('/0,+/', '0', $format);
|
||||
$format = preg_replace('/#,+/', '#', $format);
|
||||
}
|
||||
|
||||
if (preg_match('/#?.*\?\/\?/', $format, $m)) {
|
||||
if ($value != (int) $value) {
|
||||
self::formatAsFraction($value, $format);
|
||||
}
|
||||
} else {
|
||||
// Handle the number itself
|
||||
|
||||
// scale number
|
||||
$value = $value / $scale;
|
||||
// Strip #
|
||||
$format = preg_replace('/\\#/', '0', $format);
|
||||
// Remove locale code [$-###]
|
||||
$format = preg_replace('/\[\$\-.*\]/', '', $format);
|
||||
|
||||
$n = '/\\[[^\\]]+\\]/';
|
||||
$m = preg_replace($n, '', $format);
|
||||
$number_regex = '/(0+)(\\.?)(0*)/';
|
||||
if (preg_match($number_regex, $m, $matches)) {
|
||||
$value = self::formatStraightNumericValue($value, $format, $matches, $useThousands, $number_regex);
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
|
||||
// Currency or Accounting
|
||||
$currencyCode = $m[1];
|
||||
[$currencyCode] = explode('-', $currencyCode);
|
||||
if ($currencyCode == '') {
|
||||
$currencyCode = StringHelper::getCurrencyCode();
|
||||
}
|
||||
$value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value in a pre-defined format to a PHP string.
|
||||
*
|
||||
@ -660,7 +789,9 @@ class NumberFormat extends Supervisor
|
||||
|
||||
// Save format with color information for later use below
|
||||
$formatColor = $format;
|
||||
|
||||
// Strip colour information
|
||||
$color_regex = '/\[(' . implode('|', Color::NAMED_COLORS) . ')\]/';
|
||||
$format = preg_replace($color_regex, '', $format);
|
||||
// Let's begin inspecting the format and converting the value to a formatted string
|
||||
|
||||
// Check for date/time characters (not inside quotes)
|
||||
@ -668,105 +799,19 @@ class NumberFormat extends Supervisor
|
||||
// datetime format
|
||||
self::formatAsDate($value, $format);
|
||||
} else {
|
||||
// Strip color information
|
||||
$color_regex = '/^\\[[a-zA-Z]+\\]/';
|
||||
$format = preg_replace($color_regex, '', $format);
|
||||
if (preg_match('/%$/', $format)) {
|
||||
if (substr($format, 0, 1) === '"' && substr($format, -1, 1) === '"') {
|
||||
$value = substr($format, 1, -1);
|
||||
} elseif (preg_match('/%$/', $format)) {
|
||||
// % number format
|
||||
self::formatAsPercentage($value, $format);
|
||||
} else {
|
||||
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
||||
$value = 'EUR ' . sprintf('%1.2f', $value);
|
||||
} else {
|
||||
// Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
|
||||
$format = str_replace(['"', '*'], '', $format);
|
||||
|
||||
// Find out if we need thousands separator
|
||||
// This is indicated by a comma enclosed by a digit placeholder:
|
||||
// #,# or 0,0
|
||||
$useThousands = preg_match('/(#,#|0,0)/', $format);
|
||||
if ($useThousands) {
|
||||
$format = preg_replace('/0,0/', '00', $format);
|
||||
$format = preg_replace('/#,#/', '##', $format);
|
||||
}
|
||||
|
||||
// Scale thousands, millions,...
|
||||
// This is indicated by a number of commas after a digit placeholder:
|
||||
// #, or 0.0,,
|
||||
$scale = 1; // same as no scale
|
||||
$matches = [];
|
||||
if (preg_match('/(#|0)(,+)/', $format, $matches)) {
|
||||
$scale = pow(1000, strlen($matches[2]));
|
||||
|
||||
// strip the commas
|
||||
$format = preg_replace('/0,+/', '0', $format);
|
||||
$format = preg_replace('/#,+/', '#', $format);
|
||||
}
|
||||
|
||||
if (preg_match('/#?.*\?\/\?/', $format, $m)) {
|
||||
if ($value != (int) $value) {
|
||||
self::formatAsFraction($value, $format);
|
||||
}
|
||||
} else {
|
||||
// Handle the number itself
|
||||
|
||||
// scale number
|
||||
$value = $value / $scale;
|
||||
|
||||
// Strip #
|
||||
$format = preg_replace('/\\#/', '0', $format);
|
||||
|
||||
// Remove locale code [$-###]
|
||||
$format = preg_replace('/\[\$\-.*\]/', '', $format);
|
||||
|
||||
$n = '/\\[[^\\]]+\\]/';
|
||||
$m = preg_replace($n, '', $format);
|
||||
$number_regex = '/(0+)(\\.?)(0*)/';
|
||||
if (preg_match($number_regex, $m, $matches)) {
|
||||
$left = $matches[1];
|
||||
$dec = $matches[2];
|
||||
$right = $matches[3];
|
||||
|
||||
// minimun width of formatted number (including dot)
|
||||
$minWidth = strlen($left) + strlen($dec) + strlen($right);
|
||||
if ($useThousands) {
|
||||
$value = number_format(
|
||||
$value,
|
||||
strlen($right),
|
||||
StringHelper::getDecimalSeparator(),
|
||||
StringHelper::getThousandsSeparator()
|
||||
);
|
||||
$value = preg_replace($number_regex, $value, $format);
|
||||
} else {
|
||||
if (preg_match('/[0#]E[+-]0/i', $format)) {
|
||||
// Scientific format
|
||||
$value = sprintf('%5.2E', $value);
|
||||
} elseif (preg_match('/0([^\d\.]+)0/', $format)) {
|
||||
$value = self::complexNumberFormatMask($value, $format);
|
||||
} else {
|
||||
$sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
|
||||
$value = sprintf($sprintf_pattern, $value);
|
||||
$value = preg_replace($number_regex, $value, $format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
|
||||
// Currency or Accounting
|
||||
$currencyCode = $m[1];
|
||||
list($currencyCode) = explode('-', $currencyCode);
|
||||
if ($currencyCode == '') {
|
||||
$currencyCode = StringHelper::getCurrencyCode();
|
||||
}
|
||||
$value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
|
||||
}
|
||||
}
|
||||
$value = self::formatAsNumber($value, $format);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional formatting provided by callback function
|
||||
if ($callBack !== null) {
|
||||
list($writerInstance, $function) = $callBack;
|
||||
[$writerInstance, $function] = $callBack;
|
||||
$value = $writerInstance->$function($value, $formatColor);
|
||||
}
|
||||
|
||||
|
||||
@ -86,7 +86,7 @@ class Protection extends Supervisor
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Protection
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles)
|
||||
{
|
||||
@ -123,7 +123,7 @@ class Protection extends Supervisor
|
||||
*
|
||||
* @param string $pValue see self::PROTECTION_*
|
||||
*
|
||||
* @return Protection
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocked($pValue)
|
||||
{
|
||||
@ -156,7 +156,7 @@ class Protection extends Supervisor
|
||||
*
|
||||
* @param string $pValue see self::PROTECTION_*
|
||||
*
|
||||
* @return Protection
|
||||
* @return $this
|
||||
*/
|
||||
public function setHidden($pValue)
|
||||
{
|
||||
|
||||
@ -189,7 +189,7 @@ class Style extends Supervisor
|
||||
* @param array $pStyles Array containing style information
|
||||
* @param bool $pAdvanced advanced mode for setting borders
|
||||
*
|
||||
* @return Style
|
||||
* @return $this
|
||||
*/
|
||||
public function applyFromArray(array $pStyles, $pAdvanced = true)
|
||||
{
|
||||
@ -204,7 +204,7 @@ class Style extends Supervisor
|
||||
$rangeA = $pRange;
|
||||
$rangeB = $pRange;
|
||||
} else {
|
||||
list($rangeA, $rangeB) = explode(':', $pRange);
|
||||
[$rangeA, $rangeB] = explode(':', $pRange);
|
||||
}
|
||||
|
||||
// Calculate range outer borders
|
||||
@ -485,7 +485,7 @@ class Style extends Supervisor
|
||||
*
|
||||
* @param Font $font
|
||||
*
|
||||
* @return Style
|
||||
* @return $this
|
||||
*/
|
||||
public function setFont(Font $font)
|
||||
{
|
||||
@ -539,7 +539,7 @@ class Style extends Supervisor
|
||||
*
|
||||
* @param Conditional[] $pValue Array of conditional styles
|
||||
*
|
||||
* @return Style
|
||||
* @return $this
|
||||
*/
|
||||
public function setConditionalStyles(array $pValue)
|
||||
{
|
||||
@ -577,7 +577,7 @@ class Style extends Supervisor
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return Style
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuotePrefix($pValue)
|
||||
{
|
||||
|
||||
@ -48,7 +48,7 @@ abstract class Supervisor implements IComparable
|
||||
* @param Spreadsheet|Style $parent
|
||||
* @param null|string $parentPropertyName
|
||||
*
|
||||
* @return Supervisor
|
||||
* @return $this
|
||||
*/
|
||||
public function bindParent($parent, $parentPropertyName = null)
|
||||
{
|
||||
|
||||
@ -59,7 +59,7 @@ class AutoFilter
|
||||
*
|
||||
* @param Worksheet $pSheet
|
||||
*
|
||||
* @return AutoFilter
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(Worksheet $pSheet = null)
|
||||
{
|
||||
@ -85,12 +85,12 @@ class AutoFilter
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return AutoFilter
|
||||
* @return $this
|
||||
*/
|
||||
public function setRange($pRange)
|
||||
{
|
||||
// extract coordinate
|
||||
list($worksheet, $pRange) = Worksheet::extractSheetTitle($pRange, true);
|
||||
[$worksheet, $pRange] = Worksheet::extractSheetTitle($pRange, true);
|
||||
|
||||
if (strpos($pRange, ':') !== false) {
|
||||
$this->range = $pRange;
|
||||
@ -105,7 +105,7 @@ class AutoFilter
|
||||
$this->columns = [];
|
||||
} else {
|
||||
// Discard any column rules that are no longer valid within this range
|
||||
list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
|
||||
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||
foreach ($this->columns as $key => $value) {
|
||||
$colIndex = Coordinate::columnIndexFromString($key);
|
||||
if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
|
||||
@ -143,7 +143,7 @@ class AutoFilter
|
||||
}
|
||||
|
||||
$columnIndex = Coordinate::columnIndexFromString($column);
|
||||
list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
|
||||
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||
if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
|
||||
throw new PhpSpreadsheetException('Column is outside of current autofilter range.');
|
||||
}
|
||||
@ -196,7 +196,7 @@ class AutoFilter
|
||||
*/
|
||||
public function getColumnByOffset($pColumnOffset)
|
||||
{
|
||||
list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
|
||||
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||
$pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $pColumnOffset);
|
||||
|
||||
return $this->getColumn($pColumn);
|
||||
@ -210,7 +210,7 @@ class AutoFilter
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return AutoFilter
|
||||
* @return $this
|
||||
*/
|
||||
public function setColumn($pColumn)
|
||||
{
|
||||
@ -241,7 +241,7 @@ class AutoFilter
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return AutoFilter
|
||||
* @return $this
|
||||
*/
|
||||
public function clearColumn($pColumn)
|
||||
{
|
||||
@ -264,7 +264,7 @@ class AutoFilter
|
||||
* @param string $fromColumn Column name (e.g. A)
|
||||
* @param string $toColumn Column name (e.g. B)
|
||||
*
|
||||
* @return AutoFilter
|
||||
* @return $this
|
||||
*/
|
||||
public function shiftColumn($fromColumn, $toColumn)
|
||||
{
|
||||
@ -357,7 +357,7 @@ class AutoFilter
|
||||
{
|
||||
$dataSet = $ruleSet['filterRules'];
|
||||
$join = $ruleSet['join'];
|
||||
$customRuleForBlanks = isset($ruleSet['customRuleForBlanks']) ? $ruleSet['customRuleForBlanks'] : false;
|
||||
$customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
|
||||
|
||||
if (!$customRuleForBlanks) {
|
||||
// Blank cells are always ignored, so return a FALSE
|
||||
@ -617,11 +617,11 @@ class AutoFilter
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return AutoFilter
|
||||
* @return $this
|
||||
*/
|
||||
public function showHideRows()
|
||||
{
|
||||
list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
|
||||
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||
|
||||
// The heading row should always be visible
|
||||
$this->workSheet->getRowDimension($rangeStart[1])->setVisible(true);
|
||||
|
||||
@ -101,7 +101,7 @@ class Column
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AutoFilter Column Index.
|
||||
* Get AutoFilter column index as string eg: 'A'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -111,13 +111,13 @@ class Column
|
||||
}
|
||||
|
||||
/**
|
||||
* Set AutoFilter Column Index.
|
||||
* Set AutoFilter column index as string eg: 'A'.
|
||||
*
|
||||
* @param string $pColumn Column (e.g. A)
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function setColumnIndex($pColumn)
|
||||
{
|
||||
@ -147,7 +147,7 @@ class Column
|
||||
*
|
||||
* @param AutoFilter $pParent
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(AutoFilter $pParent = null)
|
||||
{
|
||||
@ -173,7 +173,7 @@ class Column
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function setFilterType($pFilterType)
|
||||
{
|
||||
@ -203,7 +203,7 @@ class Column
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function setJoin($pJoin)
|
||||
{
|
||||
@ -223,7 +223,7 @@ class Column
|
||||
*
|
||||
* @param string[] $attributes
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttributes(array $attributes)
|
||||
{
|
||||
@ -238,7 +238,7 @@ class Column
|
||||
* @param string $pName Attribute Name
|
||||
* @param string $pValue Attribute Value
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($pName, $pValue)
|
||||
{
|
||||
@ -316,7 +316,7 @@ class Column
|
||||
*
|
||||
* @param Column\Rule $pRule
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function addRule(Column\Rule $pRule)
|
||||
{
|
||||
@ -332,7 +332,7 @@ class Column
|
||||
*
|
||||
* @param int $pIndex Rule index in the ruleset array
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function deleteRule($pIndex)
|
||||
{
|
||||
@ -350,7 +350,7 @@ class Column
|
||||
/**
|
||||
* Delete all AutoFilter Column Rules.
|
||||
*
|
||||
* @return Column
|
||||
* @return $this
|
||||
*/
|
||||
public function clearRules()
|
||||
{
|
||||
|
||||
@ -262,7 +262,7 @@ class Rule
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Rule
|
||||
* @return $this
|
||||
*/
|
||||
public function setRuleType($pRuleType)
|
||||
{
|
||||
@ -292,7 +292,7 @@ class Rule
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Rule
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($pValue)
|
||||
{
|
||||
@ -336,7 +336,7 @@ class Rule
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Rule
|
||||
* @return $this
|
||||
*/
|
||||
public function setOperator($pOperator)
|
||||
{
|
||||
@ -369,7 +369,7 @@ class Rule
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Rule
|
||||
* @return $this
|
||||
*/
|
||||
public function setGrouping($pGrouping)
|
||||
{
|
||||
@ -393,7 +393,7 @@ class Rule
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return Rule
|
||||
* @return $this
|
||||
*/
|
||||
public function setRule($pOperator, $pValue, $pGrouping = null)
|
||||
{
|
||||
@ -424,7 +424,7 @@ class Rule
|
||||
*
|
||||
* @param Column $pParent
|
||||
*
|
||||
* @return Rule
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(Column $pParent = null)
|
||||
{
|
||||
|
||||
@ -154,7 +154,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param string $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($pValue)
|
||||
{
|
||||
@ -178,7 +178,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param string $description
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
@ -205,7 +205,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @throws PhpSpreadsheetException
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setWorksheet(Worksheet $pValue = null, $pOverrideOld = false)
|
||||
{
|
||||
@ -253,7 +253,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param string $pValue eg: 'A1'
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setCoordinates($pValue)
|
||||
{
|
||||
@ -277,7 +277,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setOffsetX($pValue)
|
||||
{
|
||||
@ -301,7 +301,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setOffsetY($pValue)
|
||||
{
|
||||
@ -325,7 +325,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidth($pValue)
|
||||
{
|
||||
@ -356,7 +356,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeight($pValue)
|
||||
{
|
||||
@ -386,7 +386,7 @@ class BaseDrawing implements IComparable
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidthAndHeight($width, $height)
|
||||
{
|
||||
@ -423,7 +423,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setResizeProportional($pValue)
|
||||
{
|
||||
@ -447,7 +447,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param int $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setRotation($pValue)
|
||||
{
|
||||
@ -471,7 +471,7 @@ class BaseDrawing implements IComparable
|
||||
*
|
||||
* @param Drawing\Shadow $pValue
|
||||
*
|
||||
* @return BaseDrawing
|
||||
* @return $this
|
||||
*/
|
||||
public function setShadow(Drawing\Shadow $pValue = null)
|
||||
{
|
||||
|
||||
@ -40,7 +40,7 @@ class Column
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column index.
|
||||
* Get column index as string eg: 'A'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user