diff --git a/composer.json b/composer.json
index 75c218a..478de83 100644
--- a/composer.json
+++ b/composer.json
@@ -9,7 +9,8 @@
"require": {
"tinymce/tinymce": "*",
"phpmailer/phpmailer": "dev-master",
- "webklex/php-imap": "^4.1"
+ "webklex/php-imap": "^4.1",
+ "phpoffice/phpspreadsheet": "dev-master"
},
"minimum-stability": "dev"
}
diff --git a/composer.lock b/composer.lock
index bc6392a..ce3ccb9 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "f05155f56696cab73e4da7f772d9cc96",
+ "content-hash": "72b352d0dd8de809a4596cf92be7dab3",
"packages": [
{
"name": "doctrine/inflector",
@@ -363,6 +363,256 @@
},
"time": "2023-06-11T21:11:10+00:00"
},
+ {
+ "name": "maennchen/zipstream-php",
+ "version": "v2.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maennchen/ZipStream-PHP.git",
+ "reference": "620c2aab9d4f109aef4949049cc909c24479f8e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/620c2aab9d4f109aef4949049cc909c24479f8e0",
+ "reference": "620c2aab9d4f109aef4949049cc909c24479f8e0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "myclabs/php-enum": "^1.5",
+ "php": "^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.9",
+ "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+ "mikey179/vfsstream": "^1.6",
+ "php-coveralls/php-coveralls": "^2.4",
+ "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+ "vimeo/psalm": "^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": [
+ "stream",
+ "zip"
+ ],
+ "support": {
+ "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/v2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/maennchen",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/zipstream",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2023-04-19T20:02:44+00:00"
+ },
+ {
+ "name": "markbaker/complex",
+ "version": "3.0.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPComplex.git",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "default-branch": true,
+ "type": "library",
+ "autoload": {
+ "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/3.0.2"
+ },
+ "time": "2022-12-06T16:21:08+00:00"
+ },
+ {
+ "name": "markbaker/matrix",
+ "version": "3.0.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPMatrix.git",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpdocumentor/phpdocumentor": "2.*",
+ "phploc/phploc": "^4.0",
+ "phpmd/phpmd": "2.*",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "sebastian/phpcpd": "^4.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "default-branch": true,
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Matrix\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@demon-angel.eu"
+ }
+ ],
+ "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/3.0.1"
+ },
+ "time": "2022-12-02T22:17:43+00:00"
+ },
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
+ "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "http://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-08-04T09:53:51+00:00"
+ },
{
"name": "nesbot/carbon",
"version": "dev-master",
@@ -551,6 +801,112 @@
],
"time": "2023-10-09T11:27:29+00:00"
},
+ {
+ "name": "phpoffice/phpspreadsheet",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+ "reference": "a17a56b6e01dad0c04e2bfc7ddf5038240a6facf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/a17a56b6e01dad0c04e2bfc7ddf5038240a6facf",
+ "reference": "a17a56b6e01dad0c04e2bfc7ddf5038240a6facf",
+ "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": "*",
+ "maennchen/zipstream-php": "^2.1 || ^3.0",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "php": "^8.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
+ "voku/anti-xss": "^4.1"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+ "dompdf/dompdf": "^2.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "mitoteam/jpgraph": "^10.3",
+ "mpdf/mpdf": "^8.1.1",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.6",
+ "squizlabs/php_codesniffer": "^3.7",
+ "tecnickcom/tcpdf": "^6.5"
+ },
+ "suggest": {
+ "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+ "ext-intl": "PHP Internationalization Functions",
+ "mitoteam/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"
+ },
+ "default-branch": true,
+ "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/master"
+ },
+ "time": "2023-12-26T15:40:38+00:00"
+ },
{
"name": "psr/clock",
"version": "1.0.0",
@@ -647,6 +1003,168 @@
},
"time": "2021-11-05T16:50:12+00:00"
},
+ {
+ "name": "psr/http-client",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "7037f4b0950474e9d1350e8df89b15f1842085f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/7037f4b0950474e9d1350e8df89b15f1842085f6",
+ "reference": "7037f4b0950474e9d1350e8df89b15f1842085f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2023-09-22T11:16:44+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/1.1"
+ },
+ "time": "2023-04-04T09:50:52+00:00"
+ },
{
"name": "psr/simple-cache",
"version": "1.0.1",
@@ -841,6 +1359,257 @@
],
"time": "2023-08-21T07:23:18+00:00"
},
+ {
+ "name": "symfony/polyfill-iconv",
+ "version": "1.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-iconv.git",
+ "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6de50471469b8c9afc38164452ab2b6170ee71c1",
+ "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-iconv": "*"
+ },
+ "suggest": {
+ "ext-iconv": "For best performance"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Iconv\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Iconv extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "iconv",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-iconv/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "1.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "875e90aeea2777b6f135677f618529449334a612"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
+ "reference": "875e90aeea2777b6f135677f618529449334a612",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "1.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
{
"name": "symfony/polyfill-mbstring",
"version": "1.x-dev",
@@ -925,6 +1694,83 @@
],
"time": "2023-07-28T09:04:16+00:00"
},
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "1.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
+ "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-26T09:26:14+00:00"
+ },
{
"name": "symfony/polyfill-php80",
"version": "1.x-dev",
@@ -1244,6 +2090,89 @@
},
"time": "2023-08-30T11:10:35+00:00"
},
+ {
+ "name": "voku/anti-xss",
+ "version": "4.1.35",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/anti-xss.git",
+ "reference": "4d7ec2816cd6eeebb7d5abbde8b0349f66709496"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/anti-xss/zipball/4d7ec2816cd6eeebb7d5abbde8b0349f66709496",
+ "reference": "4d7ec2816cd6eeebb7d5abbde8b0349f66709496",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "voku/portable-utf8": "~6.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "voku\\helper\\": "src/voku/helper/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "EllisLab Dev Team",
+ "homepage": "http://ellislab.com/"
+ },
+ {
+ "name": "Lars Moelleken",
+ "email": "lars@moelleken.org",
+ "homepage": "https://www.moelleken.org/"
+ }
+ ],
+ "description": "anti xss-library",
+ "homepage": "https://github.com/voku/anti-xss",
+ "keywords": [
+ "anti-xss",
+ "clean",
+ "security",
+ "xss"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/anti-xss/issues",
+ "source": "https://github.com/voku/anti-xss/tree/4.1.35"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/anti-xss",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/anti-xss",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-08T15:20:33+00:00"
+ },
{
"name": "voku/portable-ascii",
"version": "1.6.1",
@@ -1318,6 +2247,105 @@
],
"time": "2022-01-24T18:55:24+00:00"
},
+ {
+ "name": "voku/portable-utf8",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-utf8.git",
+ "reference": "e4365df3f7b63f9e57e1015f6c3ceec19c169f7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-utf8/zipball/e4365df3f7b63f9e57e1015f6c3ceec19c169f7a",
+ "reference": "e4365df3f7b63f9e57e1015f6c3ceec19c169f7a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "symfony/polyfill-iconv": "~1.0",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php72": "~1.0",
+ "voku/portable-ascii": "~1.6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-ctype": "Use Ctype for e.g. hexadecimal digit detection",
+ "ext-fileinfo": "Use Fileinfo for better binary file detection",
+ "ext-iconv": "Use iconv for best performance",
+ "ext-intl": "Use Intl for best performance",
+ "ext-json": "Use JSON for string detection",
+ "ext-mbstring": "Use Mbstring for best performance"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "(Apache-2.0 or GPL-2.0)"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Hamid Sarfraz",
+ "homepage": "http://pageconfig.com/"
+ },
+ {
+ "name": "Lars Moelleken",
+ "homepage": "http://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable UTF-8 library - performance optimized (unicode) string functions for php.",
+ "homepage": "https://github.com/voku/portable-utf8",
+ "keywords": [
+ "UTF",
+ "clean",
+ "php",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-utf8/issues",
+ "source": "https://github.com/voku/portable-utf8/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-utf8",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-utf8",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-24T09:49:08+00:00"
+ },
{
"name": "webklex/php-imap",
"version": "4.1.2",
diff --git a/conf/internalpages.conf b/conf/internalpages.conf
index 67e1a2d..6d13141 100644
--- a/conf/internalpages.conf
+++ b/conf/internalpages.conf
@@ -13,6 +13,7 @@
"logout": "",
"savemail": "Ideenboard",
"planboard": "",
- "projectsmanagement": "Förderprojekte"
+ "projectsmanagement": "Förderprojekte",
+ "memberlistdownload": "Download der Mitgliederlist"
}
diff --git a/include/memberlistdownload.php b/include/memberlistdownload.php
new file mode 100644
index 0000000..2c3d79a
--- /dev/null
+++ b/include/memberlistdownload.php
@@ -0,0 +1,64 @@
+loadMembers();
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $columnHeaders = [];
+ $sheet->fromArray([$columnHeaders], null, 'A1');
+ $sheet->fromArray($dataArray, null, 'A2');
+ $writer = new Xlsx($spreadsheet);
+ header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ header('Content-Disposition: attachment;filename="excel_file.xlsx"');
+ header('Cache-Control: max-age=0');
+ $writer->save('php://output');
+ }
+
+ private function loadMembers(): array {
+ $query = <<dbConnection, $query);
+ $result = [];
+ while ($row = mysqli_fetch_assoc($result)) {
+ $result[] = [
+ 'first_name' => $this->decode($row['first_name'], $row['salt']),
+ 'last_name' => $this->decode($row['last_name'], $row['salt']),
+ 'street' => $this->decode($row['street'], $row['salt']),
+ 'zip' => $this->decode($row['zip'], $row['salt']),
+ 'town' => $this->decode($row['town'], $row['salt']),
+ 'birthdate' => $this->decode($row['birthdate'], $row['salt']),
+ 'phone' => $this->decode($row['phone'], $row['salt']),
+ 'email' => $this->decode($row['email'], $row['salt']),
+ 'child_name' => $this->decode($row['child_name'], $row['salt']),
+ 'child_street' => $this->decode($row['child_street'], $row['salt']),
+ 'subscription' => $this->decode($row['subscription'], $row['salt']),
+ 'iban' => $this->decode($row['iban'], $row['salt']),
+ 'bank_name' => $this->decode($row['bank_name'], $row['salt']),
+ 'bic' => $this->decode($row['bic'], $row['salt']),
+ 'account_member_name' => $this->decode($row['account_member_name'], $row['salt']),
+ 'membership_status' => $row['membership_status'],
+ 'membership_start' => $row['membership_start'],
+ 'status_text' => $row['status_text'],
+ 'last_payment' => $row['last_payment'],
+ 'description' => $row['description'],
+ ];
+ }
+ return $result;
+ }
+}
diff --git a/templates/imprint.html b/templates/imprint.html
index c1d8cac..89dc7e8 100644
--- a/templates/imprint.html
+++ b/templates/imprint.html
@@ -3,10 +3,10 @@ Harheimer Weg 16
60437 Frankfurt am Main (Bonames)
Email: foerderverein-AJS@gmx.de
Tel: xxx-xxxxxxx
-Vorstand
+Geschäftsführender Vorstand
Vorsitzender: Lucas Fastabend
- Kassenwartin: Alexandra Grabmann
+ Kassenwartin: Peter Werner
Schriftführer: Torsten Schulz
Amtliche Informationen
diff --git a/templates/members.html b/templates/members.html
index 872e914..73e026d 100644
--- a/templates/members.html
+++ b/templates/members.html
@@ -13,3 +13,4 @@
{{memberlist}}
+Mitgliederliste als Excel herunterladen
diff --git a/vendor/bin/carbon b/vendor/bin/carbon
new file mode 100755
index 0000000..86fbfdf
--- /dev/null
+++ b/vendor/bin/carbon
@@ -0,0 +1,119 @@
+#!/usr/bin/env php
+realpath = realpath($opened_path) ?: $opened_path;
+ $opened_path = $this->realpath;
+ $this->handle = fopen($this->realpath, $mode);
+ $this->position = 0;
+
+ return (bool) $this->handle;
+ }
+
+ public function stream_read($count)
+ {
+ $data = fread($this->handle, $count);
+
+ if ($this->position === 0) {
+ $data = preg_replace('{^#!.*\r?\n}', '', $data);
+ }
+
+ $this->position += strlen($data);
+
+ return $data;
+ }
+
+ public function stream_cast($castAs)
+ {
+ return $this->handle;
+ }
+
+ public function stream_close()
+ {
+ fclose($this->handle);
+ }
+
+ public function stream_lock($operation)
+ {
+ return $operation ? flock($this->handle, $operation) : true;
+ }
+
+ public function stream_seek($offset, $whence)
+ {
+ if (0 === fseek($this->handle, $offset, $whence)) {
+ $this->position = ftell($this->handle);
+ return true;
+ }
+
+ return false;
+ }
+
+ public function stream_tell()
+ {
+ return $this->position;
+ }
+
+ public function stream_eof()
+ {
+ return feof($this->handle);
+ }
+
+ public function stream_stat()
+ {
+ return array();
+ }
+
+ public function stream_set_option($option, $arg1, $arg2)
+ {
+ return true;
+ }
+
+ public function url_stat($path, $flags)
+ {
+ $path = substr($path, 17);
+ if (file_exists($path)) {
+ return stat($path);
+ }
+
+ return false;
+ }
+ }
+ }
+
+ if (
+ (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
+ || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
+ ) {
+ return include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
+ }
+}
+
+return include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 5490b88..3d9765b 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -8,8 +8,9 @@ $baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+ 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
- 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+ 'Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644
index 0000000..5c812ea
--- /dev/null
+++ b/vendor/composer/autoload_files.php
@@ -0,0 +1,20 @@
+ $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+ 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
+ '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
+ '60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
+ 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
+ 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
+ '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
+ 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+ '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
+ '72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
+ 'b46ad4fe52f4d1899a2951c7e6ea56b0' => $vendorDir . '/voku/portable-utf8/bootstrap.php',
+);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index a9966aa..536fa0a 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -6,20 +6,32 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
- 'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
+ 'voku\\helper\\' => array($vendorDir . '/voku/anti-xss/src/voku/helper'),
+ 'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku', $vendorDir . '/voku/portable-utf8/src/voku'),
+ 'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'),
'Webklex\\PHPIMAP\\' => array($vendorDir . '/webklex/php-imap/src'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
+ 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+ 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
+ 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'),
+ 'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
+ 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
+ 'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
- 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/support'),
+ 'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
+ 'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'),
+ 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/support'),
'Illuminate\\Pagination\\' => array($vendorDir . '/illuminate/pagination'),
'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
+ 'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
);
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 85166ea..7a3def4 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -12,14 +12,24 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php',
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
+ 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
+ '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
+ 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
+ '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
+ 'b46ad4fe52f4d1899a2951c7e6ea56b0' => __DIR__ . '/..' . '/voku/portable-utf8/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'v' =>
array (
+ 'voku\\helper\\' => 12,
'voku\\' => 5,
),
+ 'Z' =>
+ array (
+ 'ZipStream\\' => 10,
+ ),
'W' =>
array (
'Webklex\\PHPIMAP\\' => 16,
@@ -27,7 +37,11 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
'S' =>
array (
'Symfony\\Polyfill\\Php80\\' => 23,
+ 'Symfony\\Polyfill\\Php72\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26,
+ 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
+ 'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31,
+ 'Symfony\\Polyfill\\Iconv\\' => 23,
'Symfony\\Contracts\\Translation\\' => 30,
'Symfony\\Component\\Translation\\' => 30,
'Symfony\\Component\\HttpFoundation\\' => 33,
@@ -35,10 +49,18 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
'P' =>
array (
'Psr\\SimpleCache\\' => 16,
+ 'Psr\\Http\\Message\\' => 17,
+ 'Psr\\Http\\Client\\' => 16,
'Psr\\Container\\' => 14,
'Psr\\Clock\\' => 10,
+ 'PhpOffice\\PhpSpreadsheet\\' => 25,
'PHPMailer\\PHPMailer\\' => 20,
),
+ 'M' =>
+ array (
+ 'MyCLabs\\Enum\\' => 13,
+ 'Matrix\\' => 7,
+ ),
'I' =>
array (
'Illuminate\\Support\\' => 19,
@@ -51,14 +73,24 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
),
'C' =>
array (
+ 'Complex\\' => 8,
'Carbon\\' => 7,
),
);
public static $prefixDirsPsr4 = array (
+ 'voku\\helper\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/voku/anti-xss/src/voku/helper',
+ ),
'voku\\' =>
array (
0 => __DIR__ . '/..' . '/voku/portable-ascii/src/voku',
+ 1 => __DIR__ . '/..' . '/voku/portable-utf8/src/voku',
+ ),
+ 'ZipStream\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/maennchen/zipstream-php/src',
),
'Webklex\\PHPIMAP\\' =>
array (
@@ -68,10 +100,26 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
+ 'Symfony\\Polyfill\\Php72\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
+ ),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
+ 'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
+ ),
+ 'Symfony\\Polyfill\\Intl\\Grapheme\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme',
+ ),
+ 'Symfony\\Polyfill\\Iconv\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-iconv',
+ ),
'Symfony\\Contracts\\Translation\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
@@ -88,6 +136,15 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
array (
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
),
+ 'Psr\\Http\\Message\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/http-factory/src',
+ 1 => __DIR__ . '/..' . '/psr/http-message/src',
+ ),
+ 'Psr\\Http\\Client\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/http-client/src',
+ ),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',
@@ -96,14 +153,26 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
array (
0 => __DIR__ . '/..' . '/psr/clock/src',
),
+ 'PhpOffice\\PhpSpreadsheet\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
+ ),
'PHPMailer\\PHPMailer\\' =>
array (
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
),
+ 'MyCLabs\\Enum\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
+ ),
+ 'Matrix\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src',
+ ),
'Illuminate\\Support\\' =>
array (
- 0 => __DIR__ . '/..' . '/illuminate/macroable',
- 1 => __DIR__ . '/..' . '/illuminate/collections',
+ 0 => __DIR__ . '/..' . '/illuminate/collections',
+ 1 => __DIR__ . '/..' . '/illuminate/macroable',
2 => __DIR__ . '/..' . '/illuminate/support',
),
'Illuminate\\Pagination\\' =>
@@ -118,6 +187,10 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
array (
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Inflector',
),
+ 'Complex\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/markbaker/complex/classes/src',
+ ),
'Carbon\\' =>
array (
0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
@@ -127,8 +200,9 @@ class ComposerStaticInitb3a3dfb766a515d49b7f8665bad574b3
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
- 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+ 'Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index d271251..cf95cd1 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -375,6 +375,268 @@
},
"install-path": "../illuminate/support"
},
+ {
+ "name": "maennchen/zipstream-php",
+ "version": "v2.x-dev",
+ "version_normalized": "2.9999999.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maennchen/ZipStream-PHP.git",
+ "reference": "620c2aab9d4f109aef4949049cc909c24479f8e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/620c2aab9d4f109aef4949049cc909c24479f8e0",
+ "reference": "620c2aab9d4f109aef4949049cc909c24479f8e0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "myclabs/php-enum": "^1.5",
+ "php": "^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.9",
+ "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+ "mikey179/vfsstream": "^1.6",
+ "php-coveralls/php-coveralls": "^2.4",
+ "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+ "vimeo/psalm": "^5.0"
+ },
+ "time": "2023-04-19T20:02:44+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": [
+ "stream",
+ "zip"
+ ],
+ "support": {
+ "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/v2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/maennchen",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/zipstream",
+ "type": "open_collective"
+ }
+ ],
+ "install-path": "../maennchen/zipstream-php"
+ },
+ {
+ "name": "markbaker/complex",
+ "version": "3.0.x-dev",
+ "version_normalized": "3.0.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPComplex.git",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "time": "2022-12-06T16:21:08+00:00",
+ "default-branch": true,
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "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/3.0.2"
+ },
+ "install-path": "../markbaker/complex"
+ },
+ {
+ "name": "markbaker/matrix",
+ "version": "3.0.x-dev",
+ "version_normalized": "3.0.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPMatrix.git",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpdocumentor/phpdocumentor": "2.*",
+ "phploc/phploc": "^4.0",
+ "phpmd/phpmd": "2.*",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "sebastian/phpcpd": "^4.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "time": "2022-12-02T22:17:43+00:00",
+ "default-branch": true,
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Matrix\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@demon-angel.eu"
+ }
+ ],
+ "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/3.0.1"
+ },
+ "install-path": "../markbaker/matrix"
+ },
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.4",
+ "version_normalized": "1.8.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
+ "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2"
+ },
+ "time": "2022-08-04T09:53:51+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "http://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../myclabs/php-enum"
+ },
{
"name": "nesbot/carbon",
"version": "dev-master",
@@ -569,6 +831,115 @@
],
"install-path": "../phpmailer/phpmailer"
},
+ {
+ "name": "phpoffice/phpspreadsheet",
+ "version": "dev-master",
+ "version_normalized": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+ "reference": "a17a56b6e01dad0c04e2bfc7ddf5038240a6facf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/a17a56b6e01dad0c04e2bfc7ddf5038240a6facf",
+ "reference": "a17a56b6e01dad0c04e2bfc7ddf5038240a6facf",
+ "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": "*",
+ "maennchen/zipstream-php": "^2.1 || ^3.0",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "php": "^8.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
+ "voku/anti-xss": "^4.1"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+ "dompdf/dompdf": "^2.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "mitoteam/jpgraph": "^10.3",
+ "mpdf/mpdf": "^8.1.1",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.6",
+ "squizlabs/php_codesniffer": "^3.7",
+ "tecnickcom/tcpdf": "^6.5"
+ },
+ "suggest": {
+ "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+ "ext-intl": "PHP Internationalization Functions",
+ "mitoteam/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"
+ },
+ "time": "2023-12-26T15:40:38+00:00",
+ "default-branch": true,
+ "type": "library",
+ "installation-source": "dist",
+ "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/master"
+ },
+ "install-path": "../phpoffice/phpspreadsheet"
+ },
{
"name": "psr/clock",
"version": "1.0.0",
@@ -671,6 +1042,177 @@
},
"install-path": "../psr/container"
},
+ {
+ "name": "psr/http-client",
+ "version": "dev-master",
+ "version_normalized": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "time": "2023-09-23T14:17:50+00:00",
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "install-path": "../psr/http-client"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "dev-master",
+ "version_normalized": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "7037f4b0950474e9d1350e8df89b15f1842085f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/7037f4b0950474e9d1350e8df89b15f1842085f6",
+ "reference": "7037f4b0950474e9d1350e8df89b15f1842085f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "time": "2023-09-22T11:16:44+00:00",
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "install-path": "../psr/http-factory"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.1",
+ "version_normalized": "1.1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "time": "2023-04-04T09:50:52+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/1.1"
+ },
+ "install-path": "../psr/http-message"
+ },
{
"name": "psr/simple-cache",
"version": "1.0.1",
@@ -874,6 +1416,266 @@
],
"install-path": "../symfony/http-foundation"
},
+ {
+ "name": "symfony/polyfill-iconv",
+ "version": "1.x-dev",
+ "version_normalized": "1.9999999.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-iconv.git",
+ "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6de50471469b8c9afc38164452ab2b6170ee71c1",
+ "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-iconv": "*"
+ },
+ "suggest": {
+ "ext-iconv": "For best performance"
+ },
+ "time": "2023-01-26T09:26:14+00:00",
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Iconv\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Iconv extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "iconv",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-iconv/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../symfony/polyfill-iconv"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "1.x-dev",
+ "version_normalized": "1.9999999.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "875e90aeea2777b6f135677f618529449334a612"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
+ "reference": "875e90aeea2777b6f135677f618529449334a612",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "time": "2023-01-26T09:26:14+00:00",
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../symfony/polyfill-intl-grapheme"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "1.x-dev",
+ "version_normalized": "1.9999999.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "time": "2023-01-26T09:26:14+00:00",
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../symfony/polyfill-intl-normalizer"
+ },
{
"name": "symfony/polyfill-mbstring",
"version": "1.x-dev",
@@ -961,6 +1763,86 @@
],
"install-path": "../symfony/polyfill-mbstring"
},
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "1.x-dev",
+ "version_normalized": "1.9999999.9999999.9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
+ "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "time": "2023-01-26T09:26:14+00:00",
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.28-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../symfony/polyfill-php72"
+ },
{
"name": "symfony/polyfill-php80",
"version": "1.x-dev",
@@ -1292,6 +2174,92 @@
},
"install-path": "../tinymce/tinymce"
},
+ {
+ "name": "voku/anti-xss",
+ "version": "4.1.35",
+ "version_normalized": "4.1.35.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/anti-xss.git",
+ "reference": "4d7ec2816cd6eeebb7d5abbde8b0349f66709496"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/anti-xss/zipball/4d7ec2816cd6eeebb7d5abbde8b0349f66709496",
+ "reference": "4d7ec2816cd6eeebb7d5abbde8b0349f66709496",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "voku/portable-utf8": "~6.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "time": "2021-12-08T15:20:33+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "voku\\helper\\": "src/voku/helper/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "EllisLab Dev Team",
+ "homepage": "http://ellislab.com/"
+ },
+ {
+ "name": "Lars Moelleken",
+ "email": "lars@moelleken.org",
+ "homepage": "https://www.moelleken.org/"
+ }
+ ],
+ "description": "anti xss-library",
+ "homepage": "https://github.com/voku/anti-xss",
+ "keywords": [
+ "anti-xss",
+ "clean",
+ "security",
+ "xss"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/anti-xss/issues",
+ "source": "https://github.com/voku/anti-xss/tree/4.1.35"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/anti-xss",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/anti-xss",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../voku/anti-xss"
+ },
{
"name": "voku/portable-ascii",
"version": "1.6.1",
@@ -1369,6 +2337,108 @@
],
"install-path": "../voku/portable-ascii"
},
+ {
+ "name": "voku/portable-utf8",
+ "version": "6.0.1",
+ "version_normalized": "6.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-utf8.git",
+ "reference": "e4365df3f7b63f9e57e1015f6c3ceec19c169f7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-utf8/zipball/e4365df3f7b63f9e57e1015f6c3ceec19c169f7a",
+ "reference": "e4365df3f7b63f9e57e1015f6c3ceec19c169f7a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "symfony/polyfill-iconv": "~1.0",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php72": "~1.0",
+ "voku/portable-ascii": "~1.6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-ctype": "Use Ctype for e.g. hexadecimal digit detection",
+ "ext-fileinfo": "Use Fileinfo for better binary file detection",
+ "ext-iconv": "Use iconv for best performance",
+ "ext-intl": "Use Intl for best performance",
+ "ext-json": "Use JSON for string detection",
+ "ext-mbstring": "Use Mbstring for best performance"
+ },
+ "time": "2022-01-24T09:49:08+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "(Apache-2.0 or GPL-2.0)"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Hamid Sarfraz",
+ "homepage": "http://pageconfig.com/"
+ },
+ {
+ "name": "Lars Moelleken",
+ "homepage": "http://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable UTF-8 library - performance optimized (unicode) string functions for php.",
+ "homepage": "https://github.com/voku/portable-utf8",
+ "keywords": [
+ "UTF",
+ "clean",
+ "php",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-utf8/issues",
+ "source": "https://github.com/voku/portable-utf8/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-utf8",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-utf8",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../voku/portable-utf8"
+ },
{
"name": "webklex/php-imap",
"version": "4.1.2",
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 8f52ac8..c23de0c 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
- 'reference' => '54d30dd3c3ff737ecc0ef62de19a7c9ed29fea2d',
+ 'reference' => 'ea29b477f6aeffb4f7f9f09aacbe245f418aa029',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
- 'reference' => '54d30dd3c3ff737ecc0ef62de19a7c9ed29fea2d',
+ 'reference' => 'ea29b477f6aeffb4f7f9f09aacbe245f418aa029',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -73,6 +73,42 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'maennchen/zipstream-php' => array(
+ 'pretty_version' => 'v2.x-dev',
+ 'version' => '2.9999999.9999999.9999999-dev',
+ 'reference' => '620c2aab9d4f109aef4949049cc909c24479f8e0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../maennchen/zipstream-php',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'markbaker/complex' => array(
+ 'pretty_version' => '3.0.x-dev',
+ 'version' => '3.0.9999999.9999999-dev',
+ 'reference' => '95c56caa1cf5c766ad6d65b6344b807c1e8405b9',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../markbaker/complex',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'markbaker/matrix' => array(
+ 'pretty_version' => '3.0.x-dev',
+ 'version' => '3.0.9999999.9999999-dev',
+ 'reference' => '728434227fe21be27ff6d86621a1b13107a2562c',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../markbaker/matrix',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'myclabs/php-enum' => array(
+ 'pretty_version' => '1.8.4',
+ 'version' => '1.8.4.0',
+ 'reference' => 'a867478eae49c9f59ece437ae7f9506bfaa27483',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../myclabs/php-enum',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'nesbot/carbon' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
@@ -95,6 +131,17 @@
),
'dev_requirement' => false,
),
+ 'phpoffice/phpspreadsheet' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => 'a17a56b6e01dad0c04e2bfc7ddf5038240a6facf',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../phpoffice/phpspreadsheet',
+ 'aliases' => array(
+ 0 => '9999999-dev',
+ ),
+ 'dev_requirement' => false,
+ ),
'psr/clock' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
@@ -119,6 +166,37 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'psr/http-client' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/http-client',
+ 'aliases' => array(
+ 0 => '1.0.x-dev',
+ ),
+ 'dev_requirement' => false,
+ ),
+ 'psr/http-factory' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => '7037f4b0950474e9d1350e8df89b15f1842085f6',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/http-factory',
+ 'aliases' => array(
+ 0 => '1.0.x-dev',
+ ),
+ 'dev_requirement' => false,
+ ),
+ 'psr/http-message' => array(
+ 'pretty_version' => '1.1',
+ 'version' => '1.1.0.0',
+ 'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/http-message',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'psr/simple-cache' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
@@ -146,6 +224,33 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'symfony/polyfill-iconv' => array(
+ 'pretty_version' => '1.x-dev',
+ 'version' => '1.9999999.9999999.9999999-dev',
+ 'reference' => '6de50471469b8c9afc38164452ab2b6170ee71c1',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'symfony/polyfill-intl-grapheme' => array(
+ 'pretty_version' => '1.x-dev',
+ 'version' => '1.9999999.9999999.9999999-dev',
+ 'reference' => '875e90aeea2777b6f135677f618529449334a612',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'symfony/polyfill-intl-normalizer' => array(
+ 'pretty_version' => '1.x-dev',
+ 'version' => '1.9999999.9999999.9999999-dev',
+ 'reference' => '8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'symfony/polyfill-mbstring' => array(
'pretty_version' => '1.x-dev',
'version' => '1.9999999.9999999.9999999-dev',
@@ -155,6 +260,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'symfony/polyfill-php72' => array(
+ 'pretty_version' => '1.x-dev',
+ 'version' => '1.9999999.9999999.9999999-dev',
+ 'reference' => '70f4aebd92afca2f865444d30a4d2151c13c3179',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../symfony/polyfill-php72',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'symfony/polyfill-php80' => array(
'pretty_version' => '1.x-dev',
'version' => '1.9999999.9999999.9999999-dev',
@@ -199,6 +313,15 @@
),
'dev_requirement' => false,
),
+ 'voku/anti-xss' => array(
+ 'pretty_version' => '4.1.35',
+ 'version' => '4.1.35.0',
+ 'reference' => '4d7ec2816cd6eeebb7d5abbde8b0349f66709496',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../voku/anti-xss',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'voku/portable-ascii' => array(
'pretty_version' => '1.6.1',
'version' => '1.6.1.0',
@@ -208,6 +331,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'voku/portable-utf8' => array(
+ 'pretty_version' => '6.0.1',
+ 'version' => '6.0.1.0',
+ 'reference' => 'e4365df3f7b63f9e57e1015f6c3ceec19c169f7a',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../voku/portable-utf8',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'webklex/php-imap' => array(
'pretty_version' => '4.1.2',
'version' => '4.1.2.0',
diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php
index 580fa96..adfb472 100644
--- a/vendor/composer/platform_check.php
+++ b/vendor/composer/platform_check.php
@@ -4,8 +4,8 @@
$issues = array();
-if (!(PHP_VERSION_ID >= 70400)) {
- $issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
+if (!(PHP_VERSION_ID >= 80000)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
diff --git a/vendor/doctrine/inflector/LICENSE b/vendor/doctrine/inflector/LICENSE
new file mode 100644
index 0000000..8c38cc1
--- /dev/null
+++ b/vendor/doctrine/inflector/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2006-2015 Doctrine Project
+
+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.
diff --git a/vendor/doctrine/inflector/README.md b/vendor/doctrine/inflector/README.md
new file mode 100644
index 0000000..6e3a97f
--- /dev/null
+++ b/vendor/doctrine/inflector/README.md
@@ -0,0 +1,7 @@
+# Doctrine Inflector
+
+Doctrine Inflector is a small library that can perform string manipulations
+with regard to uppercase/lowercase and singular/plural forms of words.
+
+[](https://github.com/doctrine/inflector/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x)
+[](https://codecov.io/gh/doctrine/inflector/branch/2.0.x)
diff --git a/vendor/doctrine/inflector/composer.json b/vendor/doctrine/inflector/composer.json
new file mode 100644
index 0000000..91d7707
--- /dev/null
+++ b/vendor/doctrine/inflector/composer.json
@@ -0,0 +1,41 @@
+{
+ "name": "doctrine/inflector",
+ "type": "library",
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "keywords": ["php", "strings", "words", "manipulation", "inflector", "inflection", "uppercase", "lowercase", "singular", "plural"],
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "license": "MIT",
+ "authors": [
+ {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+ {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+ {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+ {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
+ {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+ ],
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11.0",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.3",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "vimeo/psalm": "^4.25 || ^5.4"
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Doctrine\\Tests\\Inflector\\": "tests/Doctrine/Tests/Inflector"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ }
+}
diff --git a/vendor/doctrine/inflector/docs/en/index.rst b/vendor/doctrine/inflector/docs/en/index.rst
new file mode 100644
index 0000000..1e094c8
--- /dev/null
+++ b/vendor/doctrine/inflector/docs/en/index.rst
@@ -0,0 +1,227 @@
+Introduction
+============
+
+The Doctrine Inflector has methods for inflecting text. The features include pluralization,
+singularization, converting between camelCase and under_score and capitalizing
+words.
+
+Installation
+============
+
+You can install the Inflector with composer:
+
+.. code-block:: console
+
+ $ composer require doctrine/inflector
+
+Usage
+=====
+
+Using the inflector is easy, you can create a new ``Doctrine\Inflector\Inflector`` instance by using
+the ``Doctrine\Inflector\InflectorFactory`` class:
+
+.. code-block:: php
+
+ use Doctrine\Inflector\InflectorFactory;
+
+ $inflector = InflectorFactory::create()->build();
+
+By default it will create an English inflector. If you want to use another language, just pass the language
+you want to create an inflector for to the ``createForLanguage()`` method:
+
+.. code-block:: php
+
+ use Doctrine\Inflector\InflectorFactory;
+ use Doctrine\Inflector\Language;
+
+ $inflector = InflectorFactory::createForLanguage(Language::SPANISH)->build();
+
+The supported languages are as follows:
+
+- ``Language::ENGLISH``
+- ``Language::ESPERANTO``
+- ``Language::FRENCH``
+- ``Language::NORWEGIAN_BOKMAL``
+- ``Language::PORTUGUESE``
+- ``Language::SPANISH``
+- ``Language::TURKISH``
+
+If you want to manually construct the inflector instead of using a factory, you can do so like this:
+
+.. code-block:: php
+
+ use Doctrine\Inflector\CachedWordInflector;
+ use Doctrine\Inflector\RulesetInflector;
+ use Doctrine\Inflector\Rules\English;
+
+ $inflector = new Inflector(
+ new CachedWordInflector(new RulesetInflector(
+ English\Rules::getSingularRuleset()
+ )),
+ new CachedWordInflector(new RulesetInflector(
+ English\Rules::getPluralRuleset()
+ ))
+ );
+
+Adding Languages
+----------------
+
+If you are interested in adding support for your language, take a look at the other languages defined in the
+``Doctrine\Inflector\Rules`` namespace and the tests located in ``Doctrine\Tests\Inflector\Rules``. You can copy
+one of the languages and update the rules for your language.
+
+Once you have done this, send a pull request to the ``doctrine/inflector`` repository with the additions.
+
+Custom Setup
+============
+
+If you want to setup custom singular and plural rules, you can configure these in the factory:
+
+.. code-block:: php
+
+ use Doctrine\Inflector\InflectorFactory;
+ use Doctrine\Inflector\Rules\Pattern;
+ use Doctrine\Inflector\Rules\Patterns;
+ use Doctrine\Inflector\Rules\Ruleset;
+ use Doctrine\Inflector\Rules\Substitution;
+ use Doctrine\Inflector\Rules\Substitutions;
+ use Doctrine\Inflector\Rules\Transformation;
+ use Doctrine\Inflector\Rules\Transformations;
+ use Doctrine\Inflector\Rules\Word;
+
+ $inflector = InflectorFactory::create()
+ ->withSingularRules(
+ new Ruleset(
+ new Transformations(
+ new Transformation(new Pattern('/^(bil)er$/i'), '\1'),
+ new Transformation(new Pattern('/^(inflec|contribu)tors$/i'), '\1ta')
+ ),
+ new Patterns(new Pattern('singulars')),
+ new Substitutions(new Substitution(new Word('spins'), new Word('spinor')))
+ )
+ )
+ ->withPluralRules(
+ new Ruleset(
+ new Transformations(
+ new Transformation(new Pattern('^(bil)er$'), '\1'),
+ new Transformation(new Pattern('^(inflec|contribu)tors$'), '\1ta')
+ ),
+ new Patterns(new Pattern('noflect'), new Pattern('abtuse')),
+ new Substitutions(
+ new Substitution(new Word('amaze'), new Word('amazable')),
+ new Substitution(new Word('phone'), new Word('phonezes'))
+ )
+ )
+ )
+ ->build();
+
+No operation inflector
+----------------------
+
+The ``Doctrine\Inflector\NoopWordInflector`` may be used to configure an inflector that doesn't perform any operation for
+pluralization and/or singularization. If will simply return the input as output.
+
+This is an implementation of the `Null Object design pattern `_.
+
+.. code-block:: php
+
+ use Doctrine\Inflector\Inflector;
+ use Doctrine\Inflector\NoopWordInflector;
+
+ $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector());
+
+Tableize
+========
+
+Converts ``ModelName`` to ``model_name``:
+
+.. code-block:: php
+
+ echo $inflector->tableize('ModelName'); // model_name
+
+Classify
+========
+
+Converts ``model_name`` to ``ModelName``:
+
+.. code-block:: php
+
+ echo $inflector->classify('model_name'); // ModelName
+
+Camelize
+========
+
+This method uses `Classify`_ and then converts the first character to lowercase:
+
+.. code-block:: php
+
+ echo $inflector->camelize('model_name'); // modelName
+
+Capitalize
+==========
+
+Takes a string and capitalizes all of the words, like PHP's built-in
+``ucwords`` function. This extends that behavior, however, by allowing the
+word delimiters to be configured, rather than only separating on
+whitespace.
+
+Here is an example:
+
+.. code-block:: php
+
+ $string = 'top-o-the-morning to all_of_you!';
+
+ echo $inflector->capitalize($string); // Top-O-The-Morning To All_of_you!
+
+ echo $inflector->capitalize($string, '-_ '); // Top-O-The-Morning To All_Of_You!
+
+Pluralize
+=========
+
+Returns a word in plural form.
+
+.. code-block:: php
+
+ echo $inflector->pluralize('browser'); // browsers
+
+Singularize
+===========
+
+Returns a word in singular form.
+
+.. code-block:: php
+
+ echo $inflector->singularize('browsers'); // browser
+
+Urlize
+======
+
+Generate a URL friendly string from a string of text:
+
+.. code-block:: php
+
+ echo $inflector->urlize('My first blog post'); // my-first-blog-post
+
+Unaccent
+========
+
+You can unaccent a string of text using the ``unaccent()`` method:
+
+.. code-block:: php
+
+ echo $inflector->unaccent('año'); // ano
+
+Legacy API
+==========
+
+The API present in Inflector 1.x is still available, but will be deprecated in a future release and dropped for 3.0.
+Support for languages other than English is available in the 2.0 API only.
+
+Acknowledgements
+================
+
+The language rules in this library have been adapted from several different sources, including but not limited to:
+
+- `Ruby On Rails Inflector `_
+- `ICanBoogie Inflector `_
+- `CakePHP Inflector `_
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php
new file mode 100644
index 0000000..2d52908
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/CachedWordInflector.php
@@ -0,0 +1,24 @@
+wordInflector = $wordInflector;
+ }
+
+ public function inflect(string $word): string
+ {
+ return $this->cache[$word] ?? $this->cache[$word] = $this->wordInflector->inflect($word);
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php
new file mode 100644
index 0000000..166061d
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.php
@@ -0,0 +1,66 @@
+singularRulesets[] = $this->getSingularRuleset();
+ $this->pluralRulesets[] = $this->getPluralRuleset();
+ }
+
+ final public function build(): Inflector
+ {
+ return new Inflector(
+ new CachedWordInflector(new RulesetInflector(
+ ...$this->singularRulesets
+ )),
+ new CachedWordInflector(new RulesetInflector(
+ ...$this->pluralRulesets
+ ))
+ );
+ }
+
+ final public function withSingularRules(?Ruleset $singularRules, bool $reset = false): LanguageInflectorFactory
+ {
+ if ($reset) {
+ $this->singularRulesets = [];
+ }
+
+ if ($singularRules instanceof Ruleset) {
+ array_unshift($this->singularRulesets, $singularRules);
+ }
+
+ return $this;
+ }
+
+ final public function withPluralRules(?Ruleset $pluralRules, bool $reset = false): LanguageInflectorFactory
+ {
+ if ($reset) {
+ $this->pluralRulesets = [];
+ }
+
+ if ($pluralRules instanceof Ruleset) {
+ array_unshift($this->pluralRulesets, $pluralRules);
+ }
+
+ return $this;
+ }
+
+ abstract protected function getSingularRuleset(): Ruleset;
+
+ abstract protected function getPluralRuleset(): Ruleset;
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php
new file mode 100644
index 0000000..610a4cf
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Inflector.php
@@ -0,0 +1,507 @@
+ 'A',
+ 'Á' => 'A',
+ 'Â' => 'A',
+ 'Ã' => 'A',
+ 'Ä' => 'Ae',
+ 'Æ' => 'Ae',
+ 'Å' => 'Aa',
+ 'æ' => 'a',
+ 'Ç' => 'C',
+ 'È' => 'E',
+ 'É' => 'E',
+ 'Ê' => 'E',
+ 'Ë' => 'E',
+ 'Ì' => 'I',
+ 'Í' => 'I',
+ 'Î' => 'I',
+ 'Ï' => 'I',
+ 'Ñ' => 'N',
+ 'Ò' => 'O',
+ 'Ó' => 'O',
+ 'Ô' => 'O',
+ 'Õ' => 'O',
+ 'Ö' => 'Oe',
+ 'Ù' => 'U',
+ 'Ú' => 'U',
+ 'Û' => 'U',
+ 'Ü' => 'Ue',
+ 'Ý' => 'Y',
+ 'ß' => 'ss',
+ 'à' => 'a',
+ 'á' => 'a',
+ 'â' => 'a',
+ 'ã' => 'a',
+ 'ä' => 'ae',
+ 'å' => 'aa',
+ 'ç' => 'c',
+ 'è' => 'e',
+ 'é' => 'e',
+ 'ê' => 'e',
+ 'ë' => 'e',
+ 'ì' => 'i',
+ 'í' => 'i',
+ 'î' => 'i',
+ 'ï' => 'i',
+ 'ñ' => 'n',
+ 'ò' => 'o',
+ 'ó' => 'o',
+ 'ô' => 'o',
+ 'õ' => 'o',
+ 'ö' => 'oe',
+ 'ù' => 'u',
+ 'ú' => 'u',
+ 'û' => 'u',
+ 'ü' => 'ue',
+ 'ý' => 'y',
+ 'ÿ' => 'y',
+ 'Ā' => 'A',
+ 'ā' => 'a',
+ 'Ă' => 'A',
+ 'ă' => 'a',
+ 'Ą' => 'A',
+ 'ą' => 'a',
+ 'Ć' => 'C',
+ 'ć' => 'c',
+ 'Ĉ' => 'C',
+ 'ĉ' => 'c',
+ 'Ċ' => 'C',
+ 'ċ' => 'c',
+ 'Č' => 'C',
+ 'č' => 'c',
+ 'Ď' => 'D',
+ 'ď' => 'd',
+ 'Đ' => 'D',
+ 'đ' => 'd',
+ 'Ē' => 'E',
+ 'ē' => 'e',
+ 'Ĕ' => 'E',
+ 'ĕ' => 'e',
+ 'Ė' => 'E',
+ 'ė' => 'e',
+ 'Ę' => 'E',
+ 'ę' => 'e',
+ 'Ě' => 'E',
+ 'ě' => 'e',
+ 'Ĝ' => 'G',
+ 'ĝ' => 'g',
+ 'Ğ' => 'G',
+ 'ğ' => 'g',
+ 'Ġ' => 'G',
+ 'ġ' => 'g',
+ 'Ģ' => 'G',
+ 'ģ' => 'g',
+ 'Ĥ' => 'H',
+ 'ĥ' => 'h',
+ 'Ħ' => 'H',
+ 'ħ' => 'h',
+ 'Ĩ' => 'I',
+ 'ĩ' => 'i',
+ 'Ī' => 'I',
+ 'ī' => 'i',
+ 'Ĭ' => 'I',
+ 'ĭ' => 'i',
+ 'Į' => 'I',
+ 'į' => 'i',
+ 'İ' => 'I',
+ 'ı' => 'i',
+ 'IJ' => 'IJ',
+ 'ij' => 'ij',
+ 'Ĵ' => 'J',
+ 'ĵ' => 'j',
+ 'Ķ' => 'K',
+ 'ķ' => 'k',
+ 'ĸ' => 'k',
+ 'Ĺ' => 'L',
+ 'ĺ' => 'l',
+ 'Ļ' => 'L',
+ 'ļ' => 'l',
+ 'Ľ' => 'L',
+ 'ľ' => 'l',
+ 'Ŀ' => 'L',
+ 'ŀ' => 'l',
+ 'Ł' => 'L',
+ 'ł' => 'l',
+ 'Ń' => 'N',
+ 'ń' => 'n',
+ 'Ņ' => 'N',
+ 'ņ' => 'n',
+ 'Ň' => 'N',
+ 'ň' => 'n',
+ 'ʼn' => 'N',
+ 'Ŋ' => 'n',
+ 'ŋ' => 'N',
+ 'Ō' => 'O',
+ 'ō' => 'o',
+ 'Ŏ' => 'O',
+ 'ŏ' => 'o',
+ 'Ő' => 'O',
+ 'ő' => 'o',
+ 'Œ' => 'OE',
+ 'œ' => 'oe',
+ 'Ø' => 'O',
+ 'ø' => 'o',
+ 'Ŕ' => 'R',
+ 'ŕ' => 'r',
+ 'Ŗ' => 'R',
+ 'ŗ' => 'r',
+ 'Ř' => 'R',
+ 'ř' => 'r',
+ 'Ś' => 'S',
+ 'ś' => 's',
+ 'Ŝ' => 'S',
+ 'ŝ' => 's',
+ 'Ş' => 'S',
+ 'ş' => 's',
+ 'Š' => 'S',
+ 'š' => 's',
+ 'Ţ' => 'T',
+ 'ţ' => 't',
+ 'Ť' => 'T',
+ 'ť' => 't',
+ 'Ŧ' => 'T',
+ 'ŧ' => 't',
+ 'Ũ' => 'U',
+ 'ũ' => 'u',
+ 'Ū' => 'U',
+ 'ū' => 'u',
+ 'Ŭ' => 'U',
+ 'ŭ' => 'u',
+ 'Ů' => 'U',
+ 'ů' => 'u',
+ 'Ű' => 'U',
+ 'ű' => 'u',
+ 'Ų' => 'U',
+ 'ų' => 'u',
+ 'Ŵ' => 'W',
+ 'ŵ' => 'w',
+ 'Ŷ' => 'Y',
+ 'ŷ' => 'y',
+ 'Ÿ' => 'Y',
+ 'Ź' => 'Z',
+ 'ź' => 'z',
+ 'Ż' => 'Z',
+ 'ż' => 'z',
+ 'Ž' => 'Z',
+ 'ž' => 'z',
+ 'ſ' => 's',
+ '€' => 'E',
+ '£' => '',
+ ];
+
+ /** @var WordInflector */
+ private $singularizer;
+
+ /** @var WordInflector */
+ private $pluralizer;
+
+ public function __construct(WordInflector $singularizer, WordInflector $pluralizer)
+ {
+ $this->singularizer = $singularizer;
+ $this->pluralizer = $pluralizer;
+ }
+
+ /**
+ * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
+ */
+ public function tableize(string $word): string
+ {
+ $tableized = preg_replace('~(?<=\\w)([A-Z])~u', '_$1', $word);
+
+ if ($tableized === null) {
+ throw new RuntimeException(sprintf(
+ 'preg_replace returned null for value "%s"',
+ $word
+ ));
+ }
+
+ return mb_strtolower($tableized);
+ }
+
+ /**
+ * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
+ */
+ public function classify(string $word): string
+ {
+ return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
+ }
+
+ /**
+ * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
+ */
+ public function camelize(string $word): string
+ {
+ return lcfirst($this->classify($word));
+ }
+
+ /**
+ * Uppercases words with configurable delimiters between words.
+ *
+ * Takes a string and capitalizes all of the words, like PHP's built-in
+ * ucwords function. This extends that behavior, however, by allowing the
+ * word delimiters to be configured, rather than only separating on
+ * whitespace.
+ *
+ * Here is an example:
+ *
+ * capitalize($string);
+ * // Top-O-The-Morning To All_of_you!
+ *
+ * echo $inflector->capitalize($string, '-_ ');
+ * // Top-O-The-Morning To All_Of_You!
+ * ?>
+ *
+ *
+ * @param string $string The string to operate on.
+ * @param string $delimiters A list of word separators.
+ *
+ * @return string The string with all delimiter-separated words capitalized.
+ */
+ public function capitalize(string $string, string $delimiters = " \n\t\r\0\x0B-"): string
+ {
+ return ucwords($string, $delimiters);
+ }
+
+ /**
+ * Checks if the given string seems like it has utf8 characters in it.
+ *
+ * @param string $string The string to check for utf8 characters in.
+ */
+ public function seemsUtf8(string $string): bool
+ {
+ for ($i = 0; $i < strlen($string); $i++) {
+ if (ord($string[$i]) < 0x80) {
+ continue; // 0bbbbbbb
+ }
+
+ if ((ord($string[$i]) & 0xE0) === 0xC0) {
+ $n = 1; // 110bbbbb
+ } elseif ((ord($string[$i]) & 0xF0) === 0xE0) {
+ $n = 2; // 1110bbbb
+ } elseif ((ord($string[$i]) & 0xF8) === 0xF0) {
+ $n = 3; // 11110bbb
+ } elseif ((ord($string[$i]) & 0xFC) === 0xF8) {
+ $n = 4; // 111110bb
+ } elseif ((ord($string[$i]) & 0xFE) === 0xFC) {
+ $n = 5; // 1111110b
+ } else {
+ return false; // Does not match any model
+ }
+
+ for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
+ if (++$i === strlen($string) || ((ord($string[$i]) & 0xC0) !== 0x80)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove any illegal characters, accents, etc.
+ *
+ * @param string $string String to unaccent
+ *
+ * @return string Unaccented string
+ */
+ public function unaccent(string $string): string
+ {
+ if (preg_match('/[\x80-\xff]/', $string) === false) {
+ return $string;
+ }
+
+ if ($this->seemsUtf8($string)) {
+ $string = strtr($string, self::ACCENTED_CHARACTERS);
+ } else {
+ $characters = [];
+
+ // Assume ISO-8859-1 if not UTF-8
+ $characters['in'] =
+ chr(128)
+ . chr(131)
+ . chr(138)
+ . chr(142)
+ . chr(154)
+ . chr(158)
+ . chr(159)
+ . chr(162)
+ . chr(165)
+ . chr(181)
+ . chr(192)
+ . chr(193)
+ . chr(194)
+ . chr(195)
+ . chr(196)
+ . chr(197)
+ . chr(199)
+ . chr(200)
+ . chr(201)
+ . chr(202)
+ . chr(203)
+ . chr(204)
+ . chr(205)
+ . chr(206)
+ . chr(207)
+ . chr(209)
+ . chr(210)
+ . chr(211)
+ . chr(212)
+ . chr(213)
+ . chr(214)
+ . chr(216)
+ . chr(217)
+ . chr(218)
+ . chr(219)
+ . chr(220)
+ . chr(221)
+ . chr(224)
+ . chr(225)
+ . chr(226)
+ . chr(227)
+ . chr(228)
+ . chr(229)
+ . chr(231)
+ . chr(232)
+ . chr(233)
+ . chr(234)
+ . chr(235)
+ . chr(236)
+ . chr(237)
+ . chr(238)
+ . chr(239)
+ . chr(241)
+ . chr(242)
+ . chr(243)
+ . chr(244)
+ . chr(245)
+ . chr(246)
+ . chr(248)
+ . chr(249)
+ . chr(250)
+ . chr(251)
+ . chr(252)
+ . chr(253)
+ . chr(255);
+
+ $characters['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy';
+
+ $string = strtr($string, $characters['in'], $characters['out']);
+
+ $doubleChars = [];
+
+ $doubleChars['in'] = [
+ chr(140),
+ chr(156),
+ chr(198),
+ chr(208),
+ chr(222),
+ chr(223),
+ chr(230),
+ chr(240),
+ chr(254),
+ ];
+
+ $doubleChars['out'] = ['OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'];
+
+ $string = str_replace($doubleChars['in'], $doubleChars['out'], $string);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Convert any passed string to a url friendly string.
+ * Converts 'My first blog post' to 'my-first-blog-post'
+ *
+ * @param string $string String to urlize.
+ *
+ * @return string Urlized string.
+ */
+ public function urlize(string $string): string
+ {
+ // Remove all non url friendly characters with the unaccent function
+ $unaccented = $this->unaccent($string);
+
+ if (function_exists('mb_strtolower')) {
+ $lowered = mb_strtolower($unaccented);
+ } else {
+ $lowered = strtolower($unaccented);
+ }
+
+ $replacements = [
+ '/\W/' => ' ',
+ '/([A-Z]+)([A-Z][a-z])/' => '\1_\2',
+ '/([a-z\d])([A-Z])/' => '\1_\2',
+ '/[^A-Z^a-z^0-9^\/]+/' => '-',
+ ];
+
+ $urlized = $lowered;
+
+ foreach ($replacements as $pattern => $replacement) {
+ $replaced = preg_replace($pattern, $replacement, $urlized);
+
+ if ($replaced === null) {
+ throw new RuntimeException(sprintf(
+ 'preg_replace returned null for value "%s"',
+ $urlized
+ ));
+ }
+
+ $urlized = $replaced;
+ }
+
+ return trim($urlized, '-');
+ }
+
+ /**
+ * Returns a word in singular form.
+ *
+ * @param string $word The word in plural form.
+ *
+ * @return string The word in singular form.
+ */
+ public function singularize(string $word): string
+ {
+ return $this->singularizer->inflect($word);
+ }
+
+ /**
+ * Returns a word in plural form.
+ *
+ * @param string $word The word in singular form.
+ *
+ * @return string The word in plural form.
+ */
+ public function pluralize(string $word): string
+ {
+ return $this->pluralizer->inflect($word);
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php
new file mode 100644
index 0000000..dcad85f
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/InflectorFactory.php
@@ -0,0 +1,56 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php
new file mode 100644
index 0000000..02257de
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.php
@@ -0,0 +1,189 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Esperanto/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Esperanto/Uninflected.php
new file mode 100644
index 0000000..ed04c93
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Esperanto/Uninflected.php
@@ -0,0 +1,28 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php
new file mode 100644
index 0000000..9747f91
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.php
@@ -0,0 +1,28 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php
new file mode 100644
index 0000000..5d8d3b3
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.php
@@ -0,0 +1,30 @@
+pattern = $pattern;
+
+ if (isset($this->pattern[0]) && $this->pattern[0] === '/') {
+ $this->regex = $this->pattern;
+ } else {
+ $this->regex = '/' . $this->pattern . '/i';
+ }
+ }
+
+ public function getPattern(): string
+ {
+ return $this->pattern;
+ }
+
+ public function getRegex(): string
+ {
+ return $this->regex;
+ }
+
+ public function matches(string $word): bool
+ {
+ return preg_match($this->getRegex(), $word) === 1;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php
new file mode 100644
index 0000000..e8d45cb
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Patterns.php
@@ -0,0 +1,34 @@
+patterns = $patterns;
+
+ $patterns = array_map(static function (Pattern $pattern): string {
+ return $pattern->getPattern();
+ }, $this->patterns);
+
+ $this->regex = '/^(?:' . implode('|', $patterns) . ')$/i';
+ }
+
+ public function matches(string $word): bool
+ {
+ return preg_match($this->regex, $word, $regs) === 1;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php
new file mode 100644
index 0000000..0d41fe7
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.php
@@ -0,0 +1,98 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php
new file mode 100644
index 0000000..b8e988f
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.php
@@ -0,0 +1,32 @@
+regular = $regular;
+ $this->uninflected = $uninflected;
+ $this->irregular = $irregular;
+ }
+
+ public function getRegular(): Transformations
+ {
+ return $this->regular;
+ }
+
+ public function getUninflected(): Patterns
+ {
+ return $this->uninflected;
+ }
+
+ public function getIrregular(): Substitutions
+ {
+ return $this->irregular;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php
new file mode 100644
index 0000000..9129460
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.php
@@ -0,0 +1,47 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php
new file mode 100644
index 0000000..c26ebe9
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.php
@@ -0,0 +1,30 @@
+from = $from;
+ $this->to = $to;
+ }
+
+ public function getFrom(): Word
+ {
+ return $this->from;
+ }
+
+ public function getTo(): Word
+ {
+ return $this->to;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php
new file mode 100644
index 0000000..17ee296
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Substitutions.php
@@ -0,0 +1,57 @@
+substitutions[$substitution->getFrom()->getWord()] = $substitution;
+ }
+ }
+
+ public function getFlippedSubstitutions(): Substitutions
+ {
+ $substitutions = [];
+
+ foreach ($this->substitutions as $substitution) {
+ $substitutions[] = new Substitution(
+ $substitution->getTo(),
+ $substitution->getFrom()
+ );
+ }
+
+ return new Substitutions(...$substitutions);
+ }
+
+ public function inflect(string $word): string
+ {
+ $lowerWord = strtolower($word);
+
+ if (isset($this->substitutions[$lowerWord])) {
+ $firstLetterUppercase = $lowerWord[0] !== $word[0];
+
+ $toWord = $this->substitutions[$lowerWord]->getTo()->getWord();
+
+ if ($firstLetterUppercase) {
+ return strtoupper($toWord[0]) . substr($toWord, 1);
+ }
+
+ return $toWord;
+ }
+
+ return $word;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php
new file mode 100644
index 0000000..30dcd59
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformation.php
@@ -0,0 +1,39 @@
+pattern = $pattern;
+ $this->replacement = $replacement;
+ }
+
+ public function getPattern(): Pattern
+ {
+ return $this->pattern;
+ }
+
+ public function getReplacement(): string
+ {
+ return $this->replacement;
+ }
+
+ public function inflect(string $word): string
+ {
+ return (string) preg_replace($this->pattern->getRegex(), $this->replacement, $word);
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php
new file mode 100644
index 0000000..b6a48fa
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Transformations.php
@@ -0,0 +1,29 @@
+transformations = $transformations;
+ }
+
+ public function inflect(string $word): string
+ {
+ foreach ($this->transformations as $transformation) {
+ if ($transformation->getPattern()->matches($word)) {
+ return $transformation->inflect($word);
+ }
+ }
+
+ return $word;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php
new file mode 100644
index 0000000..a2bda0d
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.php
@@ -0,0 +1,34 @@
+getFlippedSubstitutions()
+ );
+ }
+
+ public static function getPluralRuleset(): Ruleset
+ {
+ return new Ruleset(
+ new Transformations(...Inflectible::getPlural()),
+ new Patterns(...Uninflected::getPlural()),
+ new Substitutions(...Inflectible::getIrregular())
+ );
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php
new file mode 100644
index 0000000..ec1c37d
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.php
@@ -0,0 +1,30 @@
+word = $word;
+ }
+
+ public function getWord(): string
+ {
+ return $this->word;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php
new file mode 100644
index 0000000..12b2ed5
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/RulesetInflector.php
@@ -0,0 +1,56 @@
+rulesets = array_merge([$ruleset], $rulesets);
+ }
+
+ public function inflect(string $word): string
+ {
+ if ($word === '') {
+ return '';
+ }
+
+ foreach ($this->rulesets as $ruleset) {
+ if ($ruleset->getUninflected()->matches($word)) {
+ return $word;
+ }
+
+ $inflected = $ruleset->getIrregular()->inflect($word);
+
+ if ($inflected !== $word) {
+ return $inflected;
+ }
+
+ $inflected = $ruleset->getRegular()->inflect($word);
+
+ if ($inflected !== $word) {
+ return $inflected;
+ }
+ }
+
+ return $word;
+ }
+}
diff --git a/vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php b/vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php
new file mode 100644
index 0000000..b88b1d6
--- /dev/null
+++ b/vendor/doctrine/inflector/lib/Doctrine/Inflector/WordInflector.php
@@ -0,0 +1,10 @@
+all();
+ } elseif (! is_array($values)) {
+ continue;
+ }
+
+ $results[] = $values;
+ }
+
+ return array_merge([], ...$results);
+ }
+
+ /**
+ * Cross join the given arrays, returning all possible permutations.
+ *
+ * @param iterable ...$arrays
+ * @return array
+ */
+ public static function crossJoin(...$arrays)
+ {
+ $results = [[]];
+
+ foreach ($arrays as $index => $array) {
+ $append = [];
+
+ foreach ($results as $product) {
+ foreach ($array as $item) {
+ $product[$index] = $item;
+
+ $append[] = $product;
+ }
+ }
+
+ $results = $append;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Divide an array into two arrays. One with keys and the other with values.
+ *
+ * @param array $array
+ * @return array
+ */
+ public static function divide($array)
+ {
+ return [array_keys($array), array_values($array)];
+ }
+
+ /**
+ * Flatten a multi-dimensional associative array with dots.
+ *
+ * @param iterable $array
+ * @param string $prepend
+ * @return array
+ */
+ public static function dot($array, $prepend = '')
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ if (is_array($value) && ! empty($value)) {
+ $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+ } else {
+ $results[$prepend.$key] = $value;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Convert a flatten "dot" notation array into an expanded array.
+ *
+ * @param iterable $array
+ * @return array
+ */
+ public static function undot($array)
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ static::set($results, $key, $value);
+ }
+
+ return $results;
+ }
+
+ /**
+ * Get all of the given array except for a specified array of keys.
+ *
+ * @param array $array
+ * @param array|string $keys
+ * @return array
+ */
+ public static function except($array, $keys)
+ {
+ static::forget($array, $keys);
+
+ return $array;
+ }
+
+ /**
+ * Determine if the given key exists in the provided array.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|int $key
+ * @return bool
+ */
+ public static function exists($array, $key)
+ {
+ if ($array instanceof Enumerable) {
+ return $array->has($key);
+ }
+
+ if ($array instanceof ArrayAccess) {
+ return $array->offsetExists($key);
+ }
+
+ return array_key_exists($key, $array);
+ }
+
+ /**
+ * Return the first element in an array passing a given truth test.
+ *
+ * @param iterable $array
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function first($array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ if (empty($array)) {
+ return value($default);
+ }
+
+ foreach ($array as $item) {
+ return $item;
+ }
+ }
+
+ foreach ($array as $key => $value) {
+ if ($callback($value, $key)) {
+ return $value;
+ }
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Return the last element in an array passing a given truth test.
+ *
+ * @param array $array
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function last($array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ return empty($array) ? value($default) : end($array);
+ }
+
+ return static::first(array_reverse($array, true), $callback, $default);
+ }
+
+ /**
+ * Flatten a multi-dimensional array into a single level.
+ *
+ * @param iterable $array
+ * @param int $depth
+ * @return array
+ */
+ public static function flatten($array, $depth = INF)
+ {
+ $result = [];
+
+ foreach ($array as $item) {
+ $item = $item instanceof Collection ? $item->all() : $item;
+
+ if (! is_array($item)) {
+ $result[] = $item;
+ } else {
+ $values = $depth === 1
+ ? array_values($item)
+ : static::flatten($item, $depth - 1);
+
+ foreach ($values as $value) {
+ $result[] = $value;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove one or many array items from a given array using "dot" notation.
+ *
+ * @param array $array
+ * @param array|string $keys
+ * @return void
+ */
+ public static function forget(&$array, $keys)
+ {
+ $original = &$array;
+
+ $keys = (array) $keys;
+
+ if (count($keys) === 0) {
+ return;
+ }
+
+ foreach ($keys as $key) {
+ // if the exact key exists in the top-level, remove it
+ if (static::exists($array, $key)) {
+ unset($array[$key]);
+
+ continue;
+ }
+
+ $parts = explode('.', $key);
+
+ // clean up before each pass
+ $array = &$original;
+
+ while (count($parts) > 1) {
+ $part = array_shift($parts);
+
+ if (isset($array[$part]) && is_array($array[$part])) {
+ $array = &$array[$part];
+ } else {
+ continue 2;
+ }
+ }
+
+ unset($array[array_shift($parts)]);
+ }
+ }
+
+ /**
+ * Get an item from an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|int|null $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function get($array, $key, $default = null)
+ {
+ if (! static::accessible($array)) {
+ return value($default);
+ }
+
+ if (is_null($key)) {
+ return $array;
+ }
+
+ if (static::exists($array, $key)) {
+ return $array[$key];
+ }
+
+ if (strpos($key, '.') === false) {
+ return $array[$key] ?? value($default);
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($array) && static::exists($array, $segment)) {
+ $array = $array[$segment];
+ } else {
+ return value($default);
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Check if an item or items exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ * @return bool
+ */
+ public static function has($array, $keys)
+ {
+ $keys = (array) $keys;
+
+ if (! $array || $keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ $subKeyArray = $array;
+
+ if (static::exists($array, $key)) {
+ continue;
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+ $subKeyArray = $subKeyArray[$segment];
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if any of the keys exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ * @return bool
+ */
+ public static function hasAny($array, $keys)
+ {
+ if (is_null($keys)) {
+ return false;
+ }
+
+ $keys = (array) $keys;
+
+ if (! $array) {
+ return false;
+ }
+
+ if ($keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ if (static::has($array, $key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if an array is associative.
+ *
+ * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
+ *
+ * @param array $array
+ * @return bool
+ */
+ public static function isAssoc(array $array)
+ {
+ $keys = array_keys($array);
+
+ return array_keys($keys) !== $keys;
+ }
+
+ /**
+ * Determines if an array is a list.
+ *
+ * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
+ *
+ * @param array $array
+ * @return bool
+ */
+ public static function isList($array)
+ {
+ return ! self::isAssoc($array);
+ }
+
+ /**
+ * Get a subset of the items from the given array.
+ *
+ * @param array $array
+ * @param array|string $keys
+ * @return array
+ */
+ public static function only($array, $keys)
+ {
+ return array_intersect_key($array, array_flip((array) $keys));
+ }
+
+ /**
+ * Pluck an array of values from an array.
+ *
+ * @param iterable $array
+ * @param string|array|int|null $value
+ * @param string|array|null $key
+ * @return array
+ */
+ public static function pluck($array, $value, $key = null)
+ {
+ $results = [];
+
+ [$value, $key] = static::explodePluckParameters($value, $key);
+
+ foreach ($array as $item) {
+ $itemValue = data_get($item, $value);
+
+ // If the key is "null", we will just append the value to the array and keep
+ // looping. Otherwise we will key the array using the value of the key we
+ // received from the developer. Then we'll return the final array form.
+ if (is_null($key)) {
+ $results[] = $itemValue;
+ } else {
+ $itemKey = data_get($item, $key);
+
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+ $itemKey = (string) $itemKey;
+ }
+
+ $results[$itemKey] = $itemValue;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Explode the "value" and "key" arguments passed to "pluck".
+ *
+ * @param string|array $value
+ * @param string|array|null $key
+ * @return array
+ */
+ protected static function explodePluckParameters($value, $key)
+ {
+ $value = is_string($value) ? explode('.', $value) : $value;
+
+ $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+ return [$value, $key];
+ }
+
+ /**
+ * Push an item onto the beginning of an array.
+ *
+ * @param array $array
+ * @param mixed $value
+ * @param mixed $key
+ * @return array
+ */
+ public static function prepend($array, $value, $key = null)
+ {
+ if (func_num_args() == 2) {
+ array_unshift($array, $value);
+ } else {
+ $array = [$key => $value] + $array;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Get a value from the array, and remove it.
+ *
+ * @param array $array
+ * @param string|int $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function pull(&$array, $key, $default = null)
+ {
+ $value = static::get($array, $key, $default);
+
+ static::forget($array, $key);
+
+ return $value;
+ }
+
+ /**
+ * Convert the array into a query string.
+ *
+ * @param array $array
+ * @return string
+ */
+ public static function query($array)
+ {
+ return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
+ }
+
+ /**
+ * Get one or a specified number of random values from an array.
+ *
+ * @param array $array
+ * @param int|null $number
+ * @param bool|false $preserveKeys
+ * @return mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ public static function random($array, $number = null, $preserveKeys = false)
+ {
+ $requested = is_null($number) ? 1 : $number;
+
+ $count = count($array);
+
+ if ($requested > $count) {
+ throw new InvalidArgumentException(
+ "You requested {$requested} items, but there are only {$count} items available."
+ );
+ }
+
+ if (is_null($number)) {
+ return $array[array_rand($array)];
+ }
+
+ if ((int) $number === 0) {
+ return [];
+ }
+
+ $keys = array_rand($array, $number);
+
+ $results = [];
+
+ if ($preserveKeys) {
+ foreach ((array) $keys as $key) {
+ $results[$key] = $array[$key];
+ }
+ } else {
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Set an array item to a given value using "dot" notation.
+ *
+ * If no key is given to the method, the entire array will be replaced.
+ *
+ * @param array $array
+ * @param string|null $key
+ * @param mixed $value
+ * @return array
+ */
+ public static function set(&$array, $key, $value)
+ {
+ if (is_null($key)) {
+ return $array = $value;
+ }
+
+ $keys = explode('.', $key);
+
+ foreach ($keys as $i => $key) {
+ if (count($keys) === 1) {
+ break;
+ }
+
+ unset($keys[$i]);
+
+ // If the key doesn't exist at this depth, we will just create an empty array
+ // to hold the next value, allowing us to create the arrays to hold final
+ // values at the correct depth. Then we'll keep digging into the array.
+ if (! isset($array[$key]) || ! is_array($array[$key])) {
+ $array[$key] = [];
+ }
+
+ $array = &$array[$key];
+ }
+
+ $array[array_shift($keys)] = $value;
+
+ return $array;
+ }
+
+ /**
+ * Shuffle the given array and return the result.
+ *
+ * @param array $array
+ * @param int|null $seed
+ * @return array
+ */
+ public static function shuffle($array, $seed = null)
+ {
+ if (is_null($seed)) {
+ shuffle($array);
+ } else {
+ mt_srand($seed);
+ shuffle($array);
+ mt_srand();
+ }
+
+ return $array;
+ }
+
+ /**
+ * Sort the array using the given callback or "dot" notation.
+ *
+ * @param array $array
+ * @param callable|array|string|null $callback
+ * @return array
+ */
+ public static function sort($array, $callback = null)
+ {
+ return Collection::make($array)->sortBy($callback)->all();
+ }
+
+ /**
+ * Recursively sort an array by keys and values.
+ *
+ * @param array $array
+ * @param int $options
+ * @param bool $descending
+ * @return array
+ */
+ public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
+ {
+ foreach ($array as &$value) {
+ if (is_array($value)) {
+ $value = static::sortRecursive($value, $options, $descending);
+ }
+ }
+
+ if (static::isAssoc($array)) {
+ $descending
+ ? krsort($array, $options)
+ : ksort($array, $options);
+ } else {
+ $descending
+ ? rsort($array, $options)
+ : sort($array, $options);
+ }
+
+ return $array;
+ }
+
+ /**
+ * Conditionally compile classes from an array into a CSS class list.
+ *
+ * @param array $array
+ * @return string
+ */
+ public static function toCssClasses($array)
+ {
+ $classList = static::wrap($array);
+
+ $classes = [];
+
+ foreach ($classList as $class => $constraint) {
+ if (is_numeric($class)) {
+ $classes[] = $constraint;
+ } elseif ($constraint) {
+ $classes[] = $class;
+ }
+ }
+
+ return implode(' ', $classes);
+ }
+
+ /**
+ * Filter the array using the given callback.
+ *
+ * @param array $array
+ * @param callable $callback
+ * @return array
+ */
+ public static function where($array, callable $callback)
+ {
+ return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+ }
+
+ /**
+ * Filter items where the value is not null.
+ *
+ * @param array $array
+ * @return array
+ */
+ public static function whereNotNull($array)
+ {
+ return static::where($array, function ($value) {
+ return ! is_null($value);
+ });
+ }
+
+ /**
+ * If the given value is not an array and not null, wrap it in one.
+ *
+ * @param mixed $value
+ * @return array
+ */
+ public static function wrap($value)
+ {
+ if (is_null($value)) {
+ return [];
+ }
+
+ return is_array($value) ? $value : [$value];
+ }
+}
diff --git a/vendor/illuminate/collections/Collection.php b/vendor/illuminate/collections/Collection.php
new file mode 100644
index 0000000..61a4884
--- /dev/null
+++ b/vendor/illuminate/collections/Collection.php
@@ -0,0 +1,1672 @@
+items = $this->getArrayableItems($items);
+ }
+
+ /**
+ * Create a collection with the given range.
+ *
+ * @param int $from
+ * @param int $to
+ * @return static
+ */
+ public static function range($from, $to)
+ {
+ return new static(range($from, $to));
+ }
+
+ /**
+ * Get all of the items in the collection.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Get a lazy collection for the items in this collection.
+ *
+ * @return \Illuminate\Support\LazyCollection
+ */
+ public function lazy()
+ {
+ return new LazyCollection($this->items);
+ }
+
+ /**
+ * Get the average value of a given key.
+ *
+ * @param callable|string|null $callback
+ * @return mixed
+ */
+ public function avg($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+
+ $items = $this->map(function ($value) use ($callback) {
+ return $callback($value);
+ })->filter(function ($value) {
+ return ! is_null($value);
+ });
+
+ if ($count = $items->count()) {
+ return $items->sum() / $count;
+ }
+ }
+
+ /**
+ * Get the median of a given key.
+ *
+ * @param string|array|null $key
+ * @return mixed
+ */
+ public function median($key = null)
+ {
+ $values = (isset($key) ? $this->pluck($key) : $this)
+ ->filter(function ($item) {
+ return ! is_null($item);
+ })->sort()->values();
+
+ $count = $values->count();
+
+ if ($count === 0) {
+ return;
+ }
+
+ $middle = (int) ($count / 2);
+
+ if ($count % 2) {
+ return $values->get($middle);
+ }
+
+ return (new static([
+ $values->get($middle - 1), $values->get($middle),
+ ]))->average();
+ }
+
+ /**
+ * Get the mode of a given key.
+ *
+ * @param string|array|null $key
+ * @return array|null
+ */
+ public function mode($key = null)
+ {
+ if ($this->count() === 0) {
+ return;
+ }
+
+ $collection = isset($key) ? $this->pluck($key) : $this;
+
+ $counts = new static;
+
+ $collection->each(function ($value) use ($counts) {
+ $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
+ });
+
+ $sorted = $counts->sort();
+
+ $highestValue = $sorted->last();
+
+ return $sorted->filter(function ($value) use ($highestValue) {
+ return $value == $highestValue;
+ })->sort()->keys()->all();
+ }
+
+ /**
+ * Collapse the collection of items into a single array.
+ *
+ * @return static
+ */
+ public function collapse()
+ {
+ return new static(Arr::collapse($this->items));
+ }
+
+ /**
+ * Determine if an item exists in the collection.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function contains($key, $operator = null, $value = null)
+ {
+ if (func_num_args() === 1) {
+ if ($this->useAsCallable($key)) {
+ $placeholder = new stdClass;
+
+ return $this->first($key, $placeholder) !== $placeholder;
+ }
+
+ return in_array($key, $this->items);
+ }
+
+ return $this->contains($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Determine if an item is not contained in the collection.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function doesntContain($key, $operator = null, $value = null)
+ {
+ return ! $this->contains(...func_get_args());
+ }
+
+ /**
+ * Cross join with the given lists, returning all possible permutations.
+ *
+ * @param mixed ...$lists
+ * @return static
+ */
+ public function crossJoin(...$lists)
+ {
+ return new static(Arr::crossJoin(
+ $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
+ ));
+ }
+
+ /**
+ * Get the items in the collection that are not present in the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function diff($items)
+ {
+ return new static(array_diff($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection that are not present in the given items, using the callback.
+ *
+ * @param mixed $items
+ * @param callable $callback
+ * @return static
+ */
+ public function diffUsing($items, callable $callback)
+ {
+ return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Get the items in the collection whose keys and values are not present in the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function diffAssoc($items)
+ {
+ return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection whose keys and values are not present in the given items, using the callback.
+ *
+ * @param mixed $items
+ * @param callable $callback
+ * @return static
+ */
+ public function diffAssocUsing($items, callable $callback)
+ {
+ return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Get the items in the collection whose keys are not present in the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function diffKeys($items)
+ {
+ return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection whose keys are not present in the given items, using the callback.
+ *
+ * @param mixed $items
+ * @param callable $callback
+ * @return static
+ */
+ public function diffKeysUsing($items, callable $callback)
+ {
+ return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Retrieve duplicate items from the collection.
+ *
+ * @param callable|string|null $callback
+ * @param bool $strict
+ * @return static
+ */
+ public function duplicates($callback = null, $strict = false)
+ {
+ $items = $this->map($this->valueRetriever($callback));
+
+ $uniqueItems = $items->unique(null, $strict);
+
+ $compare = $this->duplicateComparator($strict);
+
+ $duplicates = new static;
+
+ foreach ($items as $key => $value) {
+ if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
+ $uniqueItems->shift();
+ } else {
+ $duplicates[$key] = $value;
+ }
+ }
+
+ return $duplicates;
+ }
+
+ /**
+ * Retrieve duplicate items from the collection using strict comparison.
+ *
+ * @param callable|string|null $callback
+ * @return static
+ */
+ public function duplicatesStrict($callback = null)
+ {
+ return $this->duplicates($callback, true);
+ }
+
+ /**
+ * Get the comparison function to detect duplicates.
+ *
+ * @param bool $strict
+ * @return \Closure
+ */
+ protected function duplicateComparator($strict)
+ {
+ if ($strict) {
+ return function ($a, $b) {
+ return $a === $b;
+ };
+ }
+
+ return function ($a, $b) {
+ return $a == $b;
+ };
+ }
+
+ /**
+ * Get all items except for those with the specified keys.
+ *
+ * @param \Illuminate\Support\Collection|mixed $keys
+ * @return static
+ */
+ public function except($keys)
+ {
+ if ($keys instanceof Enumerable) {
+ $keys = $keys->all();
+ } elseif (! is_array($keys)) {
+ $keys = func_get_args();
+ }
+
+ return new static(Arr::except($this->items, $keys));
+ }
+
+ /**
+ * Run a filter over each of the items.
+ *
+ * @param callable|null $callback
+ * @return static
+ */
+ public function filter(callable $callback = null)
+ {
+ if ($callback) {
+ return new static(Arr::where($this->items, $callback));
+ }
+
+ return new static(array_filter($this->items));
+ }
+
+ /**
+ * Get the first item from the collection passing the given truth test.
+ *
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public function first(callable $callback = null, $default = null)
+ {
+ return Arr::first($this->items, $callback, $default);
+ }
+
+ /**
+ * Get a flattened array of the items in the collection.
+ *
+ * @param int $depth
+ * @return static
+ */
+ public function flatten($depth = INF)
+ {
+ return new static(Arr::flatten($this->items, $depth));
+ }
+
+ /**
+ * Flip the items in the collection.
+ *
+ * @return static
+ */
+ public function flip()
+ {
+ return new static(array_flip($this->items));
+ }
+
+ /**
+ * Remove an item from the collection by key.
+ *
+ * @param string|int|array $keys
+ * @return $this
+ */
+ public function forget($keys)
+ {
+ foreach ((array) $keys as $key) {
+ $this->offsetUnset($key);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get an item from the collection by key.
+ *
+ * @param mixed $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if (array_key_exists($key, $this->items)) {
+ return $this->items[$key];
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Get an item from the collection by key or add it to collection if it does not exist.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return mixed
+ */
+ public function getOrPut($key, $value)
+ {
+ if (array_key_exists($key, $this->items)) {
+ return $this->items[$key];
+ }
+
+ $this->offsetSet($key, $value = value($value));
+
+ return $value;
+ }
+
+ /**
+ * Group an associative array by a field or using a callback.
+ *
+ * @param array|callable|string $groupBy
+ * @param bool $preserveKeys
+ * @return static
+ */
+ public function groupBy($groupBy, $preserveKeys = false)
+ {
+ if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
+ $nextGroups = $groupBy;
+
+ $groupBy = array_shift($nextGroups);
+ }
+
+ $groupBy = $this->valueRetriever($groupBy);
+
+ $results = [];
+
+ foreach ($this->items as $key => $value) {
+ $groupKeys = $groupBy($value, $key);
+
+ if (! is_array($groupKeys)) {
+ $groupKeys = [$groupKeys];
+ }
+
+ foreach ($groupKeys as $groupKey) {
+ $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
+
+ if (! array_key_exists($groupKey, $results)) {
+ $results[$groupKey] = new static;
+ }
+
+ $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
+ }
+ }
+
+ $result = new static($results);
+
+ if (! empty($nextGroups)) {
+ return $result->map->groupBy($nextGroups, $preserveKeys);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Key an associative array by a field or using a callback.
+ *
+ * @param callable|string $keyBy
+ * @return static
+ */
+ public function keyBy($keyBy)
+ {
+ $keyBy = $this->valueRetriever($keyBy);
+
+ $results = [];
+
+ foreach ($this->items as $key => $item) {
+ $resolvedKey = $keyBy($item, $key);
+
+ if (is_object($resolvedKey)) {
+ $resolvedKey = (string) $resolvedKey;
+ }
+
+ $results[$resolvedKey] = $item;
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Determine if an item exists in the collection by key.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function has($key)
+ {
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $value) {
+ if (! array_key_exists($value, $this->items)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if any of the keys exist in the collection.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function hasAny($key)
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $value) {
+ if ($this->has($value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Concatenate values of a given key as a string.
+ *
+ * @param string $value
+ * @param string|null $glue
+ * @return string
+ */
+ public function implode($value, $glue = null)
+ {
+ $first = $this->first();
+
+ if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
+ return implode($glue ?? '', $this->pluck($value)->all());
+ }
+
+ return implode($value ?? '', $this->items);
+ }
+
+ /**
+ * Intersect the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function intersect($items)
+ {
+ return new static(array_intersect($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Intersect the collection with the given items by key.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function intersectByKeys($items)
+ {
+ return new static(array_intersect_key(
+ $this->items, $this->getArrayableItems($items)
+ ));
+ }
+
+ /**
+ * Determine if the collection is empty or not.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return empty($this->items);
+ }
+
+ /**
+ * Determine if the collection contains a single item.
+ *
+ * @return bool
+ */
+ public function containsOneItem()
+ {
+ return $this->count() === 1;
+ }
+
+ /**
+ * Join all items from the collection using a string. The final items can use a separate glue string.
+ *
+ * @param string $glue
+ * @param string $finalGlue
+ * @return string
+ */
+ public function join($glue, $finalGlue = '')
+ {
+ if ($finalGlue === '') {
+ return $this->implode($glue);
+ }
+
+ $count = $this->count();
+
+ if ($count === 0) {
+ return '';
+ }
+
+ if ($count === 1) {
+ return $this->last();
+ }
+
+ $collection = new static($this->items);
+
+ $finalItem = $collection->pop();
+
+ return $collection->implode($glue).$finalGlue.$finalItem;
+ }
+
+ /**
+ * Get the keys of the collection items.
+ *
+ * @return static
+ */
+ public function keys()
+ {
+ return new static(array_keys($this->items));
+ }
+
+ /**
+ * Get the last item from the collection.
+ *
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public function last(callable $callback = null, $default = null)
+ {
+ return Arr::last($this->items, $callback, $default);
+ }
+
+ /**
+ * Get the values of a given key.
+ *
+ * @param string|array|int|null $value
+ * @param string|null $key
+ * @return static
+ */
+ public function pluck($value, $key = null)
+ {
+ return new static(Arr::pluck($this->items, $value, $key));
+ }
+
+ /**
+ * Run a map over each of the items.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function map(callable $callback)
+ {
+ $keys = array_keys($this->items);
+
+ $items = array_map($callback, $this->items, $keys);
+
+ return new static(array_combine($keys, $items));
+ }
+
+ /**
+ * Run a dictionary map over the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function mapToDictionary(callable $callback)
+ {
+ $dictionary = [];
+
+ foreach ($this->items as $key => $item) {
+ $pair = $callback($item, $key);
+
+ $key = key($pair);
+
+ $value = reset($pair);
+
+ if (! isset($dictionary[$key])) {
+ $dictionary[$key] = [];
+ }
+
+ $dictionary[$key][] = $value;
+ }
+
+ return new static($dictionary);
+ }
+
+ /**
+ * Run an associative map over each of the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function mapWithKeys(callable $callback)
+ {
+ $result = [];
+
+ foreach ($this->items as $key => $value) {
+ $assoc = $callback($value, $key);
+
+ foreach ($assoc as $mapKey => $mapValue) {
+ $result[$mapKey] = $mapValue;
+ }
+ }
+
+ return new static($result);
+ }
+
+ /**
+ * Merge the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function merge($items)
+ {
+ return new static(array_merge($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Recursively merge the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function mergeRecursive($items)
+ {
+ return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Create a collection by using this collection for keys and another for its values.
+ *
+ * @param mixed $values
+ * @return static
+ */
+ public function combine($values)
+ {
+ return new static(array_combine($this->all(), $this->getArrayableItems($values)));
+ }
+
+ /**
+ * Union the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function union($items)
+ {
+ return new static($this->items + $this->getArrayableItems($items));
+ }
+
+ /**
+ * Create a new collection consisting of every n-th element.
+ *
+ * @param int $step
+ * @param int $offset
+ * @return static
+ */
+ public function nth($step, $offset = 0)
+ {
+ $new = [];
+
+ $position = 0;
+
+ foreach ($this->slice($offset)->items as $item) {
+ if ($position % $step === 0) {
+ $new[] = $item;
+ }
+
+ $position++;
+ }
+
+ return new static($new);
+ }
+
+ /**
+ * Get the items with the specified keys.
+ *
+ * @param mixed $keys
+ * @return static
+ */
+ public function only($keys)
+ {
+ if (is_null($keys)) {
+ return new static($this->items);
+ }
+
+ if ($keys instanceof Enumerable) {
+ $keys = $keys->all();
+ }
+
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ return new static(Arr::only($this->items, $keys));
+ }
+
+ /**
+ * Get and remove the last N items from the collection.
+ *
+ * @param int $count
+ * @return mixed
+ */
+ public function pop($count = 1)
+ {
+ if ($count === 1) {
+ return array_pop($this->items);
+ }
+
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $results = [];
+
+ $collectionCount = $this->count();
+
+ foreach (range(1, min($count, $collectionCount)) as $item) {
+ array_push($results, array_pop($this->items));
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Push an item onto the beginning of the collection.
+ *
+ * @param mixed $value
+ * @param mixed $key
+ * @return $this
+ */
+ public function prepend($value, $key = null)
+ {
+ $this->items = Arr::prepend($this->items, ...func_get_args());
+
+ return $this;
+ }
+
+ /**
+ * Push one or more items onto the end of the collection.
+ *
+ * @param mixed $values
+ * @return $this
+ */
+ public function push(...$values)
+ {
+ foreach ($values as $value) {
+ $this->items[] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Push all of the given items onto the collection.
+ *
+ * @param iterable $source
+ * @return static
+ */
+ public function concat($source)
+ {
+ $result = new static($this);
+
+ foreach ($source as $item) {
+ $result->push($item);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get and remove an item from the collection.
+ *
+ * @param mixed $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function pull($key, $default = null)
+ {
+ return Arr::pull($this->items, $key, $default);
+ }
+
+ /**
+ * Put an item in the collection by key.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return $this
+ */
+ public function put($key, $value)
+ {
+ $this->offsetSet($key, $value);
+
+ return $this;
+ }
+
+ /**
+ * Get one or a specified number of items randomly from the collection.
+ *
+ * @param int|null $number
+ * @return static|mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function random($number = null)
+ {
+ if (is_null($number)) {
+ return Arr::random($this->items);
+ }
+
+ return new static(Arr::random($this->items, $number));
+ }
+
+ /**
+ * Replace the collection items with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function replace($items)
+ {
+ return new static(array_replace($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Recursively replace the collection items with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function replaceRecursive($items)
+ {
+ return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Reverse items order.
+ *
+ * @return static
+ */
+ public function reverse()
+ {
+ return new static(array_reverse($this->items, true));
+ }
+
+ /**
+ * Search the collection for a given value and return the corresponding key if successful.
+ *
+ * @param mixed $value
+ * @param bool $strict
+ * @return mixed
+ */
+ public function search($value, $strict = false)
+ {
+ if (! $this->useAsCallable($value)) {
+ return array_search($value, $this->items, $strict);
+ }
+
+ foreach ($this->items as $key => $item) {
+ if ($value($item, $key)) {
+ return $key;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get and remove the first N items from the collection.
+ *
+ * @param int $count
+ * @return mixed
+ */
+ public function shift($count = 1)
+ {
+ if ($count === 1) {
+ return array_shift($this->items);
+ }
+
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $results = [];
+
+ $collectionCount = $this->count();
+
+ foreach (range(1, min($count, $collectionCount)) as $item) {
+ array_push($results, array_shift($this->items));
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Shuffle the items in the collection.
+ *
+ * @param int|null $seed
+ * @return static
+ */
+ public function shuffle($seed = null)
+ {
+ return new static(Arr::shuffle($this->items, $seed));
+ }
+
+ /**
+ * Create chunks representing a "sliding window" view of the items in the collection.
+ *
+ * @param int $size
+ * @param int $step
+ * @return static
+ */
+ public function sliding($size = 2, $step = 1)
+ {
+ $chunks = floor(($this->count() - $size) / $step) + 1;
+
+ return static::times($chunks, function ($number) use ($size, $step) {
+ return $this->slice(($number - 1) * $step, $size);
+ });
+ }
+
+ /**
+ * Skip the first {$count} items.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function skip($count)
+ {
+ return $this->slice($count);
+ }
+
+ /**
+ * Skip items in the collection until the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function skipUntil($value)
+ {
+ return new static($this->lazy()->skipUntil($value)->all());
+ }
+
+ /**
+ * Skip items in the collection while the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function skipWhile($value)
+ {
+ return new static($this->lazy()->skipWhile($value)->all());
+ }
+
+ /**
+ * Slice the underlying collection array.
+ *
+ * @param int $offset
+ * @param int|null $length
+ * @return static
+ */
+ public function slice($offset, $length = null)
+ {
+ return new static(array_slice($this->items, $offset, $length, true));
+ }
+
+ /**
+ * Split a collection into a certain number of groups.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function split($numberOfGroups)
+ {
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $groups = new static;
+
+ $groupSize = floor($this->count() / $numberOfGroups);
+
+ $remain = $this->count() % $numberOfGroups;
+
+ $start = 0;
+
+ for ($i = 0; $i < $numberOfGroups; $i++) {
+ $size = $groupSize;
+
+ if ($i < $remain) {
+ $size++;
+ }
+
+ if ($size) {
+ $groups->push(new static(array_slice($this->items, $start, $size)));
+
+ $start += $size;
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Split a collection into a certain number of groups, and fill the first groups completely.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function splitIn($numberOfGroups)
+ {
+ return $this->chunk(ceil($this->count() / $numberOfGroups));
+ }
+
+ /**
+ * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return mixed
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ * @throws \Illuminate\Support\MultipleItemsFoundException
+ */
+ public function sole($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ $items = $this->when($filter)->filter($filter);
+
+ if ($items->isEmpty()) {
+ throw new ItemNotFoundException;
+ }
+
+ if ($items->count() > 1) {
+ throw new MultipleItemsFoundException;
+ }
+
+ return $items->first();
+ }
+
+ /**
+ * Get the first item in the collection but throw an exception if no matching items exist.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return mixed
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ */
+ public function firstOrFail($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ $placeholder = new stdClass();
+
+ $item = $this->first($filter, $placeholder);
+
+ if ($item === $placeholder) {
+ throw new ItemNotFoundException;
+ }
+
+ return $item;
+ }
+
+ /**
+ * Chunk the collection into chunks of the given size.
+ *
+ * @param int $size
+ * @return static
+ */
+ public function chunk($size)
+ {
+ if ($size <= 0) {
+ return new static;
+ }
+
+ $chunks = [];
+
+ foreach (array_chunk($this->items, $size, true) as $chunk) {
+ $chunks[] = new static($chunk);
+ }
+
+ return new static($chunks);
+ }
+
+ /**
+ * Chunk the collection into chunks with a callback.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function chunkWhile(callable $callback)
+ {
+ return new static(
+ $this->lazy()->chunkWhile($callback)->mapInto(static::class)
+ );
+ }
+
+ /**
+ * Sort through each item with a callback.
+ *
+ * @param callable|int|null $callback
+ * @return static
+ */
+ public function sort($callback = null)
+ {
+ $items = $this->items;
+
+ $callback && is_callable($callback)
+ ? uasort($items, $callback)
+ : asort($items, $callback ?? SORT_REGULAR);
+
+ return new static($items);
+ }
+
+ /**
+ * Sort items in descending order.
+ *
+ * @param int $options
+ * @return static
+ */
+ public function sortDesc($options = SORT_REGULAR)
+ {
+ $items = $this->items;
+
+ arsort($items, $options);
+
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection using the given callback.
+ *
+ * @param callable|array|string $callback
+ * @param int $options
+ * @param bool $descending
+ * @return static
+ */
+ public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
+ {
+ if (is_array($callback) && ! is_callable($callback)) {
+ return $this->sortByMany($callback);
+ }
+
+ $results = [];
+
+ $callback = $this->valueRetriever($callback);
+
+ // First we will loop through the items and get the comparator from a callback
+ // function which we were given. Then, we will sort the returned values and
+ // grab all the corresponding values for the sorted keys from this array.
+ foreach ($this->items as $key => $value) {
+ $results[$key] = $callback($value, $key);
+ }
+
+ $descending ? arsort($results, $options)
+ : asort($results, $options);
+
+ // Once we have sorted all of the keys in the array, we will loop through them
+ // and grab the corresponding model so we can set the underlying items list
+ // to the sorted version. Then we'll just return the collection instance.
+ foreach (array_keys($results) as $key) {
+ $results[$key] = $this->items[$key];
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Sort the collection using multiple comparisons.
+ *
+ * @param array $comparisons
+ * @return static
+ */
+ protected function sortByMany(array $comparisons = [])
+ {
+ $items = $this->items;
+
+ usort($items, function ($a, $b) use ($comparisons) {
+ foreach ($comparisons as $comparison) {
+ $comparison = Arr::wrap($comparison);
+
+ $prop = $comparison[0];
+
+ $ascending = Arr::get($comparison, 1, true) === true ||
+ Arr::get($comparison, 1, true) === 'asc';
+
+ $result = 0;
+
+ if (! is_string($prop) && is_callable($prop)) {
+ $result = $prop($a, $b);
+ } else {
+ $values = [data_get($a, $prop), data_get($b, $prop)];
+
+ if (! $ascending) {
+ $values = array_reverse($values);
+ }
+
+ $result = $values[0] <=> $values[1];
+ }
+
+ if ($result === 0) {
+ continue;
+ }
+
+ return $result;
+ }
+ });
+
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection in descending order using the given callback.
+ *
+ * @param callable|string $callback
+ * @param int $options
+ * @return static
+ */
+ public function sortByDesc($callback, $options = SORT_REGULAR)
+ {
+ return $this->sortBy($callback, $options, true);
+ }
+
+ /**
+ * Sort the collection keys.
+ *
+ * @param int $options
+ * @param bool $descending
+ * @return static
+ */
+ public function sortKeys($options = SORT_REGULAR, $descending = false)
+ {
+ $items = $this->items;
+
+ $descending ? krsort($items, $options) : ksort($items, $options);
+
+ return new static($items);
+ }
+
+ /**
+ * Sort the collection keys in descending order.
+ *
+ * @param int $options
+ * @return static
+ */
+ public function sortKeysDesc($options = SORT_REGULAR)
+ {
+ return $this->sortKeys($options, true);
+ }
+
+ /**
+ * Sort the collection keys using a callback.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function sortKeysUsing(callable $callback)
+ {
+ $items = $this->items;
+
+ uksort($items, $callback);
+
+ return new static($items);
+ }
+
+ /**
+ * Splice a portion of the underlying collection array.
+ *
+ * @param int $offset
+ * @param int|null $length
+ * @param mixed $replacement
+ * @return static
+ */
+ public function splice($offset, $length = null, $replacement = [])
+ {
+ if (func_num_args() === 1) {
+ return new static(array_splice($this->items, $offset));
+ }
+
+ return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement)));
+ }
+
+ /**
+ * Take the first or last {$limit} items.
+ *
+ * @param int $limit
+ * @return static
+ */
+ public function take($limit)
+ {
+ if ($limit < 0) {
+ return $this->slice($limit, abs($limit));
+ }
+
+ return $this->slice(0, $limit);
+ }
+
+ /**
+ * Take items in the collection until the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function takeUntil($value)
+ {
+ return new static($this->lazy()->takeUntil($value)->all());
+ }
+
+ /**
+ * Take items in the collection while the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function takeWhile($value)
+ {
+ return new static($this->lazy()->takeWhile($value)->all());
+ }
+
+ /**
+ * Transform each item in the collection using a callback.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function transform(callable $callback)
+ {
+ $this->items = $this->map($callback)->all();
+
+ return $this;
+ }
+
+ /**
+ * Convert a flatten "dot" notation array into an expanded array.
+ *
+ * @return static
+ */
+ public function undot()
+ {
+ return new static(Arr::undot($this->all()));
+ }
+
+ /**
+ * Return only unique items from the collection array.
+ *
+ * @param string|callable|null $key
+ * @param bool $strict
+ * @return static
+ */
+ public function unique($key = null, $strict = false)
+ {
+ if (is_null($key) && $strict === false) {
+ return new static(array_unique($this->items, SORT_REGULAR));
+ }
+
+ $callback = $this->valueRetriever($key);
+
+ $exists = [];
+
+ return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
+ if (in_array($id = $callback($item, $key), $exists, $strict)) {
+ return true;
+ }
+
+ $exists[] = $id;
+ });
+ }
+
+ /**
+ * Reset the keys on the underlying array.
+ *
+ * @return static
+ */
+ public function values()
+ {
+ return new static(array_values($this->items));
+ }
+
+ /**
+ * Zip the collection together with one or more arrays.
+ *
+ * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
+ * => [[1, 4], [2, 5], [3, 6]]
+ *
+ * @param mixed ...$items
+ * @return static
+ */
+ public function zip($items)
+ {
+ $arrayableItems = array_map(function ($items) {
+ return $this->getArrayableItems($items);
+ }, func_get_args());
+
+ $params = array_merge([function () {
+ return new static(func_get_args());
+ }, $this->items], $arrayableItems);
+
+ return new static(array_map(...$params));
+ }
+
+ /**
+ * Pad collection to the specified length with a value.
+ *
+ * @param int $size
+ * @param mixed $value
+ * @return static
+ */
+ public function pad($size, $value)
+ {
+ return new static(array_pad($this->items, $size, $value));
+ }
+
+ /**
+ * Get an iterator for the items.
+ *
+ * @return \ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return new ArrayIterator($this->items);
+ }
+
+ /**
+ * Count the number of items in the collection.
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->items);
+ }
+
+ /**
+ * Count the number of items in the collection by a field or using a callback.
+ *
+ * @param callable|string $countBy
+ * @return static
+ */
+ public function countBy($countBy = null)
+ {
+ return new static($this->lazy()->countBy($countBy)->all());
+ }
+
+ /**
+ * Add an item to the collection.
+ *
+ * @param mixed $item
+ * @return $this
+ */
+ public function add($item)
+ {
+ $this->items[] = $item;
+
+ return $this;
+ }
+
+ /**
+ * Get a base Support collection instance from this collection.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function toBase()
+ {
+ return new self($this);
+ }
+
+ /**
+ * Determine if an item exists at an offset.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return isset($this->items[$key]);
+ }
+
+ /**
+ * Get an item at a given offset.
+ *
+ * @param mixed $key
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->items[$key];
+ }
+
+ /**
+ * Set the item at a given offset.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ if (is_null($key)) {
+ $this->items[] = $value;
+ } else {
+ $this->items[$key] = $value;
+ }
+ }
+
+ /**
+ * Unset the item at a given offset.
+ *
+ * @param mixed $key
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ unset($this->items[$key]);
+ }
+}
diff --git a/vendor/illuminate/collections/Enumerable.php b/vendor/illuminate/collections/Enumerable.php
new file mode 100644
index 0000000..261a0c8
--- /dev/null
+++ b/vendor/illuminate/collections/Enumerable.php
@@ -0,0 +1,1027 @@
+zip([4, 5, 6]);
+ * => [[1, 4], [2, 5], [3, 6]]
+ *
+ * @param mixed ...$items
+ * @return static
+ */
+ public function zip($items);
+
+ /**
+ * Collect the values into a collection.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function collect();
+
+ /**
+ * Convert the collection to its string representation.
+ *
+ * @return string
+ */
+ public function __toString();
+
+ /**
+ * Add a method to the list of proxied methods.
+ *
+ * @param string $method
+ * @return void
+ */
+ public static function proxy($method);
+
+ /**
+ * Dynamically access collection proxies.
+ *
+ * @param string $key
+ * @return mixed
+ *
+ * @throws \Exception
+ */
+ public function __get($key);
+}
diff --git a/vendor/illuminate/collections/HigherOrderCollectionProxy.php b/vendor/illuminate/collections/HigherOrderCollectionProxy.php
new file mode 100644
index 0000000..106356c
--- /dev/null
+++ b/vendor/illuminate/collections/HigherOrderCollectionProxy.php
@@ -0,0 +1,63 @@
+method = $method;
+ $this->collection = $collection;
+ }
+
+ /**
+ * Proxy accessing an attribute onto the collection items.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->collection->{$this->method}(function ($value) use ($key) {
+ return is_array($value) ? $value[$key] : $value->{$key};
+ });
+ }
+
+ /**
+ * Proxy a method call onto the collection items.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
+ return $value->{$method}(...$parameters);
+ });
+ }
+}
diff --git a/vendor/illuminate/collections/HigherOrderWhenProxy.php b/vendor/illuminate/collections/HigherOrderWhenProxy.php
new file mode 100644
index 0000000..6653c03
--- /dev/null
+++ b/vendor/illuminate/collections/HigherOrderWhenProxy.php
@@ -0,0 +1,63 @@
+condition = $condition;
+ $this->collection = $collection;
+ }
+
+ /**
+ * Proxy accessing an attribute onto the collection.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->condition
+ ? $this->collection->{$key}
+ : $this->collection;
+ }
+
+ /**
+ * Proxy a method call onto the collection.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->condition
+ ? $this->collection->{$method}(...$parameters)
+ : $this->collection;
+ }
+}
diff --git a/vendor/illuminate/collections/ItemNotFoundException.php b/vendor/illuminate/collections/ItemNotFoundException.php
new file mode 100644
index 0000000..05a51d9
--- /dev/null
+++ b/vendor/illuminate/collections/ItemNotFoundException.php
@@ -0,0 +1,9 @@
+source = $source;
+ } elseif (is_null($source)) {
+ $this->source = static::empty();
+ } else {
+ $this->source = $this->getArrayableItems($source);
+ }
+ }
+
+ /**
+ * Create a collection with the given range.
+ *
+ * @param int $from
+ * @param int $to
+ * @return static
+ */
+ public static function range($from, $to)
+ {
+ return new static(function () use ($from, $to) {
+ if ($from <= $to) {
+ for (; $from <= $to; $from++) {
+ yield $from;
+ }
+ } else {
+ for (; $from >= $to; $from--) {
+ yield $from;
+ }
+ }
+ });
+ }
+
+ /**
+ * Get all items in the enumerable.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ if (is_array($this->source)) {
+ return $this->source;
+ }
+
+ return iterator_to_array($this->getIterator());
+ }
+
+ /**
+ * Eager load all items into a new lazy collection backed by an array.
+ *
+ * @return static
+ */
+ public function eager()
+ {
+ return new static($this->all());
+ }
+
+ /**
+ * Cache values as they're enumerated.
+ *
+ * @return static
+ */
+ public function remember()
+ {
+ $iterator = $this->getIterator();
+
+ $iteratorIndex = 0;
+
+ $cache = [];
+
+ return new static(function () use ($iterator, &$iteratorIndex, &$cache) {
+ for ($index = 0; true; $index++) {
+ if (array_key_exists($index, $cache)) {
+ yield $cache[$index][0] => $cache[$index][1];
+
+ continue;
+ }
+
+ if ($iteratorIndex < $index) {
+ $iterator->next();
+
+ $iteratorIndex++;
+ }
+
+ if (! $iterator->valid()) {
+ break;
+ }
+
+ $cache[$index] = [$iterator->key(), $iterator->current()];
+
+ yield $cache[$index][0] => $cache[$index][1];
+ }
+ });
+ }
+
+ /**
+ * Get the average value of a given key.
+ *
+ * @param callable|string|null $callback
+ * @return mixed
+ */
+ public function avg($callback = null)
+ {
+ return $this->collect()->avg($callback);
+ }
+
+ /**
+ * Get the median of a given key.
+ *
+ * @param string|array|null $key
+ * @return mixed
+ */
+ public function median($key = null)
+ {
+ return $this->collect()->median($key);
+ }
+
+ /**
+ * Get the mode of a given key.
+ *
+ * @param string|array|null $key
+ * @return array|null
+ */
+ public function mode($key = null)
+ {
+ return $this->collect()->mode($key);
+ }
+
+ /**
+ * Collapse the collection of items into a single array.
+ *
+ * @return static
+ */
+ public function collapse()
+ {
+ return new static(function () {
+ foreach ($this as $values) {
+ if (is_array($values) || $values instanceof Enumerable) {
+ foreach ($values as $value) {
+ yield $value;
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Determine if an item exists in the enumerable.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function contains($key, $operator = null, $value = null)
+ {
+ if (func_num_args() === 1 && $this->useAsCallable($key)) {
+ $placeholder = new stdClass;
+
+ return $this->first($key, $placeholder) !== $placeholder;
+ }
+
+ if (func_num_args() === 1) {
+ $needle = $key;
+
+ foreach ($this as $value) {
+ if ($value == $needle) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return $this->contains($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Determine if an item is not contained in the enumerable.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function doesntContain($key, $operator = null, $value = null)
+ {
+ return ! $this->contains(...func_get_args());
+ }
+
+ /**
+ * Cross join the given iterables, returning all possible permutations.
+ *
+ * @param array ...$arrays
+ * @return static
+ */
+ public function crossJoin(...$arrays)
+ {
+ return $this->passthru('crossJoin', func_get_args());
+ }
+
+ /**
+ * Count the number of items in the collection by a field or using a callback.
+ *
+ * @param callable|string $countBy
+ * @return static
+ */
+ public function countBy($countBy = null)
+ {
+ $countBy = is_null($countBy)
+ ? $this->identity()
+ : $this->valueRetriever($countBy);
+
+ return new static(function () use ($countBy) {
+ $counts = [];
+
+ foreach ($this as $key => $value) {
+ $group = $countBy($value, $key);
+
+ if (empty($counts[$group])) {
+ $counts[$group] = 0;
+ }
+
+ $counts[$group]++;
+ }
+
+ yield from $counts;
+ });
+ }
+
+ /**
+ * Get the items that are not present in the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function diff($items)
+ {
+ return $this->passthru('diff', func_get_args());
+ }
+
+ /**
+ * Get the items that are not present in the given items, using the callback.
+ *
+ * @param mixed $items
+ * @param callable $callback
+ * @return static
+ */
+ public function diffUsing($items, callable $callback)
+ {
+ return $this->passthru('diffUsing', func_get_args());
+ }
+
+ /**
+ * Get the items whose keys and values are not present in the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function diffAssoc($items)
+ {
+ return $this->passthru('diffAssoc', func_get_args());
+ }
+
+ /**
+ * Get the items whose keys and values are not present in the given items, using the callback.
+ *
+ * @param mixed $items
+ * @param callable $callback
+ * @return static
+ */
+ public function diffAssocUsing($items, callable $callback)
+ {
+ return $this->passthru('diffAssocUsing', func_get_args());
+ }
+
+ /**
+ * Get the items whose keys are not present in the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function diffKeys($items)
+ {
+ return $this->passthru('diffKeys', func_get_args());
+ }
+
+ /**
+ * Get the items whose keys are not present in the given items, using the callback.
+ *
+ * @param mixed $items
+ * @param callable $callback
+ * @return static
+ */
+ public function diffKeysUsing($items, callable $callback)
+ {
+ return $this->passthru('diffKeysUsing', func_get_args());
+ }
+
+ /**
+ * Retrieve duplicate items.
+ *
+ * @param callable|string|null $callback
+ * @param bool $strict
+ * @return static
+ */
+ public function duplicates($callback = null, $strict = false)
+ {
+ return $this->passthru('duplicates', func_get_args());
+ }
+
+ /**
+ * Retrieve duplicate items using strict comparison.
+ *
+ * @param callable|string|null $callback
+ * @return static
+ */
+ public function duplicatesStrict($callback = null)
+ {
+ return $this->passthru('duplicatesStrict', func_get_args());
+ }
+
+ /**
+ * Get all items except for those with the specified keys.
+ *
+ * @param mixed $keys
+ * @return static
+ */
+ public function except($keys)
+ {
+ return $this->passthru('except', func_get_args());
+ }
+
+ /**
+ * Run a filter over each of the items.
+ *
+ * @param callable|null $callback
+ * @return static
+ */
+ public function filter(callable $callback = null)
+ {
+ if (is_null($callback)) {
+ $callback = function ($value) {
+ return (bool) $value;
+ };
+ }
+
+ return new static(function () use ($callback) {
+ foreach ($this as $key => $value) {
+ if ($callback($value, $key)) {
+ yield $key => $value;
+ }
+ }
+ });
+ }
+
+ /**
+ * Get the first item from the enumerable passing the given truth test.
+ *
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public function first(callable $callback = null, $default = null)
+ {
+ $iterator = $this->getIterator();
+
+ if (is_null($callback)) {
+ if (! $iterator->valid()) {
+ return value($default);
+ }
+
+ return $iterator->current();
+ }
+
+ foreach ($iterator as $key => $value) {
+ if ($callback($value, $key)) {
+ return $value;
+ }
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Get a flattened list of the items in the collection.
+ *
+ * @param int $depth
+ * @return static
+ */
+ public function flatten($depth = INF)
+ {
+ $instance = new static(function () use ($depth) {
+ foreach ($this as $item) {
+ if (! is_array($item) && ! $item instanceof Enumerable) {
+ yield $item;
+ } elseif ($depth === 1) {
+ yield from $item;
+ } else {
+ yield from (new static($item))->flatten($depth - 1);
+ }
+ }
+ });
+
+ return $instance->values();
+ }
+
+ /**
+ * Flip the items in the collection.
+ *
+ * @return static
+ */
+ public function flip()
+ {
+ return new static(function () {
+ foreach ($this as $key => $value) {
+ yield $value => $key;
+ }
+ });
+ }
+
+ /**
+ * Get an item by key.
+ *
+ * @param mixed $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if (is_null($key)) {
+ return;
+ }
+
+ foreach ($this as $outerKey => $outerValue) {
+ if ($outerKey == $key) {
+ return $outerValue;
+ }
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Group an associative array by a field or using a callback.
+ *
+ * @param array|callable|string $groupBy
+ * @param bool $preserveKeys
+ * @return static
+ */
+ public function groupBy($groupBy, $preserveKeys = false)
+ {
+ return $this->passthru('groupBy', func_get_args());
+ }
+
+ /**
+ * Key an associative array by a field or using a callback.
+ *
+ * @param callable|string $keyBy
+ * @return static
+ */
+ public function keyBy($keyBy)
+ {
+ return new static(function () use ($keyBy) {
+ $keyBy = $this->valueRetriever($keyBy);
+
+ foreach ($this as $key => $item) {
+ $resolvedKey = $keyBy($item, $key);
+
+ if (is_object($resolvedKey)) {
+ $resolvedKey = (string) $resolvedKey;
+ }
+
+ yield $resolvedKey => $item;
+ }
+ });
+ }
+
+ /**
+ * Determine if an item exists in the collection by key.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function has($key)
+ {
+ $keys = array_flip(is_array($key) ? $key : func_get_args());
+ $count = count($keys);
+
+ foreach ($this as $key => $value) {
+ if (array_key_exists($key, $keys) && --$count == 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if any of the keys exist in the collection.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function hasAny($key)
+ {
+ $keys = array_flip(is_array($key) ? $key : func_get_args());
+
+ foreach ($this as $key => $value) {
+ if (array_key_exists($key, $keys)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Concatenate values of a given key as a string.
+ *
+ * @param string $value
+ * @param string|null $glue
+ * @return string
+ */
+ public function implode($value, $glue = null)
+ {
+ return $this->collect()->implode(...func_get_args());
+ }
+
+ /**
+ * Intersect the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function intersect($items)
+ {
+ return $this->passthru('intersect', func_get_args());
+ }
+
+ /**
+ * Intersect the collection with the given items by key.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function intersectByKeys($items)
+ {
+ return $this->passthru('intersectByKeys', func_get_args());
+ }
+
+ /**
+ * Determine if the items are empty or not.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return ! $this->getIterator()->valid();
+ }
+
+ /**
+ * Determine if the collection contains a single item.
+ *
+ * @return bool
+ */
+ public function containsOneItem()
+ {
+ return $this->take(2)->count() === 1;
+ }
+
+ /**
+ * Join all items from the collection using a string. The final items can use a separate glue string.
+ *
+ * @param string $glue
+ * @param string $finalGlue
+ * @return string
+ */
+ public function join($glue, $finalGlue = '')
+ {
+ return $this->collect()->join(...func_get_args());
+ }
+
+ /**
+ * Get the keys of the collection items.
+ *
+ * @return static
+ */
+ public function keys()
+ {
+ return new static(function () {
+ foreach ($this as $key => $value) {
+ yield $key;
+ }
+ });
+ }
+
+ /**
+ * Get the last item from the collection.
+ *
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public function last(callable $callback = null, $default = null)
+ {
+ $needle = $placeholder = new stdClass;
+
+ foreach ($this as $key => $value) {
+ if (is_null($callback) || $callback($value, $key)) {
+ $needle = $value;
+ }
+ }
+
+ return $needle === $placeholder ? value($default) : $needle;
+ }
+
+ /**
+ * Get the values of a given key.
+ *
+ * @param string|array $value
+ * @param string|null $key
+ * @return static
+ */
+ public function pluck($value, $key = null)
+ {
+ return new static(function () use ($value, $key) {
+ [$value, $key] = $this->explodePluckParameters($value, $key);
+
+ foreach ($this as $item) {
+ $itemValue = data_get($item, $value);
+
+ if (is_null($key)) {
+ yield $itemValue;
+ } else {
+ $itemKey = data_get($item, $key);
+
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+ $itemKey = (string) $itemKey;
+ }
+
+ yield $itemKey => $itemValue;
+ }
+ }
+ });
+ }
+
+ /**
+ * Run a map over each of the items.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function map(callable $callback)
+ {
+ return new static(function () use ($callback) {
+ foreach ($this as $key => $value) {
+ yield $key => $callback($value, $key);
+ }
+ });
+ }
+
+ /**
+ * Run a dictionary map over the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function mapToDictionary(callable $callback)
+ {
+ return $this->passthru('mapToDictionary', func_get_args());
+ }
+
+ /**
+ * Run an associative map over each of the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function mapWithKeys(callable $callback)
+ {
+ return new static(function () use ($callback) {
+ foreach ($this as $key => $value) {
+ yield from $callback($value, $key);
+ }
+ });
+ }
+
+ /**
+ * Merge the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function merge($items)
+ {
+ return $this->passthru('merge', func_get_args());
+ }
+
+ /**
+ * Recursively merge the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function mergeRecursive($items)
+ {
+ return $this->passthru('mergeRecursive', func_get_args());
+ }
+
+ /**
+ * Create a collection by using this collection for keys and another for its values.
+ *
+ * @param mixed $values
+ * @return static
+ */
+ public function combine($values)
+ {
+ return new static(function () use ($values) {
+ $values = $this->makeIterator($values);
+
+ $errorMessage = 'Both parameters should have an equal number of elements';
+
+ foreach ($this as $key) {
+ if (! $values->valid()) {
+ trigger_error($errorMessage, E_USER_WARNING);
+
+ break;
+ }
+
+ yield $key => $values->current();
+
+ $values->next();
+ }
+
+ if ($values->valid()) {
+ trigger_error($errorMessage, E_USER_WARNING);
+ }
+ });
+ }
+
+ /**
+ * Union the collection with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function union($items)
+ {
+ return $this->passthru('union', func_get_args());
+ }
+
+ /**
+ * Create a new collection consisting of every n-th element.
+ *
+ * @param int $step
+ * @param int $offset
+ * @return static
+ */
+ public function nth($step, $offset = 0)
+ {
+ return new static(function () use ($step, $offset) {
+ $position = 0;
+
+ foreach ($this->slice($offset) as $item) {
+ if ($position % $step === 0) {
+ yield $item;
+ }
+
+ $position++;
+ }
+ });
+ }
+
+ /**
+ * Get the items with the specified keys.
+ *
+ * @param mixed $keys
+ * @return static
+ */
+ public function only($keys)
+ {
+ if ($keys instanceof Enumerable) {
+ $keys = $keys->all();
+ } elseif (! is_null($keys)) {
+ $keys = is_array($keys) ? $keys : func_get_args();
+ }
+
+ return new static(function () use ($keys) {
+ if (is_null($keys)) {
+ yield from $this;
+ } else {
+ $keys = array_flip($keys);
+
+ foreach ($this as $key => $value) {
+ if (array_key_exists($key, $keys)) {
+ yield $key => $value;
+
+ unset($keys[$key]);
+
+ if (empty($keys)) {
+ break;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Push all of the given items onto the collection.
+ *
+ * @param iterable $source
+ * @return static
+ */
+ public function concat($source)
+ {
+ return (new static(function () use ($source) {
+ yield from $this;
+ yield from $source;
+ }))->values();
+ }
+
+ /**
+ * Get one or a specified number of items randomly from the collection.
+ *
+ * @param int|null $number
+ * @return static|mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function random($number = null)
+ {
+ $result = $this->collect()->random(...func_get_args());
+
+ return is_null($number) ? $result : new static($result);
+ }
+
+ /**
+ * Replace the collection items with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function replace($items)
+ {
+ return new static(function () use ($items) {
+ $items = $this->getArrayableItems($items);
+
+ foreach ($this as $key => $value) {
+ if (array_key_exists($key, $items)) {
+ yield $key => $items[$key];
+
+ unset($items[$key]);
+ } else {
+ yield $key => $value;
+ }
+ }
+
+ foreach ($items as $key => $value) {
+ yield $key => $value;
+ }
+ });
+ }
+
+ /**
+ * Recursively replace the collection items with the given items.
+ *
+ * @param mixed $items
+ * @return static
+ */
+ public function replaceRecursive($items)
+ {
+ return $this->passthru('replaceRecursive', func_get_args());
+ }
+
+ /**
+ * Reverse items order.
+ *
+ * @return static
+ */
+ public function reverse()
+ {
+ return $this->passthru('reverse', func_get_args());
+ }
+
+ /**
+ * Search the collection for a given value and return the corresponding key if successful.
+ *
+ * @param mixed $value
+ * @param bool $strict
+ * @return mixed
+ */
+ public function search($value, $strict = false)
+ {
+ $predicate = $this->useAsCallable($value)
+ ? $value
+ : function ($item) use ($value, $strict) {
+ return $strict ? $item === $value : $item == $value;
+ };
+
+ foreach ($this as $key => $item) {
+ if ($predicate($item, $key)) {
+ return $key;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Shuffle the items in the collection.
+ *
+ * @param int|null $seed
+ * @return static
+ */
+ public function shuffle($seed = null)
+ {
+ return $this->passthru('shuffle', func_get_args());
+ }
+
+ /**
+ * Create chunks representing a "sliding window" view of the items in the collection.
+ *
+ * @param int $size
+ * @param int $step
+ * @return static
+ */
+ public function sliding($size = 2, $step = 1)
+ {
+ return new static(function () use ($size, $step) {
+ $iterator = $this->getIterator();
+
+ $chunk = [];
+
+ while ($iterator->valid()) {
+ $chunk[$iterator->key()] = $iterator->current();
+
+ if (count($chunk) == $size) {
+ yield tap(new static($chunk), function () use (&$chunk, $step) {
+ $chunk = array_slice($chunk, $step, null, true);
+ });
+
+ // If the $step between chunks is bigger than each chunk's $size
+ // we will skip the extra items (which should never be in any
+ // chunk) before we continue to the next chunk in the loop.
+ if ($step > $size) {
+ $skip = $step - $size;
+
+ for ($i = 0; $i < $skip && $iterator->valid(); $i++) {
+ $iterator->next();
+ }
+ }
+ }
+
+ $iterator->next();
+ }
+ });
+ }
+
+ /**
+ * Skip the first {$count} items.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function skip($count)
+ {
+ return new static(function () use ($count) {
+ $iterator = $this->getIterator();
+
+ while ($iterator->valid() && $count--) {
+ $iterator->next();
+ }
+
+ while ($iterator->valid()) {
+ yield $iterator->key() => $iterator->current();
+
+ $iterator->next();
+ }
+ });
+ }
+
+ /**
+ * Skip items in the collection until the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function skipUntil($value)
+ {
+ $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+ return $this->skipWhile($this->negate($callback));
+ }
+
+ /**
+ * Skip items in the collection while the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function skipWhile($value)
+ {
+ $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+ return new static(function () use ($callback) {
+ $iterator = $this->getIterator();
+
+ while ($iterator->valid() && $callback($iterator->current(), $iterator->key())) {
+ $iterator->next();
+ }
+
+ while ($iterator->valid()) {
+ yield $iterator->key() => $iterator->current();
+
+ $iterator->next();
+ }
+ });
+ }
+
+ /**
+ * Get a slice of items from the enumerable.
+ *
+ * @param int $offset
+ * @param int|null $length
+ * @return static
+ */
+ public function slice($offset, $length = null)
+ {
+ if ($offset < 0 || $length < 0) {
+ return $this->passthru('slice', func_get_args());
+ }
+
+ $instance = $this->skip($offset);
+
+ return is_null($length) ? $instance : $instance->take($length);
+ }
+
+ /**
+ * Split a collection into a certain number of groups.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function split($numberOfGroups)
+ {
+ return $this->passthru('split', func_get_args());
+ }
+
+ /**
+ * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return mixed
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ * @throws \Illuminate\Support\MultipleItemsFoundException
+ */
+ public function sole($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ return $this
+ ->when($filter)
+ ->filter($filter)
+ ->take(2)
+ ->collect()
+ ->sole();
+ }
+
+ /**
+ * Get the first item in the collection but throw an exception if no matching items exist.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return mixed
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ */
+ public function firstOrFail($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ return $this
+ ->when($filter)
+ ->filter($filter)
+ ->take(1)
+ ->collect()
+ ->firstOrFail();
+ }
+
+ /**
+ * Chunk the collection into chunks of the given size.
+ *
+ * @param int $size
+ * @return static
+ */
+ public function chunk($size)
+ {
+ if ($size <= 0) {
+ return static::empty();
+ }
+
+ return new static(function () use ($size) {
+ $iterator = $this->getIterator();
+
+ while ($iterator->valid()) {
+ $chunk = [];
+
+ while (true) {
+ $chunk[$iterator->key()] = $iterator->current();
+
+ if (count($chunk) < $size) {
+ $iterator->next();
+
+ if (! $iterator->valid()) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ yield new static($chunk);
+
+ $iterator->next();
+ }
+ });
+ }
+
+ /**
+ * Split a collection into a certain number of groups, and fill the first groups completely.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function splitIn($numberOfGroups)
+ {
+ return $this->chunk(ceil($this->count() / $numberOfGroups));
+ }
+
+ /**
+ * Chunk the collection into chunks with a callback.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function chunkWhile(callable $callback)
+ {
+ return new static(function () use ($callback) {
+ $iterator = $this->getIterator();
+
+ $chunk = new Collection;
+
+ if ($iterator->valid()) {
+ $chunk[$iterator->key()] = $iterator->current();
+
+ $iterator->next();
+ }
+
+ while ($iterator->valid()) {
+ if (! $callback($iterator->current(), $iterator->key(), $chunk)) {
+ yield new static($chunk);
+
+ $chunk = new Collection;
+ }
+
+ $chunk[$iterator->key()] = $iterator->current();
+
+ $iterator->next();
+ }
+
+ if ($chunk->isNotEmpty()) {
+ yield new static($chunk);
+ }
+ });
+ }
+
+ /**
+ * Sort through each item with a callback.
+ *
+ * @param callable|null|int $callback
+ * @return static
+ */
+ public function sort($callback = null)
+ {
+ return $this->passthru('sort', func_get_args());
+ }
+
+ /**
+ * Sort items in descending order.
+ *
+ * @param int $options
+ * @return static
+ */
+ public function sortDesc($options = SORT_REGULAR)
+ {
+ return $this->passthru('sortDesc', func_get_args());
+ }
+
+ /**
+ * Sort the collection using the given callback.
+ *
+ * @param callable|string $callback
+ * @param int $options
+ * @param bool $descending
+ * @return static
+ */
+ public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
+ {
+ return $this->passthru('sortBy', func_get_args());
+ }
+
+ /**
+ * Sort the collection in descending order using the given callback.
+ *
+ * @param callable|string $callback
+ * @param int $options
+ * @return static
+ */
+ public function sortByDesc($callback, $options = SORT_REGULAR)
+ {
+ return $this->passthru('sortByDesc', func_get_args());
+ }
+
+ /**
+ * Sort the collection keys.
+ *
+ * @param int $options
+ * @param bool $descending
+ * @return static
+ */
+ public function sortKeys($options = SORT_REGULAR, $descending = false)
+ {
+ return $this->passthru('sortKeys', func_get_args());
+ }
+
+ /**
+ * Sort the collection keys in descending order.
+ *
+ * @param int $options
+ * @return static
+ */
+ public function sortKeysDesc($options = SORT_REGULAR)
+ {
+ return $this->passthru('sortKeysDesc', func_get_args());
+ }
+
+ /**
+ * Sort the collection keys using a callback.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function sortKeysUsing(callable $callback)
+ {
+ return $this->passthru('sortKeysUsing', func_get_args());
+ }
+
+ /**
+ * Take the first or last {$limit} items.
+ *
+ * @param int $limit
+ * @return static
+ */
+ public function take($limit)
+ {
+ if ($limit < 0) {
+ return $this->passthru('take', func_get_args());
+ }
+
+ return new static(function () use ($limit) {
+ $iterator = $this->getIterator();
+
+ while ($limit--) {
+ if (! $iterator->valid()) {
+ break;
+ }
+
+ yield $iterator->key() => $iterator->current();
+
+ if ($limit) {
+ $iterator->next();
+ }
+ }
+ });
+ }
+
+ /**
+ * Take items in the collection until the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function takeUntil($value)
+ {
+ $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+ return new static(function () use ($callback) {
+ foreach ($this as $key => $item) {
+ if ($callback($item, $key)) {
+ break;
+ }
+
+ yield $key => $item;
+ }
+ });
+ }
+
+ /**
+ * Take items in the collection until a given point in time.
+ *
+ * @param \DateTimeInterface $timeout
+ * @return static
+ */
+ public function takeUntilTimeout(DateTimeInterface $timeout)
+ {
+ $timeout = $timeout->getTimestamp();
+
+ return $this->takeWhile(function () use ($timeout) {
+ return $this->now() < $timeout;
+ });
+ }
+
+ /**
+ * Take items in the collection while the given condition is met.
+ *
+ * @param mixed $value
+ * @return static
+ */
+ public function takeWhile($value)
+ {
+ $callback = $this->useAsCallable($value) ? $value : $this->equality($value);
+
+ return $this->takeUntil(function ($item, $key) use ($callback) {
+ return ! $callback($item, $key);
+ });
+ }
+
+ /**
+ * Pass each item in the collection to the given callback, lazily.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function tapEach(callable $callback)
+ {
+ return new static(function () use ($callback) {
+ foreach ($this as $key => $value) {
+ $callback($value, $key);
+
+ yield $key => $value;
+ }
+ });
+ }
+
+ /**
+ * Convert a flatten "dot" notation array into an expanded array.
+ *
+ * @return static
+ */
+ public function undot()
+ {
+ return $this->passthru('undot', []);
+ }
+
+ /**
+ * Return only unique items from the collection array.
+ *
+ * @param string|callable|null $key
+ * @param bool $strict
+ * @return static
+ */
+ public function unique($key = null, $strict = false)
+ {
+ $callback = $this->valueRetriever($key);
+
+ return new static(function () use ($callback, $strict) {
+ $exists = [];
+
+ foreach ($this as $key => $item) {
+ if (! in_array($id = $callback($item, $key), $exists, $strict)) {
+ yield $key => $item;
+
+ $exists[] = $id;
+ }
+ }
+ });
+ }
+
+ /**
+ * Reset the keys on the underlying array.
+ *
+ * @return static
+ */
+ public function values()
+ {
+ return new static(function () {
+ foreach ($this as $item) {
+ yield $item;
+ }
+ });
+ }
+
+ /**
+ * Zip the collection together with one or more arrays.
+ *
+ * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]);
+ * => [[1, 4], [2, 5], [3, 6]]
+ *
+ * @param mixed ...$items
+ * @return static
+ */
+ public function zip($items)
+ {
+ $iterables = func_get_args();
+
+ return new static(function () use ($iterables) {
+ $iterators = Collection::make($iterables)->map(function ($iterable) {
+ return $this->makeIterator($iterable);
+ })->prepend($this->getIterator());
+
+ while ($iterators->contains->valid()) {
+ yield new static($iterators->map->current());
+
+ $iterators->each->next();
+ }
+ });
+ }
+
+ /**
+ * Pad collection to the specified length with a value.
+ *
+ * @param int $size
+ * @param mixed $value
+ * @return static
+ */
+ public function pad($size, $value)
+ {
+ if ($size < 0) {
+ return $this->passthru('pad', func_get_args());
+ }
+
+ return new static(function () use ($size, $value) {
+ $yielded = 0;
+
+ foreach ($this as $index => $item) {
+ yield $index => $item;
+
+ $yielded++;
+ }
+
+ while ($yielded++ < $size) {
+ yield $value;
+ }
+ });
+ }
+
+ /**
+ * Get the values iterator.
+ *
+ * @return \Traversable
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return $this->makeIterator($this->source);
+ }
+
+ /**
+ * Count the number of items in the collection.
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ if (is_array($this->source)) {
+ return count($this->source);
+ }
+
+ return iterator_count($this->getIterator());
+ }
+
+ /**
+ * Make an iterator from the given source.
+ *
+ * @param mixed $source
+ * @return \Traversable
+ */
+ protected function makeIterator($source)
+ {
+ if ($source instanceof IteratorAggregate) {
+ return $source->getIterator();
+ }
+
+ if (is_array($source)) {
+ return new ArrayIterator($source);
+ }
+
+ return $source();
+ }
+
+ /**
+ * Explode the "value" and "key" arguments passed to "pluck".
+ *
+ * @param string|array $value
+ * @param string|array|null $key
+ * @return array
+ */
+ protected function explodePluckParameters($value, $key)
+ {
+ $value = is_string($value) ? explode('.', $value) : $value;
+
+ $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+ return [$value, $key];
+ }
+
+ /**
+ * Pass this lazy collection through a method on the collection class.
+ *
+ * @param string $method
+ * @param array $params
+ * @return static
+ */
+ protected function passthru($method, array $params)
+ {
+ return new static(function () use ($method, $params) {
+ yield from $this->collect()->$method(...$params);
+ });
+ }
+
+ /**
+ * Get the current time.
+ *
+ * @return int
+ */
+ protected function now()
+ {
+ return time();
+ }
+}
diff --git a/vendor/illuminate/collections/MultipleItemsFoundException.php b/vendor/illuminate/collections/MultipleItemsFoundException.php
new file mode 100644
index 0000000..944b2dc
--- /dev/null
+++ b/vendor/illuminate/collections/MultipleItemsFoundException.php
@@ -0,0 +1,9 @@
+all() : $value;
+ }
+
+ /**
+ * Create a new instance with no items.
+ *
+ * @return static
+ */
+ public static function empty()
+ {
+ return new static([]);
+ }
+
+ /**
+ * Create a new collection by invoking the callback a given amount of times.
+ *
+ * @param int $number
+ * @param callable|null $callback
+ * @return static
+ */
+ public static function times($number, callable $callback = null)
+ {
+ if ($number < 1) {
+ return new static;
+ }
+
+ return static::range(1, $number)
+ ->when($callback)
+ ->map($callback);
+ }
+
+ /**
+ * Alias for the "avg" method.
+ *
+ * @param callable|string|null $callback
+ * @return mixed
+ */
+ public function average($callback = null)
+ {
+ return $this->avg($callback);
+ }
+
+ /**
+ * Alias for the "contains" method.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function some($key, $operator = null, $value = null)
+ {
+ return $this->contains(...func_get_args());
+ }
+
+ /**
+ * Determine if an item exists, using strict comparison.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function containsStrict($key, $value = null)
+ {
+ if (func_num_args() === 2) {
+ return $this->contains(function ($item) use ($key, $value) {
+ return data_get($item, $key) === $value;
+ });
+ }
+
+ if ($this->useAsCallable($key)) {
+ return ! is_null($this->first($key));
+ }
+
+ foreach ($this as $item) {
+ if ($item === $key) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Dump the items and end the script.
+ *
+ * @param mixed ...$args
+ * @return void
+ */
+ public function dd(...$args)
+ {
+ $this->dump(...$args);
+
+ exit(1);
+ }
+
+ /**
+ * Dump the items.
+ *
+ * @return $this
+ */
+ public function dump()
+ {
+ (new Collection(func_get_args()))
+ ->push($this->all())
+ ->each(function ($item) {
+ VarDumper::dump($item);
+ });
+
+ return $this;
+ }
+
+ /**
+ * Execute a callback over each item.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function each(callable $callback)
+ {
+ foreach ($this as $key => $item) {
+ if ($callback($item, $key) === false) {
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Execute a callback over each nested chunk of items.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function eachSpread(callable $callback)
+ {
+ return $this->each(function ($chunk, $key) use ($callback) {
+ $chunk[] = $key;
+
+ return $callback(...$chunk);
+ });
+ }
+
+ /**
+ * Determine if all items pass the given truth test.
+ *
+ * @param string|callable $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function every($key, $operator = null, $value = null)
+ {
+ if (func_num_args() === 1) {
+ $callback = $this->valueRetriever($key);
+
+ foreach ($this as $k => $v) {
+ if (! $callback($v, $k)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return $this->every($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Get the first item by the given key value pair.
+ *
+ * @param string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return mixed
+ */
+ public function firstWhere($key, $operator = null, $value = null)
+ {
+ return $this->first($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Determine if the collection is not empty.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return ! $this->isEmpty();
+ }
+
+ /**
+ * Run a map over each nested chunk of items.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function mapSpread(callable $callback)
+ {
+ return $this->map(function ($chunk, $key) use ($callback) {
+ $chunk[] = $key;
+
+ return $callback(...$chunk);
+ });
+ }
+
+ /**
+ * Run a grouping map over the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function mapToGroups(callable $callback)
+ {
+ $groups = $this->mapToDictionary($callback);
+
+ return $groups->map([$this, 'make']);
+ }
+
+ /**
+ * Map a collection and flatten the result by a single level.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function flatMap(callable $callback)
+ {
+ return $this->map($callback)->collapse();
+ }
+
+ /**
+ * Map the values into a new class.
+ *
+ * @param string $class
+ * @return static
+ */
+ public function mapInto($class)
+ {
+ return $this->map(function ($value, $key) use ($class) {
+ return new $class($value, $key);
+ });
+ }
+
+ /**
+ * Get the min value of a given key.
+ *
+ * @param callable|string|null $callback
+ * @return mixed
+ */
+ public function min($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+
+ return $this->map(function ($value) use ($callback) {
+ return $callback($value);
+ })->filter(function ($value) {
+ return ! is_null($value);
+ })->reduce(function ($result, $value) {
+ return is_null($result) || $value < $result ? $value : $result;
+ });
+ }
+
+ /**
+ * Get the max value of a given key.
+ *
+ * @param callable|string|null $callback
+ * @return mixed
+ */
+ public function max($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+
+ return $this->filter(function ($value) {
+ return ! is_null($value);
+ })->reduce(function ($result, $item) use ($callback) {
+ $value = $callback($item);
+
+ return is_null($result) || $value > $result ? $value : $result;
+ });
+ }
+
+ /**
+ * "Paginate" the collection by slicing it into a smaller collection.
+ *
+ * @param int $page
+ * @param int $perPage
+ * @return static
+ */
+ public function forPage($page, $perPage)
+ {
+ $offset = max(0, ($page - 1) * $perPage);
+
+ return $this->slice($offset, $perPage);
+ }
+
+ /**
+ * Partition the collection into two arrays using the given callback or key.
+ *
+ * @param callable|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return static
+ */
+ public function partition($key, $operator = null, $value = null)
+ {
+ $passed = [];
+ $failed = [];
+
+ $callback = func_num_args() === 1
+ ? $this->valueRetriever($key)
+ : $this->operatorForWhere(...func_get_args());
+
+ foreach ($this as $key => $item) {
+ if ($callback($item, $key)) {
+ $passed[$key] = $item;
+ } else {
+ $failed[$key] = $item;
+ }
+ }
+
+ return new static([new static($passed), new static($failed)]);
+ }
+
+ /**
+ * Get the sum of the given values.
+ *
+ * @param callable|string|null $callback
+ * @return mixed
+ */
+ public function sum($callback = null)
+ {
+ $callback = is_null($callback)
+ ? $this->identity()
+ : $this->valueRetriever($callback);
+
+ return $this->reduce(function ($result, $item) use ($callback) {
+ return $result + $callback($item);
+ }, 0);
+ }
+
+ /**
+ * Apply the callback if the value is truthy.
+ *
+ * @param bool|mixed $value
+ * @param callable|null $callback
+ * @param callable|null $default
+ * @return static|mixed
+ */
+ public function when($value, callable $callback = null, callable $default = null)
+ {
+ if (! $callback) {
+ return new HigherOrderWhenProxy($this, $value);
+ }
+
+ if ($value) {
+ return $callback($this, $value);
+ } elseif ($default) {
+ return $default($this, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Apply the callback if the collection is empty.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static|mixed
+ */
+ public function whenEmpty(callable $callback, callable $default = null)
+ {
+ return $this->when($this->isEmpty(), $callback, $default);
+ }
+
+ /**
+ * Apply the callback if the collection is not empty.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static|mixed
+ */
+ public function whenNotEmpty(callable $callback, callable $default = null)
+ {
+ return $this->when($this->isNotEmpty(), $callback, $default);
+ }
+
+ /**
+ * Apply the callback if the value is falsy.
+ *
+ * @param bool $value
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static|mixed
+ */
+ public function unless($value, callable $callback, callable $default = null)
+ {
+ return $this->when(! $value, $callback, $default);
+ }
+
+ /**
+ * Apply the callback unless the collection is empty.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static|mixed
+ */
+ public function unlessEmpty(callable $callback, callable $default = null)
+ {
+ return $this->whenNotEmpty($callback, $default);
+ }
+
+ /**
+ * Apply the callback unless the collection is not empty.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static|mixed
+ */
+ public function unlessNotEmpty(callable $callback, callable $default = null)
+ {
+ return $this->whenEmpty($callback, $default);
+ }
+
+ /**
+ * Filter items by the given key value pair.
+ *
+ * @param string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return static
+ */
+ public function where($key, $operator = null, $value = null)
+ {
+ return $this->filter($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Filter items where the value for the given key is null.
+ *
+ * @param string|null $key
+ * @return static
+ */
+ public function whereNull($key = null)
+ {
+ return $this->whereStrict($key, null);
+ }
+
+ /**
+ * Filter items where the value for the given key is not null.
+ *
+ * @param string|null $key
+ * @return static
+ */
+ public function whereNotNull($key = null)
+ {
+ return $this->where($key, '!==', null);
+ }
+
+ /**
+ * Filter items by the given key value pair using strict comparison.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return static
+ */
+ public function whereStrict($key, $value)
+ {
+ return $this->where($key, '===', $value);
+ }
+
+ /**
+ * Filter items by the given key value pair.
+ *
+ * @param string $key
+ * @param mixed $values
+ * @param bool $strict
+ * @return static
+ */
+ public function whereIn($key, $values, $strict = false)
+ {
+ $values = $this->getArrayableItems($values);
+
+ return $this->filter(function ($item) use ($key, $values, $strict) {
+ return in_array(data_get($item, $key), $values, $strict);
+ });
+ }
+
+ /**
+ * Filter items by the given key value pair using strict comparison.
+ *
+ * @param string $key
+ * @param mixed $values
+ * @return static
+ */
+ public function whereInStrict($key, $values)
+ {
+ return $this->whereIn($key, $values, true);
+ }
+
+ /**
+ * Filter items such that the value of the given key is between the given values.
+ *
+ * @param string $key
+ * @param array $values
+ * @return static
+ */
+ public function whereBetween($key, $values)
+ {
+ return $this->where($key, '>=', reset($values))->where($key, '<=', end($values));
+ }
+
+ /**
+ * Filter items such that the value of the given key is not between the given values.
+ *
+ * @param string $key
+ * @param array $values
+ * @return static
+ */
+ public function whereNotBetween($key, $values)
+ {
+ return $this->filter(function ($item) use ($key, $values) {
+ return data_get($item, $key) < reset($values) || data_get($item, $key) > end($values);
+ });
+ }
+
+ /**
+ * Filter items by the given key value pair.
+ *
+ * @param string $key
+ * @param mixed $values
+ * @param bool $strict
+ * @return static
+ */
+ public function whereNotIn($key, $values, $strict = false)
+ {
+ $values = $this->getArrayableItems($values);
+
+ return $this->reject(function ($item) use ($key, $values, $strict) {
+ return in_array(data_get($item, $key), $values, $strict);
+ });
+ }
+
+ /**
+ * Filter items by the given key value pair using strict comparison.
+ *
+ * @param string $key
+ * @param mixed $values
+ * @return static
+ */
+ public function whereNotInStrict($key, $values)
+ {
+ return $this->whereNotIn($key, $values, true);
+ }
+
+ /**
+ * Filter the items, removing any items that don't match the given type(s).
+ *
+ * @param string|string[] $type
+ * @return static
+ */
+ public function whereInstanceOf($type)
+ {
+ return $this->filter(function ($value) use ($type) {
+ if (is_array($type)) {
+ foreach ($type as $classType) {
+ if ($value instanceof $classType) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return $value instanceof $type;
+ });
+ }
+
+ /**
+ * Pass the collection to the given callback and return the result.
+ *
+ * @param callable $callback
+ * @return mixed
+ */
+ public function pipe(callable $callback)
+ {
+ return $callback($this);
+ }
+
+ /**
+ * Pass the collection into a new class.
+ *
+ * @param string $class
+ * @return mixed
+ */
+ public function pipeInto($class)
+ {
+ return new $class($this);
+ }
+
+ /**
+ * Pass the collection through a series of callable pipes and return the result.
+ *
+ * @param array $pipes
+ * @return mixed
+ */
+ public function pipeThrough($pipes)
+ {
+ return static::make($pipes)->reduce(
+ function ($carry, $pipe) {
+ return $pipe($carry);
+ },
+ $this,
+ );
+ }
+
+ /**
+ * Pass the collection to the given callback and then return it.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function tap(callable $callback)
+ {
+ $callback(clone $this);
+
+ return $this;
+ }
+
+ /**
+ * Reduce the collection to a single value.
+ *
+ * @param callable $callback
+ * @param mixed $initial
+ * @return mixed
+ */
+ public function reduce(callable $callback, $initial = null)
+ {
+ $result = $initial;
+
+ foreach ($this as $key => $value) {
+ $result = $callback($result, $value, $key);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Reduce the collection to multiple aggregate values.
+ *
+ * @param callable $callback
+ * @param mixed ...$initial
+ * @return array
+ *
+ * @deprecated Use "reduceSpread" instead
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function reduceMany(callable $callback, ...$initial)
+ {
+ return $this->reduceSpread($callback, ...$initial);
+ }
+
+ /**
+ * Reduce the collection to multiple aggregate values.
+ *
+ * @param callable $callback
+ * @param mixed ...$initial
+ * @return array
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function reduceSpread(callable $callback, ...$initial)
+ {
+ $result = $initial;
+
+ foreach ($this as $key => $value) {
+ $result = call_user_func_array($callback, array_merge($result, [$value, $key]));
+
+ if (! is_array($result)) {
+ throw new UnexpectedValueException(sprintf(
+ "%s::reduceMany expects reducer to return an array, but got a '%s' instead.",
+ class_basename(static::class), gettype($result)
+ ));
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Reduce an associative collection to a single value.
+ *
+ * @param callable $callback
+ * @param mixed $initial
+ * @return mixed
+ */
+ public function reduceWithKeys(callable $callback, $initial = null)
+ {
+ return $this->reduce($callback, $initial);
+ }
+
+ /**
+ * Create a collection of all elements that do not pass a given truth test.
+ *
+ * @param callable|mixed $callback
+ * @return static
+ */
+ public function reject($callback = true)
+ {
+ $useAsCallable = $this->useAsCallable($callback);
+
+ return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
+ return $useAsCallable
+ ? ! $callback($value, $key)
+ : $value != $callback;
+ });
+ }
+
+ /**
+ * Return only unique items from the collection array using strict comparison.
+ *
+ * @param string|callable|null $key
+ * @return static
+ */
+ public function uniqueStrict($key = null)
+ {
+ return $this->unique($key, true);
+ }
+
+ /**
+ * Collect the values into a collection.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function collect()
+ {
+ return new Collection($this->all());
+ }
+
+ /**
+ * Get the collection of items as a plain array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->map(function ($value) {
+ return $value instanceof Arrayable ? $value->toArray() : $value;
+ })->all();
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return array_map(function ($value) {
+ if ($value instanceof JsonSerializable) {
+ return $value->jsonSerialize();
+ } elseif ($value instanceof Jsonable) {
+ return json_decode($value->toJson(), true);
+ } elseif ($value instanceof Arrayable) {
+ return $value->toArray();
+ }
+
+ return $value;
+ }, $this->all());
+ }
+
+ /**
+ * Get the collection of items as JSON.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+
+ /**
+ * Get a CachingIterator instance.
+ *
+ * @param int $flags
+ * @return \CachingIterator
+ */
+ public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
+ {
+ return new CachingIterator($this->getIterator(), $flags);
+ }
+
+ /**
+ * Convert the collection to its string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->escapeWhenCastingToString
+ ? e($this->toJson())
+ : $this->toJson();
+ }
+
+ /**
+ * Indicate that the model's string representation should be escaped when __toString is invoked.
+ *
+ * @param bool $escape
+ * @return $this
+ */
+ public function escapeWhenCastingToString($escape = true)
+ {
+ $this->escapeWhenCastingToString = $escape;
+
+ return $this;
+ }
+
+ /**
+ * Add a method to the list of proxied methods.
+ *
+ * @param string $method
+ * @return void
+ */
+ public static function proxy($method)
+ {
+ static::$proxies[] = $method;
+ }
+
+ /**
+ * Dynamically access collection proxies.
+ *
+ * @param string $key
+ * @return mixed
+ *
+ * @throws \Exception
+ */
+ public function __get($key)
+ {
+ if (! in_array($key, static::$proxies)) {
+ throw new Exception("Property [{$key}] does not exist on this collection instance.");
+ }
+
+ return new HigherOrderCollectionProxy($this, $key);
+ }
+
+ /**
+ * Results array of items from Collection or Arrayable.
+ *
+ * @param mixed $items
+ * @return array
+ */
+ protected function getArrayableItems($items)
+ {
+ if (is_array($items)) {
+ return $items;
+ } elseif ($items instanceof Enumerable) {
+ return $items->all();
+ } elseif ($items instanceof Arrayable) {
+ return $items->toArray();
+ } elseif ($items instanceof Jsonable) {
+ return json_decode($items->toJson(), true);
+ } elseif ($items instanceof JsonSerializable) {
+ return (array) $items->jsonSerialize();
+ } elseif ($items instanceof Traversable) {
+ return iterator_to_array($items);
+ } elseif ($items instanceof UnitEnum) {
+ return [$items];
+ }
+
+ return (array) $items;
+ }
+
+ /**
+ * Get an operator checker callback.
+ *
+ * @param string $key
+ * @param string|null $operator
+ * @param mixed $value
+ * @return \Closure
+ */
+ protected function operatorForWhere($key, $operator = null, $value = null)
+ {
+ if (func_num_args() === 1) {
+ $value = true;
+
+ $operator = '=';
+ }
+
+ if (func_num_args() === 2) {
+ $value = $operator;
+
+ $operator = '=';
+ }
+
+ return function ($item) use ($key, $operator, $value) {
+ $retrieved = data_get($item, $key);
+
+ $strings = array_filter([$retrieved, $value], function ($value) {
+ return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
+ });
+
+ if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
+ return in_array($operator, ['!=', '<>', '!==']);
+ }
+
+ switch ($operator) {
+ default:
+ case '=':
+ case '==': return $retrieved == $value;
+ case '!=':
+ case '<>': return $retrieved != $value;
+ case '<': return $retrieved < $value;
+ case '>': return $retrieved > $value;
+ case '<=': return $retrieved <= $value;
+ case '>=': return $retrieved >= $value;
+ case '===': return $retrieved === $value;
+ case '!==': return $retrieved !== $value;
+ }
+ };
+ }
+
+ /**
+ * Determine if the given value is callable, but not a string.
+ *
+ * @param mixed $value
+ * @return bool
+ */
+ protected function useAsCallable($value)
+ {
+ return ! is_string($value) && is_callable($value);
+ }
+
+ /**
+ * Get a value retrieving callback.
+ *
+ * @param callable|string|null $value
+ * @return callable
+ */
+ protected function valueRetriever($value)
+ {
+ if ($this->useAsCallable($value)) {
+ return $value;
+ }
+
+ return function ($item) use ($value) {
+ return data_get($item, $value);
+ };
+ }
+
+ /**
+ * Make a function to check an item's equality.
+ *
+ * @param mixed $value
+ * @return \Closure
+ */
+ protected function equality($value)
+ {
+ return function ($item) use ($value) {
+ return $item === $value;
+ };
+ }
+
+ /**
+ * Make a function using another function, by negating its result.
+ *
+ * @param \Closure $callback
+ * @return \Closure
+ */
+ protected function negate(Closure $callback)
+ {
+ return function (...$params) use ($callback) {
+ return ! $callback(...$params);
+ };
+ }
+
+ /**
+ * Make a function that returns what's passed to it.
+ *
+ * @return \Closure
+ */
+ protected function identity()
+ {
+ return function ($value) {
+ return $value;
+ };
+ }
+}
diff --git a/vendor/illuminate/collections/composer.json b/vendor/illuminate/collections/composer.json
new file mode 100644
index 0000000..ecc4537
--- /dev/null
+++ b/vendor/illuminate/collections/composer.json
@@ -0,0 +1,41 @@
+{
+ "name": "illuminate/collections",
+ "description": "The Illuminate Collections package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^7.3|^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/macroable": "^8.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ },
+ "files": [
+ "helpers.php"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "suggest": {
+ "symfony/var-dumper": "Required to use the dump method (^5.4)."
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/collections/helpers.php b/vendor/illuminate/collections/helpers.php
new file mode 100644
index 0000000..67669e5
--- /dev/null
+++ b/vendor/illuminate/collections/helpers.php
@@ -0,0 +1,186 @@
+ $segment) {
+ unset($key[$i]);
+
+ if (is_null($segment)) {
+ return $target;
+ }
+
+ if ($segment === '*') {
+ if ($target instanceof Collection) {
+ $target = $target->all();
+ } elseif (! is_array($target)) {
+ return value($default);
+ }
+
+ $result = [];
+
+ foreach ($target as $item) {
+ $result[] = data_get($item, $key);
+ }
+
+ return in_array('*', $key) ? Arr::collapse($result) : $result;
+ }
+
+ if (Arr::accessible($target) && Arr::exists($target, $segment)) {
+ $target = $target[$segment];
+ } elseif (is_object($target) && isset($target->{$segment})) {
+ $target = $target->{$segment};
+ } else {
+ return value($default);
+ }
+ }
+
+ return $target;
+ }
+}
+
+if (! function_exists('data_set')) {
+ /**
+ * Set an item on an array or object using dot notation.
+ *
+ * @param mixed $target
+ * @param string|array $key
+ * @param mixed $value
+ * @param bool $overwrite
+ * @return mixed
+ */
+ function data_set(&$target, $key, $value, $overwrite = true)
+ {
+ $segments = is_array($key) ? $key : explode('.', $key);
+
+ if (($segment = array_shift($segments)) === '*') {
+ if (! Arr::accessible($target)) {
+ $target = [];
+ }
+
+ if ($segments) {
+ foreach ($target as &$inner) {
+ data_set($inner, $segments, $value, $overwrite);
+ }
+ } elseif ($overwrite) {
+ foreach ($target as &$inner) {
+ $inner = $value;
+ }
+ }
+ } elseif (Arr::accessible($target)) {
+ if ($segments) {
+ if (! Arr::exists($target, $segment)) {
+ $target[$segment] = [];
+ }
+
+ data_set($target[$segment], $segments, $value, $overwrite);
+ } elseif ($overwrite || ! Arr::exists($target, $segment)) {
+ $target[$segment] = $value;
+ }
+ } elseif (is_object($target)) {
+ if ($segments) {
+ if (! isset($target->{$segment})) {
+ $target->{$segment} = [];
+ }
+
+ data_set($target->{$segment}, $segments, $value, $overwrite);
+ } elseif ($overwrite || ! isset($target->{$segment})) {
+ $target->{$segment} = $value;
+ }
+ } else {
+ $target = [];
+
+ if ($segments) {
+ data_set($target[$segment], $segments, $value, $overwrite);
+ } elseif ($overwrite) {
+ $target[$segment] = $value;
+ }
+ }
+
+ return $target;
+ }
+}
+
+if (! function_exists('head')) {
+ /**
+ * Get the first element of an array. Useful for method chaining.
+ *
+ * @param array $array
+ * @return mixed
+ */
+ function head($array)
+ {
+ return reset($array);
+ }
+}
+
+if (! function_exists('last')) {
+ /**
+ * Get the last element from an array.
+ *
+ * @param array $array
+ * @return mixed
+ */
+ function last($array)
+ {
+ return end($array);
+ }
+}
+
+if (! function_exists('value')) {
+ /**
+ * Return the default value of the given value.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ function value($value, ...$args)
+ {
+ return $value instanceof Closure ? $value(...$args) : $value;
+ }
+}
diff --git a/vendor/illuminate/contracts/Auth/Access/Authorizable.php b/vendor/illuminate/contracts/Auth/Access/Authorizable.php
new file mode 100644
index 0000000..cedeb6e
--- /dev/null
+++ b/vendor/illuminate/contracts/Auth/Access/Authorizable.php
@@ -0,0 +1,15 @@
+id = $id;
+ $this->class = $class;
+ $this->relations = $relations;
+ $this->connection = $connection;
+ }
+}
diff --git a/vendor/illuminate/contracts/Debug/ExceptionHandler.php b/vendor/illuminate/contracts/Debug/ExceptionHandler.php
new file mode 100644
index 0000000..54381a1
--- /dev/null
+++ b/vendor/illuminate/contracts/Debug/ExceptionHandler.php
@@ -0,0 +1,46 @@
+getMethods(
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+ );
+
+ foreach ($methods as $method) {
+ if ($replace || ! static::hasMacro($method->name)) {
+ $method->setAccessible(true);
+ static::macro($method->name, $method->invoke($mixin));
+ }
+ }
+ }
+
+ /**
+ * Checks if macro is registered.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$macros[$name]);
+ }
+
+ /**
+ * Flush the existing macros.
+ *
+ * @return void
+ */
+ public static function flushMacros()
+ {
+ static::$macros = [];
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ *
+ * @throws \BadMethodCallException
+ */
+ public static function __callStatic($method, $parameters)
+ {
+ if (! static::hasMacro($method)) {
+ throw new BadMethodCallException(sprintf(
+ 'Method %s::%s does not exist.', static::class, $method
+ ));
+ }
+
+ $macro = static::$macros[$method];
+
+ if ($macro instanceof Closure) {
+ $macro = $macro->bindTo(null, static::class);
+ }
+
+ return $macro(...$parameters);
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ *
+ * @throws \BadMethodCallException
+ */
+ public function __call($method, $parameters)
+ {
+ if (! static::hasMacro($method)) {
+ throw new BadMethodCallException(sprintf(
+ 'Method %s::%s does not exist.', static::class, $method
+ ));
+ }
+
+ $macro = static::$macros[$method];
+
+ if ($macro instanceof Closure) {
+ $macro = $macro->bindTo($this, static::class);
+ }
+
+ return $macro(...$parameters);
+ }
+}
diff --git a/vendor/illuminate/macroable/composer.json b/vendor/illuminate/macroable/composer.json
new file mode 100644
index 0000000..dfa5c62
--- /dev/null
+++ b/vendor/illuminate/macroable/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "illuminate/macroable",
+ "description": "The Illuminate Macroable package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^7.3|^8.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/pagination/AbstractCursorPaginator.php b/vendor/illuminate/pagination/AbstractCursorPaginator.php
new file mode 100644
index 0000000..1234485
--- /dev/null
+++ b/vendor/illuminate/pagination/AbstractCursorPaginator.php
@@ -0,0 +1,676 @@
+cursorName => $cursor->encode()];
+
+ if (count($this->query) > 0) {
+ $parameters = array_merge($this->query, $parameters);
+ }
+
+ return $this->path()
+ .(Str::contains($this->path(), '?') ? '&' : '?')
+ .Arr::query($parameters)
+ .$this->buildFragment();
+ }
+
+ /**
+ * Get the URL for the previous page.
+ *
+ * @return string|null
+ */
+ public function previousPageUrl()
+ {
+ if (is_null($previousCursor = $this->previousCursor())) {
+ return null;
+ }
+
+ return $this->url($previousCursor);
+ }
+
+ /**
+ * The URL for the next page, or null.
+ *
+ * @return string|null
+ */
+ public function nextPageUrl()
+ {
+ if (is_null($nextCursor = $this->nextCursor())) {
+ return null;
+ }
+
+ return $this->url($nextCursor);
+ }
+
+ /**
+ * Get the "cursor" that points to the previous set of items.
+ *
+ * @return \Illuminate\Pagination\Cursor|null
+ */
+ public function previousCursor()
+ {
+ if (is_null($this->cursor) ||
+ ($this->cursor->pointsToPreviousItems() && ! $this->hasMore)) {
+ return null;
+ }
+
+ if ($this->items->isEmpty()) {
+ return null;
+ }
+
+ return $this->getCursorForItem($this->items->first(), false);
+ }
+
+ /**
+ * Get the "cursor" that points to the next set of items.
+ *
+ * @return \Illuminate\Pagination\Cursor|null
+ */
+ public function nextCursor()
+ {
+ if ((is_null($this->cursor) && ! $this->hasMore) ||
+ (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && ! $this->hasMore)) {
+ return null;
+ }
+
+ if ($this->items->isEmpty()) {
+ return null;
+ }
+
+ return $this->getCursorForItem($this->items->last(), true);
+ }
+
+ /**
+ * Get a cursor instance for the given item.
+ *
+ * @param \ArrayAccess|\stdClass $item
+ * @param bool $isNext
+ * @return \Illuminate\Pagination\Cursor
+ */
+ public function getCursorForItem($item, $isNext = true)
+ {
+ return new Cursor($this->getParametersForItem($item), $isNext);
+ }
+
+ /**
+ * Get the cursor parameters for a given object.
+ *
+ * @param \ArrayAccess|\stdClass $item
+ * @return array
+ *
+ * @throws \Exception
+ */
+ public function getParametersForItem($item)
+ {
+ return collect($this->parameters)
+ ->flip()
+ ->map(function ($_, $parameterName) use ($item) {
+ if ($item instanceof JsonResource) {
+ $item = $item->resource;
+ }
+
+ if ($item instanceof Model &&
+ ! is_null($parameter = $this->getPivotParameterForItem($item, $parameterName))) {
+ return $parameter;
+ } elseif ($item instanceof ArrayAccess || is_array($item)) {
+ return $this->ensureParameterIsPrimitive(
+ $item[$parameterName] ?? $item[Str::afterLast($parameterName, '.')]
+ );
+ } elseif (is_object($item)) {
+ return $this->ensureParameterIsPrimitive(
+ $item->{$parameterName} ?? $item->{Str::afterLast($parameterName, '.')}
+ );
+ }
+
+ throw new Exception('Only arrays and objects are supported when cursor paginating items.');
+ })->toArray();
+ }
+
+ /**
+ * Get the cursor parameter value from a pivot model if applicable.
+ *
+ * @param \ArrayAccess|\stdClass $item
+ * @param string $parameterName
+ * @return string|null
+ */
+ protected function getPivotParameterForItem($item, $parameterName)
+ {
+ $table = Str::beforeLast($parameterName, '.');
+
+ foreach ($item->getRelations() as $relation) {
+ if ($relation instanceof Pivot && $relation->getTable() === $table) {
+ return $this->ensureParameterIsPrimitive(
+ $relation->getAttribute(Str::afterLast($parameterName, '.'))
+ );
+ }
+ }
+ }
+
+ /**
+ * Ensure the parameter is a primitive type.
+ *
+ * This can resolve issues that arise the developer uses a value object for an attribute.
+ *
+ * @param mixed $parameter
+ * @return mixed
+ */
+ protected function ensureParameterIsPrimitive($parameter)
+ {
+ return is_object($parameter) && method_exists($parameter, '__toString')
+ ? (string) $parameter
+ : $parameter;
+ }
+
+ /**
+ * Get / set the URL fragment to be appended to URLs.
+ *
+ * @param string|null $fragment
+ * @return $this|string|null
+ */
+ public function fragment($fragment = null)
+ {
+ if (is_null($fragment)) {
+ return $this->fragment;
+ }
+
+ $this->fragment = $fragment;
+
+ return $this;
+ }
+
+ /**
+ * Add a set of query string values to the paginator.
+ *
+ * @param array|string|null $key
+ * @param string|null $value
+ * @return $this
+ */
+ public function appends($key, $value = null)
+ {
+ if (is_null($key)) {
+ return $this;
+ }
+
+ if (is_array($key)) {
+ return $this->appendArray($key);
+ }
+
+ return $this->addQuery($key, $value);
+ }
+
+ /**
+ * Add an array of query string values.
+ *
+ * @param array $keys
+ * @return $this
+ */
+ protected function appendArray(array $keys)
+ {
+ foreach ($keys as $key => $value) {
+ $this->addQuery($key, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add all current query string values to the paginator.
+ *
+ * @return $this
+ */
+ public function withQueryString()
+ {
+ if (! is_null($query = Paginator::resolveQueryString())) {
+ return $this->appends($query);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a query string value to the paginator.
+ *
+ * @param string $key
+ * @param string $value
+ * @return $this
+ */
+ protected function addQuery($key, $value)
+ {
+ if ($key !== $this->cursorName) {
+ $this->query[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Build the full fragment portion of a URL.
+ *
+ * @return string
+ */
+ protected function buildFragment()
+ {
+ return $this->fragment ? '#'.$this->fragment : '';
+ }
+
+ /**
+ * Load a set of relationships onto the mixed relationship collection.
+ *
+ * @param string $relation
+ * @param array $relations
+ * @return $this
+ */
+ public function loadMorph($relation, $relations)
+ {
+ $this->getCollection()->loadMorph($relation, $relations);
+
+ return $this;
+ }
+
+ /**
+ * Load a set of relationship counts onto the mixed relationship collection.
+ *
+ * @param string $relation
+ * @param array $relations
+ * @return $this
+ */
+ public function loadMorphCount($relation, $relations)
+ {
+ $this->getCollection()->loadMorphCount($relation, $relations);
+
+ return $this;
+ }
+
+ /**
+ * Get the slice of items being paginated.
+ *
+ * @return array
+ */
+ public function items()
+ {
+ return $this->items->all();
+ }
+
+ /**
+ * Transform each item in the slice of items using a callback.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function through(callable $callback)
+ {
+ $this->items->transform($callback);
+
+ return $this;
+ }
+
+ /**
+ * Get the number of items shown per page.
+ *
+ * @return int
+ */
+ public function perPage()
+ {
+ return $this->perPage;
+ }
+
+ /**
+ * Get the current cursor being paginated.
+ *
+ * @return \Illuminate\Pagination\Cursor|null
+ */
+ public function cursor()
+ {
+ return $this->cursor;
+ }
+
+ /**
+ * Get the query string variable used to store the cursor.
+ *
+ * @return string
+ */
+ public function getCursorName()
+ {
+ return $this->cursorName;
+ }
+
+ /**
+ * Set the query string variable used to store the cursor.
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function setCursorName($name)
+ {
+ $this->cursorName = $name;
+
+ return $this;
+ }
+
+ /**
+ * Set the base path to assign to all URLs.
+ *
+ * @param string $path
+ * @return $this
+ */
+ public function withPath($path)
+ {
+ return $this->setPath($path);
+ }
+
+ /**
+ * Set the base path to assign to all URLs.
+ *
+ * @param string $path
+ * @return $this
+ */
+ public function setPath($path)
+ {
+ $this->path = $path;
+
+ return $this;
+ }
+
+ /**
+ * Get the base path for paginator generated URLs.
+ *
+ * @return string|null
+ */
+ public function path()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Resolve the current cursor or return the default value.
+ *
+ * @param string $cursorName
+ * @return \Illuminate\Pagination\Cursor|null
+ */
+ public static function resolveCurrentCursor($cursorName = 'cursor', $default = null)
+ {
+ if (isset(static::$currentCursorResolver)) {
+ return call_user_func(static::$currentCursorResolver, $cursorName);
+ }
+
+ return $default;
+ }
+
+ /**
+ * Set the current cursor resolver callback.
+ *
+ * @param \Closure $resolver
+ * @return void
+ */
+ public static function currentCursorResolver(Closure $resolver)
+ {
+ static::$currentCursorResolver = $resolver;
+ }
+
+ /**
+ * Get an instance of the view factory from the resolver.
+ *
+ * @return \Illuminate\Contracts\View\Factory
+ */
+ public static function viewFactory()
+ {
+ return Paginator::viewFactory();
+ }
+
+ /**
+ * Get an iterator for the items.
+ *
+ * @return \ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return $this->items->getIterator();
+ }
+
+ /**
+ * Determine if the list of items is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->items->isEmpty();
+ }
+
+ /**
+ * Determine if the list of items is not empty.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return $this->items->isNotEmpty();
+ }
+
+ /**
+ * Get the number of items for the current page.
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return $this->items->count();
+ }
+
+ /**
+ * Get the paginator's underlying collection.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function getCollection()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Set the paginator's underlying collection.
+ *
+ * @param \Illuminate\Support\Collection $collection
+ * @return $this
+ */
+ public function setCollection(Collection $collection)
+ {
+ $this->items = $collection;
+
+ return $this;
+ }
+
+ /**
+ * Get the paginator options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Determine if the given item exists.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return $this->items->has($key);
+ }
+
+ /**
+ * Get the item at the given offset.
+ *
+ * @param mixed $key
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->items->get($key);
+ }
+
+ /**
+ * Set the item at the given offset.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ $this->items->put($key, $value);
+ }
+
+ /**
+ * Unset the item at the given key.
+ *
+ * @param mixed $key
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ $this->items->forget($key);
+ }
+
+ /**
+ * Render the contents of the paginator to HTML.
+ *
+ * @return string
+ */
+ public function toHtml()
+ {
+ return (string) $this->render();
+ }
+
+ /**
+ * Make dynamic calls into the collection.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->forwardCallTo($this->getCollection(), $method, $parameters);
+ }
+
+ /**
+ * Render the contents of the paginator when casting to a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->render();
+ }
+}
diff --git a/vendor/illuminate/pagination/AbstractPaginator.php b/vendor/illuminate/pagination/AbstractPaginator.php
new file mode 100644
index 0000000..ac9ef40
--- /dev/null
+++ b/vendor/illuminate/pagination/AbstractPaginator.php
@@ -0,0 +1,782 @@
+= 1 && filter_var($page, FILTER_VALIDATE_INT) !== false;
+ }
+
+ /**
+ * Get the URL for the previous page.
+ *
+ * @return string|null
+ */
+ public function previousPageUrl()
+ {
+ if ($this->currentPage() > 1) {
+ return $this->url($this->currentPage() - 1);
+ }
+ }
+
+ /**
+ * Create a range of pagination URLs.
+ *
+ * @param int $start
+ * @param int $end
+ * @return array
+ */
+ public function getUrlRange($start, $end)
+ {
+ return collect(range($start, $end))->mapWithKeys(function ($page) {
+ return [$page => $this->url($page)];
+ })->all();
+ }
+
+ /**
+ * Get the URL for a given page number.
+ *
+ * @param int $page
+ * @return string
+ */
+ public function url($page)
+ {
+ if ($page <= 0) {
+ $page = 1;
+ }
+
+ // If we have any extra query string key / value pairs that need to be added
+ // onto the URL, we will put them in query string form and then attach it
+ // to the URL. This allows for extra information like sortings storage.
+ $parameters = [$this->pageName => $page];
+
+ if (count($this->query) > 0) {
+ $parameters = array_merge($this->query, $parameters);
+ }
+
+ return $this->path()
+ .(Str::contains($this->path(), '?') ? '&' : '?')
+ .Arr::query($parameters)
+ .$this->buildFragment();
+ }
+
+ /**
+ * Get / set the URL fragment to be appended to URLs.
+ *
+ * @param string|null $fragment
+ * @return $this|string|null
+ */
+ public function fragment($fragment = null)
+ {
+ if (is_null($fragment)) {
+ return $this->fragment;
+ }
+
+ $this->fragment = $fragment;
+
+ return $this;
+ }
+
+ /**
+ * Add a set of query string values to the paginator.
+ *
+ * @param array|string|null $key
+ * @param string|null $value
+ * @return $this
+ */
+ public function appends($key, $value = null)
+ {
+ if (is_null($key)) {
+ return $this;
+ }
+
+ if (is_array($key)) {
+ return $this->appendArray($key);
+ }
+
+ return $this->addQuery($key, $value);
+ }
+
+ /**
+ * Add an array of query string values.
+ *
+ * @param array $keys
+ * @return $this
+ */
+ protected function appendArray(array $keys)
+ {
+ foreach ($keys as $key => $value) {
+ $this->addQuery($key, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add all current query string values to the paginator.
+ *
+ * @return $this
+ */
+ public function withQueryString()
+ {
+ if (isset(static::$queryStringResolver)) {
+ return $this->appends(call_user_func(static::$queryStringResolver));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a query string value to the paginator.
+ *
+ * @param string $key
+ * @param string $value
+ * @return $this
+ */
+ protected function addQuery($key, $value)
+ {
+ if ($key !== $this->pageName) {
+ $this->query[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Build the full fragment portion of a URL.
+ *
+ * @return string
+ */
+ protected function buildFragment()
+ {
+ return $this->fragment ? '#'.$this->fragment : '';
+ }
+
+ /**
+ * Load a set of relationships onto the mixed relationship collection.
+ *
+ * @param string $relation
+ * @param array $relations
+ * @return $this
+ */
+ public function loadMorph($relation, $relations)
+ {
+ $this->getCollection()->loadMorph($relation, $relations);
+
+ return $this;
+ }
+
+ /**
+ * Load a set of relationship counts onto the mixed relationship collection.
+ *
+ * @param string $relation
+ * @param array $relations
+ * @return $this
+ */
+ public function loadMorphCount($relation, $relations)
+ {
+ $this->getCollection()->loadMorphCount($relation, $relations);
+
+ return $this;
+ }
+
+ /**
+ * Get the slice of items being paginated.
+ *
+ * @return array
+ */
+ public function items()
+ {
+ return $this->items->all();
+ }
+
+ /**
+ * Get the number of the first item in the slice.
+ *
+ * @return int
+ */
+ public function firstItem()
+ {
+ return count($this->items) > 0 ? ($this->currentPage - 1) * $this->perPage + 1 : null;
+ }
+
+ /**
+ * Get the number of the last item in the slice.
+ *
+ * @return int
+ */
+ public function lastItem()
+ {
+ return count($this->items) > 0 ? $this->firstItem() + $this->count() - 1 : null;
+ }
+
+ /**
+ * Transform each item in the slice of items using a callback.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function through(callable $callback)
+ {
+ $this->items->transform($callback);
+
+ return $this;
+ }
+
+ /**
+ * Get the number of items shown per page.
+ *
+ * @return int
+ */
+ public function perPage()
+ {
+ return $this->perPage;
+ }
+
+ /**
+ * Determine if there are enough items to split into multiple pages.
+ *
+ * @return bool
+ */
+ public function hasPages()
+ {
+ return $this->currentPage() != 1 || $this->hasMorePages();
+ }
+
+ /**
+ * Determine if the paginator is on the first page.
+ *
+ * @return bool
+ */
+ public function onFirstPage()
+ {
+ return $this->currentPage() <= 1;
+ }
+
+ /**
+ * Determine if the paginator is on the last page.
+ *
+ * @return bool
+ */
+ public function onLastPage()
+ {
+ return ! $this->hasMorePages();
+ }
+
+ /**
+ * Get the current page.
+ *
+ * @return int
+ */
+ public function currentPage()
+ {
+ return $this->currentPage;
+ }
+
+ /**
+ * Get the query string variable used to store the page.
+ *
+ * @return string
+ */
+ public function getPageName()
+ {
+ return $this->pageName;
+ }
+
+ /**
+ * Set the query string variable used to store the page.
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function setPageName($name)
+ {
+ $this->pageName = $name;
+
+ return $this;
+ }
+
+ /**
+ * Set the base path to assign to all URLs.
+ *
+ * @param string $path
+ * @return $this
+ */
+ public function withPath($path)
+ {
+ return $this->setPath($path);
+ }
+
+ /**
+ * Set the base path to assign to all URLs.
+ *
+ * @param string $path
+ * @return $this
+ */
+ public function setPath($path)
+ {
+ $this->path = $path;
+
+ return $this;
+ }
+
+ /**
+ * Set the number of links to display on each side of current page link.
+ *
+ * @param int $count
+ * @return $this
+ */
+ public function onEachSide($count)
+ {
+ $this->onEachSide = $count;
+
+ return $this;
+ }
+
+ /**
+ * Get the base path for paginator generated URLs.
+ *
+ * @return string|null
+ */
+ public function path()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Resolve the current request path or return the default value.
+ *
+ * @param string $default
+ * @return string
+ */
+ public static function resolveCurrentPath($default = '/')
+ {
+ if (isset(static::$currentPathResolver)) {
+ return call_user_func(static::$currentPathResolver);
+ }
+
+ return $default;
+ }
+
+ /**
+ * Set the current request path resolver callback.
+ *
+ * @param \Closure $resolver
+ * @return void
+ */
+ public static function currentPathResolver(Closure $resolver)
+ {
+ static::$currentPathResolver = $resolver;
+ }
+
+ /**
+ * Resolve the current page or return the default value.
+ *
+ * @param string $pageName
+ * @param int $default
+ * @return int
+ */
+ public static function resolveCurrentPage($pageName = 'page', $default = 1)
+ {
+ if (isset(static::$currentPageResolver)) {
+ return (int) call_user_func(static::$currentPageResolver, $pageName);
+ }
+
+ return $default;
+ }
+
+ /**
+ * Set the current page resolver callback.
+ *
+ * @param \Closure $resolver
+ * @return void
+ */
+ public static function currentPageResolver(Closure $resolver)
+ {
+ static::$currentPageResolver = $resolver;
+ }
+
+ /**
+ * Resolve the query string or return the default value.
+ *
+ * @param string|array|null $default
+ * @return string
+ */
+ public static function resolveQueryString($default = null)
+ {
+ if (isset(static::$queryStringResolver)) {
+ return (static::$queryStringResolver)();
+ }
+
+ return $default;
+ }
+
+ /**
+ * Set with query string resolver callback.
+ *
+ * @param \Closure $resolver
+ * @return void
+ */
+ public static function queryStringResolver(Closure $resolver)
+ {
+ static::$queryStringResolver = $resolver;
+ }
+
+ /**
+ * Get an instance of the view factory from the resolver.
+ *
+ * @return \Illuminate\Contracts\View\Factory
+ */
+ public static function viewFactory()
+ {
+ return call_user_func(static::$viewFactoryResolver);
+ }
+
+ /**
+ * Set the view factory resolver callback.
+ *
+ * @param \Closure $resolver
+ * @return void
+ */
+ public static function viewFactoryResolver(Closure $resolver)
+ {
+ static::$viewFactoryResolver = $resolver;
+ }
+
+ /**
+ * Set the default pagination view.
+ *
+ * @param string $view
+ * @return void
+ */
+ public static function defaultView($view)
+ {
+ static::$defaultView = $view;
+ }
+
+ /**
+ * Set the default "simple" pagination view.
+ *
+ * @param string $view
+ * @return void
+ */
+ public static function defaultSimpleView($view)
+ {
+ static::$defaultSimpleView = $view;
+ }
+
+ /**
+ * Indicate that Tailwind styling should be used for generated links.
+ *
+ * @return void
+ */
+ public static function useTailwind()
+ {
+ static::defaultView('pagination::tailwind');
+ static::defaultSimpleView('pagination::simple-tailwind');
+ }
+
+ /**
+ * Indicate that Bootstrap 4 styling should be used for generated links.
+ *
+ * @return void
+ */
+ public static function useBootstrap()
+ {
+ static::defaultView('pagination::bootstrap-4');
+ static::defaultSimpleView('pagination::simple-bootstrap-4');
+ }
+
+ /**
+ * Indicate that Bootstrap 3 styling should be used for generated links.
+ *
+ * @return void
+ */
+ public static function useBootstrapThree()
+ {
+ static::defaultView('pagination::default');
+ static::defaultSimpleView('pagination::simple-default');
+ }
+
+ /**
+ * Get an iterator for the items.
+ *
+ * @return \ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return $this->items->getIterator();
+ }
+
+ /**
+ * Determine if the list of items is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->items->isEmpty();
+ }
+
+ /**
+ * Determine if the list of items is not empty.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return $this->items->isNotEmpty();
+ }
+
+ /**
+ * Get the number of items for the current page.
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return $this->items->count();
+ }
+
+ /**
+ * Get the paginator's underlying collection.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function getCollection()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Set the paginator's underlying collection.
+ *
+ * @param \Illuminate\Support\Collection $collection
+ * @return $this
+ */
+ public function setCollection(Collection $collection)
+ {
+ $this->items = $collection;
+
+ return $this;
+ }
+
+ /**
+ * Get the paginator options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Determine if the given item exists.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return $this->items->has($key);
+ }
+
+ /**
+ * Get the item at the given offset.
+ *
+ * @param mixed $key
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->items->get($key);
+ }
+
+ /**
+ * Set the item at the given offset.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ $this->items->put($key, $value);
+ }
+
+ /**
+ * Unset the item at the given key.
+ *
+ * @param mixed $key
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ $this->items->forget($key);
+ }
+
+ /**
+ * Render the contents of the paginator to HTML.
+ *
+ * @return string
+ */
+ public function toHtml()
+ {
+ return (string) $this->render();
+ }
+
+ /**
+ * Make dynamic calls into the collection.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->forwardCallTo($this->getCollection(), $method, $parameters);
+ }
+
+ /**
+ * Render the contents of the paginator when casting to a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->render();
+ }
+}
diff --git a/vendor/illuminate/pagination/Cursor.php b/vendor/illuminate/pagination/Cursor.php
new file mode 100644
index 0000000..e8edf65
--- /dev/null
+++ b/vendor/illuminate/pagination/Cursor.php
@@ -0,0 +1,132 @@
+parameters = $parameters;
+ $this->pointsToNextItems = $pointsToNextItems;
+ }
+
+ /**
+ * Get the given parameter from the cursor.
+ *
+ * @param string $parameterName
+ * @return string|null
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function parameter(string $parameterName)
+ {
+ if (! array_key_exists($parameterName, $this->parameters)) {
+ throw new UnexpectedValueException("Unable to find parameter [{$parameterName}] in pagination item.");
+ }
+
+ return $this->parameters[$parameterName];
+ }
+
+ /**
+ * Get the given parameters from the cursor.
+ *
+ * @param array $parameterNames
+ * @return array
+ */
+ public function parameters(array $parameterNames)
+ {
+ return collect($parameterNames)->map(function ($parameterName) {
+ return $this->parameter($parameterName);
+ })->toArray();
+ }
+
+ /**
+ * Determine whether the cursor points to the next set of items.
+ *
+ * @return bool
+ */
+ public function pointsToNextItems()
+ {
+ return $this->pointsToNextItems;
+ }
+
+ /**
+ * Determine whether the cursor points to the previous set of items.
+ *
+ * @return bool
+ */
+ public function pointsToPreviousItems()
+ {
+ return ! $this->pointsToNextItems;
+ }
+
+ /**
+ * Get the array representation of the cursor.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return array_merge($this->parameters, [
+ '_pointsToNextItems' => $this->pointsToNextItems,
+ ]);
+ }
+
+ /**
+ * Get the encoded string representation of the cursor to construct a URL.
+ *
+ * @return string
+ */
+ public function encode()
+ {
+ return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(json_encode($this->toArray())));
+ }
+
+ /**
+ * Get a cursor instance from the encoded string representation.
+ *
+ * @param string|null $encodedString
+ * @return static|null
+ */
+ public static function fromEncoded($encodedString)
+ {
+ if (is_null($encodedString) || ! is_string($encodedString)) {
+ return null;
+ }
+
+ $parameters = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $encodedString)), true);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ return null;
+ }
+
+ $pointsToNextItems = $parameters['_pointsToNextItems'];
+
+ unset($parameters['_pointsToNextItems']);
+
+ return new static($parameters, $pointsToNextItems);
+ }
+}
diff --git a/vendor/illuminate/pagination/CursorPaginationException.php b/vendor/illuminate/pagination/CursorPaginationException.php
new file mode 100644
index 0000000..b12ca60
--- /dev/null
+++ b/vendor/illuminate/pagination/CursorPaginationException.php
@@ -0,0 +1,13 @@
+options = $options;
+
+ foreach ($options as $key => $value) {
+ $this->{$key} = $value;
+ }
+
+ $this->perPage = $perPage;
+ $this->cursor = $cursor;
+ $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+
+ $this->setItems($items);
+ }
+
+ /**
+ * Set the items for the paginator.
+ *
+ * @param mixed $items
+ * @return void
+ */
+ protected function setItems($items)
+ {
+ $this->items = $items instanceof Collection ? $items : Collection::make($items);
+
+ $this->hasMore = $this->items->count() > $this->perPage;
+
+ $this->items = $this->items->slice(0, $this->perPage);
+
+ if (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems()) {
+ $this->items = $this->items->reverse()->values();
+ }
+ }
+
+ /**
+ * Render the paginator using the given view.
+ *
+ * @param string|null $view
+ * @param array $data
+ * @return \Illuminate\Contracts\Support\Htmlable
+ */
+ public function links($view = null, $data = [])
+ {
+ return $this->render($view, $data);
+ }
+
+ /**
+ * Render the paginator using the given view.
+ *
+ * @param string|null $view
+ * @param array $data
+ * @return \Illuminate\Contracts\Support\Htmlable
+ */
+ public function render($view = null, $data = [])
+ {
+ return static::viewFactory()->make($view ?: Paginator::$defaultSimpleView, array_merge($data, [
+ 'paginator' => $this,
+ ]));
+ }
+
+ /**
+ * Determine if there are more items in the data source.
+ *
+ * @return bool
+ */
+ public function hasMorePages()
+ {
+ return (is_null($this->cursor) && $this->hasMore) ||
+ (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && $this->hasMore) ||
+ (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems());
+ }
+
+ /**
+ * Determine if there are enough items to split into multiple pages.
+ *
+ * @return bool
+ */
+ public function hasPages()
+ {
+ return ! $this->onFirstPage() || $this->hasMorePages();
+ }
+
+ /**
+ * Determine if the paginator is on the first page.
+ *
+ * @return bool
+ */
+ public function onFirstPage()
+ {
+ return is_null($this->cursor) || ($this->cursor->pointsToPreviousItems() && ! $this->hasMore);
+ }
+
+ /**
+ * Get the instance as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'data' => $this->items->toArray(),
+ 'path' => $this->path(),
+ 'per_page' => $this->perPage(),
+ 'next_page_url' => $this->nextPageUrl(),
+ 'prev_page_url' => $this->previousPageUrl(),
+ ];
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the object to its JSON representation.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+}
diff --git a/vendor/illuminate/pagination/LICENSE.md b/vendor/illuminate/pagination/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/pagination/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/illuminate/pagination/LengthAwarePaginator.php b/vendor/illuminate/pagination/LengthAwarePaginator.php
new file mode 100644
index 0000000..24f68b1
--- /dev/null
+++ b/vendor/illuminate/pagination/LengthAwarePaginator.php
@@ -0,0 +1,232 @@
+options = $options;
+
+ foreach ($options as $key => $value) {
+ $this->{$key} = $value;
+ }
+
+ $this->total = $total;
+ $this->perPage = $perPage;
+ $this->lastPage = max((int) ceil($total / $perPage), 1);
+ $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+ $this->currentPage = $this->setCurrentPage($currentPage, $this->pageName);
+ $this->items = $items instanceof Collection ? $items : Collection::make($items);
+ }
+
+ /**
+ * Get the current page for the request.
+ *
+ * @param int $currentPage
+ * @param string $pageName
+ * @return int
+ */
+ protected function setCurrentPage($currentPage, $pageName)
+ {
+ $currentPage = $currentPage ?: static::resolveCurrentPage($pageName);
+
+ return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1;
+ }
+
+ /**
+ * Render the paginator using the given view.
+ *
+ * @param string|null $view
+ * @param array $data
+ * @return \Illuminate\Contracts\Support\Htmlable
+ */
+ public function links($view = null, $data = [])
+ {
+ return $this->render($view, $data);
+ }
+
+ /**
+ * Render the paginator using the given view.
+ *
+ * @param string|null $view
+ * @param array $data
+ * @return \Illuminate\Contracts\Support\Htmlable
+ */
+ public function render($view = null, $data = [])
+ {
+ return static::viewFactory()->make($view ?: static::$defaultView, array_merge($data, [
+ 'paginator' => $this,
+ 'elements' => $this->elements(),
+ ]));
+ }
+
+ /**
+ * Get the paginator links as a collection (for JSON responses).
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function linkCollection()
+ {
+ return collect($this->elements())->flatMap(function ($item) {
+ if (! is_array($item)) {
+ return [['url' => null, 'label' => '...', 'active' => false]];
+ }
+
+ return collect($item)->map(function ($url, $page) {
+ return [
+ 'url' => $url,
+ 'label' => (string) $page,
+ 'active' => $this->currentPage() === $page,
+ ];
+ });
+ })->prepend([
+ 'url' => $this->previousPageUrl(),
+ 'label' => function_exists('__') ? __('pagination.previous') : 'Previous',
+ 'active' => false,
+ ])->push([
+ 'url' => $this->nextPageUrl(),
+ 'label' => function_exists('__') ? __('pagination.next') : 'Next',
+ 'active' => false,
+ ]);
+ }
+
+ /**
+ * Get the array of elements to pass to the view.
+ *
+ * @return array
+ */
+ protected function elements()
+ {
+ $window = UrlWindow::make($this);
+
+ return array_filter([
+ $window['first'],
+ is_array($window['slider']) ? '...' : null,
+ $window['slider'],
+ is_array($window['last']) ? '...' : null,
+ $window['last'],
+ ]);
+ }
+
+ /**
+ * Get the total number of items being paginated.
+ *
+ * @return int
+ */
+ public function total()
+ {
+ return $this->total;
+ }
+
+ /**
+ * Determine if there are more items in the data source.
+ *
+ * @return bool
+ */
+ public function hasMorePages()
+ {
+ return $this->currentPage() < $this->lastPage();
+ }
+
+ /**
+ * Get the URL for the next page.
+ *
+ * @return string|null
+ */
+ public function nextPageUrl()
+ {
+ if ($this->hasMorePages()) {
+ return $this->url($this->currentPage() + 1);
+ }
+ }
+
+ /**
+ * Get the last page.
+ *
+ * @return int
+ */
+ public function lastPage()
+ {
+ return $this->lastPage;
+ }
+
+ /**
+ * Get the instance as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'current_page' => $this->currentPage(),
+ 'data' => $this->items->toArray(),
+ 'first_page_url' => $this->url(1),
+ 'from' => $this->firstItem(),
+ 'last_page' => $this->lastPage(),
+ 'last_page_url' => $this->url($this->lastPage()),
+ 'links' => $this->linkCollection()->toArray(),
+ 'next_page_url' => $this->nextPageUrl(),
+ 'path' => $this->path(),
+ 'per_page' => $this->perPage(),
+ 'prev_page_url' => $this->previousPageUrl(),
+ 'to' => $this->lastItem(),
+ 'total' => $this->total(),
+ ];
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the object to its JSON representation.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+}
diff --git a/vendor/illuminate/pagination/PaginationServiceProvider.php b/vendor/illuminate/pagination/PaginationServiceProvider.php
new file mode 100755
index 0000000..e94cebd
--- /dev/null
+++ b/vendor/illuminate/pagination/PaginationServiceProvider.php
@@ -0,0 +1,34 @@
+loadViewsFrom(__DIR__.'/resources/views', 'pagination');
+
+ if ($this->app->runningInConsole()) {
+ $this->publishes([
+ __DIR__.'/resources/views' => $this->app->resourcePath('views/vendor/pagination'),
+ ], 'laravel-pagination');
+ }
+ }
+
+ /**
+ * Register the service provider.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ PaginationState::resolveUsing($this->app);
+ }
+}
diff --git a/vendor/illuminate/pagination/PaginationState.php b/vendor/illuminate/pagination/PaginationState.php
new file mode 100644
index 0000000..ff8150f
--- /dev/null
+++ b/vendor/illuminate/pagination/PaginationState.php
@@ -0,0 +1,41 @@
+url();
+ });
+
+ Paginator::currentPageResolver(function ($pageName = 'page') use ($app) {
+ $page = $app['request']->input($pageName);
+
+ if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
+ return (int) $page;
+ }
+
+ return 1;
+ });
+
+ Paginator::queryStringResolver(function () use ($app) {
+ return $app['request']->query();
+ });
+
+ CursorPaginator::currentCursorResolver(function ($cursorName = 'cursor') use ($app) {
+ return Cursor::fromEncoded($app['request']->input($cursorName));
+ });
+ }
+}
diff --git a/vendor/illuminate/pagination/Paginator.php b/vendor/illuminate/pagination/Paginator.php
new file mode 100644
index 0000000..733edb8
--- /dev/null
+++ b/vendor/illuminate/pagination/Paginator.php
@@ -0,0 +1,177 @@
+options = $options;
+
+ foreach ($options as $key => $value) {
+ $this->{$key} = $value;
+ }
+
+ $this->perPage = $perPage;
+ $this->currentPage = $this->setCurrentPage($currentPage);
+ $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+
+ $this->setItems($items);
+ }
+
+ /**
+ * Get the current page for the request.
+ *
+ * @param int $currentPage
+ * @return int
+ */
+ protected function setCurrentPage($currentPage)
+ {
+ $currentPage = $currentPage ?: static::resolveCurrentPage();
+
+ return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1;
+ }
+
+ /**
+ * Set the items for the paginator.
+ *
+ * @param mixed $items
+ * @return void
+ */
+ protected function setItems($items)
+ {
+ $this->items = $items instanceof Collection ? $items : Collection::make($items);
+
+ $this->hasMore = $this->items->count() > $this->perPage;
+
+ $this->items = $this->items->slice(0, $this->perPage);
+ }
+
+ /**
+ * Get the URL for the next page.
+ *
+ * @return string|null
+ */
+ public function nextPageUrl()
+ {
+ if ($this->hasMorePages()) {
+ return $this->url($this->currentPage() + 1);
+ }
+ }
+
+ /**
+ * Render the paginator using the given view.
+ *
+ * @param string|null $view
+ * @param array $data
+ * @return string
+ */
+ public function links($view = null, $data = [])
+ {
+ return $this->render($view, $data);
+ }
+
+ /**
+ * Render the paginator using the given view.
+ *
+ * @param string|null $view
+ * @param array $data
+ * @return \Illuminate\Contracts\Support\Htmlable
+ */
+ public function render($view = null, $data = [])
+ {
+ return static::viewFactory()->make($view ?: static::$defaultSimpleView, array_merge($data, [
+ 'paginator' => $this,
+ ]));
+ }
+
+ /**
+ * Manually indicate that the paginator does have more pages.
+ *
+ * @param bool $hasMore
+ * @return $this
+ */
+ public function hasMorePagesWhen($hasMore = true)
+ {
+ $this->hasMore = $hasMore;
+
+ return $this;
+ }
+
+ /**
+ * Determine if there are more items in the data source.
+ *
+ * @return bool
+ */
+ public function hasMorePages()
+ {
+ return $this->hasMore;
+ }
+
+ /**
+ * Get the instance as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'current_page' => $this->currentPage(),
+ 'data' => $this->items->toArray(),
+ 'first_page_url' => $this->url(1),
+ 'from' => $this->firstItem(),
+ 'next_page_url' => $this->nextPageUrl(),
+ 'path' => $this->path(),
+ 'per_page' => $this->perPage(),
+ 'prev_page_url' => $this->previousPageUrl(),
+ 'to' => $this->lastItem(),
+ ];
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the object to its JSON representation.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+}
diff --git a/vendor/illuminate/pagination/UrlWindow.php b/vendor/illuminate/pagination/UrlWindow.php
new file mode 100644
index 0000000..31c7cc2
--- /dev/null
+++ b/vendor/illuminate/pagination/UrlWindow.php
@@ -0,0 +1,220 @@
+paginator = $paginator;
+ }
+
+ /**
+ * Create a new URL window instance.
+ *
+ * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
+ * @return array
+ */
+ public static function make(PaginatorContract $paginator)
+ {
+ return (new static($paginator))->get();
+ }
+
+ /**
+ * Get the window of URLs to be shown.
+ *
+ * @return array
+ */
+ public function get()
+ {
+ $onEachSide = $this->paginator->onEachSide;
+
+ if ($this->paginator->lastPage() < ($onEachSide * 2) + 8) {
+ return $this->getSmallSlider();
+ }
+
+ return $this->getUrlSlider($onEachSide);
+ }
+
+ /**
+ * Get the slider of URLs there are not enough pages to slide.
+ *
+ * @return array
+ */
+ protected function getSmallSlider()
+ {
+ return [
+ 'first' => $this->paginator->getUrlRange(1, $this->lastPage()),
+ 'slider' => null,
+ 'last' => null,
+ ];
+ }
+
+ /**
+ * Create a URL slider links.
+ *
+ * @param int $onEachSide
+ * @return array
+ */
+ protected function getUrlSlider($onEachSide)
+ {
+ $window = $onEachSide + 4;
+
+ if (! $this->hasPages()) {
+ return ['first' => null, 'slider' => null, 'last' => null];
+ }
+
+ // If the current page is very close to the beginning of the page range, we will
+ // just render the beginning of the page range, followed by the last 2 of the
+ // links in this list, since we will not have room to create a full slider.
+ if ($this->currentPage() <= $window) {
+ return $this->getSliderTooCloseToBeginning($window, $onEachSide);
+ }
+
+ // If the current page is close to the ending of the page range we will just get
+ // this first couple pages, followed by a larger window of these ending pages
+ // since we're too close to the end of the list to create a full on slider.
+ elseif ($this->currentPage() > ($this->lastPage() - $window)) {
+ return $this->getSliderTooCloseToEnding($window, $onEachSide);
+ }
+
+ // If we have enough room on both sides of the current page to build a slider we
+ // will surround it with both the beginning and ending caps, with this window
+ // of pages in the middle providing a Google style sliding paginator setup.
+ return $this->getFullSlider($onEachSide);
+ }
+
+ /**
+ * Get the slider of URLs when too close to beginning of window.
+ *
+ * @param int $window
+ * @param int $onEachSide
+ * @return array
+ */
+ protected function getSliderTooCloseToBeginning($window, $onEachSide)
+ {
+ return [
+ 'first' => $this->paginator->getUrlRange(1, $window + $onEachSide),
+ 'slider' => null,
+ 'last' => $this->getFinish(),
+ ];
+ }
+
+ /**
+ * Get the slider of URLs when too close to ending of window.
+ *
+ * @param int $window
+ * @param int $onEachSide
+ * @return array
+ */
+ protected function getSliderTooCloseToEnding($window, $onEachSide)
+ {
+ $last = $this->paginator->getUrlRange(
+ $this->lastPage() - ($window + ($onEachSide - 1)),
+ $this->lastPage()
+ );
+
+ return [
+ 'first' => $this->getStart(),
+ 'slider' => null,
+ 'last' => $last,
+ ];
+ }
+
+ /**
+ * Get the slider of URLs when a full slider can be made.
+ *
+ * @param int $onEachSide
+ * @return array
+ */
+ protected function getFullSlider($onEachSide)
+ {
+ return [
+ 'first' => $this->getStart(),
+ 'slider' => $this->getAdjacentUrlRange($onEachSide),
+ 'last' => $this->getFinish(),
+ ];
+ }
+
+ /**
+ * Get the page range for the current page window.
+ *
+ * @param int $onEachSide
+ * @return array
+ */
+ public function getAdjacentUrlRange($onEachSide)
+ {
+ return $this->paginator->getUrlRange(
+ $this->currentPage() - $onEachSide,
+ $this->currentPage() + $onEachSide
+ );
+ }
+
+ /**
+ * Get the starting URLs of a pagination slider.
+ *
+ * @return array
+ */
+ public function getStart()
+ {
+ return $this->paginator->getUrlRange(1, 2);
+ }
+
+ /**
+ * Get the ending URLs of a pagination slider.
+ *
+ * @return array
+ */
+ public function getFinish()
+ {
+ return $this->paginator->getUrlRange(
+ $this->lastPage() - 1,
+ $this->lastPage()
+ );
+ }
+
+ /**
+ * Determine if the underlying paginator being presented has pages to show.
+ *
+ * @return bool
+ */
+ public function hasPages()
+ {
+ return $this->paginator->lastPage() > 1;
+ }
+
+ /**
+ * Get the current page from the paginator.
+ *
+ * @return int
+ */
+ protected function currentPage()
+ {
+ return $this->paginator->currentPage();
+ }
+
+ /**
+ * Get the last page from the paginator.
+ *
+ * @return int
+ */
+ protected function lastPage()
+ {
+ return $this->paginator->lastPage();
+ }
+}
diff --git a/vendor/illuminate/pagination/composer.json b/vendor/illuminate/pagination/composer.json
new file mode 100755
index 0000000..5c8a380
--- /dev/null
+++ b/vendor/illuminate/pagination/composer.json
@@ -0,0 +1,37 @@
+{
+ "name": "illuminate/pagination",
+ "description": "The Illuminate Pagination package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^7.3|^8.0",
+ "ext-json": "*",
+ "illuminate/collections": "^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/support": "^8.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Pagination\\": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php b/vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php
new file mode 100644
index 0000000..63c6f56
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/bootstrap-4.blade.php
@@ -0,0 +1,46 @@
+@if ($paginator->hasPages())
+
+
+
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/default.blade.php b/vendor/illuminate/pagination/resources/views/default.blade.php
new file mode 100644
index 0000000..0db70b5
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/default.blade.php
@@ -0,0 +1,46 @@
+@if ($paginator->hasPages())
+
+
+
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/semantic-ui.blade.php b/vendor/illuminate/pagination/resources/views/semantic-ui.blade.php
new file mode 100644
index 0000000..ef0dbb1
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/semantic-ui.blade.php
@@ -0,0 +1,36 @@
+@if ($paginator->hasPages())
+
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php b/vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php
new file mode 100644
index 0000000..4bb4917
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-bootstrap-4.blade.php
@@ -0,0 +1,27 @@
+@if ($paginator->hasPages())
+
+
+
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-default.blade.php b/vendor/illuminate/pagination/resources/views/simple-default.blade.php
new file mode 100644
index 0000000..36bdbc1
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-default.blade.php
@@ -0,0 +1,19 @@
+@if ($paginator->hasPages())
+
+
+
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php b/vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php
new file mode 100644
index 0000000..6872cca
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/simple-tailwind.blade.php
@@ -0,0 +1,25 @@
+@if ($paginator->hasPages())
+
+ {{-- Previous Page Link --}}
+ @if ($paginator->onFirstPage())
+
+ {!! __('pagination.previous') !!}
+
+ @else
+
+ {!! __('pagination.previous') !!}
+
+ @endif
+
+ {{-- Next Page Link --}}
+ @if ($paginator->hasMorePages())
+
+ {!! __('pagination.next') !!}
+
+ @else
+
+ {!! __('pagination.next') !!}
+
+ @endif
+
+@endif
diff --git a/vendor/illuminate/pagination/resources/views/tailwind.blade.php b/vendor/illuminate/pagination/resources/views/tailwind.blade.php
new file mode 100644
index 0000000..5bf323b
--- /dev/null
+++ b/vendor/illuminate/pagination/resources/views/tailwind.blade.php
@@ -0,0 +1,106 @@
+@if ($paginator->hasPages())
+
+
+
+
+
+
+ {!! __('Showing') !!}
+ @if ($paginator->firstItem())
+ {{ $paginator->firstItem() }}
+ {!! __('to') !!}
+ {{ $paginator->lastItem() }}
+ @else
+ {{ $paginator->count() }}
+ @endif
+ {!! __('of') !!}
+ {{ $paginator->total() }}
+ {!! __('results') !!}
+
+
+
+
+
+ {{-- Previous Page Link --}}
+ @if ($paginator->onFirstPage())
+
+
+
+
+
+
+
+ @else
+
+
+
+
+
+ @endif
+
+ {{-- Pagination Elements --}}
+ @foreach ($elements as $element)
+ {{-- "Three Dots" Separator --}}
+ @if (is_string($element))
+
+ {{ $element }}
+
+ @endif
+
+ {{-- Array Of Links --}}
+ @if (is_array($element))
+ @foreach ($element as $page => $url)
+ @if ($page == $paginator->currentPage())
+
+ {{ $page }}
+
+ @else
+
+ {{ $page }}
+
+ @endif
+ @endforeach
+ @endif
+ @endforeach
+
+ {{-- Next Page Link --}}
+ @if ($paginator->hasMorePages())
+
+
+
+
+
+ @else
+
+
+
+
+
+
+
+ @endif
+
+
+
+
+@endif
diff --git a/vendor/illuminate/support/AggregateServiceProvider.php b/vendor/illuminate/support/AggregateServiceProvider.php
new file mode 100644
index 0000000..d7425c5
--- /dev/null
+++ b/vendor/illuminate/support/AggregateServiceProvider.php
@@ -0,0 +1,52 @@
+instances = [];
+
+ foreach ($this->providers as $provider) {
+ $this->instances[] = $this->app->register($provider);
+ }
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ $provides = [];
+
+ foreach ($this->providers as $provider) {
+ $instance = $this->app->resolveProvider($provider);
+
+ $provides = array_merge($provides, $instance->provides());
+ }
+
+ return $provides;
+ }
+}
diff --git a/vendor/illuminate/support/Carbon.php b/vendor/illuminate/support/Carbon.php
new file mode 100644
index 0000000..004b27b
--- /dev/null
+++ b/vendor/illuminate/support/Carbon.php
@@ -0,0 +1,18 @@
+files = $files;
+ $this->workingPath = $workingPath;
+ }
+
+ /**
+ * Regenerate the Composer autoloader files.
+ *
+ * @param string|array $extra
+ * @return int
+ */
+ public function dumpAutoloads($extra = '')
+ {
+ $extra = $extra ? (array) $extra : [];
+
+ $command = array_merge($this->findComposer(), ['dump-autoload'], $extra);
+
+ return $this->getProcess($command)->run();
+ }
+
+ /**
+ * Regenerate the optimized Composer autoloader files.
+ *
+ * @return int
+ */
+ public function dumpOptimized()
+ {
+ return $this->dumpAutoloads('--optimize');
+ }
+
+ /**
+ * Get the composer command for the environment.
+ *
+ * @return array
+ */
+ protected function findComposer()
+ {
+ if ($this->files->exists($this->workingPath.'/composer.phar')) {
+ return [$this->phpBinary(), 'composer.phar'];
+ }
+
+ return ['composer'];
+ }
+
+ /**
+ * Get the PHP binary.
+ *
+ * @return string
+ */
+ protected function phpBinary()
+ {
+ return ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
+ }
+
+ /**
+ * Get a new Symfony process instance.
+ *
+ * @param array $command
+ * @return \Symfony\Component\Process\Process
+ */
+ protected function getProcess(array $command)
+ {
+ return (new Process($command, $this->workingPath))->setTimeout(null);
+ }
+
+ /**
+ * Set the working path used by the class.
+ *
+ * @param string $path
+ * @return $this
+ */
+ public function setWorkingPath($path)
+ {
+ $this->workingPath = realpath($path);
+
+ return $this;
+ }
+}
diff --git a/vendor/illuminate/support/ConfigurationUrlParser.php b/vendor/illuminate/support/ConfigurationUrlParser.php
new file mode 100644
index 0000000..be54b9a
--- /dev/null
+++ b/vendor/illuminate/support/ConfigurationUrlParser.php
@@ -0,0 +1,193 @@
+ 'sqlsrv',
+ 'mysql2' => 'mysql', // RDS
+ 'postgres' => 'pgsql',
+ 'postgresql' => 'pgsql',
+ 'sqlite3' => 'sqlite',
+ 'redis' => 'tcp',
+ 'rediss' => 'tls',
+ ];
+
+ /**
+ * Parse the database configuration, hydrating options using a database configuration URL if possible.
+ *
+ * @param array|string $config
+ * @return array
+ */
+ public function parseConfiguration($config)
+ {
+ if (is_string($config)) {
+ $config = ['url' => $config];
+ }
+
+ $url = Arr::pull($config, 'url');
+
+ if (! $url) {
+ return $config;
+ }
+
+ $rawComponents = $this->parseUrl($url);
+
+ $decodedComponents = $this->parseStringsToNativeTypes(
+ array_map('rawurldecode', $rawComponents)
+ );
+
+ return array_merge(
+ $config,
+ $this->getPrimaryOptions($decodedComponents),
+ $this->getQueryOptions($rawComponents)
+ );
+ }
+
+ /**
+ * Get the primary database connection options.
+ *
+ * @param array $url
+ * @return array
+ */
+ protected function getPrimaryOptions($url)
+ {
+ return array_filter([
+ 'driver' => $this->getDriver($url),
+ 'database' => $this->getDatabase($url),
+ 'host' => $url['host'] ?? null,
+ 'port' => $url['port'] ?? null,
+ 'username' => $url['user'] ?? null,
+ 'password' => $url['pass'] ?? null,
+ ], function ($value) {
+ return ! is_null($value);
+ });
+ }
+
+ /**
+ * Get the database driver from the URL.
+ *
+ * @param array $url
+ * @return string|null
+ */
+ protected function getDriver($url)
+ {
+ $alias = $url['scheme'] ?? null;
+
+ if (! $alias) {
+ return;
+ }
+
+ return static::$driverAliases[$alias] ?? $alias;
+ }
+
+ /**
+ * Get the database name from the URL.
+ *
+ * @param array $url
+ * @return string|null
+ */
+ protected function getDatabase($url)
+ {
+ $path = $url['path'] ?? null;
+
+ return $path && $path !== '/' ? substr($path, 1) : null;
+ }
+
+ /**
+ * Get all of the additional database options from the query string.
+ *
+ * @param array $url
+ * @return array
+ */
+ protected function getQueryOptions($url)
+ {
+ $queryString = $url['query'] ?? null;
+
+ if (! $queryString) {
+ return [];
+ }
+
+ $query = [];
+
+ parse_str($queryString, $query);
+
+ return $this->parseStringsToNativeTypes($query);
+ }
+
+ /**
+ * Parse the string URL to an array of components.
+ *
+ * @param string $url
+ * @return array
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function parseUrl($url)
+ {
+ $url = preg_replace('#^(sqlite3?):///#', '$1://null/', $url);
+
+ $parsedUrl = parse_url($url);
+
+ if ($parsedUrl === false) {
+ throw new InvalidArgumentException('The database configuration URL is malformed.');
+ }
+
+ return $parsedUrl;
+ }
+
+ /**
+ * Convert string casted values to their native types.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function parseStringsToNativeTypes($value)
+ {
+ if (is_array($value)) {
+ return array_map([$this, 'parseStringsToNativeTypes'], $value);
+ }
+
+ if (! is_string($value)) {
+ return $value;
+ }
+
+ $parsedValue = json_decode($value, true);
+
+ if (json_last_error() === JSON_ERROR_NONE) {
+ return $parsedValue;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get all of the current drivers' aliases.
+ *
+ * @return array
+ */
+ public static function getDriverAliases()
+ {
+ return static::$driverAliases;
+ }
+
+ /**
+ * Add the given driver alias to the driver aliases array.
+ *
+ * @param string $alias
+ * @param string $driver
+ * @return void
+ */
+ public static function addDriverAlias($alias, $driver)
+ {
+ static::$driverAliases[$alias] = $driver;
+ }
+}
diff --git a/vendor/illuminate/support/DateFactory.php b/vendor/illuminate/support/DateFactory.php
new file mode 100644
index 0000000..f36cb46
--- /dev/null
+++ b/vendor/illuminate/support/DateFactory.php
@@ -0,0 +1,231 @@
+$method(...$parameters);
+ }
+
+ $dateClass = static::$dateClass ?: $defaultClassName;
+
+ // Check if date can be created using public class method...
+ if (method_exists($dateClass, $method) ||
+ method_exists($dateClass, 'hasMacro') && $dateClass::hasMacro($method)) {
+ return $dateClass::$method(...$parameters);
+ }
+
+ // If that fails, create the date with the default class...
+ $date = $defaultClassName::$method(...$parameters);
+
+ // If the configured class has an "instance" method, we'll try to pass our date into there...
+ if (method_exists($dateClass, 'instance')) {
+ return $dateClass::instance($date);
+ }
+
+ // Otherwise, assume the configured class has a DateTime compatible constructor...
+ return new $dateClass($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
+ }
+}
diff --git a/vendor/illuminate/support/Env.php b/vendor/illuminate/support/Env.php
new file mode 100644
index 0000000..b310073
--- /dev/null
+++ b/vendor/illuminate/support/Env.php
@@ -0,0 +1,103 @@
+addAdapter(PutenvAdapter::class);
+ }
+
+ static::$repository = $builder->immutable()->make();
+ }
+
+ return static::$repository;
+ }
+
+ /**
+ * Gets the value of an environment variable.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function get($key, $default = null)
+ {
+ return Option::fromValue(static::getRepository()->get($key))
+ ->map(function ($value) {
+ switch (strtolower($value)) {
+ case 'true':
+ case '(true)':
+ return true;
+ case 'false':
+ case '(false)':
+ return false;
+ case 'empty':
+ case '(empty)':
+ return '';
+ case 'null':
+ case '(null)':
+ return;
+ }
+
+ if (preg_match('/\A([\'"])(.*)\1\z/', $value, $matches)) {
+ return $matches[2];
+ }
+
+ return $value;
+ })
+ ->getOrCall(function () use ($default) {
+ return value($default);
+ });
+ }
+}
diff --git a/vendor/illuminate/support/Facades/App.php b/vendor/illuminate/support/Facades/App.php
new file mode 100755
index 0000000..8fbec3d
--- /dev/null
+++ b/vendor/illuminate/support/Facades/App.php
@@ -0,0 +1,64 @@
+providerIsLoaded(UiServiceProvider::class)) {
+ throw new RuntimeException('In order to use the Auth::routes() method, please install the laravel/ui package.');
+ }
+
+ static::$app->make('router')->auth($options);
+ }
+}
diff --git a/vendor/illuminate/support/Facades/Blade.php b/vendor/illuminate/support/Facades/Blade.php
new file mode 100755
index 0000000..81019e2
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Blade.php
@@ -0,0 +1,46 @@
+dispatch();
+ }
+
+ /**
+ * Get the registered name of the component.
+ *
+ * @return string
+ */
+ protected static function getFacadeAccessor()
+ {
+ return BusDispatcherContract::class;
+ }
+}
diff --git a/vendor/illuminate/support/Facades/Cache.php b/vendor/illuminate/support/Facades/Cache.php
new file mode 100755
index 0000000..168c351
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Cache.php
@@ -0,0 +1,40 @@
+cookie($key, null));
+ }
+
+ /**
+ * Retrieve a cookie from the request.
+ *
+ * @param string|null $key
+ * @param mixed $default
+ * @return string|array|null
+ */
+ public static function get($key = null, $default = null)
+ {
+ return static::$app['request']->cookie($key, $default);
+ }
+
+ /**
+ * Get the registered name of the component.
+ *
+ * @return string
+ */
+ protected static function getFacadeAccessor()
+ {
+ return 'cookie';
+ }
+}
diff --git a/vendor/illuminate/support/Facades/Crypt.php b/vendor/illuminate/support/Facades/Crypt.php
new file mode 100755
index 0000000..61eaaa8
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Crypt.php
@@ -0,0 +1,27 @@
+resolved($accessor) === true) {
+ $callback(static::getFacadeRoot());
+ }
+
+ static::$app->afterResolving($accessor, function ($service) use ($callback) {
+ $callback($service);
+ });
+ }
+
+ /**
+ * Convert the facade into a Mockery spy.
+ *
+ * @return \Mockery\MockInterface
+ */
+ public static function spy()
+ {
+ if (! static::isMock()) {
+ $class = static::getMockableClass();
+
+ return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
+ static::swap($spy);
+ });
+ }
+ }
+
+ /**
+ * Initiate a partial mock on the facade.
+ *
+ * @return \Mockery\MockInterface
+ */
+ public static function partialMock()
+ {
+ $name = static::getFacadeAccessor();
+
+ $mock = static::isMock()
+ ? static::$resolvedInstance[$name]
+ : static::createFreshMockInstance();
+
+ return $mock->makePartial();
+ }
+
+ /**
+ * Initiate a mock expectation on the facade.
+ *
+ * @return \Mockery\Expectation
+ */
+ public static function shouldReceive()
+ {
+ $name = static::getFacadeAccessor();
+
+ $mock = static::isMock()
+ ? static::$resolvedInstance[$name]
+ : static::createFreshMockInstance();
+
+ return $mock->shouldReceive(...func_get_args());
+ }
+
+ /**
+ * Create a fresh mock instance for the given class.
+ *
+ * @return \Mockery\MockInterface
+ */
+ protected static function createFreshMockInstance()
+ {
+ return tap(static::createMock(), function ($mock) {
+ static::swap($mock);
+
+ $mock->shouldAllowMockingProtectedMethods();
+ });
+ }
+
+ /**
+ * Create a fresh mock instance for the given class.
+ *
+ * @return \Mockery\MockInterface
+ */
+ protected static function createMock()
+ {
+ $class = static::getMockableClass();
+
+ return $class ? Mockery::mock($class) : Mockery::mock();
+ }
+
+ /**
+ * Determines whether a mock is set as the instance of the facade.
+ *
+ * @return bool
+ */
+ protected static function isMock()
+ {
+ $name = static::getFacadeAccessor();
+
+ return isset(static::$resolvedInstance[$name]) &&
+ static::$resolvedInstance[$name] instanceof LegacyMockInterface;
+ }
+
+ /**
+ * Get the mockable class for the bound instance.
+ *
+ * @return string|null
+ */
+ protected static function getMockableClass()
+ {
+ if ($root = static::getFacadeRoot()) {
+ return get_class($root);
+ }
+ }
+
+ /**
+ * Hotswap the underlying instance behind the facade.
+ *
+ * @param mixed $instance
+ * @return void
+ */
+ public static function swap($instance)
+ {
+ static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
+
+ if (isset(static::$app)) {
+ static::$app->instance(static::getFacadeAccessor(), $instance);
+ }
+ }
+
+ /**
+ * Get the root object behind the facade.
+ *
+ * @return mixed
+ */
+ public static function getFacadeRoot()
+ {
+ return static::resolveFacadeInstance(static::getFacadeAccessor());
+ }
+
+ /**
+ * Get the registered name of the component.
+ *
+ * @return string
+ *
+ * @throws \RuntimeException
+ */
+ protected static function getFacadeAccessor()
+ {
+ throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
+ }
+
+ /**
+ * Resolve the facade root instance from the container.
+ *
+ * @param object|string $name
+ * @return mixed
+ */
+ protected static function resolveFacadeInstance($name)
+ {
+ if (is_object($name)) {
+ return $name;
+ }
+
+ if (isset(static::$resolvedInstance[$name])) {
+ return static::$resolvedInstance[$name];
+ }
+
+ if (static::$app) {
+ return static::$resolvedInstance[$name] = static::$app[$name];
+ }
+ }
+
+ /**
+ * Clear a resolved facade instance.
+ *
+ * @param string $name
+ * @return void
+ */
+ public static function clearResolvedInstance($name)
+ {
+ unset(static::$resolvedInstance[$name]);
+ }
+
+ /**
+ * Clear all of the resolved instances.
+ *
+ * @return void
+ */
+ public static function clearResolvedInstances()
+ {
+ static::$resolvedInstance = [];
+ }
+
+ /**
+ * Get the application instance behind the facade.
+ *
+ * @return \Illuminate\Contracts\Foundation\Application
+ */
+ public static function getFacadeApplication()
+ {
+ return static::$app;
+ }
+
+ /**
+ * Set the application instance.
+ *
+ * @param \Illuminate\Contracts\Foundation\Application $app
+ * @return void
+ */
+ public static function setFacadeApplication($app)
+ {
+ static::$app = $app;
+ }
+
+ /**
+ * Handle dynamic, static calls to the object.
+ *
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ *
+ * @throws \RuntimeException
+ */
+ public static function __callStatic($method, $args)
+ {
+ $instance = static::getFacadeRoot();
+
+ if (! $instance) {
+ throw new RuntimeException('A facade root has not been set.');
+ }
+
+ return $instance->$method(...$args);
+ }
+}
diff --git a/vendor/illuminate/support/Facades/File.php b/vendor/illuminate/support/Facades/File.php
new file mode 100755
index 0000000..c22d363
--- /dev/null
+++ b/vendor/illuminate/support/Facades/File.php
@@ -0,0 +1,62 @@
+route($channel, $route);
+ }
+
+ /**
+ * Get the registered name of the component.
+ *
+ * @return string
+ */
+ protected static function getFacadeAccessor()
+ {
+ return ChannelManager::class;
+ }
+}
diff --git a/vendor/illuminate/support/Facades/ParallelTesting.php b/vendor/illuminate/support/Facades/ParallelTesting.php
new file mode 100644
index 0000000..c397611
--- /dev/null
+++ b/vendor/illuminate/support/Facades/ParallelTesting.php
@@ -0,0 +1,26 @@
+connection($name)->getSchemaBuilder();
+ }
+
+ /**
+ * Get a schema builder instance for the default connection.
+ *
+ * @return \Illuminate\Database\Schema\Builder
+ */
+ protected static function getFacadeAccessor()
+ {
+ return static::$app['db']->connection()->getSchemaBuilder();
+ }
+}
diff --git a/vendor/illuminate/support/Facades/Session.php b/vendor/illuminate/support/Facades/Session.php
new file mode 100755
index 0000000..a072321
--- /dev/null
+++ b/vendor/illuminate/support/Facades/Session.php
@@ -0,0 +1,45 @@
+get('filesystems.default');
+
+ $root = storage_path('framework/testing/disks/'.$disk);
+
+ if ($token = ParallelTesting::token()) {
+ $root = "{$root}_test_{$token}";
+ }
+
+ (new Filesystem)->cleanDirectory($root);
+
+ static::set($disk, $fake = static::createLocalDriver(array_merge($config, [
+ 'root' => $root,
+ ])));
+
+ return $fake;
+ }
+
+ /**
+ * Replace the given disk with a persistent local testing disk.
+ *
+ * @param string|null $disk
+ * @param array $config
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
+ */
+ public static function persistentFake($disk = null, array $config = [])
+ {
+ $disk = $disk ?: static::$app['config']->get('filesystems.default');
+
+ static::set($disk, $fake = static::createLocalDriver(array_merge($config, [
+ 'root' => storage_path('framework/testing/disks/'.$disk),
+ ])));
+
+ return $fake;
+ }
+
+ /**
+ * Get the registered name of the component.
+ *
+ * @return string
+ */
+ protected static function getFacadeAccessor()
+ {
+ return 'filesystem';
+ }
+}
diff --git a/vendor/illuminate/support/Facades/URL.php b/vendor/illuminate/support/Facades/URL.php
new file mode 100755
index 0000000..7d9941d
--- /dev/null
+++ b/vendor/illuminate/support/Facades/URL.php
@@ -0,0 +1,38 @@
+ $value) {
+ $this->attributes[$key] = $value;
+ }
+ }
+
+ /**
+ * Get an attribute from the fluent instance.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if (array_key_exists($key, $this->attributes)) {
+ return $this->attributes[$key];
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Get the attributes from the fluent instance.
+ *
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Convert the fluent instance to an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the fluent instance to JSON.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+
+ /**
+ * Determine if the given offset exists.
+ *
+ * @param string $offset
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($offset)
+ {
+ return isset($this->attributes[$offset]);
+ }
+
+ /**
+ * Get the value for a given offset.
+ *
+ * @param string $offset
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->get($offset);
+ }
+
+ /**
+ * Set the value at the given offset.
+ *
+ * @param string $offset
+ * @param mixed $value
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($offset, $value)
+ {
+ $this->attributes[$offset] = $value;
+ }
+
+ /**
+ * Unset the value at the given offset.
+ *
+ * @param string $offset
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($offset)
+ {
+ unset($this->attributes[$offset]);
+ }
+
+ /**
+ * Handle dynamic calls to the fluent instance to set attributes.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return $this
+ */
+ public function __call($method, $parameters)
+ {
+ $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true;
+
+ return $this;
+ }
+
+ /**
+ * Dynamically retrieve the value of an attribute.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Dynamically set the value of an attribute.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ */
+ public function __set($key, $value)
+ {
+ $this->offsetSet($key, $value);
+ }
+
+ /**
+ * Dynamically check if an attribute is set.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function __isset($key)
+ {
+ return $this->offsetExists($key);
+ }
+
+ /**
+ * Dynamically unset an attribute.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function __unset($key)
+ {
+ $this->offsetUnset($key);
+ }
+}
diff --git a/vendor/illuminate/support/HigherOrderTapProxy.php b/vendor/illuminate/support/HigherOrderTapProxy.php
new file mode 100644
index 0000000..bbf9b2e
--- /dev/null
+++ b/vendor/illuminate/support/HigherOrderTapProxy.php
@@ -0,0 +1,38 @@
+target = $target;
+ }
+
+ /**
+ * Dynamically pass method calls to the target.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ $this->target->{$method}(...$parameters);
+
+ return $this->target;
+ }
+}
diff --git a/vendor/illuminate/support/HtmlString.php b/vendor/illuminate/support/HtmlString.php
new file mode 100644
index 0000000..d6b71d4
--- /dev/null
+++ b/vendor/illuminate/support/HtmlString.php
@@ -0,0 +1,66 @@
+html = $html;
+ }
+
+ /**
+ * Get the HTML string.
+ *
+ * @return string
+ */
+ public function toHtml()
+ {
+ return $this->html;
+ }
+
+ /**
+ * Determine if the given HTML string is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->html === '';
+ }
+
+ /**
+ * Determine if the given HTML string is not empty.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return ! $this->isEmpty();
+ }
+
+ /**
+ * Get the HTML string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toHtml();
+ }
+}
diff --git a/vendor/illuminate/support/InteractsWithTime.php b/vendor/illuminate/support/InteractsWithTime.php
new file mode 100644
index 0000000..2b617c3
--- /dev/null
+++ b/vendor/illuminate/support/InteractsWithTime.php
@@ -0,0 +1,64 @@
+parseDateInterval($delay);
+
+ return $delay instanceof DateTimeInterface
+ ? max(0, $delay->getTimestamp() - $this->currentTime())
+ : (int) $delay;
+ }
+
+ /**
+ * Get the "available at" UNIX timestamp.
+ *
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @return int
+ */
+ protected function availableAt($delay = 0)
+ {
+ $delay = $this->parseDateInterval($delay);
+
+ return $delay instanceof DateTimeInterface
+ ? $delay->getTimestamp()
+ : Carbon::now()->addRealSeconds($delay)->getTimestamp();
+ }
+
+ /**
+ * If the given value is an interval, convert it to a DateTime instance.
+ *
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @return \DateTimeInterface|int
+ */
+ protected function parseDateInterval($delay)
+ {
+ if ($delay instanceof DateInterval) {
+ $delay = Carbon::now()->add($delay);
+ }
+
+ return $delay;
+ }
+
+ /**
+ * Get the current system time as a UNIX timestamp.
+ *
+ * @return int
+ */
+ protected function currentTime()
+ {
+ return Carbon::now()->getTimestamp();
+ }
+}
diff --git a/vendor/illuminate/support/Js.php b/vendor/illuminate/support/Js.php
new file mode 100644
index 0000000..6d6de34
--- /dev/null
+++ b/vendor/illuminate/support/Js.php
@@ -0,0 +1,145 @@
+js = $this->convertDataToJavaScriptExpression($data, $flags, $depth);
+ }
+
+ /**
+ * Create a new JavaScript string from the given data.
+ *
+ * @param mixed $data
+ * @param int $flags
+ * @param int $depth
+ * @return static
+ *
+ * @throws \JsonException
+ */
+ public static function from($data, $flags = 0, $depth = 512)
+ {
+ return new static($data, $flags, $depth);
+ }
+
+ /**
+ * Convert the given data to a JavaScript expression.
+ *
+ * @param mixed $data
+ * @param int $flags
+ * @param int $depth
+ * @return string
+ *
+ * @throws \JsonException
+ */
+ protected function convertDataToJavaScriptExpression($data, $flags = 0, $depth = 512)
+ {
+ if ($data instanceof self) {
+ return $data->toHtml();
+ }
+
+ $json = $this->jsonEncode($data, $flags, $depth);
+
+ if (is_string($data)) {
+ return "'".substr($json, 1, -1)."'";
+ }
+
+ return $this->convertJsonToJavaScriptExpression($json, $flags);
+ }
+
+ /**
+ * Encode the given data as JSON.
+ *
+ * @param mixed $data
+ * @param int $flags
+ * @param int $depth
+ * @return string
+ *
+ * @throws \JsonException
+ */
+ protected function jsonEncode($data, $flags = 0, $depth = 512)
+ {
+ if ($data instanceof Jsonable) {
+ return $data->toJson($flags | static::REQUIRED_FLAGS);
+ }
+
+ if ($data instanceof Arrayable && ! ($data instanceof JsonSerializable)) {
+ $data = $data->toArray();
+ }
+
+ return json_encode($data, $flags | static::REQUIRED_FLAGS, $depth);
+ }
+
+ /**
+ * Convert the given JSON to a JavaScript expression.
+ *
+ * @param string $json
+ * @param int $flags
+ * @return string
+ *
+ * @throws \JsonException
+ */
+ protected function convertJsonToJavaScriptExpression($json, $flags = 0)
+ {
+ if ('[]' === $json || '{}' === $json) {
+ return $json;
+ }
+
+ if (Str::startsWith($json, ['"', '{', '['])) {
+ return "JSON.parse('".substr(json_encode($json, $flags | static::REQUIRED_FLAGS), 1, -1)."')";
+ }
+
+ return $json;
+ }
+
+ /**
+ * Get the string representation of the data for use in HTML.
+ *
+ * @return string
+ */
+ public function toHtml()
+ {
+ return $this->js;
+ }
+
+ /**
+ * Get the string representation of the data for use in HTML.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toHtml();
+ }
+}
diff --git a/vendor/illuminate/support/LICENSE.md b/vendor/illuminate/support/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/illuminate/support/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/illuminate/support/Manager.php b/vendor/illuminate/support/Manager.php
new file mode 100755
index 0000000..f8ae072
--- /dev/null
+++ b/vendor/illuminate/support/Manager.php
@@ -0,0 +1,193 @@
+container = $container;
+ $this->config = $container->make('config');
+ }
+
+ /**
+ * Get the default driver name.
+ *
+ * @return string
+ */
+ abstract public function getDefaultDriver();
+
+ /**
+ * Get a driver instance.
+ *
+ * @param string|null $driver
+ * @return mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function driver($driver = null)
+ {
+ $driver = $driver ?: $this->getDefaultDriver();
+
+ if (is_null($driver)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Unable to resolve NULL driver for [%s].', static::class
+ ));
+ }
+
+ // If the given driver has not been created before, we will create the instances
+ // here and cache it so we can return it next time very quickly. If there is
+ // already a driver created by this name, we'll just return that instance.
+ if (! isset($this->drivers[$driver])) {
+ $this->drivers[$driver] = $this->createDriver($driver);
+ }
+
+ return $this->drivers[$driver];
+ }
+
+ /**
+ * Create a new driver instance.
+ *
+ * @param string $driver
+ * @return mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function createDriver($driver)
+ {
+ // First, we will determine if a custom driver creator exists for the given driver and
+ // if it does not we will check for a creator method for the driver. Custom creator
+ // callbacks allow developers to build their own "drivers" easily using Closures.
+ if (isset($this->customCreators[$driver])) {
+ return $this->callCustomCreator($driver);
+ } else {
+ $method = 'create'.Str::studly($driver).'Driver';
+
+ if (method_exists($this, $method)) {
+ return $this->$method();
+ }
+ }
+
+ throw new InvalidArgumentException("Driver [$driver] not supported.");
+ }
+
+ /**
+ * Call a custom driver creator.
+ *
+ * @param string $driver
+ * @return mixed
+ */
+ protected function callCustomCreator($driver)
+ {
+ return $this->customCreators[$driver]($this->container);
+ }
+
+ /**
+ * Register a custom driver creator Closure.
+ *
+ * @param string $driver
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function extend($driver, Closure $callback)
+ {
+ $this->customCreators[$driver] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Get all of the created "drivers".
+ *
+ * @return array
+ */
+ public function getDrivers()
+ {
+ return $this->drivers;
+ }
+
+ /**
+ * Get the container instance used by the manager.
+ *
+ * @return \Illuminate\Contracts\Container\Container
+ */
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ /**
+ * Set the container instance used by the manager.
+ *
+ * @param \Illuminate\Contracts\Container\Container $container
+ * @return $this
+ */
+ public function setContainer(Container $container)
+ {
+ $this->container = $container;
+
+ return $this;
+ }
+
+ /**
+ * Forget all of the resolved driver instances.
+ *
+ * @return $this
+ */
+ public function forgetDrivers()
+ {
+ $this->drivers = [];
+
+ return $this;
+ }
+
+ /**
+ * Dynamically call the default driver instance.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->driver()->$method(...$parameters);
+ }
+}
diff --git a/vendor/illuminate/support/MessageBag.php b/vendor/illuminate/support/MessageBag.php
new file mode 100755
index 0000000..e53d509
--- /dev/null
+++ b/vendor/illuminate/support/MessageBag.php
@@ -0,0 +1,418 @@
+ $value) {
+ $value = $value instanceof Arrayable ? $value->toArray() : (array) $value;
+
+ $this->messages[$key] = array_unique($value);
+ }
+ }
+
+ /**
+ * Get the keys present in the message bag.
+ *
+ * @return array
+ */
+ public function keys()
+ {
+ return array_keys($this->messages);
+ }
+
+ /**
+ * Add a message to the message bag.
+ *
+ * @param string $key
+ * @param string $message
+ * @return $this
+ */
+ public function add($key, $message)
+ {
+ if ($this->isUnique($key, $message)) {
+ $this->messages[$key][] = $message;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a message to the message bag if the given conditional is "true".
+ *
+ * @param bool $boolean
+ * @param string $key
+ * @param string $message
+ * @return $this
+ */
+ public function addIf($boolean, $key, $message)
+ {
+ return $boolean ? $this->add($key, $message) : $this;
+ }
+
+ /**
+ * Determine if a key and message combination already exists.
+ *
+ * @param string $key
+ * @param string $message
+ * @return bool
+ */
+ protected function isUnique($key, $message)
+ {
+ $messages = (array) $this->messages;
+
+ return ! isset($messages[$key]) || ! in_array($message, $messages[$key]);
+ }
+
+ /**
+ * Merge a new array of messages into the message bag.
+ *
+ * @param \Illuminate\Contracts\Support\MessageProvider|array $messages
+ * @return $this
+ */
+ public function merge($messages)
+ {
+ if ($messages instanceof MessageProvider) {
+ $messages = $messages->getMessageBag()->getMessages();
+ }
+
+ $this->messages = array_merge_recursive($this->messages, $messages);
+
+ return $this;
+ }
+
+ /**
+ * Determine if messages exist for all of the given keys.
+ *
+ * @param array|string|null $key
+ * @return bool
+ */
+ public function has($key)
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ if (is_null($key)) {
+ return $this->any();
+ }
+
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $key) {
+ if ($this->first($key) === '') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if messages exist for any of the given keys.
+ *
+ * @param array|string $keys
+ * @return bool
+ */
+ public function hasAny($keys = [])
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ foreach ($keys as $key) {
+ if ($this->has($key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the first message from the message bag for a given key.
+ *
+ * @param string|null $key
+ * @param string|null $format
+ * @return string
+ */
+ public function first($key = null, $format = null)
+ {
+ $messages = is_null($key) ? $this->all($format) : $this->get($key, $format);
+
+ $firstMessage = Arr::first($messages, null, '');
+
+ return is_array($firstMessage) ? Arr::first($firstMessage) : $firstMessage;
+ }
+
+ /**
+ * Get all of the messages from the message bag for a given key.
+ *
+ * @param string $key
+ * @param string|null $format
+ * @return array
+ */
+ public function get($key, $format = null)
+ {
+ // If the message exists in the message bag, we will transform it and return
+ // the message. Otherwise, we will check if the key is implicit & collect
+ // all the messages that match the given key and output it as an array.
+ if (array_key_exists($key, $this->messages)) {
+ return $this->transform(
+ $this->messages[$key], $this->checkFormat($format), $key
+ );
+ }
+
+ if (Str::contains($key, '*')) {
+ return $this->getMessagesForWildcardKey($key, $format);
+ }
+
+ return [];
+ }
+
+ /**
+ * Get the messages for a wildcard key.
+ *
+ * @param string $key
+ * @param string|null $format
+ * @return array
+ */
+ protected function getMessagesForWildcardKey($key, $format)
+ {
+ return collect($this->messages)
+ ->filter(function ($messages, $messageKey) use ($key) {
+ return Str::is($key, $messageKey);
+ })
+ ->map(function ($messages, $messageKey) use ($format) {
+ return $this->transform(
+ $messages, $this->checkFormat($format), $messageKey
+ );
+ })->all();
+ }
+
+ /**
+ * Get all of the messages for every key in the message bag.
+ *
+ * @param string|null $format
+ * @return array
+ */
+ public function all($format = null)
+ {
+ $format = $this->checkFormat($format);
+
+ $all = [];
+
+ foreach ($this->messages as $key => $messages) {
+ $all = array_merge($all, $this->transform($messages, $format, $key));
+ }
+
+ return $all;
+ }
+
+ /**
+ * Get all of the unique messages for every key in the message bag.
+ *
+ * @param string|null $format
+ * @return array
+ */
+ public function unique($format = null)
+ {
+ return array_unique($this->all($format));
+ }
+
+ /**
+ * Format an array of messages.
+ *
+ * @param array $messages
+ * @param string $format
+ * @param string $messageKey
+ * @return array
+ */
+ protected function transform($messages, $format, $messageKey)
+ {
+ return collect((array) $messages)
+ ->map(function ($message) use ($format, $messageKey) {
+ // We will simply spin through the given messages and transform each one
+ // replacing the :message place holder with the real message allowing
+ // the messages to be easily formatted to each developer's desires.
+ return str_replace([':message', ':key'], [$message, $messageKey], $format);
+ })->all();
+ }
+
+ /**
+ * Get the appropriate format based on the given format.
+ *
+ * @param string $format
+ * @return string
+ */
+ protected function checkFormat($format)
+ {
+ return $format ?: $this->format;
+ }
+
+ /**
+ * Get the raw messages in the message bag.
+ *
+ * @return array
+ */
+ public function messages()
+ {
+ return $this->messages;
+ }
+
+ /**
+ * Get the raw messages in the message bag.
+ *
+ * @return array
+ */
+ public function getMessages()
+ {
+ return $this->messages();
+ }
+
+ /**
+ * Get the messages for the instance.
+ *
+ * @return \Illuminate\Support\MessageBag
+ */
+ public function getMessageBag()
+ {
+ return $this;
+ }
+
+ /**
+ * Get the default message format.
+ *
+ * @return string
+ */
+ public function getFormat()
+ {
+ return $this->format;
+ }
+
+ /**
+ * Set the default message format.
+ *
+ * @param string $format
+ * @return \Illuminate\Support\MessageBag
+ */
+ public function setFormat($format = ':message')
+ {
+ $this->format = $format;
+
+ return $this;
+ }
+
+ /**
+ * Determine if the message bag has any messages.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return ! $this->any();
+ }
+
+ /**
+ * Determine if the message bag has any messages.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return $this->any();
+ }
+
+ /**
+ * Determine if the message bag has any messages.
+ *
+ * @return bool
+ */
+ public function any()
+ {
+ return $this->count() > 0;
+ }
+
+ /**
+ * Get the number of messages in the message bag.
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->messages, COUNT_RECURSIVE) - count($this->messages);
+ }
+
+ /**
+ * Get the instance as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->getMessages();
+ }
+
+ /**
+ * Convert the object into something JSON serializable.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Convert the object to its JSON representation.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->jsonSerialize(), $options);
+ }
+
+ /**
+ * Convert the message bag to its string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toJson();
+ }
+}
diff --git a/vendor/illuminate/support/MultipleInstanceManager.php b/vendor/illuminate/support/MultipleInstanceManager.php
new file mode 100644
index 0000000..97cee33
--- /dev/null
+++ b/vendor/illuminate/support/MultipleInstanceManager.php
@@ -0,0 +1,191 @@
+app = $app;
+ }
+
+ /**
+ * Get the default instance name.
+ *
+ * @return string
+ */
+ abstract public function getDefaultInstance();
+
+ /**
+ * Set the default instance name.
+ *
+ * @param string $name
+ * @return void
+ */
+ abstract public function setDefaultInstance($name);
+
+ /**
+ * Get the instance specific configuration.
+ *
+ * @param string $name
+ * @return array
+ */
+ abstract public function getInstanceConfig($name);
+
+ /**
+ * Get an instance instance by name.
+ *
+ * @param string|null $name
+ * @return mixed
+ */
+ public function instance($name = null)
+ {
+ $name = $name ?: $this->getDefaultInstance();
+
+ return $this->instances[$name] = $this->get($name);
+ }
+
+ /**
+ * Attempt to get an instance from the local cache.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ protected function get($name)
+ {
+ return $this->instances[$name] ?? $this->resolve($name);
+ }
+
+ /**
+ * Resolve the given instance.
+ *
+ * @param string $name
+ * @return mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function resolve($name)
+ {
+ $config = $this->getInstanceConfig($name);
+
+ if (is_null($config)) {
+ throw new InvalidArgumentException("Instance [{$name}] is not defined.");
+ }
+
+ if (! array_key_exists('driver', $config)) {
+ throw new RuntimeException("Instance [{$name}] does not specify a driver.");
+ }
+
+ if (isset($this->customCreators[$config['driver']])) {
+ return $this->callCustomCreator($config);
+ } else {
+ $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
+
+ if (method_exists($this, $driverMethod)) {
+ return $this->{$driverMethod}($config);
+ } else {
+ throw new InvalidArgumentException("Instance driver [{$config['driver']}] is not supported.");
+ }
+ }
+ }
+
+ /**
+ * Call a custom instance creator.
+ *
+ * @param array $config
+ * @return mixed
+ */
+ protected function callCustomCreator(array $config)
+ {
+ return $this->customCreators[$config['driver']]($this->app, $config);
+ }
+
+ /**
+ * Unset the given instances.
+ *
+ * @param array|string|null $name
+ * @return $this
+ */
+ public function forgetInstance($name = null)
+ {
+ $name = $name ?? $this->getDefaultInstance();
+
+ foreach ((array) $name as $instanceName) {
+ if (isset($this->instances[$instanceName])) {
+ unset($this->instances[$instanceName]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Disconnect the given instance and remove from local cache.
+ *
+ * @param string|null $name
+ * @return void
+ */
+ public function purge($name = null)
+ {
+ $name = $name ?? $this->getDefaultInstance();
+
+ unset($this->instances[$name]);
+ }
+
+ /**
+ * Register a custom instance creator Closure.
+ *
+ * @param string $name
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function extend($name, Closure $callback)
+ {
+ $this->customCreators[$name] = $callback->bindTo($this, $this);
+
+ return $this;
+ }
+
+ /**
+ * Dynamically call the default instance.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->instance()->$method(...$parameters);
+ }
+}
diff --git a/vendor/illuminate/support/NamespacedItemResolver.php b/vendor/illuminate/support/NamespacedItemResolver.php
new file mode 100755
index 0000000..a0d8508
--- /dev/null
+++ b/vendor/illuminate/support/NamespacedItemResolver.php
@@ -0,0 +1,112 @@
+parsed[$key])) {
+ return $this->parsed[$key];
+ }
+
+ // If the key does not contain a double colon, it means the key is not in a
+ // namespace, and is just a regular configuration item. Namespaces are a
+ // tool for organizing configuration items for things such as modules.
+ if (strpos($key, '::') === false) {
+ $segments = explode('.', $key);
+
+ $parsed = $this->parseBasicSegments($segments);
+ } else {
+ $parsed = $this->parseNamespacedSegments($key);
+ }
+
+ // Once we have the parsed array of this key's elements, such as its groups
+ // and namespace, we will cache each array inside a simple list that has
+ // the key and the parsed array for quick look-ups for later requests.
+ return $this->parsed[$key] = $parsed;
+ }
+
+ /**
+ * Parse an array of basic segments.
+ *
+ * @param array $segments
+ * @return array
+ */
+ protected function parseBasicSegments(array $segments)
+ {
+ // The first segment in a basic array will always be the group, so we can go
+ // ahead and grab that segment. If there is only one total segment we are
+ // just pulling an entire group out of the array and not a single item.
+ $group = $segments[0];
+
+ // If there is more than one segment in this group, it means we are pulling
+ // a specific item out of a group and will need to return this item name
+ // as well as the group so we know which item to pull from the arrays.
+ $item = count($segments) === 1
+ ? null
+ : implode('.', array_slice($segments, 1));
+
+ return [null, $group, $item];
+ }
+
+ /**
+ * Parse an array of namespaced segments.
+ *
+ * @param string $key
+ * @return array
+ */
+ protected function parseNamespacedSegments($key)
+ {
+ [$namespace, $item] = explode('::', $key);
+
+ // First we'll just explode the first segment to get the namespace and group
+ // since the item should be in the remaining segments. Once we have these
+ // two pieces of data we can proceed with parsing out the item's value.
+ $itemSegments = explode('.', $item);
+
+ $groupAndItem = array_slice(
+ $this->parseBasicSegments($itemSegments), 1
+ );
+
+ return array_merge([$namespace], $groupAndItem);
+ }
+
+ /**
+ * Set the parsed value of a key.
+ *
+ * @param string $key
+ * @param array $parsed
+ * @return void
+ */
+ public function setParsedKey($key, $parsed)
+ {
+ $this->parsed[$key] = $parsed;
+ }
+
+ /**
+ * Flush the cache of parsed keys.
+ *
+ * @return void
+ */
+ public function flushParsedKeys()
+ {
+ $this->parsed = [];
+ }
+}
diff --git a/vendor/illuminate/support/Optional.php b/vendor/illuminate/support/Optional.php
new file mode 100644
index 0000000..816190d
--- /dev/null
+++ b/vendor/illuminate/support/Optional.php
@@ -0,0 +1,135 @@
+value = $value;
+ }
+
+ /**
+ * Dynamically access a property on the underlying object.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ if (is_object($this->value)) {
+ return $this->value->{$key} ?? null;
+ }
+ }
+
+ /**
+ * Dynamically check a property exists on the underlying object.
+ *
+ * @param mixed $name
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ if (is_object($this->value)) {
+ return isset($this->value->{$name});
+ }
+
+ if (is_array($this->value) || $this->value instanceof ArrayObject) {
+ return isset($this->value[$name]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if an item exists at an offset.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return Arr::accessible($this->value) && Arr::exists($this->value, $key);
+ }
+
+ /**
+ * Get an item at a given offset.
+ *
+ * @param mixed $key
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return Arr::get($this->value, $key);
+ }
+
+ /**
+ * Set the item at a given offset.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ if (Arr::accessible($this->value)) {
+ $this->value[$key] = $value;
+ }
+ }
+
+ /**
+ * Unset the item at a given offset.
+ *
+ * @param string $key
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ if (Arr::accessible($this->value)) {
+ unset($this->value[$key]);
+ }
+ }
+
+ /**
+ * Dynamically pass a method to the underlying object.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (static::hasMacro($method)) {
+ return $this->macroCall($method, $parameters);
+ }
+
+ if (is_object($this->value)) {
+ return $this->value->{$method}(...$parameters);
+ }
+ }
+}
diff --git a/vendor/illuminate/support/Pluralizer.php b/vendor/illuminate/support/Pluralizer.php
new file mode 100755
index 0000000..fbe5182
--- /dev/null
+++ b/vendor/illuminate/support/Pluralizer.php
@@ -0,0 +1,141 @@
+pluralize($value);
+
+ return static::matchCase($plural, $value);
+ }
+
+ /**
+ * Get the singular form of an English word.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function singular($value)
+ {
+ $singular = static::inflector()->singularize($value);
+
+ return static::matchCase($singular, $value);
+ }
+
+ /**
+ * Determine if the given value is uncountable.
+ *
+ * @param string $value
+ * @return bool
+ */
+ protected static function uncountable($value)
+ {
+ return in_array(strtolower($value), static::$uncountable);
+ }
+
+ /**
+ * Attempt to match the case on two strings.
+ *
+ * @param string $value
+ * @param string $comparison
+ * @return string
+ */
+ protected static function matchCase($value, $comparison)
+ {
+ $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];
+
+ foreach ($functions as $function) {
+ if ($function($comparison) === $comparison) {
+ return $function($value);
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get the inflector instance.
+ *
+ * @return \Doctrine\Inflector\Inflector
+ */
+ public static function inflector()
+ {
+ static $inflector;
+
+ if (is_null($inflector)) {
+ $inflector = InflectorFactory::createForLanguage('english')->build();
+ }
+
+ return $inflector;
+ }
+}
diff --git a/vendor/illuminate/support/ProcessUtils.php b/vendor/illuminate/support/ProcessUtils.php
new file mode 100644
index 0000000..1caa9e1
--- /dev/null
+++ b/vendor/illuminate/support/ProcessUtils.php
@@ -0,0 +1,69 @@
+isPublic();
+ }
+
+ if (is_object($var[0]) && method_exists($class, '__call')) {
+ return (new ReflectionMethod($class, '__call'))->isPublic();
+ }
+
+ if (! is_object($var[0]) && method_exists($class, '__callStatic')) {
+ return (new ReflectionMethod($class, '__callStatic'))->isPublic();
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the class name of the given parameter's type, if possible.
+ *
+ * @param \ReflectionParameter $parameter
+ * @return string|null
+ */
+ public static function getParameterClassName($parameter)
+ {
+ $type = $parameter->getType();
+
+ if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
+ return;
+ }
+
+ return static::getTypeName($parameter, $type);
+ }
+
+ /**
+ * Get the class names of the given parameter's type, including union types.
+ *
+ * @param \ReflectionParameter $parameter
+ * @return array
+ */
+ public static function getParameterClassNames($parameter)
+ {
+ $type = $parameter->getType();
+
+ if (! $type instanceof ReflectionUnionType) {
+ return array_filter([static::getParameterClassName($parameter)]);
+ }
+
+ $unionTypes = [];
+
+ foreach ($type->getTypes() as $listedType) {
+ if (! $listedType instanceof ReflectionNamedType || $listedType->isBuiltin()) {
+ continue;
+ }
+
+ $unionTypes[] = static::getTypeName($parameter, $listedType);
+ }
+
+ return array_filter($unionTypes);
+ }
+
+ /**
+ * Get the given type's class name.
+ *
+ * @param \ReflectionParameter $parameter
+ * @param \ReflectionNamedType $type
+ * @return string
+ */
+ protected static function getTypeName($parameter, $type)
+ {
+ $name = $type->getName();
+
+ if (! is_null($class = $parameter->getDeclaringClass())) {
+ if ($name === 'self') {
+ return $class->getName();
+ }
+
+ if ($name === 'parent' && $parent = $class->getParentClass()) {
+ return $parent->getName();
+ }
+ }
+
+ return $name;
+ }
+
+ /**
+ * Determine if the parameter's type is a subclass of the given type.
+ *
+ * @param \ReflectionParameter $parameter
+ * @param string $className
+ * @return bool
+ */
+ public static function isParameterSubclassOf($parameter, $className)
+ {
+ $paramClassName = static::getParameterClassName($parameter);
+
+ return $paramClassName
+ && (class_exists($paramClassName) || interface_exists($paramClassName))
+ && (new ReflectionClass($paramClassName))->isSubclassOf($className);
+ }
+}
diff --git a/vendor/illuminate/support/ServiceProvider.php b/vendor/illuminate/support/ServiceProvider.php
new file mode 100755
index 0000000..6c530c1
--- /dev/null
+++ b/vendor/illuminate/support/ServiceProvider.php
@@ -0,0 +1,437 @@
+app = $app;
+ }
+
+ /**
+ * Register any application services.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ //
+ }
+
+ /**
+ * Register a booting callback to be run before the "boot" method is called.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public function booting(Closure $callback)
+ {
+ $this->bootingCallbacks[] = $callback;
+ }
+
+ /**
+ * Register a booted callback to be run after the "boot" method is called.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public function booted(Closure $callback)
+ {
+ $this->bootedCallbacks[] = $callback;
+ }
+
+ /**
+ * Call the registered booting callbacks.
+ *
+ * @return void
+ */
+ public function callBootingCallbacks()
+ {
+ $index = 0;
+
+ while ($index < count($this->bootingCallbacks)) {
+ $this->app->call($this->bootingCallbacks[$index]);
+
+ $index++;
+ }
+ }
+
+ /**
+ * Call the registered booted callbacks.
+ *
+ * @return void
+ */
+ public function callBootedCallbacks()
+ {
+ $index = 0;
+
+ while ($index < count($this->bootedCallbacks)) {
+ $this->app->call($this->bootedCallbacks[$index]);
+
+ $index++;
+ }
+ }
+
+ /**
+ * Merge the given configuration with the existing configuration.
+ *
+ * @param string $path
+ * @param string $key
+ * @return void
+ */
+ protected function mergeConfigFrom($path, $key)
+ {
+ if (! ($this->app instanceof CachesConfiguration && $this->app->configurationIsCached())) {
+ $config = $this->app->make('config');
+
+ $config->set($key, array_merge(
+ require $path, $config->get($key, [])
+ ));
+ }
+ }
+
+ /**
+ * Load the given routes file if routes are not already cached.
+ *
+ * @param string $path
+ * @return void
+ */
+ protected function loadRoutesFrom($path)
+ {
+ if (! ($this->app instanceof CachesRoutes && $this->app->routesAreCached())) {
+ require $path;
+ }
+ }
+
+ /**
+ * Register a view file namespace.
+ *
+ * @param string|array $path
+ * @param string $namespace
+ * @return void
+ */
+ protected function loadViewsFrom($path, $namespace)
+ {
+ $this->callAfterResolving('view', function ($view) use ($path, $namespace) {
+ if (isset($this->app->config['view']['paths']) &&
+ is_array($this->app->config['view']['paths'])) {
+ foreach ($this->app->config['view']['paths'] as $viewPath) {
+ if (is_dir($appPath = $viewPath.'/vendor/'.$namespace)) {
+ $view->addNamespace($namespace, $appPath);
+ }
+ }
+ }
+
+ $view->addNamespace($namespace, $path);
+ });
+ }
+
+ /**
+ * Register the given view components with a custom prefix.
+ *
+ * @param string $prefix
+ * @param array $components
+ * @return void
+ */
+ protected function loadViewComponentsAs($prefix, array $components)
+ {
+ $this->callAfterResolving(BladeCompiler::class, function ($blade) use ($prefix, $components) {
+ foreach ($components as $alias => $component) {
+ $blade->component($component, is_string($alias) ? $alias : null, $prefix);
+ }
+ });
+ }
+
+ /**
+ * Register a translation file namespace.
+ *
+ * @param string $path
+ * @param string $namespace
+ * @return void
+ */
+ protected function loadTranslationsFrom($path, $namespace)
+ {
+ $this->callAfterResolving('translator', function ($translator) use ($path, $namespace) {
+ $translator->addNamespace($namespace, $path);
+ });
+ }
+
+ /**
+ * Register a JSON translation file path.
+ *
+ * @param string $path
+ * @return void
+ */
+ protected function loadJsonTranslationsFrom($path)
+ {
+ $this->callAfterResolving('translator', function ($translator) use ($path) {
+ $translator->addJsonPath($path);
+ });
+ }
+
+ /**
+ * Register database migration paths.
+ *
+ * @param array|string $paths
+ * @return void
+ */
+ protected function loadMigrationsFrom($paths)
+ {
+ $this->callAfterResolving('migrator', function ($migrator) use ($paths) {
+ foreach ((array) $paths as $path) {
+ $migrator->path($path);
+ }
+ });
+ }
+
+ /**
+ * Register Eloquent model factory paths.
+ *
+ * @deprecated Will be removed in a future Laravel version.
+ *
+ * @param array|string $paths
+ * @return void
+ */
+ protected function loadFactoriesFrom($paths)
+ {
+ $this->callAfterResolving(ModelFactory::class, function ($factory) use ($paths) {
+ foreach ((array) $paths as $path) {
+ $factory->load($path);
+ }
+ });
+ }
+
+ /**
+ * Setup an after resolving listener, or fire immediately if already resolved.
+ *
+ * @param string $name
+ * @param callable $callback
+ * @return void
+ */
+ protected function callAfterResolving($name, $callback)
+ {
+ $this->app->afterResolving($name, $callback);
+
+ if ($this->app->resolved($name)) {
+ $callback($this->app->make($name), $this->app);
+ }
+ }
+
+ /**
+ * Register paths to be published by the publish command.
+ *
+ * @param array $paths
+ * @param mixed $groups
+ * @return void
+ */
+ protected function publishes(array $paths, $groups = null)
+ {
+ $this->ensurePublishArrayInitialized($class = static::class);
+
+ static::$publishes[$class] = array_merge(static::$publishes[$class], $paths);
+
+ foreach ((array) $groups as $group) {
+ $this->addPublishGroup($group, $paths);
+ }
+ }
+
+ /**
+ * Ensure the publish array for the service provider is initialized.
+ *
+ * @param string $class
+ * @return void
+ */
+ protected function ensurePublishArrayInitialized($class)
+ {
+ if (! array_key_exists($class, static::$publishes)) {
+ static::$publishes[$class] = [];
+ }
+ }
+
+ /**
+ * Add a publish group / tag to the service provider.
+ *
+ * @param string $group
+ * @param array $paths
+ * @return void
+ */
+ protected function addPublishGroup($group, $paths)
+ {
+ if (! array_key_exists($group, static::$publishGroups)) {
+ static::$publishGroups[$group] = [];
+ }
+
+ static::$publishGroups[$group] = array_merge(
+ static::$publishGroups[$group], $paths
+ );
+ }
+
+ /**
+ * Get the paths to publish.
+ *
+ * @param string|null $provider
+ * @param string|null $group
+ * @return array
+ */
+ public static function pathsToPublish($provider = null, $group = null)
+ {
+ if (! is_null($paths = static::pathsForProviderOrGroup($provider, $group))) {
+ return $paths;
+ }
+
+ return collect(static::$publishes)->reduce(function ($paths, $p) {
+ return array_merge($paths, $p);
+ }, []);
+ }
+
+ /**
+ * Get the paths for the provider or group (or both).
+ *
+ * @param string|null $provider
+ * @param string|null $group
+ * @return array
+ */
+ protected static function pathsForProviderOrGroup($provider, $group)
+ {
+ if ($provider && $group) {
+ return static::pathsForProviderAndGroup($provider, $group);
+ } elseif ($group && array_key_exists($group, static::$publishGroups)) {
+ return static::$publishGroups[$group];
+ } elseif ($provider && array_key_exists($provider, static::$publishes)) {
+ return static::$publishes[$provider];
+ } elseif ($group || $provider) {
+ return [];
+ }
+ }
+
+ /**
+ * Get the paths for the provider and group.
+ *
+ * @param string $provider
+ * @param string $group
+ * @return array
+ */
+ protected static function pathsForProviderAndGroup($provider, $group)
+ {
+ if (! empty(static::$publishes[$provider]) && ! empty(static::$publishGroups[$group])) {
+ return array_intersect_key(static::$publishes[$provider], static::$publishGroups[$group]);
+ }
+
+ return [];
+ }
+
+ /**
+ * Get the service providers available for publishing.
+ *
+ * @return array
+ */
+ public static function publishableProviders()
+ {
+ return array_keys(static::$publishes);
+ }
+
+ /**
+ * Get the groups available for publishing.
+ *
+ * @return array
+ */
+ public static function publishableGroups()
+ {
+ return array_keys(static::$publishGroups);
+ }
+
+ /**
+ * Register the package's custom Artisan commands.
+ *
+ * @param array|mixed $commands
+ * @return void
+ */
+ public function commands($commands)
+ {
+ $commands = is_array($commands) ? $commands : func_get_args();
+
+ Artisan::starting(function ($artisan) use ($commands) {
+ $artisan->resolveCommands($commands);
+ });
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return [];
+ }
+
+ /**
+ * Get the events that trigger this service provider to register.
+ *
+ * @return array
+ */
+ public function when()
+ {
+ return [];
+ }
+
+ /**
+ * Determine if the provider is deferred.
+ *
+ * @return bool
+ */
+ public function isDeferred()
+ {
+ return $this instanceof DeferrableProvider;
+ }
+}
diff --git a/vendor/illuminate/support/Str.php b/vendor/illuminate/support/Str.php
new file mode 100644
index 0000000..21e1904
--- /dev/null
+++ b/vendor/illuminate/support/Str.php
@@ -0,0 +1,1033 @@
+ 0;
+ }
+
+ /**
+ * Convert a string to kebab case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function kebab($value)
+ {
+ return static::snake($value, '-');
+ }
+
+ /**
+ * Return the length of the given string.
+ *
+ * @param string $value
+ * @param string|null $encoding
+ * @return int
+ */
+ public static function length($value, $encoding = null)
+ {
+ if ($encoding) {
+ return mb_strlen($value, $encoding);
+ }
+
+ return mb_strlen($value);
+ }
+
+ /**
+ * Limit the number of characters in a string.
+ *
+ * @param string $value
+ * @param int $limit
+ * @param string $end
+ * @return string
+ */
+ public static function limit($value, $limit = 100, $end = '...')
+ {
+ if (mb_strwidth($value, 'UTF-8') <= $limit) {
+ return $value;
+ }
+
+ return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
+ }
+
+ /**
+ * Convert the given string to lower-case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function lower($value)
+ {
+ return mb_strtolower($value, 'UTF-8');
+ }
+
+ /**
+ * Limit the number of words in a string.
+ *
+ * @param string $value
+ * @param int $words
+ * @param string $end
+ * @return string
+ */
+ public static function words($value, $words = 100, $end = '...')
+ {
+ preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
+
+ if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
+ return $value;
+ }
+
+ return rtrim($matches[0]).$end;
+ }
+
+ /**
+ * Converts GitHub flavored Markdown into HTML.
+ *
+ * @param string $string
+ * @param array $options
+ * @return string
+ */
+ public static function markdown($string, array $options = [])
+ {
+ $converter = new GithubFlavoredMarkdownConverter($options);
+
+ return (string) $converter->convertToHtml($string);
+ }
+
+ /**
+ * Masks a portion of a string with a repeated character.
+ *
+ * @param string $string
+ * @param string $character
+ * @param int $index
+ * @param int|null $length
+ * @param string $encoding
+ * @return string
+ */
+ public static function mask($string, $character, $index, $length = null, $encoding = 'UTF-8')
+ {
+ if ($character === '') {
+ return $string;
+ }
+
+ if (is_null($length) && PHP_MAJOR_VERSION < 8) {
+ $length = mb_strlen($string, $encoding);
+ }
+
+ $segment = mb_substr($string, $index, $length, $encoding);
+
+ if ($segment === '') {
+ return $string;
+ }
+
+ $strlen = mb_strlen($string, $encoding);
+ $startIndex = $index;
+
+ if ($index < 0) {
+ $startIndex = $index < -$strlen ? 0 : $strlen + $index;
+ }
+
+ $start = mb_substr($string, 0, $startIndex, $encoding);
+ $segmentLen = mb_strlen($segment, $encoding);
+ $end = mb_substr($string, $startIndex + $segmentLen);
+
+ return $start.str_repeat(mb_substr($character, 0, 1, $encoding), $segmentLen).$end;
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @param string $subject
+ * @return string
+ */
+ public static function match($pattern, $subject)
+ {
+ preg_match($pattern, $subject, $matches);
+
+ if (! $matches) {
+ return '';
+ }
+
+ return $matches[1] ?? $matches[0];
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @param string $subject
+ * @return \Illuminate\Support\Collection
+ */
+ public static function matchAll($pattern, $subject)
+ {
+ preg_match_all($pattern, $subject, $matches);
+
+ if (empty($matches[0])) {
+ return collect();
+ }
+
+ return collect($matches[1] ?? $matches[0]);
+ }
+
+ /**
+ * Pad both sides of a string with another.
+ *
+ * @param string $value
+ * @param int $length
+ * @param string $pad
+ * @return string
+ */
+ public static function padBoth($value, $length, $pad = ' ')
+ {
+ return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_BOTH);
+ }
+
+ /**
+ * Pad the left side of a string with another.
+ *
+ * @param string $value
+ * @param int $length
+ * @param string $pad
+ * @return string
+ */
+ public static function padLeft($value, $length, $pad = ' ')
+ {
+ return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_LEFT);
+ }
+
+ /**
+ * Pad the right side of a string with another.
+ *
+ * @param string $value
+ * @param int $length
+ * @param string $pad
+ * @return string
+ */
+ public static function padRight($value, $length, $pad = ' ')
+ {
+ return str_pad($value, strlen($value) - mb_strlen($value) + $length, $pad, STR_PAD_RIGHT);
+ }
+
+ /**
+ * Parse a Class[@]method style callback into class and method.
+ *
+ * @param string $callback
+ * @param string|null $default
+ * @return array
+ */
+ public static function parseCallback($callback, $default = null)
+ {
+ return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
+ }
+
+ /**
+ * Get the plural form of an English word.
+ *
+ * @param string $value
+ * @param int|array|\Countable $count
+ * @return string
+ */
+ public static function plural($value, $count = 2)
+ {
+ return Pluralizer::plural($value, $count);
+ }
+
+ /**
+ * Pluralize the last word of an English, studly caps case string.
+ *
+ * @param string $value
+ * @param int|array|\Countable $count
+ * @return string
+ */
+ public static function pluralStudly($value, $count = 2)
+ {
+ $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ $lastWord = array_pop($parts);
+
+ return implode('', $parts).self::plural($lastWord, $count);
+ }
+
+ /**
+ * Generate a more truly "random" alpha-numeric string.
+ *
+ * @param int $length
+ * @return string
+ */
+ public static function random($length = 16)
+ {
+ $string = '';
+
+ while (($len = strlen($string)) < $length) {
+ $size = $length - $len;
+
+ $bytes = random_bytes($size);
+
+ $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Repeat the given string.
+ *
+ * @param string $string
+ * @param int $times
+ * @return string
+ */
+ public static function repeat(string $string, int $times)
+ {
+ return str_repeat($string, $times);
+ }
+
+ /**
+ * Replace a given value in the string sequentially with an array.
+ *
+ * @param string $search
+ * @param array $replace
+ * @param string $subject
+ * @return string
+ */
+ public static function replaceArray($search, array $replace, $subject)
+ {
+ $segments = explode($search, $subject);
+
+ $result = array_shift($segments);
+
+ foreach ($segments as $segment) {
+ $result .= (array_shift($replace) ?? $search).$segment;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Replace the given value in the given string.
+ *
+ * @param string|string[] $search
+ * @param string|string[] $replace
+ * @param string|string[] $subject
+ * @return string
+ */
+ public static function replace($search, $replace, $subject)
+ {
+ return str_replace($search, $replace, $subject);
+ }
+
+ /**
+ * Replace the first occurrence of a given value in the string.
+ *
+ * @param string $search
+ * @param string $replace
+ * @param string $subject
+ * @return string
+ */
+ public static function replaceFirst($search, $replace, $subject)
+ {
+ if ($search === '') {
+ return $subject;
+ }
+
+ $position = strpos($subject, $search);
+
+ if ($position !== false) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Replace the last occurrence of a given value in the string.
+ *
+ * @param string $search
+ * @param string $replace
+ * @param string $subject
+ * @return string
+ */
+ public static function replaceLast($search, $replace, $subject)
+ {
+ if ($search === '') {
+ return $subject;
+ }
+
+ $position = strrpos($subject, $search);
+
+ if ($position !== false) {
+ return substr_replace($subject, $replace, $position, strlen($search));
+ }
+
+ return $subject;
+ }
+
+ /**
+ * Remove any occurrence of the given string in the subject.
+ *
+ * @param string|array $search
+ * @param string $subject
+ * @param bool $caseSensitive
+ * @return string
+ */
+ public static function remove($search, $subject, $caseSensitive = true)
+ {
+ $subject = $caseSensitive
+ ? str_replace($search, '', $subject)
+ : str_ireplace($search, '', $subject);
+
+ return $subject;
+ }
+
+ /**
+ * Reverse the given string.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function reverse(string $value)
+ {
+ return implode(array_reverse(mb_str_split($value)));
+ }
+
+ /**
+ * Begin a string with a single instance of a given value.
+ *
+ * @param string $value
+ * @param string $prefix
+ * @return string
+ */
+ public static function start($value, $prefix)
+ {
+ $quoted = preg_quote($prefix, '/');
+
+ return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
+ }
+
+ /**
+ * Convert the given string to upper-case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function upper($value)
+ {
+ return mb_strtoupper($value, 'UTF-8');
+ }
+
+ /**
+ * Convert the given string to title case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function title($value)
+ {
+ return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
+ }
+
+ /**
+ * Convert the given string to title case for each word.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function headline($value)
+ {
+ $parts = explode(' ', $value);
+
+ $parts = count($parts) > 1
+ ? $parts = array_map([static::class, 'title'], $parts)
+ : $parts = array_map([static::class, 'title'], static::ucsplit(implode('_', $parts)));
+
+ $collapsed = static::replace(['-', '_', ' '], '_', implode('_', $parts));
+
+ return implode(' ', array_filter(explode('_', $collapsed)));
+ }
+
+ /**
+ * Get the singular form of an English word.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function singular($value)
+ {
+ return Pluralizer::singular($value);
+ }
+
+ /**
+ * Generate a URL friendly "slug" from a given string.
+ *
+ * @param string $title
+ * @param string $separator
+ * @param string|null $language
+ * @return string
+ */
+ public static function slug($title, $separator = '-', $language = 'en')
+ {
+ $title = $language ? static::ascii($title, $language) : $title;
+
+ // Convert all dashes/underscores into separator
+ $flip = $separator === '-' ? '_' : '-';
+
+ $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
+
+ // Replace @ with the word 'at'
+ $title = str_replace('@', $separator.'at'.$separator, $title);
+
+ // Remove all characters that are not the separator, letters, numbers, or whitespace.
+ $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', static::lower($title));
+
+ // Replace all separator characters and whitespace by a single separator
+ $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
+
+ return trim($title, $separator);
+ }
+
+ /**
+ * Convert a string to snake case.
+ *
+ * @param string $value
+ * @param string $delimiter
+ * @return string
+ */
+ public static function snake($value, $delimiter = '_')
+ {
+ $key = $value;
+
+ if (isset(static::$snakeCache[$key][$delimiter])) {
+ return static::$snakeCache[$key][$delimiter];
+ }
+
+ if (! ctype_lower($value)) {
+ $value = preg_replace('/\s+/u', '', ucwords($value));
+
+ $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
+ }
+
+ return static::$snakeCache[$key][$delimiter] = $value;
+ }
+
+ /**
+ * Determine if a given string starts with a given substring.
+ *
+ * @param string $haystack
+ * @param string|string[] $needles
+ * @return bool
+ */
+ public static function startsWith($haystack, $needles)
+ {
+ foreach ((array) $needles as $needle) {
+ if ((string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a value to studly caps case.
+ *
+ * @param string $value
+ * @return string
+ */
+ public static function studly($value)
+ {
+ $key = $value;
+
+ if (isset(static::$studlyCache[$key])) {
+ return static::$studlyCache[$key];
+ }
+
+ $words = explode(' ', static::replace(['-', '_'], ' ', $value));
+
+ $studlyWords = array_map(function ($word) {
+ return static::ucfirst($word);
+ }, $words);
+
+ return static::$studlyCache[$key] = implode($studlyWords);
+ }
+
+ /**
+ * Returns the portion of the string specified by the start and length parameters.
+ *
+ * @param string $string
+ * @param int $start
+ * @param int|null $length
+ * @return string
+ */
+ public static function substr($string, $start, $length = null)
+ {
+ return mb_substr($string, $start, $length, 'UTF-8');
+ }
+
+ /**
+ * Returns the number of substring occurrences.
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @param int $offset
+ * @param int|null $length
+ * @return int
+ */
+ public static function substrCount($haystack, $needle, $offset = 0, $length = null)
+ {
+ if (! is_null($length)) {
+ return substr_count($haystack, $needle, $offset, $length);
+ } else {
+ return substr_count($haystack, $needle, $offset);
+ }
+ }
+
+ /**
+ * Replace text within a portion of a string.
+ *
+ * @param string|array $string
+ * @param string|array $replace
+ * @param array|int $offset
+ * @param array|int|null $length
+ * @return string|array
+ */
+ public static function substrReplace($string, $replace, $offset = 0, $length = null)
+ {
+ if ($length === null) {
+ $length = strlen($string);
+ }
+
+ return substr_replace($string, $replace, $offset, $length);
+ }
+
+ /**
+ * Swap multiple keywords in a string with other keywords.
+ *
+ * @param array $map
+ * @param string $subject
+ * @return string
+ */
+ public static function swap(array $map, $subject)
+ {
+ return strtr($subject, $map);
+ }
+
+ /**
+ * Make a string's first character uppercase.
+ *
+ * @param string $string
+ * @return string
+ */
+ public static function ucfirst($string)
+ {
+ return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
+ }
+
+ /**
+ * Split a string into pieces by uppercase characters.
+ *
+ * @param string $string
+ * @return array
+ */
+ public static function ucsplit($string)
+ {
+ return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ /**
+ * Get the number of words a string contains.
+ *
+ * @param string $string
+ * @return int
+ */
+ public static function wordCount($string)
+ {
+ return str_word_count($string);
+ }
+
+ /**
+ * Generate a UUID (version 4).
+ *
+ * @return \Ramsey\Uuid\UuidInterface
+ */
+ public static function uuid()
+ {
+ return static::$uuidFactory
+ ? call_user_func(static::$uuidFactory)
+ : Uuid::uuid4();
+ }
+
+ /**
+ * Generate a time-ordered UUID (version 4).
+ *
+ * @return \Ramsey\Uuid\UuidInterface
+ */
+ public static function orderedUuid()
+ {
+ if (static::$uuidFactory) {
+ return call_user_func(static::$uuidFactory);
+ }
+
+ $factory = new UuidFactory;
+
+ $factory->setRandomGenerator(new CombGenerator(
+ $factory->getRandomGenerator(),
+ $factory->getNumberConverter()
+ ));
+
+ $factory->setCodec(new TimestampFirstCombCodec(
+ $factory->getUuidBuilder()
+ ));
+
+ return $factory->uuid4();
+ }
+
+ /**
+ * Set the callable that will be used to generate UUIDs.
+ *
+ * @param callable|null $factory
+ * @return void
+ */
+ public static function createUuidsUsing(callable $factory = null)
+ {
+ static::$uuidFactory = $factory;
+ }
+
+ /**
+ * Indicate that UUIDs should be created normally and not using a custom factory.
+ *
+ * @return void
+ */
+ public static function createUuidsNormally()
+ {
+ static::$uuidFactory = null;
+ }
+
+ /**
+ * Remove all strings from the casing caches.
+ *
+ * @return void
+ */
+ public static function flushCache()
+ {
+ static::$snakeCache = [];
+ static::$camelCache = [];
+ static::$studlyCache = [];
+ }
+}
diff --git a/vendor/illuminate/support/Stringable.php b/vendor/illuminate/support/Stringable.php
new file mode 100644
index 0000000..414be0c
--- /dev/null
+++ b/vendor/illuminate/support/Stringable.php
@@ -0,0 +1,1026 @@
+value = (string) $value;
+ }
+
+ /**
+ * Return the remainder of a string after the first occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function after($search)
+ {
+ return new static(Str::after($this->value, $search));
+ }
+
+ /**
+ * Return the remainder of a string after the last occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function afterLast($search)
+ {
+ return new static(Str::afterLast($this->value, $search));
+ }
+
+ /**
+ * Append the given values to the string.
+ *
+ * @param array $values
+ * @return static
+ */
+ public function append(...$values)
+ {
+ return new static($this->value.implode('', $values));
+ }
+
+ /**
+ * Transliterate a UTF-8 value to ASCII.
+ *
+ * @param string $language
+ * @return static
+ */
+ public function ascii($language = 'en')
+ {
+ return new static(Str::ascii($this->value, $language));
+ }
+
+ /**
+ * Get the trailing name component of the path.
+ *
+ * @param string $suffix
+ * @return static
+ */
+ public function basename($suffix = '')
+ {
+ return new static(basename($this->value, $suffix));
+ }
+
+ /**
+ * Get the basename of the class path.
+ *
+ * @return static
+ */
+ public function classBasename()
+ {
+ return new static(class_basename($this->value));
+ }
+
+ /**
+ * Get the portion of a string before the first occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function before($search)
+ {
+ return new static(Str::before($this->value, $search));
+ }
+
+ /**
+ * Get the portion of a string before the last occurrence of a given value.
+ *
+ * @param string $search
+ * @return static
+ */
+ public function beforeLast($search)
+ {
+ return new static(Str::beforeLast($this->value, $search));
+ }
+
+ /**
+ * Get the portion of a string between two given values.
+ *
+ * @param string $from
+ * @param string $to
+ * @return static
+ */
+ public function between($from, $to)
+ {
+ return new static(Str::between($this->value, $from, $to));
+ }
+
+ /**
+ * Convert a value to camel case.
+ *
+ * @return static
+ */
+ public function camel()
+ {
+ return new static(Str::camel($this->value));
+ }
+
+ /**
+ * Determine if a given string contains a given substring.
+ *
+ * @param string|array $needles
+ * @return bool
+ */
+ public function contains($needles)
+ {
+ return Str::contains($this->value, $needles);
+ }
+
+ /**
+ * Determine if a given string contains all array values.
+ *
+ * @param array $needles
+ * @return bool
+ */
+ public function containsAll(array $needles)
+ {
+ return Str::containsAll($this->value, $needles);
+ }
+
+ /**
+ * Get the parent directory's path.
+ *
+ * @param int $levels
+ * @return static
+ */
+ public function dirname($levels = 1)
+ {
+ return new static(dirname($this->value, $levels));
+ }
+
+ /**
+ * Determine if a given string ends with a given substring.
+ *
+ * @param string|array $needles
+ * @return bool
+ */
+ public function endsWith($needles)
+ {
+ return Str::endsWith($this->value, $needles);
+ }
+
+ /**
+ * Determine if the string is an exact match with the given value.
+ *
+ * @param string $value
+ * @return bool
+ */
+ public function exactly($value)
+ {
+ return $this->value === $value;
+ }
+
+ /**
+ * Explode the string into an array.
+ *
+ * @param string $delimiter
+ * @param int $limit
+ * @return \Illuminate\Support\Collection
+ */
+ public function explode($delimiter, $limit = PHP_INT_MAX)
+ {
+ return collect(explode($delimiter, $this->value, $limit));
+ }
+
+ /**
+ * Split a string using a regular expression or by length.
+ *
+ * @param string|int $pattern
+ * @param int $limit
+ * @param int $flags
+ * @return \Illuminate\Support\Collection
+ */
+ public function split($pattern, $limit = -1, $flags = 0)
+ {
+ if (filter_var($pattern, FILTER_VALIDATE_INT) !== false) {
+ return collect(mb_str_split($this->value, $pattern));
+ }
+
+ $segments = preg_split($pattern, $this->value, $limit, $flags);
+
+ return ! empty($segments) ? collect($segments) : collect();
+ }
+
+ /**
+ * Cap a string with a single instance of a given value.
+ *
+ * @param string $cap
+ * @return static
+ */
+ public function finish($cap)
+ {
+ return new static(Str::finish($this->value, $cap));
+ }
+
+ /**
+ * Determine if a given string matches a given pattern.
+ *
+ * @param string|array $pattern
+ * @return bool
+ */
+ public function is($pattern)
+ {
+ return Str::is($pattern, $this->value);
+ }
+
+ /**
+ * Determine if a given string is 7 bit ASCII.
+ *
+ * @return bool
+ */
+ public function isAscii()
+ {
+ return Str::isAscii($this->value);
+ }
+
+ /**
+ * Determine if a given string is a valid UUID.
+ *
+ * @return bool
+ */
+ public function isUuid()
+ {
+ return Str::isUuid($this->value);
+ }
+
+ /**
+ * Determine if the given string is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->value === '';
+ }
+
+ /**
+ * Determine if the given string is not empty.
+ *
+ * @return bool
+ */
+ public function isNotEmpty()
+ {
+ return ! $this->isEmpty();
+ }
+
+ /**
+ * Convert a string to kebab case.
+ *
+ * @return static
+ */
+ public function kebab()
+ {
+ return new static(Str::kebab($this->value));
+ }
+
+ /**
+ * Return the length of the given string.
+ *
+ * @param string $encoding
+ * @return int
+ */
+ public function length($encoding = null)
+ {
+ return Str::length($this->value, $encoding);
+ }
+
+ /**
+ * Limit the number of characters in a string.
+ *
+ * @param int $limit
+ * @param string $end
+ * @return static
+ */
+ public function limit($limit = 100, $end = '...')
+ {
+ return new static(Str::limit($this->value, $limit, $end));
+ }
+
+ /**
+ * Convert the given string to lower-case.
+ *
+ * @return static
+ */
+ public function lower()
+ {
+ return new static(Str::lower($this->value));
+ }
+
+ /**
+ * Convert GitHub flavored Markdown into HTML.
+ *
+ * @param array $options
+ * @return static
+ */
+ public function markdown(array $options = [])
+ {
+ return new static(Str::markdown($this->value, $options));
+ }
+
+ /**
+ * Masks a portion of a string with a repeated character.
+ *
+ * @param string $character
+ * @param int $index
+ * @param int|null $length
+ * @param string $encoding
+ * @return static
+ */
+ public function mask($character, $index, $length = null, $encoding = 'UTF-8')
+ {
+ return new static(Str::mask($this->value, $character, $index, $length, $encoding));
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @return static
+ */
+ public function match($pattern)
+ {
+ return new static(Str::match($pattern, $this->value));
+ }
+
+ /**
+ * Get the string matching the given pattern.
+ *
+ * @param string $pattern
+ * @return \Illuminate\Support\Collection
+ */
+ public function matchAll($pattern)
+ {
+ return Str::matchAll($pattern, $this->value);
+ }
+
+ /**
+ * Determine if the string matches the given pattern.
+ *
+ * @param string $pattern
+ * @return bool
+ */
+ public function test($pattern)
+ {
+ return $this->match($pattern)->isNotEmpty();
+ }
+
+ /**
+ * Pad both sides of the string with another.
+ *
+ * @param int $length
+ * @param string $pad
+ * @return static
+ */
+ public function padBoth($length, $pad = ' ')
+ {
+ return new static(Str::padBoth($this->value, $length, $pad));
+ }
+
+ /**
+ * Pad the left side of the string with another.
+ *
+ * @param int $length
+ * @param string $pad
+ * @return static
+ */
+ public function padLeft($length, $pad = ' ')
+ {
+ return new static(Str::padLeft($this->value, $length, $pad));
+ }
+
+ /**
+ * Pad the right side of the string with another.
+ *
+ * @param int $length
+ * @param string $pad
+ * @return static
+ */
+ public function padRight($length, $pad = ' ')
+ {
+ return new static(Str::padRight($this->value, $length, $pad));
+ }
+
+ /**
+ * Parse a Class@method style callback into class and method.
+ *
+ * @param string|null $default
+ * @return array
+ */
+ public function parseCallback($default = null)
+ {
+ return Str::parseCallback($this->value, $default);
+ }
+
+ /**
+ * Call the given callback and return a new string.
+ *
+ * @param callable $callback
+ * @return static
+ */
+ public function pipe(callable $callback)
+ {
+ return new static(call_user_func($callback, $this));
+ }
+
+ /**
+ * Get the plural form of an English word.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function plural($count = 2)
+ {
+ return new static(Str::plural($this->value, $count));
+ }
+
+ /**
+ * Pluralize the last word of an English, studly caps case string.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function pluralStudly($count = 2)
+ {
+ return new static(Str::pluralStudly($this->value, $count));
+ }
+
+ /**
+ * Prepend the given values to the string.
+ *
+ * @param array $values
+ * @return static
+ */
+ public function prepend(...$values)
+ {
+ return new static(implode('', $values).$this->value);
+ }
+
+ /**
+ * Remove any occurrence of the given string in the subject.
+ *
+ * @param string|array $search
+ * @param bool $caseSensitive
+ * @return static
+ */
+ public function remove($search, $caseSensitive = true)
+ {
+ return new static(Str::remove($search, $this->value, $caseSensitive));
+ }
+
+ /**
+ * Reverse the string.
+ *
+ * @return static
+ */
+ public function reverse()
+ {
+ return new static(Str::reverse($this->value));
+ }
+
+ /**
+ * Repeat the string.
+ *
+ * @param int $times
+ * @return static
+ */
+ public function repeat(int $times)
+ {
+ return new static(Str::repeat($this->value, $times));
+ }
+
+ /**
+ * Replace the given value in the given string.
+ *
+ * @param string|string[] $search
+ * @param string|string[] $replace
+ * @return static
+ */
+ public function replace($search, $replace)
+ {
+ return new static(Str::replace($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace a given value in the string sequentially with an array.
+ *
+ * @param string $search
+ * @param array $replace
+ * @return static
+ */
+ public function replaceArray($search, array $replace)
+ {
+ return new static(Str::replaceArray($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace the first occurrence of a given value in the string.
+ *
+ * @param string $search
+ * @param string $replace
+ * @return static
+ */
+ public function replaceFirst($search, $replace)
+ {
+ return new static(Str::replaceFirst($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace the last occurrence of a given value in the string.
+ *
+ * @param string $search
+ * @param string $replace
+ * @return static
+ */
+ public function replaceLast($search, $replace)
+ {
+ return new static(Str::replaceLast($search, $replace, $this->value));
+ }
+
+ /**
+ * Replace the patterns matching the given regular expression.
+ *
+ * @param string $pattern
+ * @param \Closure|string $replace
+ * @param int $limit
+ * @return static
+ */
+ public function replaceMatches($pattern, $replace, $limit = -1)
+ {
+ if ($replace instanceof Closure) {
+ return new static(preg_replace_callback($pattern, $replace, $this->value, $limit));
+ }
+
+ return new static(preg_replace($pattern, $replace, $this->value, $limit));
+ }
+
+ /**
+ * Parse input from a string to a collection, according to a format.
+ *
+ * @param string $format
+ * @return \Illuminate\Support\Collection
+ */
+ public function scan($format)
+ {
+ return collect(sscanf($this->value, $format));
+ }
+
+ /**
+ * Begin a string with a single instance of a given value.
+ *
+ * @param string $prefix
+ * @return static
+ */
+ public function start($prefix)
+ {
+ return new static(Str::start($this->value, $prefix));
+ }
+
+ /**
+ * Strip HTML and PHP tags from the given string.
+ *
+ * @param string $allowedTags
+ * @return static
+ */
+ public function stripTags($allowedTags = null)
+ {
+ return new static(strip_tags($this->value, $allowedTags));
+ }
+
+ /**
+ * Convert the given string to upper-case.
+ *
+ * @return static
+ */
+ public function upper()
+ {
+ return new static(Str::upper($this->value));
+ }
+
+ /**
+ * Convert the given string to title case.
+ *
+ * @return static
+ */
+ public function title()
+ {
+ return new static(Str::title($this->value));
+ }
+
+ /**
+ * Convert the given string to title case for each word.
+ *
+ * @return static
+ */
+ public function headline()
+ {
+ return new static(Str::headline($this->value));
+ }
+
+ /**
+ * Get the singular form of an English word.
+ *
+ * @return static
+ */
+ public function singular()
+ {
+ return new static(Str::singular($this->value));
+ }
+
+ /**
+ * Generate a URL friendly "slug" from a given string.
+ *
+ * @param string $separator
+ * @param string|null $language
+ * @return static
+ */
+ public function slug($separator = '-', $language = 'en')
+ {
+ return new static(Str::slug($this->value, $separator, $language));
+ }
+
+ /**
+ * Convert a string to snake case.
+ *
+ * @param string $delimiter
+ * @return static
+ */
+ public function snake($delimiter = '_')
+ {
+ return new static(Str::snake($this->value, $delimiter));
+ }
+
+ /**
+ * Determine if a given string starts with a given substring.
+ *
+ * @param string|array $needles
+ * @return bool
+ */
+ public function startsWith($needles)
+ {
+ return Str::startsWith($this->value, $needles);
+ }
+
+ /**
+ * Convert a value to studly caps case.
+ *
+ * @return static
+ */
+ public function studly()
+ {
+ return new static(Str::studly($this->value));
+ }
+
+ /**
+ * Returns the portion of the string specified by the start and length parameters.
+ *
+ * @param int $start
+ * @param int|null $length
+ * @return static
+ */
+ public function substr($start, $length = null)
+ {
+ return new static(Str::substr($this->value, $start, $length));
+ }
+
+ /**
+ * Returns the number of substring occurrences.
+ *
+ * @param string $needle
+ * @param int|null $offset
+ * @param int|null $length
+ * @return int
+ */
+ public function substrCount($needle, $offset = null, $length = null)
+ {
+ return Str::substrCount($this->value, $needle, $offset ?? 0, $length);
+ }
+
+ /**
+ * Replace text within a portion of a string.
+ *
+ * @param string|array $replace
+ * @param array|int $offset
+ * @param array|int|null $length
+ * @return static
+ */
+ public function substrReplace($replace, $offset = 0, $length = null)
+ {
+ return new static(Str::substrReplace($this->value, $replace, $offset, $length));
+ }
+
+ /**
+ * Swap multiple keywords in a string with other keywords.
+ *
+ * @param array $map
+ * @return static
+ */
+ public function swap(array $map)
+ {
+ return new static(strtr($this->value, $map));
+ }
+
+ /**
+ * Trim the string of the given characters.
+ *
+ * @param string $characters
+ * @return static
+ */
+ public function trim($characters = null)
+ {
+ return new static(trim(...array_merge([$this->value], func_get_args())));
+ }
+
+ /**
+ * Left trim the string of the given characters.
+ *
+ * @param string $characters
+ * @return static
+ */
+ public function ltrim($characters = null)
+ {
+ return new static(ltrim(...array_merge([$this->value], func_get_args())));
+ }
+
+ /**
+ * Right trim the string of the given characters.
+ *
+ * @param string $characters
+ * @return static
+ */
+ public function rtrim($characters = null)
+ {
+ return new static(rtrim(...array_merge([$this->value], func_get_args())));
+ }
+
+ /**
+ * Make a string's first character uppercase.
+ *
+ * @return static
+ */
+ public function ucfirst()
+ {
+ return new static(Str::ucfirst($this->value));
+ }
+
+ /**
+ * Split a string by uppercase characters.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function ucsplit()
+ {
+ return collect(Str::ucsplit($this->value));
+ }
+
+ /**
+ * Execute the given callback if the string contains a given substring.
+ *
+ * @param string|array $needles
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenContains($needles, $callback, $default = null)
+ {
+ return $this->when($this->contains($needles), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string contains all array values.
+ *
+ * @param array $needles
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenContainsAll(array $needles, $callback, $default = null)
+ {
+ return $this->when($this->containsAll($needles), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string is empty.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenEmpty($callback, $default = null)
+ {
+ return $this->when($this->isEmpty(), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string is not empty.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenNotEmpty($callback, $default = null)
+ {
+ return $this->when($this->isNotEmpty(), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string ends with a given substring.
+ *
+ * @param string|array $needles
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenEndsWith($needles, $callback, $default = null)
+ {
+ return $this->when($this->endsWith($needles), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string is an exact match with the given value.
+ *
+ * @param string $value
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenExactly($value, $callback, $default = null)
+ {
+ return $this->when($this->exactly($value), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string matches a given pattern.
+ *
+ * @param string|array $pattern
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenIs($pattern, $callback, $default = null)
+ {
+ return $this->when($this->is($pattern), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string is 7 bit ASCII.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenIsAscii($callback, $default = null)
+ {
+ return $this->when($this->isAscii(), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string is a valid UUID.
+ *
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenIsUuid($callback, $default = null)
+ {
+ return $this->when($this->isUuid(), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string starts with a given substring.
+ *
+ * @param string|array $needles
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenStartsWith($needles, $callback, $default = null)
+ {
+ return $this->when($this->startsWith($needles), $callback, $default);
+ }
+
+ /**
+ * Execute the given callback if the string matches the given pattern.
+ *
+ * @param string $pattern
+ * @param callable $callback
+ * @param callable|null $default
+ * @return static
+ */
+ public function whenTest($pattern, $callback, $default = null)
+ {
+ return $this->when($this->test($pattern), $callback, $default);
+ }
+
+ /**
+ * Limit the number of words in a string.
+ *
+ * @param int $words
+ * @param string $end
+ * @return static
+ */
+ public function words($words = 100, $end = '...')
+ {
+ return new static(Str::words($this->value, $words, $end));
+ }
+
+ /**
+ * Get the number of words a string contains.
+ *
+ * @return int
+ */
+ public function wordCount()
+ {
+ return str_word_count($this->value);
+ }
+
+ /**
+ * Convert the string into a `HtmlString` instance.
+ *
+ * @return \Illuminate\Support\HtmlString
+ */
+ public function toHtmlString()
+ {
+ return new HtmlString($this->value);
+ }
+
+ /**
+ * Dump the string.
+ *
+ * @return $this
+ */
+ public function dump()
+ {
+ VarDumper::dump($this->value);
+
+ return $this;
+ }
+
+ /**
+ * Dump the string and end the script.
+ *
+ * @return never
+ */
+ public function dd()
+ {
+ $this->dump();
+
+ exit(1);
+ }
+
+ /**
+ * Convert the object to a string when JSON encoded.
+ *
+ * @return string
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->__toString();
+ }
+
+ /**
+ * Proxy dynamic properties onto methods.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->{$key}();
+ }
+
+ /**
+ * Get the raw string value.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->value;
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php b/vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php
new file mode 100644
index 0000000..d966133
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/BatchRepositoryFake.php
@@ -0,0 +1,142 @@
+name,
+ count($batch->jobs),
+ count($batch->jobs),
+ 0,
+ [],
+ $batch->options,
+ CarbonImmutable::now(),
+ null,
+ null
+ );
+ }
+
+ /**
+ * Increment the total number of jobs within the batch.
+ *
+ * @param string $batchId
+ * @param int $amount
+ * @return void
+ */
+ public function incrementTotalJobs(string $batchId, int $amount)
+ {
+ //
+ }
+
+ /**
+ * Decrement the total number of pending jobs for the batch.
+ *
+ * @param string $batchId
+ * @param string $jobId
+ * @return \Illuminate\Bus\UpdatedBatchJobCounts
+ */
+ public function decrementPendingJobs(string $batchId, string $jobId)
+ {
+ return new UpdatedBatchJobCounts;
+ }
+
+ /**
+ * Increment the total number of failed jobs for the batch.
+ *
+ * @param string $batchId
+ * @param string $jobId
+ * @return \Illuminate\Bus\UpdatedBatchJobCounts
+ */
+ public function incrementFailedJobs(string $batchId, string $jobId)
+ {
+ return new UpdatedBatchJobCounts;
+ }
+
+ /**
+ * Mark the batch that has the given ID as finished.
+ *
+ * @param string $batchId
+ * @return void
+ */
+ public function markAsFinished(string $batchId)
+ {
+ //
+ }
+
+ /**
+ * Cancel the batch that has the given ID.
+ *
+ * @param string $batchId
+ * @return void
+ */
+ public function cancel(string $batchId)
+ {
+ //
+ }
+
+ /**
+ * Delete the batch that has the given ID.
+ *
+ * @param string $batchId
+ * @return void
+ */
+ public function delete(string $batchId)
+ {
+ //
+ }
+
+ /**
+ * Execute the given Closure within a storage specific transaction.
+ *
+ * @param \Closure $callback
+ * @return mixed
+ */
+ public function transaction(Closure $callback)
+ {
+ return $callback();
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/BusFake.php b/vendor/illuminate/support/Testing/Fakes/BusFake.php
new file mode 100644
index 0000000..122252d
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/BusFake.php
@@ -0,0 +1,739 @@
+dispatcher = $dispatcher;
+
+ $this->jobsToFake = Arr::wrap($jobsToFake);
+ }
+
+ /**
+ * Assert if a job was dispatched based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|int|null $callback
+ * @return void
+ */
+ public function assertDispatched($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ if (is_numeric($callback)) {
+ return $this->assertDispatchedTimes($command, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatched($command, $callback)->count() > 0 ||
+ $this->dispatchedAfterResponse($command, $callback)->count() > 0 ||
+ $this->dispatchedSync($command, $callback)->count() > 0,
+ "The expected [{$command}] job was not dispatched."
+ );
+ }
+
+ /**
+ * Assert if a job was pushed a number of times.
+ *
+ * @param string $command
+ * @param int $times
+ * @return void
+ */
+ public function assertDispatchedTimes($command, $times = 1)
+ {
+ $count = $this->dispatched($command)->count() +
+ $this->dispatchedAfterResponse($command)->count() +
+ $this->dispatchedSync($command)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$command}] job was pushed {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Determine if a job was dispatched based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotDispatched($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatched($command, $callback)->count() === 0 &&
+ $this->dispatchedAfterResponse($command, $callback)->count() === 0 &&
+ $this->dispatchedSync($command, $callback)->count() === 0,
+ "The unexpected [{$command}] job was dispatched."
+ );
+ }
+
+ /**
+ * Assert that no jobs were dispatched.
+ *
+ * @return void
+ */
+ public function assertNothingDispatched()
+ {
+ PHPUnit::assertEmpty($this->commands, 'Jobs were dispatched unexpectedly.');
+ }
+
+ /**
+ * Assert if a job was explicitly dispatched synchronously based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|int|null $callback
+ * @return void
+ */
+ public function assertDispatchedSync($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ if (is_numeric($callback)) {
+ return $this->assertDispatchedSyncTimes($command, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatchedSync($command, $callback)->count() > 0,
+ "The expected [{$command}] job was not dispatched synchronously."
+ );
+ }
+
+ /**
+ * Assert if a job was pushed synchronously a number of times.
+ *
+ * @param string $command
+ * @param int $times
+ * @return void
+ */
+ public function assertDispatchedSyncTimes($command, $times = 1)
+ {
+ $count = $this->dispatchedSync($command)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$command}] job was synchronously pushed {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Determine if a job was dispatched based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotDispatchedSync($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ PHPUnit::assertCount(
+ 0, $this->dispatchedSync($command, $callback),
+ "The unexpected [{$command}] job was dispatched synchronously."
+ );
+ }
+
+ /**
+ * Assert if a job was dispatched after the response was sent based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|int|null $callback
+ * @return void
+ */
+ public function assertDispatchedAfterResponse($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ if (is_numeric($callback)) {
+ return $this->assertDispatchedAfterResponseTimes($command, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatchedAfterResponse($command, $callback)->count() > 0,
+ "The expected [{$command}] job was not dispatched after sending the response."
+ );
+ }
+
+ /**
+ * Assert if a job was pushed after the response was sent a number of times.
+ *
+ * @param string $command
+ * @param int $times
+ * @return void
+ */
+ public function assertDispatchedAfterResponseTimes($command, $times = 1)
+ {
+ $count = $this->dispatchedAfterResponse($command)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$command}] job was pushed {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Determine if a job was dispatched based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotDispatchedAfterResponse($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ PHPUnit::assertCount(
+ 0, $this->dispatchedAfterResponse($command, $callback),
+ "The unexpected [{$command}] job was dispatched after sending the response."
+ );
+ }
+
+ /**
+ * Assert if a chain of jobs was dispatched.
+ *
+ * @param array $expectedChain
+ * @return void
+ */
+ public function assertChained(array $expectedChain)
+ {
+ $command = $expectedChain[0];
+
+ $expectedChain = array_slice($expectedChain, 1);
+
+ $callback = null;
+
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ } elseif (! is_string($command)) {
+ $instance = $command;
+
+ $command = get_class($instance);
+
+ $callback = function ($job) use ($instance) {
+ return serialize($this->resetChainPropertiesToDefaults($job)) === serialize($instance);
+ };
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatched($command, $callback)->isNotEmpty(),
+ "The expected [{$command}] job was not dispatched."
+ );
+
+ PHPUnit::assertTrue(
+ collect($expectedChain)->isNotEmpty(),
+ 'The expected chain can not be empty.'
+ );
+
+ $this->isChainOfObjects($expectedChain)
+ ? $this->assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
+ : $this->assertDispatchedWithChainOfClasses($command, $expectedChain, $callback);
+ }
+
+ /**
+ * Reset the chain properties to their default values on the job.
+ *
+ * @param mixed $job
+ * @return mixed
+ */
+ protected function resetChainPropertiesToDefaults($job)
+ {
+ return tap(clone $job, function ($job) {
+ $job->chainConnection = null;
+ $job->chainQueue = null;
+ $job->chainCatchCallbacks = null;
+ $job->chained = [];
+ });
+ }
+
+ /**
+ * Assert if a job was dispatched with an empty chain based on a truth-test callback.
+ *
+ * @param string|\Closure $command
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertDispatchedWithoutChain($command, $callback = null)
+ {
+ if ($command instanceof Closure) {
+ [$command, $callback] = [$this->firstClosureParameterType($command), $command];
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatched($command, $callback)->isNotEmpty(),
+ "The expected [{$command}] job was not dispatched."
+ );
+
+ $this->assertDispatchedWithChainOfClasses($command, [], $callback);
+ }
+
+ /**
+ * Assert if a job was dispatched with chained jobs based on a truth-test callback.
+ *
+ * @param string $command
+ * @param array $expectedChain
+ * @param callable|null $callback
+ * @return void
+ */
+ protected function assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
+ {
+ $chain = collect($expectedChain)->map(function ($job) {
+ return serialize($job);
+ })->all();
+
+ PHPUnit::assertTrue(
+ $this->dispatched($command, $callback)->filter(function ($job) use ($chain) {
+ return $job->chained == $chain;
+ })->isNotEmpty(),
+ 'The expected chain was not dispatched.'
+ );
+ }
+
+ /**
+ * Assert if a job was dispatched with chained jobs based on a truth-test callback.
+ *
+ * @param string $command
+ * @param array $expectedChain
+ * @param callable|null $callback
+ * @return void
+ */
+ protected function assertDispatchedWithChainOfClasses($command, $expectedChain, $callback)
+ {
+ $matching = $this->dispatched($command, $callback)->map->chained->map(function ($chain) {
+ return collect($chain)->map(function ($job) {
+ return get_class(unserialize($job));
+ });
+ })->filter(function ($chain) use ($expectedChain) {
+ return $chain->all() === $expectedChain;
+ });
+
+ PHPUnit::assertTrue(
+ $matching->isNotEmpty(), 'The expected chain was not dispatched.'
+ );
+ }
+
+ /**
+ * Determine if the given chain is entirely composed of objects.
+ *
+ * @param array $chain
+ * @return bool
+ */
+ protected function isChainOfObjects($chain)
+ {
+ return ! collect($chain)->contains(function ($job) {
+ return ! is_object($job);
+ });
+ }
+
+ /**
+ * Assert if a batch was dispatched based on a truth-test callback.
+ *
+ * @param callable $callback
+ * @return void
+ */
+ public function assertBatched(callable $callback)
+ {
+ PHPUnit::assertTrue(
+ $this->batched($callback)->count() > 0,
+ 'The expected batch was not dispatched.'
+ );
+ }
+
+ /**
+ * Assert the number of batches that have been dispatched.
+ *
+ * @param int $count
+ * @return void
+ */
+ public function assertBatchCount($count)
+ {
+ PHPUnit::assertCount(
+ $count, $this->batches,
+ );
+ }
+
+ /**
+ * Get all of the jobs matching a truth-test callback.
+ *
+ * @param string $command
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function dispatched($command, $callback = null)
+ {
+ if (! $this->hasDispatched($command)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return collect($this->commands[$command])->filter(function ($command) use ($callback) {
+ return $callback($command);
+ });
+ }
+
+ /**
+ * Get all of the jobs dispatched synchronously matching a truth-test callback.
+ *
+ * @param string $command
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function dispatchedSync(string $command, $callback = null)
+ {
+ if (! $this->hasDispatchedSync($command)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return collect($this->commandsSync[$command])->filter(function ($command) use ($callback) {
+ return $callback($command);
+ });
+ }
+
+ /**
+ * Get all of the jobs dispatched after the response was sent matching a truth-test callback.
+ *
+ * @param string $command
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function dispatchedAfterResponse(string $command, $callback = null)
+ {
+ if (! $this->hasDispatchedAfterResponse($command)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return collect($this->commandsAfterResponse[$command])->filter(function ($command) use ($callback) {
+ return $callback($command);
+ });
+ }
+
+ /**
+ * Get all of the pending batches matching a truth-test callback.
+ *
+ * @param callable $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function batched(callable $callback)
+ {
+ if (empty($this->batches)) {
+ return collect();
+ }
+
+ return collect($this->batches)->filter(function ($batch) use ($callback) {
+ return $callback($batch);
+ });
+ }
+
+ /**
+ * Determine if there are any stored commands for a given class.
+ *
+ * @param string $command
+ * @return bool
+ */
+ public function hasDispatched($command)
+ {
+ return isset($this->commands[$command]) && ! empty($this->commands[$command]);
+ }
+
+ /**
+ * Determine if there are any stored commands for a given class.
+ *
+ * @param string $command
+ * @return bool
+ */
+ public function hasDispatchedSync($command)
+ {
+ return isset($this->commandsSync[$command]) && ! empty($this->commandsSync[$command]);
+ }
+
+ /**
+ * Determine if there are any stored commands for a given class.
+ *
+ * @param string $command
+ * @return bool
+ */
+ public function hasDispatchedAfterResponse($command)
+ {
+ return isset($this->commandsAfterResponse[$command]) && ! empty($this->commandsAfterResponse[$command]);
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler.
+ *
+ * @param mixed $command
+ * @return mixed
+ */
+ public function dispatch($command)
+ {
+ if ($this->shouldFakeJob($command)) {
+ $this->commands[get_class($command)][] = $command;
+ } else {
+ return $this->dispatcher->dispatch($command);
+ }
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler in the current process.
+ *
+ * Queueable jobs will be dispatched to the "sync" queue.
+ *
+ * @param mixed $command
+ * @param mixed $handler
+ * @return mixed
+ */
+ public function dispatchSync($command, $handler = null)
+ {
+ if ($this->shouldFakeJob($command)) {
+ $this->commandsSync[get_class($command)][] = $command;
+ } else {
+ return $this->dispatcher->dispatchSync($command, $handler);
+ }
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler in the current process.
+ *
+ * @param mixed $command
+ * @param mixed $handler
+ * @return mixed
+ */
+ public function dispatchNow($command, $handler = null)
+ {
+ if ($this->shouldFakeJob($command)) {
+ $this->commands[get_class($command)][] = $command;
+ } else {
+ return $this->dispatcher->dispatchNow($command, $handler);
+ }
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler behind a queue.
+ *
+ * @param mixed $command
+ * @return mixed
+ */
+ public function dispatchToQueue($command)
+ {
+ if ($this->shouldFakeJob($command)) {
+ $this->commands[get_class($command)][] = $command;
+ } else {
+ return $this->dispatcher->dispatchToQueue($command);
+ }
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler.
+ *
+ * @param mixed $command
+ * @return mixed
+ */
+ public function dispatchAfterResponse($command)
+ {
+ if ($this->shouldFakeJob($command)) {
+ $this->commandsAfterResponse[get_class($command)][] = $command;
+ } else {
+ return $this->dispatcher->dispatch($command);
+ }
+ }
+
+ /**
+ * Create a new chain of queueable jobs.
+ *
+ * @param \Illuminate\Support\Collection|array $jobs
+ * @return \Illuminate\Foundation\Bus\PendingChain
+ */
+ public function chain($jobs)
+ {
+ $jobs = Collection::wrap($jobs);
+
+ return new PendingChainFake($this, $jobs->shift(), $jobs->toArray());
+ }
+
+ /**
+ * Attempt to find the batch with the given ID.
+ *
+ * @param string $batchId
+ * @return \Illuminate\Bus\Batch|null
+ */
+ public function findBatch(string $batchId)
+ {
+ //
+ }
+
+ /**
+ * Create a new batch of queueable jobs.
+ *
+ * @param \Illuminate\Support\Collection|array $jobs
+ * @return \Illuminate\Bus\PendingBatch
+ */
+ public function batch($jobs)
+ {
+ return new PendingBatchFake($this, Collection::wrap($jobs));
+ }
+
+ /**
+ * Record the fake pending batch dispatch.
+ *
+ * @param \Illuminate\Bus\PendingBatch $pendingBatch
+ * @return \Illuminate\Bus\Batch
+ */
+ public function recordPendingBatch(PendingBatch $pendingBatch)
+ {
+ $this->batches[] = $pendingBatch;
+
+ return (new BatchRepositoryFake)->store($pendingBatch);
+ }
+
+ /**
+ * Determine if a command should be faked or actually dispatched.
+ *
+ * @param mixed $command
+ * @return bool
+ */
+ protected function shouldFakeJob($command)
+ {
+ if (empty($this->jobsToFake)) {
+ return true;
+ }
+
+ return collect($this->jobsToFake)
+ ->filter(function ($job) use ($command) {
+ return $job instanceof Closure
+ ? $job($command)
+ : $job === get_class($command);
+ })->isNotEmpty();
+ }
+
+ /**
+ * Set the pipes commands should be piped through before dispatching.
+ *
+ * @param array $pipes
+ * @return $this
+ */
+ public function pipeThrough(array $pipes)
+ {
+ $this->dispatcher->pipeThrough($pipes);
+
+ return $this;
+ }
+
+ /**
+ * Determine if the given command has a handler.
+ *
+ * @param mixed $command
+ * @return bool
+ */
+ public function hasCommandHandler($command)
+ {
+ return $this->dispatcher->hasCommandHandler($command);
+ }
+
+ /**
+ * Retrieve the handler for a command.
+ *
+ * @param mixed $command
+ * @return mixed
+ */
+ public function getCommandHandler($command)
+ {
+ return $this->dispatcher->getCommandHandler($command);
+ }
+
+ /**
+ * Map a command to a handler.
+ *
+ * @param array $map
+ * @return $this
+ */
+ public function map(array $map)
+ {
+ $this->dispatcher->map($map);
+
+ return $this;
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/EventFake.php b/vendor/illuminate/support/Testing/Fakes/EventFake.php
new file mode 100644
index 0000000..436173e
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/EventFake.php
@@ -0,0 +1,325 @@
+dispatcher = $dispatcher;
+
+ $this->eventsToFake = Arr::wrap($eventsToFake);
+ }
+
+ /**
+ * Assert if an event has a listener attached to it.
+ *
+ * @param string $expectedEvent
+ * @param string $expectedListener
+ * @return void
+ */
+ public function assertListening($expectedEvent, $expectedListener)
+ {
+ foreach ($this->dispatcher->getListeners($expectedEvent) as $listenerClosure) {
+ $actualListener = (new ReflectionFunction($listenerClosure))
+ ->getStaticVariables()['listener'];
+
+ if (is_string($actualListener) && Str::endsWith($actualListener, '@handle')) {
+ $actualListener = Str::parseCallback($actualListener)[0];
+ }
+
+ if ($actualListener === $expectedListener ||
+ ($actualListener instanceof Closure &&
+ $expectedListener === Closure::class)) {
+ PHPUnit::assertTrue(true);
+
+ return;
+ }
+ }
+
+ PHPUnit::assertTrue(
+ false,
+ sprintf(
+ 'Event [%s] does not have the [%s] listener attached to it',
+ $expectedEvent,
+ print_r($expectedListener, true)
+ )
+ );
+ }
+
+ /**
+ * Assert if an event was dispatched based on a truth-test callback.
+ *
+ * @param string|\Closure $event
+ * @param callable|int|null $callback
+ * @return void
+ */
+ public function assertDispatched($event, $callback = null)
+ {
+ if ($event instanceof Closure) {
+ [$event, $callback] = [$this->firstClosureParameterType($event), $event];
+ }
+
+ if (is_int($callback)) {
+ return $this->assertDispatchedTimes($event, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->dispatched($event, $callback)->count() > 0,
+ "The expected [{$event}] event was not dispatched."
+ );
+ }
+
+ /**
+ * Assert if an event was dispatched a number of times.
+ *
+ * @param string $event
+ * @param int $times
+ * @return void
+ */
+ public function assertDispatchedTimes($event, $times = 1)
+ {
+ $count = $this->dispatched($event)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$event}] event was dispatched {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Determine if an event was dispatched based on a truth-test callback.
+ *
+ * @param string|\Closure $event
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotDispatched($event, $callback = null)
+ {
+ if ($event instanceof Closure) {
+ [$event, $callback] = [$this->firstClosureParameterType($event), $event];
+ }
+
+ PHPUnit::assertCount(
+ 0, $this->dispatched($event, $callback),
+ "The unexpected [{$event}] event was dispatched."
+ );
+ }
+
+ /**
+ * Assert that no events were dispatched.
+ *
+ * @return void
+ */
+ public function assertNothingDispatched()
+ {
+ $count = count(Arr::flatten($this->events));
+
+ PHPUnit::assertSame(
+ 0, $count,
+ "{$count} unexpected events were dispatched."
+ );
+ }
+
+ /**
+ * Get all of the events matching a truth-test callback.
+ *
+ * @param string $event
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function dispatched($event, $callback = null)
+ {
+ if (! $this->hasDispatched($event)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return collect($this->events[$event])->filter(function ($arguments) use ($callback) {
+ return $callback(...$arguments);
+ });
+ }
+
+ /**
+ * Determine if the given event has been dispatched.
+ *
+ * @param string $event
+ * @return bool
+ */
+ public function hasDispatched($event)
+ {
+ return isset($this->events[$event]) && ! empty($this->events[$event]);
+ }
+
+ /**
+ * Register an event listener with the dispatcher.
+ *
+ * @param \Closure|string|array $events
+ * @param mixed $listener
+ * @return void
+ */
+ public function listen($events, $listener = null)
+ {
+ $this->dispatcher->listen($events, $listener);
+ }
+
+ /**
+ * Determine if a given event has listeners.
+ *
+ * @param string $eventName
+ * @return bool
+ */
+ public function hasListeners($eventName)
+ {
+ return $this->dispatcher->hasListeners($eventName);
+ }
+
+ /**
+ * Register an event and payload to be dispatched later.
+ *
+ * @param string $event
+ * @param array $payload
+ * @return void
+ */
+ public function push($event, $payload = [])
+ {
+ //
+ }
+
+ /**
+ * Register an event subscriber with the dispatcher.
+ *
+ * @param object|string $subscriber
+ * @return void
+ */
+ public function subscribe($subscriber)
+ {
+ $this->dispatcher->subscribe($subscriber);
+ }
+
+ /**
+ * Flush a set of pushed events.
+ *
+ * @param string $event
+ * @return void
+ */
+ public function flush($event)
+ {
+ //
+ }
+
+ /**
+ * Fire an event and call the listeners.
+ *
+ * @param string|object $event
+ * @param mixed $payload
+ * @param bool $halt
+ * @return array|null
+ */
+ public function dispatch($event, $payload = [], $halt = false)
+ {
+ $name = is_object($event) ? get_class($event) : (string) $event;
+
+ if ($this->shouldFakeEvent($name, $payload)) {
+ $this->events[$name][] = func_get_args();
+ } else {
+ return $this->dispatcher->dispatch($event, $payload, $halt);
+ }
+ }
+
+ /**
+ * Determine if an event should be faked or actually dispatched.
+ *
+ * @param string $eventName
+ * @param mixed $payload
+ * @return bool
+ */
+ protected function shouldFakeEvent($eventName, $payload)
+ {
+ if (empty($this->eventsToFake)) {
+ return true;
+ }
+
+ return collect($this->eventsToFake)
+ ->filter(function ($event) use ($eventName, $payload) {
+ return $event instanceof Closure
+ ? $event($eventName, $payload)
+ : $event === $eventName;
+ })
+ ->isNotEmpty();
+ }
+
+ /**
+ * Remove a set of listeners from the dispatcher.
+ *
+ * @param string $event
+ * @return void
+ */
+ public function forget($event)
+ {
+ //
+ }
+
+ /**
+ * Forget all of the queued listeners.
+ *
+ * @return void
+ */
+ public function forgetPushed()
+ {
+ //
+ }
+
+ /**
+ * Dispatch an event and call the listeners.
+ *
+ * @param string|object $event
+ * @param mixed $payload
+ * @return array|null
+ */
+ public function until($event, $payload = [])
+ {
+ return $this->dispatch($event, $payload, true);
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/MailFake.php b/vendor/illuminate/support/Testing/Fakes/MailFake.php
new file mode 100644
index 0000000..fff5f8f
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/MailFake.php
@@ -0,0 +1,445 @@
+prepareMailableAndCallback($mailable, $callback);
+
+ if (is_numeric($callback)) {
+ return $this->assertSentTimes($mailable, $callback);
+ }
+
+ $message = "The expected [{$mailable}] mailable was not sent.";
+
+ if (count($this->queuedMailables) > 0) {
+ $message .= ' Did you mean to use assertQueued() instead?';
+ }
+
+ PHPUnit::assertTrue(
+ $this->sent($mailable, $callback)->count() > 0,
+ $message
+ );
+ }
+
+ /**
+ * Assert if a mailable was sent a number of times.
+ *
+ * @param string $mailable
+ * @param int $times
+ * @return void
+ */
+ protected function assertSentTimes($mailable, $times = 1)
+ {
+ $count = $this->sent($mailable)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$mailable}] mailable was sent {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Determine if a mailable was not sent or queued to be sent based on a truth-test callback.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotOutgoing($mailable, $callback = null)
+ {
+ $this->assertNotSent($mailable, $callback);
+ $this->assertNotQueued($mailable, $callback);
+ }
+
+ /**
+ * Determine if a mailable was not sent based on a truth-test callback.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotSent($mailable, $callback = null)
+ {
+ [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+ PHPUnit::assertCount(
+ 0, $this->sent($mailable, $callback),
+ "The unexpected [{$mailable}] mailable was sent."
+ );
+ }
+
+ /**
+ * Assert that no mailables were sent or queued to be sent.
+ *
+ * @return void
+ */
+ public function assertNothingOutgoing()
+ {
+ $this->assertNothingSent();
+ $this->assertNothingQueued();
+ }
+
+ /**
+ * Assert that no mailables were sent.
+ *
+ * @return void
+ */
+ public function assertNothingSent()
+ {
+ $mailableNames = collect($this->mailables)->map(function ($mailable) {
+ return get_class($mailable);
+ })->join(', ');
+
+ PHPUnit::assertEmpty($this->mailables, 'The following mailables were sent unexpectedly: '.$mailableNames);
+ }
+
+ /**
+ * Assert if a mailable was queued based on a truth-test callback.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|int|null $callback
+ * @return void
+ */
+ public function assertQueued($mailable, $callback = null)
+ {
+ [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+ if (is_numeric($callback)) {
+ return $this->assertQueuedTimes($mailable, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->queued($mailable, $callback)->count() > 0,
+ "The expected [{$mailable}] mailable was not queued."
+ );
+ }
+
+ /**
+ * Assert if a mailable was queued a number of times.
+ *
+ * @param string $mailable
+ * @param int $times
+ * @return void
+ */
+ protected function assertQueuedTimes($mailable, $times = 1)
+ {
+ $count = $this->queued($mailable)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$mailable}] mailable was queued {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Determine if a mailable was not queued based on a truth-test callback.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotQueued($mailable, $callback = null)
+ {
+ [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+ PHPUnit::assertCount(
+ 0, $this->queued($mailable, $callback),
+ "The unexpected [{$mailable}] mailable was queued."
+ );
+ }
+
+ /**
+ * Assert that no mailables were queued.
+ *
+ * @return void
+ */
+ public function assertNothingQueued()
+ {
+ $mailableNames = collect($this->queuedMailables)->map(function ($mailable) {
+ return get_class($mailable);
+ })->join(', ');
+
+ PHPUnit::assertEmpty($this->queuedMailables, 'The following mailables were queued unexpectedly: '.$mailableNames);
+ }
+
+ /**
+ * Get all of the mailables matching a truth-test callback.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function sent($mailable, $callback = null)
+ {
+ [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+ if (! $this->hasSent($mailable)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return $this->mailablesOf($mailable)->filter(function ($mailable) use ($callback) {
+ return $callback($mailable);
+ });
+ }
+
+ /**
+ * Determine if the given mailable has been sent.
+ *
+ * @param string $mailable
+ * @return bool
+ */
+ public function hasSent($mailable)
+ {
+ return $this->mailablesOf($mailable)->count() > 0;
+ }
+
+ /**
+ * Get all of the queued mailables matching a truth-test callback.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function queued($mailable, $callback = null)
+ {
+ [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback);
+
+ if (! $this->hasQueued($mailable)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return $this->queuedMailablesOf($mailable)->filter(function ($mailable) use ($callback) {
+ return $callback($mailable);
+ });
+ }
+
+ /**
+ * Determine if the given mailable has been queued.
+ *
+ * @param string $mailable
+ * @return bool
+ */
+ public function hasQueued($mailable)
+ {
+ return $this->queuedMailablesOf($mailable)->count() > 0;
+ }
+
+ /**
+ * Get all of the mailed mailables for a given type.
+ *
+ * @param string $type
+ * @return \Illuminate\Support\Collection
+ */
+ protected function mailablesOf($type)
+ {
+ return collect($this->mailables)->filter(function ($mailable) use ($type) {
+ return $mailable instanceof $type;
+ });
+ }
+
+ /**
+ * Get all of the mailed mailables for a given type.
+ *
+ * @param string $type
+ * @return \Illuminate\Support\Collection
+ */
+ protected function queuedMailablesOf($type)
+ {
+ return collect($this->queuedMailables)->filter(function ($mailable) use ($type) {
+ return $mailable instanceof $type;
+ });
+ }
+
+ /**
+ * Get a mailer instance by name.
+ *
+ * @param string|null $name
+ * @return \Illuminate\Contracts\Mail\Mailer
+ */
+ public function mailer($name = null)
+ {
+ $this->currentMailer = $name;
+
+ return $this;
+ }
+
+ /**
+ * Begin the process of mailing a mailable class instance.
+ *
+ * @param mixed $users
+ * @return \Illuminate\Mail\PendingMail
+ */
+ public function to($users)
+ {
+ return (new PendingMailFake($this))->to($users);
+ }
+
+ /**
+ * Begin the process of mailing a mailable class instance.
+ *
+ * @param mixed $users
+ * @return \Illuminate\Mail\PendingMail
+ */
+ public function bcc($users)
+ {
+ return (new PendingMailFake($this))->bcc($users);
+ }
+
+ /**
+ * Send a new message with only a raw text part.
+ *
+ * @param string $text
+ * @param \Closure|string $callback
+ * @return void
+ */
+ public function raw($text, $callback)
+ {
+ //
+ }
+
+ /**
+ * Send a new message using a view.
+ *
+ * @param \Illuminate\Contracts\Mail\Mailable|string|array $view
+ * @param array $data
+ * @param \Closure|string|null $callback
+ * @return void
+ */
+ public function send($view, array $data = [], $callback = null)
+ {
+ if (! $view instanceof Mailable) {
+ return;
+ }
+
+ $view->mailer($this->currentMailer);
+
+ if ($view instanceof ShouldQueue) {
+ return $this->queue($view, $data);
+ }
+
+ $this->currentMailer = null;
+
+ $this->mailables[] = $view;
+ }
+
+ /**
+ * Queue a new e-mail message for sending.
+ *
+ * @param \Illuminate\Contracts\Mail\Mailable|string|array $view
+ * @param string|null $queue
+ * @return mixed
+ */
+ public function queue($view, $queue = null)
+ {
+ if (! $view instanceof Mailable) {
+ return;
+ }
+
+ $view->mailer($this->currentMailer);
+
+ $this->currentMailer = null;
+
+ $this->queuedMailables[] = $view;
+ }
+
+ /**
+ * Queue a new e-mail message for sending after (n) seconds.
+ *
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @param \Illuminate\Contracts\Mail\Mailable|string|array $view
+ * @param string|null $queue
+ * @return mixed
+ */
+ public function later($delay, $view, $queue = null)
+ {
+ $this->queue($view, $queue);
+ }
+
+ /**
+ * Get the array of failed recipients.
+ *
+ * @return array
+ */
+ public function failures()
+ {
+ return [];
+ }
+
+ /**
+ * Infer mailable class using reflection if a typehinted closure is passed to assertion.
+ *
+ * @param string|\Closure $mailable
+ * @param callable|null $callback
+ * @return array
+ */
+ protected function prepareMailableAndCallback($mailable, $callback)
+ {
+ if ($mailable instanceof Closure) {
+ return [$this->firstClosureParameterType($mailable), $mailable];
+ }
+
+ return [$mailable, $callback];
+ }
+
+ /**
+ * Forget all of the resolved mailer instances.
+ *
+ * @return $this
+ */
+ public function forgetMailers()
+ {
+ $this->currentMailer = null;
+
+ return $this;
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/NotificationFake.php b/vendor/illuminate/support/Testing/Fakes/NotificationFake.php
new file mode 100644
index 0000000..c7b12f4
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/NotificationFake.php
@@ -0,0 +1,327 @@
+assertSentTo(new AnonymousNotifiable, $notification, $callback);
+ }
+
+ /**
+ * Assert if a notification was sent based on a truth-test callback.
+ *
+ * @param mixed $notifiable
+ * @param string|\Closure $notification
+ * @param callable|null $callback
+ * @return void
+ *
+ * @throws \Exception
+ */
+ public function assertSentTo($notifiable, $notification, $callback = null)
+ {
+ if (is_array($notifiable) || $notifiable instanceof Collection) {
+ if (count($notifiable) === 0) {
+ throw new Exception('No notifiable given.');
+ }
+
+ foreach ($notifiable as $singleNotifiable) {
+ $this->assertSentTo($singleNotifiable, $notification, $callback);
+ }
+
+ return;
+ }
+
+ if ($notification instanceof Closure) {
+ [$notification, $callback] = [$this->firstClosureParameterType($notification), $notification];
+ }
+
+ if (is_numeric($callback)) {
+ return $this->assertSentToTimes($notifiable, $notification, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->sent($notifiable, $notification, $callback)->count() > 0,
+ "The expected [{$notification}] notification was not sent."
+ );
+ }
+
+ /**
+ * Assert if a notification was sent on-demand a number of times.
+ *
+ * @param string $notification
+ * @param int $times
+ * @return void
+ */
+ public function assertSentOnDemandTimes($notification, $times = 1)
+ {
+ return $this->assertSentToTimes(new AnonymousNotifiable, $notification, $times);
+ }
+
+ /**
+ * Assert if a notification was sent a number of times.
+ *
+ * @param mixed $notifiable
+ * @param string $notification
+ * @param int $times
+ * @return void
+ */
+ public function assertSentToTimes($notifiable, $notification, $times = 1)
+ {
+ $count = $this->sent($notifiable, $notification)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "Expected [{$notification}] to be sent {$times} times, but was sent {$count} times."
+ );
+ }
+
+ /**
+ * Determine if a notification was sent based on a truth-test callback.
+ *
+ * @param mixed $notifiable
+ * @param string|\Closure $notification
+ * @param callable|null $callback
+ * @return void
+ *
+ * @throws \Exception
+ */
+ public function assertNotSentTo($notifiable, $notification, $callback = null)
+ {
+ if (is_array($notifiable) || $notifiable instanceof Collection) {
+ if (count($notifiable) === 0) {
+ throw new Exception('No notifiable given.');
+ }
+
+ foreach ($notifiable as $singleNotifiable) {
+ $this->assertNotSentTo($singleNotifiable, $notification, $callback);
+ }
+
+ return;
+ }
+
+ if ($notification instanceof Closure) {
+ [$notification, $callback] = [$this->firstClosureParameterType($notification), $notification];
+ }
+
+ PHPUnit::assertCount(
+ 0, $this->sent($notifiable, $notification, $callback),
+ "The unexpected [{$notification}] notification was sent."
+ );
+ }
+
+ /**
+ * Assert that no notifications were sent.
+ *
+ * @return void
+ */
+ public function assertNothingSent()
+ {
+ PHPUnit::assertEmpty($this->notifications, 'Notifications were sent unexpectedly.');
+ }
+
+ /**
+ * Assert the total amount of times a notification was sent.
+ *
+ * @param string $notification
+ * @param int $expectedCount
+ * @return void
+ */
+ public function assertSentTimes($notification, $expectedCount)
+ {
+ $actualCount = collect($this->notifications)
+ ->flatten(1)
+ ->reduce(function ($count, $sent) use ($notification) {
+ return $count + count($sent[$notification] ?? []);
+ }, 0);
+
+ PHPUnit::assertSame(
+ $expectedCount, $actualCount,
+ "Expected [{$notification}] to be sent {$expectedCount} times, but was sent {$actualCount} times."
+ );
+ }
+
+ /**
+ * Assert the total amount of times a notification was sent.
+ *
+ * @param int $expectedCount
+ * @param string $notification
+ * @return void
+ *
+ * @deprecated Use the assertSentTimes method instead
+ */
+ public function assertTimesSent($expectedCount, $notification)
+ {
+ $this->assertSentTimes($notification, $expectedCount);
+ }
+
+ /**
+ * Get all of the notifications matching a truth-test callback.
+ *
+ * @param mixed $notifiable
+ * @param string $notification
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function sent($notifiable, $notification, $callback = null)
+ {
+ if (! $this->hasSent($notifiable, $notification)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ $notifications = collect($this->notificationsFor($notifiable, $notification));
+
+ return $notifications->filter(function ($arguments) use ($callback) {
+ return $callback(...array_values($arguments));
+ })->pluck('notification');
+ }
+
+ /**
+ * Determine if there are more notifications left to inspect.
+ *
+ * @param mixed $notifiable
+ * @param string $notification
+ * @return bool
+ */
+ public function hasSent($notifiable, $notification)
+ {
+ return ! empty($this->notificationsFor($notifiable, $notification));
+ }
+
+ /**
+ * Get all of the notifications for a notifiable entity by type.
+ *
+ * @param mixed $notifiable
+ * @param string $notification
+ * @return array
+ */
+ protected function notificationsFor($notifiable, $notification)
+ {
+ return $this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification] ?? [];
+ }
+
+ /**
+ * Send the given notification to the given notifiable entities.
+ *
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
+ * @param mixed $notification
+ * @return void
+ */
+ public function send($notifiables, $notification)
+ {
+ $this->sendNow($notifiables, $notification);
+ }
+
+ /**
+ * Send the given notification immediately.
+ *
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
+ * @param mixed $notification
+ * @param array|null $channels
+ * @return void
+ */
+ public function sendNow($notifiables, $notification, array $channels = null)
+ {
+ if (! $notifiables instanceof Collection && ! is_array($notifiables)) {
+ $notifiables = [$notifiables];
+ }
+
+ foreach ($notifiables as $notifiable) {
+ if (! $notification->id) {
+ $notification->id = Str::uuid()->toString();
+ }
+
+ $notifiableChannels = $channels ?: $notification->via($notifiable);
+
+ if (method_exists($notification, 'shouldSend')) {
+ $notifiableChannels = array_filter(
+ $notifiableChannels,
+ function ($channel) use ($notification, $notifiable) {
+ return $notification->shouldSend($notifiable, $channel) !== false;
+ }
+ );
+
+ if (empty($notifiableChannels)) {
+ continue;
+ }
+ }
+
+ $this->notifications[get_class($notifiable)][$notifiable->getKey()][get_class($notification)][] = [
+ 'notification' => $notification,
+ 'channels' => $notifiableChannels,
+ 'notifiable' => $notifiable,
+ 'locale' => $notification->locale ?? $this->locale ?? value(function () use ($notifiable) {
+ if ($notifiable instanceof HasLocalePreference) {
+ return $notifiable->preferredLocale();
+ }
+ }),
+ ];
+ }
+ }
+
+ /**
+ * Get a channel instance by name.
+ *
+ * @param string|null $name
+ * @return mixed
+ */
+ public function channel($name = null)
+ {
+ //
+ }
+
+ /**
+ * Set the locale of notifications.
+ *
+ * @param string $locale
+ * @return $this
+ */
+ public function locale($locale)
+ {
+ $this->locale = $locale;
+
+ return $this;
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php b/vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php
new file mode 100644
index 0000000..c60b4b5
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/PendingBatchFake.php
@@ -0,0 +1,39 @@
+bus = $bus;
+ $this->jobs = $jobs;
+ }
+
+ /**
+ * Dispatch the batch.
+ *
+ * @return \Illuminate\Bus\Batch
+ */
+ public function dispatch()
+ {
+ return $this->bus->recordPendingBatch($this);
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/PendingChainFake.php b/vendor/illuminate/support/Testing/Fakes/PendingChainFake.php
new file mode 100644
index 0000000..533c649
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/PendingChainFake.php
@@ -0,0 +1,56 @@
+bus = $bus;
+ $this->job = $job;
+ $this->chain = $chain;
+ }
+
+ /**
+ * Dispatch the job with the given arguments.
+ *
+ * @return \Illuminate\Foundation\Bus\PendingDispatch
+ */
+ public function dispatch()
+ {
+ if (is_string($this->job)) {
+ $firstJob = new $this->job(...func_get_args());
+ } elseif ($this->job instanceof Closure) {
+ $firstJob = CallQueuedClosure::create($this->job);
+ } else {
+ $firstJob = $this->job;
+ }
+
+ $firstJob->allOnConnection($this->connection);
+ $firstJob->allOnQueue($this->queue);
+ $firstJob->chain($this->chain);
+ $firstJob->delay($this->delay);
+ $firstJob->chainCatchCallbacks = $this->catchCallbacks();
+
+ return $this->bus->dispatch($firstJob);
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/PendingMailFake.php b/vendor/illuminate/support/Testing/Fakes/PendingMailFake.php
new file mode 100644
index 0000000..5225130
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/PendingMailFake.php
@@ -0,0 +1,42 @@
+mailer = $mailer;
+ }
+
+ /**
+ * Send a new mailable message instance.
+ *
+ * @param \Illuminate\Contracts\Mail\Mailable $mailable
+ * @return void
+ */
+ public function send(Mailable $mailable)
+ {
+ $this->mailer->send($this->fill($mailable));
+ }
+
+ /**
+ * Push the given mailable onto the queue.
+ *
+ * @param \Illuminate\Contracts\Mail\Mailable $mailable
+ * @return mixed
+ */
+ public function queue(Mailable $mailable)
+ {
+ return $this->mailer->queue($this->fill($mailable));
+ }
+}
diff --git a/vendor/illuminate/support/Testing/Fakes/QueueFake.php b/vendor/illuminate/support/Testing/Fakes/QueueFake.php
new file mode 100644
index 0000000..d37cd67
--- /dev/null
+++ b/vendor/illuminate/support/Testing/Fakes/QueueFake.php
@@ -0,0 +1,414 @@
+firstClosureParameterType($job), $job];
+ }
+
+ if (is_numeric($callback)) {
+ return $this->assertPushedTimes($job, $callback);
+ }
+
+ PHPUnit::assertTrue(
+ $this->pushed($job, $callback)->count() > 0,
+ "The expected [{$job}] job was not pushed."
+ );
+ }
+
+ /**
+ * Assert if a job was pushed a number of times.
+ *
+ * @param string $job
+ * @param int $times
+ * @return void
+ */
+ protected function assertPushedTimes($job, $times = 1)
+ {
+ $count = $this->pushed($job)->count();
+
+ PHPUnit::assertSame(
+ $times, $count,
+ "The expected [{$job}] job was pushed {$count} times instead of {$times} times."
+ );
+ }
+
+ /**
+ * Assert if a job was pushed based on a truth-test callback.
+ *
+ * @param string $queue
+ * @param string|\Closure $job
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertPushedOn($queue, $job, $callback = null)
+ {
+ if ($job instanceof Closure) {
+ [$job, $callback] = [$this->firstClosureParameterType($job), $job];
+ }
+
+ $this->assertPushed($job, function ($job, $pushedQueue) use ($callback, $queue) {
+ if ($pushedQueue !== $queue) {
+ return false;
+ }
+
+ return $callback ? $callback(...func_get_args()) : true;
+ });
+ }
+
+ /**
+ * Assert if a job was pushed with chained jobs based on a truth-test callback.
+ *
+ * @param string $job
+ * @param array $expectedChain
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertPushedWithChain($job, $expectedChain = [], $callback = null)
+ {
+ PHPUnit::assertTrue(
+ $this->pushed($job, $callback)->isNotEmpty(),
+ "The expected [{$job}] job was not pushed."
+ );
+
+ PHPUnit::assertTrue(
+ collect($expectedChain)->isNotEmpty(),
+ 'The expected chain can not be empty.'
+ );
+
+ $this->isChainOfObjects($expectedChain)
+ ? $this->assertPushedWithChainOfObjects($job, $expectedChain, $callback)
+ : $this->assertPushedWithChainOfClasses($job, $expectedChain, $callback);
+ }
+
+ /**
+ * Assert if a job was pushed with an empty chain based on a truth-test callback.
+ *
+ * @param string $job
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertPushedWithoutChain($job, $callback = null)
+ {
+ PHPUnit::assertTrue(
+ $this->pushed($job, $callback)->isNotEmpty(),
+ "The expected [{$job}] job was not pushed."
+ );
+
+ $this->assertPushedWithChainOfClasses($job, [], $callback);
+ }
+
+ /**
+ * Assert if a job was pushed with chained jobs based on a truth-test callback.
+ *
+ * @param string $job
+ * @param array $expectedChain
+ * @param callable|null $callback
+ * @return void
+ */
+ protected function assertPushedWithChainOfObjects($job, $expectedChain, $callback)
+ {
+ $chain = collect($expectedChain)->map(function ($job) {
+ return serialize($job);
+ })->all();
+
+ PHPUnit::assertTrue(
+ $this->pushed($job, $callback)->filter(function ($job) use ($chain) {
+ return $job->chained == $chain;
+ })->isNotEmpty(),
+ 'The expected chain was not pushed.'
+ );
+ }
+
+ /**
+ * Assert if a job was pushed with chained jobs based on a truth-test callback.
+ *
+ * @param string $job
+ * @param array $expectedChain
+ * @param callable|null $callback
+ * @return void
+ */
+ protected function assertPushedWithChainOfClasses($job, $expectedChain, $callback)
+ {
+ $matching = $this->pushed($job, $callback)->map->chained->map(function ($chain) {
+ return collect($chain)->map(function ($job) {
+ return get_class(unserialize($job));
+ });
+ })->filter(function ($chain) use ($expectedChain) {
+ return $chain->all() === $expectedChain;
+ });
+
+ PHPUnit::assertTrue(
+ $matching->isNotEmpty(), 'The expected chain was not pushed.'
+ );
+ }
+
+ /**
+ * Determine if the given chain is entirely composed of objects.
+ *
+ * @param array $chain
+ * @return bool
+ */
+ protected function isChainOfObjects($chain)
+ {
+ return ! collect($chain)->contains(function ($job) {
+ return ! is_object($job);
+ });
+ }
+
+ /**
+ * Determine if a job was pushed based on a truth-test callback.
+ *
+ * @param string|\Closure $job
+ * @param callable|null $callback
+ * @return void
+ */
+ public function assertNotPushed($job, $callback = null)
+ {
+ if ($job instanceof Closure) {
+ [$job, $callback] = [$this->firstClosureParameterType($job), $job];
+ }
+
+ PHPUnit::assertCount(
+ 0, $this->pushed($job, $callback),
+ "The unexpected [{$job}] job was pushed."
+ );
+ }
+
+ /**
+ * Assert that no jobs were pushed.
+ *
+ * @return void
+ */
+ public function assertNothingPushed()
+ {
+ PHPUnit::assertEmpty($this->jobs, 'Jobs were pushed unexpectedly.');
+ }
+
+ /**
+ * Get all of the jobs matching a truth-test callback.
+ *
+ * @param string $job
+ * @param callable|null $callback
+ * @return \Illuminate\Support\Collection
+ */
+ public function pushed($job, $callback = null)
+ {
+ if (! $this->hasPushed($job)) {
+ return collect();
+ }
+
+ $callback = $callback ?: function () {
+ return true;
+ };
+
+ return collect($this->jobs[$job])->filter(function ($data) use ($callback) {
+ return $callback($data['job'], $data['queue']);
+ })->pluck('job');
+ }
+
+ /**
+ * Determine if there are any stored jobs for a given class.
+ *
+ * @param string $job
+ * @return bool
+ */
+ public function hasPushed($job)
+ {
+ return isset($this->jobs[$job]) && ! empty($this->jobs[$job]);
+ }
+
+ /**
+ * Resolve a queue connection instance.
+ *
+ * @param mixed $value
+ * @return \Illuminate\Contracts\Queue\Queue
+ */
+ public function connection($value = null)
+ {
+ return $this;
+ }
+
+ /**
+ * Get the size of the queue.
+ *
+ * @param string|null $queue
+ * @return int
+ */
+ public function size($queue = null)
+ {
+ return collect($this->jobs)->flatten(1)->filter(function ($job) use ($queue) {
+ return $job['queue'] === $queue;
+ })->count();
+ }
+
+ /**
+ * Push a new job onto the queue.
+ *
+ * @param string|object $job
+ * @param mixed $data
+ * @param string|null $queue
+ * @return mixed
+ */
+ public function push($job, $data = '', $queue = null)
+ {
+ $this->jobs[is_object($job) ? get_class($job) : $job][] = [
+ 'job' => $job,
+ 'queue' => $queue,
+ ];
+ }
+
+ /**
+ * Push a raw payload onto the queue.
+ *
+ * @param string $payload
+ * @param string|null $queue
+ * @param array $options
+ * @return mixed
+ */
+ public function pushRaw($payload, $queue = null, array $options = [])
+ {
+ //
+ }
+
+ /**
+ * Push a new job onto the queue after a delay.
+ *
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @param string|object $job
+ * @param mixed $data
+ * @param string|null $queue
+ * @return mixed
+ */
+ public function later($delay, $job, $data = '', $queue = null)
+ {
+ return $this->push($job, $data, $queue);
+ }
+
+ /**
+ * Push a new job onto the queue.
+ *
+ * @param string $queue
+ * @param string|object $job
+ * @param mixed $data
+ * @return mixed
+ */
+ public function pushOn($queue, $job, $data = '')
+ {
+ return $this->push($job, $data, $queue);
+ }
+
+ /**
+ * Push a new job onto the queue after a delay.
+ *
+ * @param string $queue
+ * @param \DateTimeInterface|\DateInterval|int $delay
+ * @param string|object $job
+ * @param mixed $data
+ * @return mixed
+ */
+ public function laterOn($queue, $delay, $job, $data = '')
+ {
+ return $this->push($job, $data, $queue);
+ }
+
+ /**
+ * Pop the next job off of the queue.
+ *
+ * @param string|null $queue
+ * @return \Illuminate\Contracts\Queue\Job|null
+ */
+ public function pop($queue = null)
+ {
+ //
+ }
+
+ /**
+ * Push an array of jobs onto the queue.
+ *
+ * @param array $jobs
+ * @param mixed $data
+ * @param string|null $queue
+ * @return mixed
+ */
+ public function bulk($jobs, $data = '', $queue = null)
+ {
+ foreach ($jobs as $job) {
+ $this->push($job, $data, $queue);
+ }
+ }
+
+ /**
+ * Get the jobs that have been pushed.
+ *
+ * @return array
+ */
+ public function pushedJobs()
+ {
+ return $this->jobs;
+ }
+
+ /**
+ * Get the connection name for the queue.
+ *
+ * @return string
+ */
+ public function getConnectionName()
+ {
+ //
+ }
+
+ /**
+ * Set the connection name for the queue.
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function setConnectionName($name)
+ {
+ return $this;
+ }
+
+ /**
+ * Override the QueueManager to prevent circular dependency.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ *
+ * @throws \BadMethodCallException
+ */
+ public function __call($method, $parameters)
+ {
+ throw new BadMethodCallException(sprintf(
+ 'Call to undefined method %s::%s()', static::class, $method
+ ));
+ }
+}
diff --git a/vendor/illuminate/support/Timebox.php b/vendor/illuminate/support/Timebox.php
new file mode 100644
index 0000000..32fd607
--- /dev/null
+++ b/vendor/illuminate/support/Timebox.php
@@ -0,0 +1,70 @@
+earlyReturn && $remainder > 0) {
+ $this->usleep($remainder);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Indicate that the timebox can return early.
+ *
+ * @return $this
+ */
+ public function returnEarly()
+ {
+ $this->earlyReturn = true;
+
+ return $this;
+ }
+
+ /**
+ * Indicate that the timebox cannot return early.
+ *
+ * @return $this
+ */
+ public function dontReturnEarly()
+ {
+ $this->earlyReturn = false;
+
+ return $this;
+ }
+
+ /**
+ * Sleep for the specified number of microseconds.
+ *
+ * @param $microseconds
+ * @return void
+ */
+ protected function usleep($microseconds)
+ {
+ usleep($microseconds);
+ }
+}
diff --git a/vendor/illuminate/support/Traits/CapsuleManagerTrait.php b/vendor/illuminate/support/Traits/CapsuleManagerTrait.php
new file mode 100644
index 0000000..0532755
--- /dev/null
+++ b/vendor/illuminate/support/Traits/CapsuleManagerTrait.php
@@ -0,0 +1,69 @@
+container = $container;
+
+ if (! $this->container->bound('config')) {
+ $this->container->instance('config', new Fluent);
+ }
+ }
+
+ /**
+ * Make this capsule instance available globally.
+ *
+ * @return void
+ */
+ public function setAsGlobal()
+ {
+ static::$instance = $this;
+ }
+
+ /**
+ * Get the IoC container instance.
+ *
+ * @return \Illuminate\Contracts\Container\Container
+ */
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ /**
+ * Set the IoC container instance.
+ *
+ * @param \Illuminate\Contracts\Container\Container $container
+ * @return void
+ */
+ public function setContainer(Container $container)
+ {
+ $this->container = $container;
+ }
+}
diff --git a/vendor/illuminate/support/Traits/Conditionable.php b/vendor/illuminate/support/Traits/Conditionable.php
new file mode 100644
index 0000000..7980827
--- /dev/null
+++ b/vendor/illuminate/support/Traits/Conditionable.php
@@ -0,0 +1,44 @@
+{$method}(...$parameters);
+ } catch (Error|BadMethodCallException $e) {
+ $pattern = '~^Call to undefined method (?P[^:]+)::(?P[^\(]+)\(\)$~';
+
+ if (! preg_match($pattern, $e->getMessage(), $matches)) {
+ throw $e;
+ }
+
+ if ($matches['class'] != get_class($object) ||
+ $matches['method'] != $method) {
+ throw $e;
+ }
+
+ static::throwBadMethodCallException($method);
+ }
+ }
+
+ /**
+ * Forward a method call to the given object, returning $this if the forwarded call returned itself.
+ *
+ * @param mixed $object
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ *
+ * @throws \BadMethodCallException
+ */
+ protected function forwardDecoratedCallTo($object, $method, $parameters)
+ {
+ $result = $this->forwardCallTo($object, $method, $parameters);
+
+ if ($result === $object) {
+ return $this;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Throw a bad method call exception for the given method.
+ *
+ * @param string $method
+ * @return void
+ *
+ * @throws \BadMethodCallException
+ */
+ protected static function throwBadMethodCallException($method)
+ {
+ throw new BadMethodCallException(sprintf(
+ 'Call to undefined method %s::%s()', static::class, $method
+ ));
+ }
+}
diff --git a/vendor/illuminate/support/Traits/Localizable.php b/vendor/illuminate/support/Traits/Localizable.php
new file mode 100644
index 0000000..1e9fa58
--- /dev/null
+++ b/vendor/illuminate/support/Traits/Localizable.php
@@ -0,0 +1,34 @@
+getLocale();
+
+ try {
+ $app->setLocale($locale);
+
+ return $callback();
+ } finally {
+ $app->setLocale($original);
+ }
+ }
+}
diff --git a/vendor/illuminate/support/Traits/ReflectsClosures.php b/vendor/illuminate/support/Traits/ReflectsClosures.php
new file mode 100644
index 0000000..bf47d7e
--- /dev/null
+++ b/vendor/illuminate/support/Traits/ReflectsClosures.php
@@ -0,0 +1,88 @@
+closureParameterTypes($closure));
+
+ if (! $types) {
+ throw new RuntimeException('The given Closure has no parameters.');
+ }
+
+ if ($types[0] === null) {
+ throw new RuntimeException('The first parameter of the given Closure is missing a type hint.');
+ }
+
+ return $types[0];
+ }
+
+ /**
+ * Get the class names of the first parameter of the given Closure, including union types.
+ *
+ * @param \Closure $closure
+ * @return array
+ *
+ * @throws \ReflectionException
+ * @throws \RuntimeException
+ */
+ protected function firstClosureParameterTypes(Closure $closure)
+ {
+ $reflection = new ReflectionFunction($closure);
+
+ $types = collect($reflection->getParameters())->mapWithKeys(function ($parameter) {
+ if ($parameter->isVariadic()) {
+ return [$parameter->getName() => null];
+ }
+
+ return [$parameter->getName() => Reflector::getParameterClassNames($parameter)];
+ })->filter()->values()->all();
+
+ if (empty($types)) {
+ throw new RuntimeException('The given Closure has no parameters.');
+ }
+
+ if (isset($types[0]) && empty($types[0])) {
+ throw new RuntimeException('The first parameter of the given Closure is missing a type hint.');
+ }
+
+ return $types[0];
+ }
+
+ /**
+ * Get the class names / types of the parameters of the given Closure.
+ *
+ * @param \Closure $closure
+ * @return array
+ *
+ * @throws \ReflectionException
+ */
+ protected function closureParameterTypes(Closure $closure)
+ {
+ $reflection = new ReflectionFunction($closure);
+
+ return collect($reflection->getParameters())->mapWithKeys(function ($parameter) {
+ if ($parameter->isVariadic()) {
+ return [$parameter->getName() => null];
+ }
+
+ return [$parameter->getName() => Reflector::getParameterClassName($parameter)];
+ })->all();
+ }
+}
diff --git a/vendor/illuminate/support/Traits/Tappable.php b/vendor/illuminate/support/Traits/Tappable.php
new file mode 100644
index 0000000..9353451
--- /dev/null
+++ b/vendor/illuminate/support/Traits/Tappable.php
@@ -0,0 +1,17 @@
+input = $input;
+ }
+
+ /**
+ * Get a subset containing the provided keys with values from the input data.
+ *
+ * @param array|mixed $keys
+ * @return array
+ */
+ public function only($keys)
+ {
+ $results = [];
+
+ $input = $this->input;
+
+ $placeholder = new stdClass;
+
+ foreach (is_array($keys) ? $keys : func_get_args() as $key) {
+ $value = data_get($input, $key, $placeholder);
+
+ if ($value !== $placeholder) {
+ Arr::set($results, $key, $value);
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Get all of the input except for a specified array of items.
+ *
+ * @param array|mixed $keys
+ * @return array
+ */
+ public function except($keys)
+ {
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ $results = $this->input;
+
+ Arr::forget($results, $keys);
+
+ return $results;
+ }
+
+ /**
+ * Merge the validated input with the given array of additional data.
+ *
+ * @param array $items
+ * @return static
+ */
+ public function merge(array $items)
+ {
+ return new static(array_merge($this->input, $items));
+ }
+
+ /**
+ * Get the input as a collection.
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function collect()
+ {
+ return new Collection($this->input);
+ }
+
+ /**
+ * Get the raw, underlying input array.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->input;
+ }
+
+ /**
+ * Get the instance as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->all();
+ }
+
+ /**
+ * Dynamically access input data.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $this->input[$name];
+ }
+
+ /**
+ * Dynamically set input data.
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return mixed
+ */
+ public function __set($name, $value)
+ {
+ $this->input[$name] = $value;
+ }
+
+ /**
+ * Determine if an input key is set.
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ return isset($this->input[$name]);
+ }
+
+ /**
+ * Remove an input key.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function __unset($name)
+ {
+ unset($this->input[$name]);
+ }
+
+ /**
+ * Determine if an item exists at an offset.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return isset($this->input[$key]);
+ }
+
+ /**
+ * Get an item at a given offset.
+ *
+ * @param mixed $key
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->input[$key];
+ }
+
+ /**
+ * Set the item at a given offset.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ if (is_null($key)) {
+ $this->input[] = $value;
+ } else {
+ $this->input[$key] = $value;
+ }
+ }
+
+ /**
+ * Unset the item at a given offset.
+ *
+ * @param string $key
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ unset($this->input[$key]);
+ }
+
+ /**
+ * Get an iterator for the input.
+ *
+ * @return \ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ return new ArrayIterator($this->input);
+ }
+}
diff --git a/vendor/illuminate/support/ViewErrorBag.php b/vendor/illuminate/support/ViewErrorBag.php
new file mode 100644
index 0000000..d51bb53
--- /dev/null
+++ b/vendor/illuminate/support/ViewErrorBag.php
@@ -0,0 +1,131 @@
+bags[$key]);
+ }
+
+ /**
+ * Get a MessageBag instance from the bags.
+ *
+ * @param string $key
+ * @return \Illuminate\Contracts\Support\MessageBag
+ */
+ public function getBag($key)
+ {
+ return Arr::get($this->bags, $key) ?: new MessageBag;
+ }
+
+ /**
+ * Get all the bags.
+ *
+ * @return array
+ */
+ public function getBags()
+ {
+ return $this->bags;
+ }
+
+ /**
+ * Add a new MessageBag instance to the bags.
+ *
+ * @param string $key
+ * @param \Illuminate\Contracts\Support\MessageBag $bag
+ * @return $this
+ */
+ public function put($key, MessageBagContract $bag)
+ {
+ $this->bags[$key] = $bag;
+
+ return $this;
+ }
+
+ /**
+ * Determine if the default message bag has any messages.
+ *
+ * @return bool
+ */
+ public function any()
+ {
+ return $this->count() > 0;
+ }
+
+ /**
+ * Get the number of messages in the default bag.
+ *
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return $this->getBag('default')->count();
+ }
+
+ /**
+ * Dynamically call methods on the default bag.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->getBag('default')->$method(...$parameters);
+ }
+
+ /**
+ * Dynamically access a view error bag.
+ *
+ * @param string $key
+ * @return \Illuminate\Contracts\Support\MessageBag
+ */
+ public function __get($key)
+ {
+ return $this->getBag($key);
+ }
+
+ /**
+ * Dynamically set a view error bag.
+ *
+ * @param string $key
+ * @param \Illuminate\Contracts\Support\MessageBag $value
+ * @return void
+ */
+ public function __set($key, $value)
+ {
+ $this->put($key, $value);
+ }
+
+ /**
+ * Convert the default bag to its string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->getBag('default');
+ }
+}
diff --git a/vendor/illuminate/support/composer.json b/vendor/illuminate/support/composer.json
new file mode 100644
index 0000000..527bdcb
--- /dev/null
+++ b/vendor/illuminate/support/composer.json
@@ -0,0 +1,55 @@
+{
+ "name": "illuminate/support",
+ "description": "The Illuminate Support package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^7.3|^8.0",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "doctrine/inflector": "^1.4|^2.0",
+ "illuminate/collections": "^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/macroable": "^8.0",
+ "nesbot/carbon": "^2.53.1",
+ "voku/portable-ascii": "^1.6.1"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ },
+ "files": [
+ "helpers.php"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "suggest": {
+ "illuminate/filesystem": "Required to use the composer class (^8.0).",
+ "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^1.3|^2.0.2).",
+ "ramsey/uuid": "Required to use Str::uuid() (^4.2.2).",
+ "symfony/process": "Required to use the composer class (^5.4).",
+ "symfony/var-dumper": "Required to use the dd function (^5.4).",
+ "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)."
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/illuminate/support/helpers.php b/vendor/illuminate/support/helpers.php
new file mode 100755
index 0000000..0b82fe7
--- /dev/null
+++ b/vendor/illuminate/support/helpers.php
@@ -0,0 +1,379 @@
+ $value) {
+ if (is_numeric($key)) {
+ $start++;
+
+ $array[$start] = Arr::pull($array, $key);
+ }
+ }
+
+ return $array;
+ }
+}
+
+if (! function_exists('blank')) {
+ /**
+ * Determine if the given value is "blank".
+ *
+ * @param mixed $value
+ * @return bool
+ */
+ function blank($value)
+ {
+ if (is_null($value)) {
+ return true;
+ }
+
+ if (is_string($value)) {
+ return trim($value) === '';
+ }
+
+ if (is_numeric($value) || is_bool($value)) {
+ return false;
+ }
+
+ if ($value instanceof Countable) {
+ return count($value) === 0;
+ }
+
+ return empty($value);
+ }
+}
+
+if (! function_exists('class_basename')) {
+ /**
+ * Get the class "basename" of the given object / class.
+ *
+ * @param string|object $class
+ * @return string
+ */
+ function class_basename($class)
+ {
+ $class = is_object($class) ? get_class($class) : $class;
+
+ return basename(str_replace('\\', '/', $class));
+ }
+}
+
+if (! function_exists('class_uses_recursive')) {
+ /**
+ * Returns all traits used by a class, its parent classes and trait of their traits.
+ *
+ * @param object|string $class
+ * @return array
+ */
+ function class_uses_recursive($class)
+ {
+ if (is_object($class)) {
+ $class = get_class($class);
+ }
+
+ $results = [];
+
+ foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) {
+ $results += trait_uses_recursive($class);
+ }
+
+ return array_unique($results);
+ }
+}
+
+if (! function_exists('e')) {
+ /**
+ * Encode HTML special characters in a string.
+ *
+ * @param \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|string|null $value
+ * @param bool $doubleEncode
+ * @return string
+ */
+ function e($value, $doubleEncode = true)
+ {
+ if ($value instanceof DeferringDisplayableValue) {
+ $value = $value->resolveDisplayableValue();
+ }
+
+ if ($value instanceof Htmlable) {
+ return $value->toHtml();
+ }
+
+ return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8', $doubleEncode);
+ }
+}
+
+if (! function_exists('env')) {
+ /**
+ * Gets the value of an environment variable.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ function env($key, $default = null)
+ {
+ return Env::get($key, $default);
+ }
+}
+
+if (! function_exists('filled')) {
+ /**
+ * Determine if a value is "filled".
+ *
+ * @param mixed $value
+ * @return bool
+ */
+ function filled($value)
+ {
+ return ! blank($value);
+ }
+}
+
+if (! function_exists('object_get')) {
+ /**
+ * Get an item from an object using "dot" notation.
+ *
+ * @param object $object
+ * @param string|null $key
+ * @param mixed $default
+ * @return mixed
+ */
+ function object_get($object, $key, $default = null)
+ {
+ if (is_null($key) || trim($key) === '') {
+ return $object;
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (! is_object($object) || ! isset($object->{$segment})) {
+ return value($default);
+ }
+
+ $object = $object->{$segment};
+ }
+
+ return $object;
+ }
+}
+
+if (! function_exists('optional')) {
+ /**
+ * Provide access to optional objects.
+ *
+ * @param mixed $value
+ * @param callable|null $callback
+ * @return mixed
+ */
+ function optional($value = null, callable $callback = null)
+ {
+ if (is_null($callback)) {
+ return new Optional($value);
+ } elseif (! is_null($value)) {
+ return $callback($value);
+ }
+ }
+}
+
+if (! function_exists('preg_replace_array')) {
+ /**
+ * Replace a given pattern with each value in the array in sequentially.
+ *
+ * @param string $pattern
+ * @param array $replacements
+ * @param string $subject
+ * @return string
+ */
+ function preg_replace_array($pattern, array $replacements, $subject)
+ {
+ return preg_replace_callback($pattern, function () use (&$replacements) {
+ foreach ($replacements as $key => $value) {
+ return array_shift($replacements);
+ }
+ }, $subject);
+ }
+}
+
+if (! function_exists('retry')) {
+ /**
+ * Retry an operation a given number of times.
+ *
+ * @param int $times
+ * @param callable $callback
+ * @param int|\Closure $sleepMilliseconds
+ * @param callable|null $when
+ * @return mixed
+ *
+ * @throws \Exception
+ */
+ function retry($times, callable $callback, $sleepMilliseconds = 0, $when = null)
+ {
+ $attempts = 0;
+
+ beginning:
+ $attempts++;
+ $times--;
+
+ try {
+ return $callback($attempts);
+ } catch (Exception $e) {
+ if ($times < 1 || ($when && ! $when($e))) {
+ throw $e;
+ }
+
+ if ($sleepMilliseconds) {
+ usleep(value($sleepMilliseconds, $attempts) * 1000);
+ }
+
+ goto beginning;
+ }
+ }
+}
+
+if (! function_exists('tap')) {
+ /**
+ * Call the given Closure with the given value then return the value.
+ *
+ * @param mixed $value
+ * @param callable|null $callback
+ * @return mixed
+ */
+ function tap($value, $callback = null)
+ {
+ if (is_null($callback)) {
+ return new HigherOrderTapProxy($value);
+ }
+
+ $callback($value);
+
+ return $value;
+ }
+}
+
+if (! function_exists('throw_if')) {
+ /**
+ * Throw the given exception if the given condition is true.
+ *
+ * @param mixed $condition
+ * @param \Throwable|string $exception
+ * @param mixed ...$parameters
+ * @return mixed
+ *
+ * @throws \Throwable
+ */
+ function throw_if($condition, $exception = 'RuntimeException', ...$parameters)
+ {
+ if ($condition) {
+ if (is_string($exception) && class_exists($exception)) {
+ $exception = new $exception(...$parameters);
+ }
+
+ throw is_string($exception) ? new RuntimeException($exception) : $exception;
+ }
+
+ return $condition;
+ }
+}
+
+if (! function_exists('throw_unless')) {
+ /**
+ * Throw the given exception unless the given condition is true.
+ *
+ * @param mixed $condition
+ * @param \Throwable|string $exception
+ * @param mixed ...$parameters
+ * @return mixed
+ *
+ * @throws \Throwable
+ */
+ function throw_unless($condition, $exception = 'RuntimeException', ...$parameters)
+ {
+ throw_if(! $condition, $exception, ...$parameters);
+
+ return $condition;
+ }
+}
+
+if (! function_exists('trait_uses_recursive')) {
+ /**
+ * Returns all traits used by a trait and its traits.
+ *
+ * @param string $trait
+ * @return array
+ */
+ function trait_uses_recursive($trait)
+ {
+ $traits = class_uses($trait) ?: [];
+
+ foreach ($traits as $trait) {
+ $traits += trait_uses_recursive($trait);
+ }
+
+ return $traits;
+ }
+}
+
+if (! function_exists('transform')) {
+ /**
+ * Transform the given value if it is present.
+ *
+ * @param mixed $value
+ * @param callable $callback
+ * @param mixed $default
+ * @return mixed|null
+ */
+ function transform($value, callable $callback, $default = null)
+ {
+ if (filled($value)) {
+ return $callback($value);
+ }
+
+ if (is_callable($default)) {
+ return $default($value);
+ }
+
+ return $default;
+ }
+}
+
+if (! function_exists('windows_os')) {
+ /**
+ * Determine whether the current environment is Windows based.
+ *
+ * @return bool
+ */
+ function windows_os()
+ {
+ return PHP_OS_FAMILY === 'Windows';
+ }
+}
+
+if (! function_exists('with')) {
+ /**
+ * Return the given value, optionally passed through the given callback.
+ *
+ * @param mixed $value
+ * @param callable|null $callback
+ * @return mixed
+ */
+ function with($value, callable $callback = null)
+ {
+ return is_null($callback) ? $value : $callback($value);
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/.editorconfig b/vendor/maennchen/zipstream-php/.editorconfig
new file mode 100644
index 0000000..f7cd914
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.editorconfig
@@ -0,0 +1,22 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+
+[*.{yml,md,xml}]
+indent_style = space
+indent_size = 2
+
+[*.{rst,php}]
+indent_style = space
+indent_size = 4
+
+[composer.json]
+indent_style = space
+indent_size = 2
+
+[composer.lock]
+indent_style = space
+indent_size = 4
diff --git a/vendor/maennchen/zipstream-php/.phive/phars.xml b/vendor/maennchen/zipstream-php/.phive/phars.xml
new file mode 100644
index 0000000..569106a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.phive/phars.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..3ba86a4
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
@@ -0,0 +1,71 @@
+
+ * @copyright 2022 Nicolas CARPi
+ * @see https://github.com/maennchen/ZipStream-PHP
+ * @license MIT
+ * @package maennchen/ZipStream-PHP
+ */
+
+use PhpCsFixer\Config;
+use PhpCsFixer\Finder;
+
+$finder = Finder::create()
+ ->exclude('.github')
+ ->exclude('.phpdoc')
+ ->exclude('docs')
+ ->exclude('tools')
+ ->exclude('vendor')
+ ->in(__DIR__);
+
+$config = new Config();
+return $config->setRules([
+ '@PER' => true,
+ '@PER:risky' => true,
+ '@PHP81Migration' => true,
+ '@PHPUnit84Migration:risky' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'class_attributes_separation' => true,
+ 'declare_strict_types' => true,
+ 'dir_constant' => true,
+ 'is_null' => true,
+ 'no_homoglyph_names' => true,
+ 'no_null_property_initialization' => true,
+ 'no_php4_constructor' => true,
+ 'no_unused_imports' => true,
+ 'no_useless_else' => true,
+ 'non_printable_character' => true,
+ 'ordered_imports' => true,
+ 'ordered_class_elements' => true,
+ 'php_unit_construct' => true,
+ 'pow_to_exponentiation' => true,
+ 'psr_autoloading' => true,
+ 'random_api_migration' => true,
+ 'return_assignment' => true,
+ 'self_accessor' => true,
+ 'semicolon_after_instruction' => true,
+ 'short_scalar_cast' => true,
+ 'simplified_null_return' => true,
+ 'single_blank_line_before_namespace' => true,
+ 'single_class_element_per_statement' => true,
+ 'single_line_comment_style' => true,
+ 'single_quote' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'strict_param' => true,
+ 'ternary_operator_spaces' => true,
+ 'trailing_comma_in_multiline' => true,
+ 'trim_array_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'global_namespace_import' => [
+ 'import_classes' => true,
+ 'import_functions' => true,
+ 'import_constants' => true,
+ ],
+ ])
+ ->setFinder($finder)
+ ->setRiskyAllowed(true);
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig b/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig
new file mode 100644
index 0000000..b7507fb
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig
@@ -0,0 +1,15 @@
+{% extends 'layout.html.twig' %}
+
+{% set topMenu = {
+ "menu": [
+ { "name": "Guides", "url": "https://maennchen.dev/ZipStream-PHP/guide/index.html"},
+ { "name": "API", "url": "https://maennchen.dev/ZipStream-PHP/classes/ZipStream-ZipStream.html"},
+ { "name": "Issues", "url": "https://github.com/maennchen/ZipStream-PHP/issues"},
+ ],
+ "social": [
+ { "iconClass": "fab fa-github", "url": "https://github.com/maennchen/ZipStream-PHP"},
+ { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/maennchen/ZipStream-PHP/discussions"},
+ { "iconClass": "fas fa-money-bill", "url": "https://opencollective.com/zipstream"},
+ ]
+}
+%}
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/.tool-versions b/vendor/maennchen/zipstream-php/.tool-versions
new file mode 100644
index 0000000..2618178
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.tool-versions
@@ -0,0 +1 @@
+php 8.2.5
diff --git a/vendor/maennchen/zipstream-php/LICENSE b/vendor/maennchen/zipstream-php/LICENSE
new file mode 100644
index 0000000..ebe7fe2
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/LICENSE
@@ -0,0 +1,24 @@
+MIT License
+
+Copyright (C) 2007-2009 Paul Duncan
+Copyright (C) 2014 Jonatan Männchen
+Copyright (C) 2014 Jesse G. Donat
+Copyright (C) 2018 Nicolas CARPi
+
+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.
diff --git a/vendor/maennchen/zipstream-php/README.md b/vendor/maennchen/zipstream-php/README.md
new file mode 100644
index 0000000..155a265
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/README.md
@@ -0,0 +1,114 @@
+# ZipStream-PHP
+
+[](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml)
+[](https://coveralls.io/github/maennchen/ZipStream-PHP?branch=main)
+[](https://packagist.org/packages/maennchen/zipstream-php)
+[](https://packagist.org/packages/maennchen/zipstream-php)
+[](https://opencollective.com/zipstream) [](LICENSE)
+
+## Unstable Branch
+
+The `main` branch is not stable. Please see the
+[releases](https://github.com/maennchen/ZipStream-PHP/releases) for a stable
+version.
+
+## Overview
+
+A fast and simple streaming zip file downloader for PHP. Using this library will save you from having to write the Zip to disk. You can directly send it to the user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
+
+Please see the [LICENSE](LICENSE) file for licensing and warranty information.
+
+## Installation
+
+Simply add a dependency on maennchen/zipstream-php to your project's composer.json file if you use Composer to manage the dependencies of your project. Use following command to add the package to your project's dependencies:
+
+```bash
+composer require maennchen/zipstream-php
+```
+
+## Usage
+
+For detailed instructions, please check the
+[Documentation](https://maennchen.dev/ZipStream-PHP/).
+
+Here's a simple example:
+
+```php
+// Autoload the dependencies
+require 'vendor/autoload.php';
+
+// enable output of HTTP headers
+$options = new ZipStream\Option\Archive();
+$options->setSendHttpHeaders(true);
+
+// create a new zipstream object
+$zip = new ZipStream\ZipStream('example.zip', $options);
+
+// create a file named 'hello.txt'
+$zip->addFile('hello.txt', 'This is the contents of hello.txt');
+
+// add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+$zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+
+// finish the zip stream
+$zip->finish();
+```
+
+## Upgrade to version 2.0.0
+
+- Only the self opened streams will be closed (#139)
+ If you were relying on ZipStream to close streams that the library didn't open,
+ you'll need to close them yourself now.
+
+## Upgrade to version 1.0.0
+
+- All options parameters to all function have been moved from an `array` to structured option objects. See [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Available-options) for examples.
+- The whole library has been refactored. The minimal PHP requirement has been raised to PHP 7.1.
+
+## Usage with Symfony and S3
+
+You can find example code on [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Symfony-example).
+
+## Contributing
+
+ZipStream-PHP is a collaborative project. Please take a look at the
+[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file.
+
+## About the Authors
+
+- Paul Duncan - https://pablotron.org/
+- Jonatan Männchen - https://maennchen.dev
+- Jesse G. Donat - https://donatstudios.com
+- Nicolas CARPi - https://www.deltablot.com
+- Nik Barham - https://www.brokencube.co.uk
+
+## Contributors
+
+### Code Contributors
+
+This project exists thanks to all the people who contribute.
+[[Contribute](.github/CONTRIBUTING.md)].
+
+
+### Financial Contributors
+
+Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/zipstream/contribute)]
+
+#### Individuals
+
+
+
+#### Organizations
+
+Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/zipstream/contribute)]
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/maennchen/zipstream-php/composer.json b/vendor/maennchen/zipstream-php/composer.json
new file mode 100644
index 0000000..89bc9ec
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/composer.json
@@ -0,0 +1,80 @@
+{
+ "name": "maennchen/zipstream-php",
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": ["zip", "stream"],
+ "type": "library",
+ "license": "MIT",
+ "authors": [{
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "require": {
+ "php": "^8.0",
+ "ext-mbstring": "*",
+ "psr/http-message": "^1.0",
+ "myclabs/php-enum": "^1.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+ "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+ "ext-zip": "*",
+ "mikey179/vfsstream": "^1.6",
+ "vimeo/psalm": "^5.0",
+ "php-coveralls/php-coveralls": "^2.4",
+ "friendsofphp/php-cs-fixer": "^3.9"
+ },
+ "scripts": {
+ "format": "php-cs-fixer fix",
+ "test": [
+ "@test:unit",
+ "@test:formatted",
+ "@test:lint"
+ ],
+ "test:unit": "phpunit --coverage-clover=coverage.clover.xml --coverage-html cov",
+ "test:formatted": "@format --dry-run --stop-on-violation --using-cache=no",
+ "test:lint": "psalm --stats --show-info --find-unused-psalm-suppress",
+ "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json --insecure",
+ "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656",
+ "docs:generate": "tools/phpdocumentor --sourcecode"
+ },
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "archive": {
+ "exclude": [
+ "/composer.lock",
+ "/docs",
+ "/.gitattributes",
+ "/.github",
+ "/.gitignore",
+ "/guides",
+ "/.phive",
+ "/.php-cs-fixer.cache",
+ "/.php-cs-fixer.dist.php",
+ "/.phpdoc",
+ "/phpdoc.dist.xml",
+ "/.phpunit.result.cache",
+ "/phpunit.xml.dist",
+ "/psalm.xml",
+ "/test",
+ "/tools",
+ "/.tool-versions",
+ "/vendor"
+ ]
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/guides/ContentLength.rst b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
new file mode 100644
index 0000000..e51e692
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
@@ -0,0 +1,79 @@
+Adding Content-Length header
+=============
+
+Adding a ``Content-Length`` header for ``ZipStream`` is not trivial since the
+size is not known beforehand.
+
+The following workaround adds an approximated header:
+
+.. code-block:: php
+
+ class Zip
+ {
+ /** @var string */
+ private $name;
+
+ private $files = [];
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function addFile($name, $data)
+ {
+ $this->files[] = ['type' => 'addFile', 'name' => $name, 'data' => $data];
+ }
+
+ public function addFileFromPath($name, $path)
+ {
+ $this->files[] = ['type' => 'addFileFromPath', 'name' => $name, 'path' => $path];
+ }
+
+ public function getEstimate()
+ {
+ $estimate = 22;
+ foreach ($this->files as $file) {
+ $estimate += 76 + 2 * strlen($file['name']);
+ if ($file['type'] === 'addFile') {
+ $estimate += strlen($file['data']);
+ }
+ if ($file['type'] === 'addFileFromPath') {
+ $estimate += filesize($file['path']);
+ }
+ }
+ return $estimate;
+ }
+
+ public function finish()
+ {
+ header('Content-Length: ' . $this->getEstimate());
+ $options = new \ZipStream\Option\Archive();
+ $options->setSendHttpHeaders(true);
+ $options->setEnableZip64(false);
+ $options->setDeflateLevel(-1);
+ $zip = new \ZipStream\ZipStream($this->name, $options);
+
+ $fileOptions = new \ZipStream\Option\File();
+ $fileOptions->setMethod(\ZipStream\Option\Method::STORE());
+ foreach ($this->files as $file) {
+ if ($file['type'] === 'addFile') {
+ $zip->addFile($file['name'], $file['data'], $fileOptions);
+ }
+ if ($file['type'] === 'addFileFromPath') {
+ $zip->addFileFromPath($file['name'], $file['path'], $fileOptions);
+ }
+ }
+ $zip->finish();
+ exit;
+ }
+ }
+
+It only works with the following constraints:
+
+- All file content is known beforehand.
+- Content Deflation is disabled
+
+Thanks to
+`partiellkorrekt `_
+for this workaround.
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/FlySystem.rst b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
new file mode 100644
index 0000000..0243f24
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
@@ -0,0 +1,33 @@
+Usage with FlySystem
+===============
+
+For saving or uploading the generated zip, you can use the
+`Flysystem `_ package, and its many
+adapters.
+
+For that you will need to provide another stream than the ``php://output``
+default one, and pass it to Flysystem ``putStream`` method.
+
+.. code-block:: php
+
+ // Open Stream only once for read and write since it's a memory stream and
+ // the content is lost when closing the stream / opening another one
+ $tempStream = fopen('php://memory', 'w+');
+
+ // Init Options
+ $zipStreamOptions = new Archive();
+ $zipStreamOptions->setOutputStream($tempStream);
+
+ // Create Zip Archive
+ $zipStream = new ZipStream('test.zip', $zipStreamOptions);
+ $zipStream->addFile('test.txt', 'text');
+ $zipStream->finish();
+
+ // Store File (see Flysystem documentation, and all its framework integration)
+ $adapter = new Local(__DIR__.'/path/to/folder'); // Can be any adapter (AWS, Google, Ftp, etc.)
+ $filesystem = new Filesystem($adapter);
+
+ $filesystem->putStream('test.zip', $tempStream)
+
+ // Close Stream
+ fclose($tempStream);
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Nginx.rst b/vendor/maennchen/zipstream-php/guides/Nginx.rst
new file mode 100644
index 0000000..c53d300
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Nginx.rst
@@ -0,0 +1,16 @@
+Usage with nginx
+=============
+
+If you are using nginx as a webserver, it will try to buffer the response.
+So you'll want to disable this with a custom header:
+
+.. code-block:: php
+ header('X-Accel-Buffering: no');
+ # or with the Response class from Symfony
+ $response->headers->set('X-Accel-Buffering', 'no');
+
+Alternatively, you can tweak the
+`fastcgi cache parameters `_
+within nginx config.
+
+See `original issue `_.
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Options.rst b/vendor/maennchen/zipstream-php/guides/Options.rst
new file mode 100644
index 0000000..eabaa6f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Options.rst
@@ -0,0 +1,61 @@
+Available options
+===============
+
+Here is the full list of options available to you. You can also have a look at
+``src/Option/Archive.php`` file.
+
+First, an instance of ``ZipStream\Option\Archive`` needs to be created, and
+after that you use setters methods to modify the values.
+
+.. code-block:: php
+ use ZipStream\ZipStream;
+ use ZipStream\Option\Archive as ArchiveOptions;
+
+ require_once 'vendor/autoload.php';
+
+ $opt = new ArchiveOptions();
+
+ // Define output stream (argument is of type resource)
+ $opt->setOutputStream($fd);
+
+ // Set the deflate level (default is 6; use -1 to disable it)
+ $opt->setDeflateLevel(6);
+
+ // Add a comment to the zip file
+ $opt->setComment('This is a comment.');
+
+ // Size, in bytes, of the largest file to try and load into memory (used by addFileFromPath()). Large files may also be compressed differently; see the 'largeFileMethod' option.
+ $opt->setLargeFileSize(30000000);
+
+ // How to handle large files. Legal values are STORE (the default), or DEFLATE. Store sends the file raw and is significantly faster, while DEFLATE compresses the file and is much, much slower. Note that deflate must compress the file twice and is extremely slow.
+ $opt->setLargeFileMethod(ZipStream\Option\Method::STORE());
+ $opt->setLargeFileMethod(ZipStream\Option\Method::DEFLATE());
+
+ // Send http headers (default is false)
+ $opt->setSendHttpHeaders(false);
+
+ // HTTP Content-Disposition. Defaults to 'attachment', where FILENAME is the specified filename. Note that this does nothing if you are not sending HTTP headers.
+ $opt->setContentDisposition('attachment');
+
+ // Set the content type (does nothing if you are not sending HTTP headers)
+ $opt->setContentType('application/x-zip');
+
+ // Set the function called for setting headers. Default is the `header()` of PHP
+ $opt->setHttpHeaderCallback('header');
+
+ // Enable streaming files with single read where general purpose bit 3 indicates local file header contain zero values in crc and size fields, these appear only after file contents in data descriptor block. Default is false. Set to true if your input stream is remote (used with addFileFromStream()).
+ $opt->setZeroHeader(false);
+
+ // Enable reading file stat for determining file size. When a 32-bit system reads file size that is over 2 GB, invalid value appears in file size due to integer overflow. Should be disabled on 32-bit systems with method addFileFromPath if any file may exceed 2 GB. In this case file will be read in blocks and correct size will be determined from content. Default is true.
+ $opt->setStatFiles(true);
+
+ // Enable zip64 extension, allowing very large archives (> 4Gb or file count > 64k)
+ // default is true
+ $opt->setEnableZip64(true);
+
+ // Flush output buffer after every write
+ // default is false
+ $opt->setFlushOutput(true);
+
+ // Now that everything is set you can pass the options to the ZipStream instance
+ $zip = new ZipStream('example.zip', $opt);
diff --git a/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
new file mode 100644
index 0000000..4b4ca4b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
@@ -0,0 +1,18 @@
+Usage with PSR 7 Streams
+===============
+
+PSR-7 streams are `standardized streams `_.
+
+ZipStream-PHP supports working with these streams with the function
+``addFileFromPsr7Stream``.
+
+For all parameters of the function see the API documentation.
+
+Example
+---------------
+
+.. code-block:: php
+
+ $stream = $response->getBody();
+ // add a file named 'streamfile.txt' from the content of the stream
+ $zip->addFileFromPsr7Stream('streamfile.txt', $stream);
diff --git a/vendor/maennchen/zipstream-php/guides/StreamOutput.rst b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
new file mode 100644
index 0000000..1a0495f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
@@ -0,0 +1,33 @@
+Stream Output
+===============
+
+Stream to S3 Bucket
+---------------
+
+.. code-block:: php
+ use Aws\S3\S3Client;
+ use Aws\Credentials\CredentialProvider;
+ use ZipStream\Option\Archive;
+ use ZipStream\ZipStream;
+
+ $bucket = 'your bucket name';
+ $client = new S3Client([
+ 'region' => 'your region',
+ 'version' => 'latest',
+ 'bucketName' => $bucket,
+ 'credentials' => CredentialProvider::defaultProvider(),
+ ]);
+ $client->registerStreamWrapper();
+
+ $zipFile = fopen("s3://$bucket/example.zip", 'w');
+
+ $options = new Archive();
+ $options->setEnableZip64(false);
+ $options->setOutputStream($zipFile);
+
+ $zip = new ZipStream(null, $options);
+ $zip->addFile('file1.txt', 'File1 data');
+ $zip->addFile('file2.txt', 'File2 data');
+ $zip->finish();
+
+ fclose($zipFile);
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Symfony.rst b/vendor/maennchen/zipstream-php/guides/Symfony.rst
new file mode 100644
index 0000000..18f9059
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Symfony.rst
@@ -0,0 +1,126 @@
+Usage with Symfony
+===============
+
+Overview for using ZipStream in Symfony
+--------
+
+Using ZipStream in Symfony requires use of Symfony's ``StreamedResponse`` when
+used in controller actions.
+
+Wrap your call to the relevant ``ZipStream`` stream method (i.e. ``addFile``,
+``addFileFromPath``, ``addFileFromStream``) in Symfony's ``StreamedResponse``
+function passing in any required arguments for your use case.
+
+Using Symfony's ``StreamedResponse`` will allow Symfony to stream output from
+ZipStream correctly to users' browsers and avoid a corrupted final zip landing
+on the users' end.
+
+Example for using ``ZipStream`` in a controller action to zip stream files
+stored in an AWS S3 bucket by key:
+
+.. code-block:: php
+
+ use Symfony\Component\HttpFoundation\StreamedResponse;
+ use Aws\S3\S3Client;
+ use ZipStream;
+
+ //...
+
+ /**
+ * @Route("/zipstream", name="zipstream")
+ */
+ public function zipStreamAction()
+ {
+ //sample test file on s3
+ $s3keys = array(
+ "ziptestfolder/file1.txt"
+ );
+
+ $s3Client = $this->get('app.amazon.s3'); //s3client service
+ $s3Client->registerStreamWrapper(); //required
+
+ //using StreamedResponse to wrap ZipStream functionality for files on AWS s3.
+ $response = new StreamedResponse(function() use($s3keys, $s3Client)
+ {
+ // Define suitable options for ZipStream Archive.
+ $options = new \ZipStream\Option\Archive();
+ $options->setContentType('application/octet-stream');
+ // this is needed to prevent issues with truncated zip files
+ $options->setZeroHeader(true);
+ $options->setComment('test zip file.');
+
+ //initialise zipstream with output zip filename and options.
+ $zip = new ZipStream\ZipStream('test.zip', $options);
+
+ //loop keys - useful for multiple files
+ foreach ($s3keys as $key) {
+ // Get the file name in S3 key so we can save it to the zip
+ //file using the same name.
+ $fileName = basename($key);
+
+ //concatenate s3path.
+ $bucket = 'bucketname'; //replace with your bucket name or get from parameters file.
+ $s3path = "s3://" . $bucket . "/" . $key;
+
+ //addFileFromStream
+ if ($streamRead = fopen($s3path, 'r')) {
+ $zip->addFileFromStream($fileName, $streamRead);
+ } else {
+ die('Could not open stream for reading');
+ }
+ }
+
+ $zip->finish();
+
+ });
+
+ return $response;
+ }
+
+In the above example, files on AWS S3 are being streamed from S3 to the Symfon
+application via ``fopen`` call when the s3Client has ``registerStreamWrapper``
+applied. This stream is then passed to ``ZipStream`` via the
+``addFileFromStream`` function, which ZipStream then streams as a zip to the
+client browser via Symfony's ``StreamedResponse``. No Zip is created server
+side, which makes this approach a more efficient solution for streaming zips to
+the client browser especially for larger files.
+
+For the above use case you will need to have installed
+`aws/aws-sdk-php-symfony `_ to
+support accessing S3 objects in your Symfony web application. This is not
+required for locally stored files on you server you intend to stream via
+``ZipStream``.
+
+See official Symfony documentation for details on
+`Symfony's StreamedResponse `_
+``Symfony\Component\HttpFoundation\StreamedResponse``.
+
+Note from `S3 documentation `_:
+
+ Streams opened in "r" mode only allow data to be read from the stream, and
+ are not seekable by default. This is so that data can be downloaded from
+ Amazon S3 in a truly streaming manner, where previously read bytes do not
+ need to be buffered into memory. If you need a stream to be seekable, you
+ can pass seekable into the stream context options of a function.
+
+Make sure to configure your S3 context correctly!
+
+Uploading a file
+--------
+
+You need to add correct permissions
+(see `#120 `_)
+
+**example code**
+
+
+.. code-block:: php
+
+ $path = "s3://{$adapter->getBucket()}/{$this->getArchivePath()}";
+
+ // the important bit
+ $outputContext = stream_context_create([
+ 's3' => ['ACL' => 'public-read'],
+ ]);
+
+ fopen($path, 'w', null, $outputContext);
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Varnish.rst b/vendor/maennchen/zipstream-php/guides/Varnish.rst
new file mode 100644
index 0000000..952d287
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Varnish.rst
@@ -0,0 +1,22 @@
+Usage with Varnish
+=============
+
+Serving a big zip with varnish in between can cause random stream close.
+This can be solved by adding attached code to the vcl file.
+
+To avoid the problem, add the following to your varnish config file:
+
+.. code-block::
+ sub vcl_recv {
+ # Varnish can’t intercept the discussion anymore
+ # helps for streaming big zips
+ if (req.url ~ "\.(tar|gz|zip|7z|exe)$") {
+ return (pipe);
+ }
+ }
+ # Varnish can’t intercept the discussion anymore
+ # helps for streaming big zips
+ sub vcl_pipe {
+ set bereq.http.connection = "close";
+ return (pipe);
+ }
diff --git a/vendor/maennchen/zipstream-php/guides/index.rst b/vendor/maennchen/zipstream-php/guides/index.rst
new file mode 100644
index 0000000..67f504b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/index.rst
@@ -0,0 +1,100 @@
+ZipStream PHP
+=============
+
+A fast and simple streaming zip file downloader for PHP. Using this library will
+save you from having to write the Zip to disk. You can directly send it to the
+user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
+
+.. toctree::
+
+ index
+ Symfony
+ Options
+ StreamOutput
+ FlySystem
+ PSR7Streams
+ Nginx
+ Varnish
+ ContentLength
+
+Installation
+---------------
+
+Simply add a dependency on ``maennchen/zipstream-php`` to your project's
+``composer.json`` file if you use Composer to manage the dependencies of your
+project. Use following command to add the package to your project's dependencies:
+
+.. code-block:: sh
+ composer require maennchen/zipstream-php
+
+If ``composer install`` yields the following error, your installation is missing
+the `mbstring extension `_,
+either `install it `_
+or run the follwoing command:
+
+.. code-block::
+ Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires PHP extension ext-mbstring * but it is
+ missing from your system. Install or enable PHP's mbstrings extension.
+
+.. code-block:: sh
+ composer require symfony/polyfill-mbstring
+
+Usage Intro
+---------------
+
+Here's a simple example:
+
+.. code-block:: php
+
+ // Autoload the dependencies
+ require 'vendor/autoload.php';
+
+ // enable output of HTTP headers
+ $options = new ZipStream\Option\Archive();
+ $options->setSendHttpHeaders(true);
+
+ // create a new zipstream object
+ $zip = new ZipStream\ZipStream('example.zip', $options);
+
+ // create a file named 'hello.txt'
+ $zip->addFile('hello.txt', 'This is the contents of hello.txt');
+
+ // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+ $zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+
+ // add a file named 'goodbye.txt' from an open stream resource
+ $fp = tmpfile();
+ fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
+ rewind($fp);
+ $zip->addFileFromStream('goodbye.txt', $fp);
+ fclose($fp);
+
+ // finish the zip stream
+ $zip->finish();
+
+You can also add comments, modify file timestamps, and customize (or
+disable) the HTTP headers. It is also possible to specify the storage method
+when adding files, the current default storage method is ``DEFLATE``
+i.e files are stored with Compression mode 0x08.
+
+Known Issues
+---------------
+
+The native Mac OS archive extraction tool prior to macOS 10.15 might not open
+archives in some conditions. A workaround is to disable the Zip64 feature with
+the option ``enableZip64: false``. This limits the archive to 4 Gb and 64k files
+but will allow users on macOS 10.14 and below to open them without issue.
+See `#116 `_.
+
+The linux ``unzip`` utility might not handle properly unicode characters.
+It is recommended to extract with another tool like
+`7-zip `_.
+See `#146 `_.
+
+It is the responsability of the client code to make sure that files are not
+saved with the same path, as it is not possible for the library to figure it out
+while streaming a zip.
+See `#154 `_.
diff --git a/vendor/maennchen/zipstream-php/phpdoc.dist.xml b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
new file mode 100644
index 0000000..b98fe1c
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
@@ -0,0 +1,39 @@
+
+
+ 💾 ZipStream-PHP
+
+ docs
+
+
+ latest
+
+
+ src
+
+ api
+
+ tests/**/*
+ vendor/**/*
+
+
+ php
+
+ public
+ ZipStream
+ true
+
+
+
+ guides
+
+ guide
+
+
+
+
+
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/phpunit.xml.dist b/vendor/maennchen/zipstream-php/phpunit.xml.dist
new file mode 100644
index 0000000..8a2f318
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/phpunit.xml.dist
@@ -0,0 +1,14 @@
+
+
+
+
+ src
+
+
+
+
+ test
+
+
+
+
diff --git a/vendor/maennchen/zipstream-php/psalm.xml b/vendor/maennchen/zipstream-php/psalm.xml
new file mode 100644
index 0000000..4e4c4f6
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/psalm.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/maennchen/zipstream-php/src/Bigint.php b/vendor/maennchen/zipstream-php/src/Bigint.php
new file mode 100644
index 0000000..f2565e9
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Bigint.php
@@ -0,0 +1,174 @@
+fillBytes($value, 0, 8);
+ }
+
+ /**
+ * Get an instance
+ *
+ * @param int $value
+ * @return Bigint
+ */
+ public static function init(int $value = 0): self
+ {
+ return new self($value);
+ }
+
+ /**
+ * Fill bytes from low to high
+ *
+ * @param int $low
+ * @param int $high
+ * @return Bigint
+ */
+ public static function fromLowHigh(int $low, int $high): self
+ {
+ $bigint = new self();
+ $bigint->fillBytes($low, 0, 4);
+ $bigint->fillBytes($high, 4, 4);
+ return $bigint;
+ }
+
+ /**
+ * Get high 32
+ *
+ * @return int
+ */
+ public function getHigh32(): int
+ {
+ return $this->getValue(4, 4);
+ }
+
+ /**
+ * Get value from bytes array
+ *
+ * @param int $end
+ * @param int $length
+ * @return int
+ */
+ public function getValue(int $end = 0, int $length = 8): int
+ {
+ $result = 0;
+ for ($i = $end + $length - 1; $i >= $end; $i--) {
+ $result <<= 8;
+ $result |= $this->bytes[$i];
+ }
+ return $result;
+ }
+
+ /**
+ * Get low FF
+ *
+ * @param bool $force
+ * @return float
+ */
+ public function getLowFF(bool $force = false): float
+ {
+ if ($force || $this->isOver32()) {
+ return (float)0xFFFFFFFF;
+ }
+ return (float)$this->getLow32();
+ }
+
+ /**
+ * Check if is over 32
+ *
+ * @psalm-suppress ArgumentTypeCoercion
+ * @param bool $force
+ * @return bool
+ */
+ public function isOver32(bool $force = false): bool
+ {
+ // value 0xFFFFFFFF already needs a Zip64 header
+ return $force ||
+ max(array_slice($this->bytes, 4, 4)) > 0 ||
+ min(array_slice($this->bytes, 0, 4)) === 0xFF;
+ }
+
+ /**
+ * Get low 32
+ *
+ * @return int
+ */
+ public function getLow32(): int
+ {
+ return $this->getValue(0, 4);
+ }
+
+ /**
+ * Get hexadecimal
+ *
+ * @return string
+ */
+ public function getHex64(): string
+ {
+ $result = '0x';
+ for ($i = 7; $i >= 0; $i--) {
+ $result .= sprintf('%02X', $this->bytes[$i]);
+ }
+ return $result;
+ }
+
+ /**
+ * Add
+ *
+ * @param Bigint $other
+ * @return Bigint
+ */
+ public function add(self $other): self
+ {
+ $result = clone $this;
+ $overflow = false;
+ for ($i = 0; $i < 8; $i++) {
+ $result->bytes[$i] += $other->bytes[$i];
+ if ($overflow) {
+ $result->bytes[$i]++;
+ $overflow = false;
+ }
+ if ($result->bytes[$i] & 0x100) {
+ $overflow = true;
+ $result->bytes[$i] &= 0xFF;
+ }
+ }
+ if ($overflow) {
+ throw new OverflowException();
+ }
+ return $result;
+ }
+
+ /**
+ * Fill the bytes field with int
+ *
+ * @param int $value
+ * @param int $start
+ * @param int $count
+ * @return void
+ */
+ protected function fillBytes(int $value, int $start, int $count): void
+ {
+ for ($i = 0; $i < $count; $i++) {
+ $this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
+ $value >>= 8;
+ }
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/DeflateStream.php b/vendor/maennchen/zipstream-php/src/DeflateStream.php
new file mode 100644
index 0000000..210cbd2
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/DeflateStream.php
@@ -0,0 +1,27 @@
+zip = $zip;
+
+ $this->name = $name;
+ $this->opt = $opt ?: new FileOptions();
+ $this->method = $this->opt->getMethod();
+ $this->version = Version::STORE();
+ $this->ofs = new Bigint();
+ }
+
+ public function processPath(string $path): void
+ {
+ if (!is_readable($path)) {
+ if (!file_exists($path)) {
+ throw new FileNotFoundException($path);
+ }
+ throw new FileNotReadableException($path);
+ }
+ if ($this->zip->isLargeFile($path) === false) {
+ $data = file_get_contents($path);
+ $this->processData($data);
+ } else {
+ $this->method = $this->zip->opt->getLargeFileMethod();
+
+ $stream = new Stream(fopen($path, 'rb'));
+ $this->processStream($stream);
+ $stream->close();
+ }
+ }
+
+ public function processData(string $data): void
+ {
+ $this->len = new Bigint(strlen($data));
+ $this->crc = crc32($data);
+
+ // compress data if needed
+ if ($this->method->equals(Method::DEFLATE())) {
+ $data = gzdeflate($data);
+ }
+
+ $this->zlen = new Bigint(strlen($data));
+ $this->addFileHeader();
+ $this->zip->send($data);
+ $this->addFileFooter();
+ }
+
+ /**
+ * Create and send zip header for this file.
+ *
+ * @return void
+ * @throws \ZipStream\Exception\EncodingException
+ */
+ public function addFileHeader(): void
+ {
+ $name = static::filterFilename($this->name);
+
+ // calculate name length
+ $nameLength = strlen($name);
+
+ // create dos timestamp
+ $time = static::dosTime($this->opt->getTime()->getTimestamp());
+
+ $comment = $this->opt->getComment();
+
+ if (!mb_check_encoding($name, 'ASCII') ||
+ !mb_check_encoding($comment, 'ASCII')) {
+ // Sets Bit 11: Language encoding flag (EFS). If this bit is set,
+ // the filename and comment fields for this file
+ // MUST be encoded using UTF-8. (see APPENDIX D)
+ if (mb_check_encoding($name, 'UTF-8') &&
+ mb_check_encoding($comment, 'UTF-8')) {
+ $this->bits |= self::BIT_EFS_UTF8;
+ }
+ }
+
+ if ($this->method->equals(Method::DEFLATE())) {
+ $this->version = Version::DEFLATE();
+ }
+
+ $force = (bool)($this->bits & self::BIT_ZERO_HEADER) &&
+ $this->zip->opt->isEnableZip64();
+
+ $footer = $this->buildZip64ExtraBlock($force);
+
+ // If this file will start over 4GB limit in ZIP file,
+ // CDR record will have to use Zip64 extension to describe offset
+ // to keep consistency we use the same value here
+ if ($this->zip->ofs->isOver32()) {
+ $this->version = Version::ZIP64();
+ }
+
+ $fields = [
+ ['V', ZipStream::FILE_HEADER_SIGNATURE],
+ ['v', $this->version->getValue()], // Version needed to Extract
+ ['v', $this->bits], // General purpose bit flags - data descriptor flag set
+ ['v', $this->method->getValue()], // Compression method
+ ['V', $time], // Timestamp (DOS Format)
+ ['V', $this->crc], // CRC32 of data (0 -> moved to data descriptor footer)
+ ['V', $this->zlen->getLowFF($force)], // Length of compressed data (forced to 0xFFFFFFFF for zero header)
+ ['V', $this->len->getLowFF($force)], // Length of original data (forced to 0xFFFFFFFF for zero header)
+ ['v', $nameLength], // Length of filename
+ ['v', strlen($footer)], // Extra data (see above)
+ ];
+
+ // pack fields and calculate "total" length
+ $header = ZipStream::packFields($fields);
+
+ // print header and filename
+ $data = $header . $name . $footer;
+ $this->zip->send($data);
+
+ // save header length
+ $this->hlen = Bigint::init(strlen($data));
+ }
+
+ /**
+ * Strip characters that are not legal in Windows filenames
+ * to prevent compatibility issues
+ *
+ * @param string $filename Unprocessed filename
+ * @return string
+ */
+ public static function filterFilename(string $filename): string
+ {
+ // strip leading slashes from file name
+ // (fixes bug in windows archive viewer)
+ $filename = preg_replace('/^\\/+/', '', $filename);
+
+ return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename);
+ }
+
+ /**
+ * Create and send data descriptor footer for this file.
+ *
+ * @return void
+ */
+ public function addFileFooter(): void
+ {
+ if ($this->bits & self::BIT_ZERO_HEADER) {
+ // compressed and uncompressed size
+ $sizeFormat = 'V';
+ if ($this->zip->opt->isEnableZip64()) {
+ $sizeFormat = 'P';
+ }
+ $fields = [
+ ['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE],
+ ['V', $this->crc], // CRC32
+ [$sizeFormat, $this->zlen], // Length of compressed data
+ [$sizeFormat, $this->len], // Length of original data
+ ];
+
+ $footer = ZipStream::packFields($fields);
+ $this->zip->send($footer);
+ } else {
+ $footer = '';
+ }
+ $this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer)));
+ $this->zip->addToCdr($this);
+ }
+
+ public function processStream(StreamInterface $stream): void
+ {
+ $this->zlen = new Bigint();
+ $this->len = new Bigint();
+
+ if ($this->zip->opt->isZeroHeader()) {
+ $this->processStreamWithZeroHeader($stream);
+ } else {
+ $this->processStreamWithComputedHeader($stream);
+ }
+ }
+
+ /**
+ * Send CDR record for specified file.
+ *
+ * @return string
+ */
+ public function getCdrFile(): string
+ {
+ $name = static::filterFilename($this->name);
+
+ // get attributes
+ $comment = $this->opt->getComment();
+
+ // get dos timestamp
+ $time = static::dosTime($this->opt->getTime()->getTimestamp());
+
+ $footer = $this->buildZip64ExtraBlock();
+
+ $fields = [
+ ['V', ZipStream::CDR_FILE_SIGNATURE], // Central file header signature
+ ['v', ZipStream::ZIP_VERSION_MADE_BY], // Made by version
+ ['v', $this->version->getValue()], // Extract by version
+ ['v', $this->bits], // General purpose bit flags - data descriptor flag set
+ ['v', $this->method->getValue()], // Compression method
+ ['V', $time], // Timestamp (DOS Format)
+ ['V', $this->crc], // CRC32
+ ['V', $this->zlen->getLowFF()], // Compressed Data Length
+ ['V', $this->len->getLowFF()], // Original Data Length
+ ['v', strlen($name)], // Length of filename
+ ['v', strlen($footer)], // Extra data len (see above)
+ ['v', strlen($comment)], // Length of comment
+ ['v', 0], // Disk number
+ ['v', 0], // Internal File Attributes
+ ['V', 32], // External File Attributes
+ ['V', $this->ofs->getLowFF()], // Relative offset of local header
+ ];
+
+ // pack fields, then append name and comment
+ $header = ZipStream::packFields($fields);
+
+ return $header . $name . $footer . $comment;
+ }
+
+ /**
+ * @return Bigint
+ */
+ public function getTotalLength(): Bigint
+ {
+ return $this->totalLength;
+ }
+
+ /**
+ * Convert a UNIX timestamp to a DOS timestamp.
+ *
+ * @param int $when
+ * @return int DOS Timestamp
+ */
+ final protected static function dosTime(int $when): int
+ {
+ // get date array for timestamp
+ $d = getdate($when);
+
+ // set lower-bound on dates
+ if ($d['year'] < 1980) {
+ $d = [
+ 'year' => 1980,
+ 'mon' => 1,
+ 'mday' => 1,
+ 'hours' => 0,
+ 'minutes' => 0,
+ 'seconds' => 0,
+ ];
+ }
+
+ // remove extra years from 1980
+ $d['year'] -= 1980;
+
+ // return date string
+ return
+ ($d['year'] << 25) |
+ ($d['mon'] << 21) |
+ ($d['mday'] << 16) |
+ ($d['hours'] << 11) |
+ ($d['minutes'] << 5) |
+ ($d['seconds'] >> 1);
+ }
+
+ protected function buildZip64ExtraBlock(bool $force = false): string
+ {
+ $fields = [];
+ if ($this->len->isOver32($force)) {
+ $fields[] = ['P', $this->len]; // Length of original data
+ }
+
+ if ($this->len->isOver32($force)) {
+ $fields[] = ['P', $this->zlen]; // Length of compressed data
+ }
+
+ if ($this->ofs->isOver32()) {
+ $fields[] = ['P', $this->ofs]; // Offset of local header record
+ }
+
+ if (!empty($fields)) {
+ if (!$this->zip->opt->isEnableZip64()) {
+ throw new OverflowException();
+ }
+
+ array_unshift(
+ $fields,
+ ['v', 0x0001], // 64 bit extension
+ ['v', count($fields) * 8] // Length of data block
+ );
+ $this->version = Version::ZIP64();
+ }
+
+ if ($this->bits & self::BIT_EFS_UTF8) {
+ // Put the tricky entry to
+ // force Linux unzip to lookup EFS flag.
+ $fields[] = ['v', 0x5653]; // Choose 'ZS' for proprietary usage
+ $fields[] = ['v', 0x0000]; // zero length
+ }
+
+ return ZipStream::packFields($fields);
+ }
+
+ protected function processStreamWithZeroHeader(StreamInterface $stream): void
+ {
+ $this->bits |= self::BIT_ZERO_HEADER;
+ $this->addFileHeader();
+ $this->readStream($stream, self::COMPUTE | self::SEND);
+ $this->addFileFooter();
+ }
+
+ protected function readStream(StreamInterface $stream, ?int $options = null): void
+ {
+ $this->deflateInit();
+ $total = 0;
+ $size = $this->opt->getSize();
+ while (!$stream->eof() && ($size === 0 || $total < $size)) {
+ $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
+ $total += strlen($data);
+ if ($size > 0 && $total > $size) {
+ $data = substr($data, 0, strlen($data)-($total - $size));
+ }
+ $this->deflateData($stream, $data, $options);
+ if ($options & self::SEND) {
+ $this->zip->send($data);
+ }
+ }
+ $this->deflateFinish($options);
+ }
+
+ protected function deflateInit(): void
+ {
+ $hash = hash_init(self::HASH_ALGORITHM);
+ $this->hash = $hash;
+ if ($this->method->equals(Method::DEFLATE())) {
+ $this->deflate = deflate_init(
+ ZLIB_ENCODING_RAW,
+ ['level' => $this->opt->getDeflateLevel()]
+ );
+ }
+ }
+
+ protected function deflateData(StreamInterface $stream, string &$data, ?int $options = null): void
+ {
+ if ($options & self::COMPUTE) {
+ $this->len = $this->len->add(Bigint::init(strlen($data)));
+ hash_update($this->hash, $data);
+ }
+ if ($this->deflate) {
+ /** @psalm-suppress PossiblyInvalidArgument */
+ $data = deflate_add(
+ $this->deflate,
+ $data,
+ $stream->eof()
+ ? ZLIB_FINISH
+ : ZLIB_NO_FLUSH
+ );
+ }
+ if ($options & self::COMPUTE) {
+ $this->zlen = $this->zlen->add(Bigint::init(strlen($data)));
+ }
+ }
+
+ protected function deflateFinish(?int $options = null): void
+ {
+ if ($options & self::COMPUTE) {
+ $this->crc = hexdec(hash_final($this->hash));
+ }
+ }
+
+ protected function processStreamWithComputedHeader(StreamInterface $stream): void
+ {
+ $this->readStream($stream, self::COMPUTE);
+ $stream->rewind();
+
+ $this->addFileHeader();
+ $this->readStream($stream, self::SEND);
+ $this->addFileFooter();
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Archive.php b/vendor/maennchen/zipstream-php/src/Option/Archive.php
new file mode 100644
index 0000000..374dd1d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/Archive.php
@@ -0,0 +1,276 @@
+ 4 GB or file count > 64k)
+ *
+ * @var bool
+ */
+ private $enableZip64 = true;
+
+ /**
+ * Enable streaming files with single read where
+ * general purpose bit 3 indicates local file header
+ * contain zero values in crc and size fields,
+ * these appear only after file contents
+ * in data descriptor block.
+ *
+ * @var bool
+ */
+ private $zeroHeader = false;
+
+ /**
+ * Enable reading file stat for determining file size.
+ * When a 32-bit system reads file size that is
+ * over 2 GB, invalid value appears in file size
+ * due to integer overflow. Should be disabled on
+ * 32-bit systems with method addFileFromPath
+ * if any file may exceed 2 GB. In this case file
+ * will be read in blocks and correct size will be
+ * determined from content.
+ *
+ * @var bool
+ */
+ private $statFiles = true;
+
+ /**
+ * Enable flush after every write to output stream.
+ * @var bool
+ */
+ private $flushOutput = false;
+
+ /**
+ * HTTP Content-Disposition. Defaults to
+ * 'attachment', where
+ * FILENAME is the specified filename.
+ *
+ * Note that this does nothing if you are
+ * not sending HTTP headers.
+ *
+ * @var string
+ */
+ private $contentDisposition = 'attachment';
+
+ /**
+ * Note that this does nothing if you are
+ * not sending HTTP headers.
+ *
+ * @var string
+ */
+ private $contentType = 'application/x-zip';
+
+ /**
+ * @var int
+ */
+ private $deflateLevel = 6;
+
+ /**
+ * @var StreamInterface|resource
+ */
+ private $outputStream;
+
+ /**
+ * Options constructor.
+ */
+ public function __construct()
+ {
+ $this->largeFileMethod = Method::STORE();
+ $this->outputStream = fopen('php://output', 'wb');
+ }
+
+ public function getComment(): string
+ {
+ return $this->comment;
+ }
+
+ public function setComment(string $comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ public function getLargeFileSize(): int
+ {
+ return $this->largeFileSize;
+ }
+
+ public function setLargeFileSize(int $largeFileSize): void
+ {
+ $this->largeFileSize = $largeFileSize;
+ }
+
+ public function getLargeFileMethod(): Method
+ {
+ return $this->largeFileMethod;
+ }
+
+ public function setLargeFileMethod(Method $largeFileMethod): void
+ {
+ $this->largeFileMethod = $largeFileMethod;
+ }
+
+ public function isSendHttpHeaders(): bool
+ {
+ return $this->sendHttpHeaders;
+ }
+
+ public function setSendHttpHeaders(bool $sendHttpHeaders): void
+ {
+ $this->sendHttpHeaders = $sendHttpHeaders;
+ }
+
+ public function getHttpHeaderCallback(): callable
+ {
+ return $this->httpHeaderCallback;
+ }
+
+ public function setHttpHeaderCallback(callable $httpHeaderCallback): void
+ {
+ $this->httpHeaderCallback = $httpHeaderCallback;
+ }
+
+ public function isEnableZip64(): bool
+ {
+ return $this->enableZip64;
+ }
+
+ public function setEnableZip64(bool $enableZip64): void
+ {
+ $this->enableZip64 = $enableZip64;
+ }
+
+ public function isZeroHeader(): bool
+ {
+ return $this->zeroHeader;
+ }
+
+ public function setZeroHeader(bool $zeroHeader): void
+ {
+ $this->zeroHeader = $zeroHeader;
+ }
+
+ public function isFlushOutput(): bool
+ {
+ return $this->flushOutput;
+ }
+
+ public function setFlushOutput(bool $flushOutput): void
+ {
+ $this->flushOutput = $flushOutput;
+ }
+
+ public function isStatFiles(): bool
+ {
+ return $this->statFiles;
+ }
+
+ public function setStatFiles(bool $statFiles): void
+ {
+ $this->statFiles = $statFiles;
+ }
+
+ public function getContentDisposition(): string
+ {
+ return $this->contentDisposition;
+ }
+
+ public function setContentDisposition(string $contentDisposition): void
+ {
+ $this->contentDisposition = $contentDisposition;
+ }
+
+ public function getContentType(): string
+ {
+ return $this->contentType;
+ }
+
+ public function setContentType(string $contentType): void
+ {
+ $this->contentType = $contentType;
+ }
+
+ /**
+ * @return StreamInterface|resource
+ */
+ public function getOutputStream()
+ {
+ return $this->outputStream;
+ }
+
+ /**
+ * @param StreamInterface|resource $outputStream
+ */
+ public function setOutputStream($outputStream): void
+ {
+ $this->outputStream = $outputStream;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDeflateLevel(): int
+ {
+ return $this->deflateLevel;
+ }
+
+ /**
+ * @param int $deflateLevel
+ */
+ public function setDeflateLevel(int $deflateLevel): void
+ {
+ $this->deflateLevel = $deflateLevel;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/File.php b/vendor/maennchen/zipstream-php/src/Option/File.php
new file mode 100644
index 0000000..37e37ce
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/File.php
@@ -0,0 +1,122 @@
+deflateLevel = $this->deflateLevel ?: $archiveOptions->getDeflateLevel();
+ $this->time = $this->time ?: new DateTime();
+ }
+
+ /**
+ * @return string
+ */
+ public function getComment(): string
+ {
+ return $this->comment;
+ }
+
+ /**
+ * @param string $comment
+ */
+ public function setComment(string $comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ /**
+ * @return Method
+ */
+ public function getMethod(): Method
+ {
+ return $this->method ?: Method::DEFLATE();
+ }
+
+ /**
+ * @param Method $method
+ */
+ public function setMethod(Method $method): void
+ {
+ $this->method = $method;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDeflateLevel(): int
+ {
+ return $this->deflateLevel ?: Archive::DEFAULT_DEFLATE_LEVEL;
+ }
+
+ /**
+ * @param int $deflateLevel
+ */
+ public function setDeflateLevel(int $deflateLevel): void
+ {
+ $this->deflateLevel = $deflateLevel;
+ }
+
+ /**
+ * @return DateTimeInterface
+ */
+ public function getTime(): DateTimeInterface
+ {
+ return $this->time;
+ }
+
+ /**
+ * @param DateTimeInterface $time
+ */
+ public function setTime(DateTimeInterface $time): void
+ {
+ $this->time = $time;
+ }
+
+ /**
+ * @return int
+ */
+ public function getSize(): int
+ {
+ return $this->size;
+ }
+
+ /**
+ * @param int $size
+ */
+ public function setSize(int $size): void
+ {
+ $this->size = $size;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Method.php b/vendor/maennchen/zipstream-php/src/Option/Method.php
new file mode 100644
index 0000000..0dfce1b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/Method.php
@@ -0,0 +1,23 @@
+
+ */
+class Method extends Enum
+{
+ public const STORE = 0x00;
+
+ public const DEFLATE = 0x08;
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Version.php b/vendor/maennchen/zipstream-php/src/Option/Version.php
new file mode 100644
index 0000000..f3daa85
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/Version.php
@@ -0,0 +1,27 @@
+
+ */
+class Version extends Enum
+{
+ public const STORE = 0x000A; // 1.00
+
+ public const DEFLATE = 0x0014; // 2.00
+
+ public const ZIP64 = 0x002D; // 4.50
+}
diff --git a/vendor/maennchen/zipstream-php/src/Stream.php b/vendor/maennchen/zipstream-php/src/Stream.php
new file mode 100644
index 0000000..d80e70f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Stream.php
@@ -0,0 +1,265 @@
+stream = $stream;
+ }
+
+ /**
+ * Reads all data from the stream into a string, from the beginning to end.
+ *
+ * This method MUST attempt to seek to the beginning of the stream before
+ * reading data and read the stream until the end is reached.
+ *
+ * Warning: This could attempt to load a large amount of data into memory.
+ *
+ * This method MUST NOT raise an exception in order to conform with PHP's
+ * string casting operations.
+ *
+ * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
+ * @return string
+ */
+ public function __toString(): string
+ {
+ try {
+ $this->seek(0);
+ } catch (RuntimeException $e) {
+ }
+ return (string) stream_get_contents($this->stream);
+ }
+
+ /**
+ * Closes the stream and any underlying resources.
+ *
+ * @return void
+ */
+ public function close(): void
+ {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ $this->detach();
+ }
+
+ /**
+ * Separates any underlying resources from the stream.
+ *
+ * After the stream has been detached, the stream is in an unusable state.
+ *
+ * @return resource|null Underlying PHP stream, if any
+ */
+ public function detach()
+ {
+ $result = $this->stream;
+ $this->stream = null;
+ return $result;
+ }
+
+ /**
+ * Seek to a position in the stream.
+ *
+ * @link http://www.php.net/manual/en/function.fseek.php
+ * @param int $offset Stream offset
+ * @param int $whence Specifies how the cursor position will be calculated
+ * based on the seek offset. Valid values are identical to the built-in
+ * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
+ * offset bytes SEEK_CUR: Set position to current location plus offset
+ * SEEK_END: Set position to end-of-stream plus offset.
+ * @throws RuntimeException on failure.
+ */
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ if (!$this->isSeekable()) {
+ throw new RuntimeException();
+ }
+ if (fseek($this->stream, $offset, $whence) !== 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Returns whether or not the stream is seekable.
+ *
+ * @return bool
+ */
+ public function isSeekable(): bool
+ {
+ return (bool)$this->getMetadata('seekable');
+ }
+
+ /**
+ * Get stream metadata as an associative array or retrieve a specific key.
+ *
+ * The keys returned are identical to the keys returned from PHP's
+ * stream_get_meta_data() function.
+ *
+ * @link http://php.net/manual/en/function.stream-get-meta-data.php
+ * @param string $key Specific metadata to retrieve.
+ * @return array|mixed|null Returns an associative array if no key is
+ * provided. Returns a specific key value if a key is provided and the
+ * value is found, or null if the key is not found.
+ */
+ public function getMetadata($key = null)
+ {
+ $metadata = stream_get_meta_data($this->stream);
+ return $key !== null ? @$metadata[$key] : $metadata;
+ }
+
+ /**
+ * Get the size of the stream if known.
+ *
+ * @return int|null Returns the size in bytes if known, or null if unknown.
+ */
+ public function getSize(): ?int
+ {
+ $stats = fstat($this->stream);
+ return $stats['size'];
+ }
+
+ /**
+ * Returns the current position of the file read/write pointer
+ *
+ * @return int Position of the file pointer
+ * @throws RuntimeException on error.
+ */
+ public function tell(): int
+ {
+ $position = ftell($this->stream);
+ if ($position === false) {
+ throw new RuntimeException();
+ }
+ return $position;
+ }
+
+ /**
+ * Returns true if the stream is at the end of the stream.
+ *
+ * @return bool
+ */
+ public function eof(): bool
+ {
+ return feof($this->stream);
+ }
+
+ /**
+ * Seek to the beginning of the stream.
+ *
+ * If the stream is not seekable, this method will raise an exception;
+ * otherwise, it will perform a seek(0).
+ *
+ * @see seek()
+ * @link http://www.php.net/manual/en/function.fseek.php
+ * @throws RuntimeException on failure.
+ */
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ /**
+ * Write data to the stream.
+ *
+ * @param string $string The string that is to be written.
+ * @return int Returns the number of bytes written to the stream.
+ * @throws RuntimeException on failure.
+ */
+ public function write($string): int
+ {
+ if (!$this->isWritable()) {
+ throw new RuntimeException();
+ }
+ if (fwrite($this->stream, $string) === false) {
+ throw new RuntimeException();
+ }
+ return mb_strlen($string);
+ }
+
+ /**
+ * Returns whether or not the stream is writable.
+ *
+ * @return bool
+ */
+ public function isWritable(): bool
+ {
+ $mode = $this->getMetadata('mode');
+ if (!is_string($mode)) {
+ throw new RuntimeException('Could not get stream mode from metadata!');
+ }
+ return preg_match('/[waxc+]/', $mode) === 1;
+ }
+
+ /**
+ * Read data from the stream.
+ *
+ * @param int $length Read up to $length bytes from the object and return
+ * them. Fewer than $length bytes may be returned if underlying stream
+ * call returns fewer bytes.
+ * @return string Returns the data read from the stream, or an empty string
+ * if no bytes are available.
+ * @throws RuntimeException if an error occurs.
+ */
+ public function read($length): string
+ {
+ if (!$this->isReadable()) {
+ throw new RuntimeException();
+ }
+ $result = fread($this->stream, $length);
+ if ($result === false) {
+ throw new RuntimeException();
+ }
+ return $result;
+ }
+
+ /**
+ * Returns whether or not the stream is readable.
+ *
+ * @return bool
+ */
+ public function isReadable(): bool
+ {
+ $mode = $this->getMetadata('mode');
+ if (!is_string($mode)) {
+ throw new RuntimeException('Could not get stream mode from metadata!');
+ }
+ return preg_match('/[r+]/', $mode) === 1;
+ }
+
+ /**
+ * Returns the remaining contents in a string
+ *
+ * @return string
+ * @throws RuntimeException if unable to read or an error occurs while
+ * reading.
+ */
+ public function getContents(): string
+ {
+ if (!$this->isReadable()) {
+ throw new RuntimeException();
+ }
+ $result = stream_get_contents($this->stream);
+ if ($result === false) {
+ throw new RuntimeException();
+ }
+ return $result;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/ZipStream.php b/vendor/maennchen/zipstream-php/src/ZipStream.php
new file mode 100644
index 0000000..c01da72
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/ZipStream.php
@@ -0,0 +1,608 @@
+addFile('some_file.gif', $data);
+ *
+ * * add second file
+ * $data = file_get_contents('some_file.gif');
+ * $zip->addFile('another_file.png', $data);
+ *
+ * 3. Finish the zip stream:
+ *
+ * $zip->finish();
+ *
+ * You can also add an archive comment, add comments to individual files,
+ * and adjust the timestamp of files. See the API documentation for each
+ * method below for additional information.
+ *
+ * Example:
+ *
+ * // create a new zip stream object
+ * $zip = new ZipStream('some_files.zip');
+ *
+ * // list of local files
+ * $files = array('foo.txt', 'bar.jpg');
+ *
+ * // read and add each file to the archive
+ * foreach ($files as $path)
+ * $zip->addFile($path, file_get_contents($path));
+ *
+ * // write archive footer to stream
+ * $zip->finish();
+ */
+class ZipStream
+{
+ /**
+ * This number corresponds to the ZIP version/OS used (2 bytes)
+ * From: https://www.iana.org/assignments/media-types/application/zip
+ * The upper byte (leftmost one) indicates the host system (OS) for the
+ * file. Software can use this information to determine
+ * the line record format for text files etc. The current
+ * mappings are:
+ *
+ * 0 - MS-DOS and OS/2 (F.A.T. file systems)
+ * 1 - Amiga 2 - VAX/VMS
+ * 3 - *nix 4 - VM/CMS
+ * 5 - Atari ST 6 - OS/2 H.P.F.S.
+ * 7 - Macintosh 8 - Z-System
+ * 9 - CP/M 10 thru 255 - unused
+ *
+ * The lower byte (rightmost one) indicates the version number of the
+ * software used to encode the file. The value/10
+ * indicates the major version number, and the value
+ * mod 10 is the minor version number.
+ * Here we are using 6 for the OS, indicating OS/2 H.P.F.S.
+ * to prevent file permissions issues upon extract (see #84)
+ * 0x603 is 00000110 00000011 in binary, so 6 and 3
+ */
+ public const ZIP_VERSION_MADE_BY = 0x603;
+
+ /**
+ * The following signatures end with 0x4b50, which in ASCII is PK,
+ * the initials of the inventor Phil Katz.
+ * See https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
+ */
+ public const FILE_HEADER_SIGNATURE = 0x04034b50;
+
+ public const CDR_FILE_SIGNATURE = 0x02014b50;
+
+ public const CDR_EOF_SIGNATURE = 0x06054b50;
+
+ public const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
+
+ public const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
+
+ public const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
+
+ /**
+ * Global Options
+ *
+ * @var ArchiveOptions
+ */
+ public $opt;
+
+ /**
+ * @var array
+ */
+ public $files = [];
+
+ /**
+ * @var Bigint
+ */
+ public $cdr_ofs;
+
+ /**
+ * @var Bigint
+ */
+ public $ofs;
+
+ /**
+ * @var bool
+ */
+ protected $need_headers;
+
+ /**
+ * @var null|String
+ */
+ protected $output_name;
+
+ /**
+ * Create a new ZipStream object.
+ *
+ * Parameters:
+ *
+ * @param String $name - Name of output file (optional).
+ * @param ArchiveOptions $opt - Archive Options
+ *
+ * Large File Support:
+ *
+ * By default, the method addFileFromPath() will send send files
+ * larger than 20 megabytes along raw rather than attempting to
+ * compress them. You can change both the maximum size and the
+ * compression behavior using the largeFile* options above, with the
+ * following caveats:
+ *
+ * * For "small" files (e.g. files smaller than largeFileSize), the
+ * memory use can be up to twice that of the actual file. In other
+ * words, adding a 10 megabyte file to the archive could potentially
+ * occupy 20 megabytes of memory.
+ *
+ * * Enabling compression on large files (e.g. files larger than
+ * large_file_size) is extremely slow, because ZipStream has to pass
+ * over the large file once to calculate header information, and then
+ * again to compress and send the actual data.
+ *
+ * Examples:
+ *
+ * // create a new zip file named 'foo.zip'
+ * $zip = new ZipStream('foo.zip');
+ *
+ * // create a new zip file named 'bar.zip' with a comment
+ * $opt->setComment = 'this is a comment for the zip file.';
+ * $zip = new ZipStream('bar.zip', $opt);
+ *
+ * Notes:
+ *
+ * In order to let this library send HTTP headers, a filename must be given
+ * _and_ the option `sendHttpHeaders` must be `true`. This behavior is to
+ * allow software to send its own headers (including the filename), and
+ * still use this library.
+ */
+ public function __construct(?string $name = null, ?ArchiveOptions $opt = null)
+ {
+ $this->opt = $opt ?: new ArchiveOptions();
+
+ $this->output_name = $name;
+ $this->need_headers = $name && $this->opt->isSendHttpHeaders();
+
+ $this->cdr_ofs = new Bigint();
+ $this->ofs = new Bigint();
+ }
+
+ /**
+ * addFile
+ *
+ * Add a file to the archive.
+ *
+ * @param String $name - path of file in archive (including directory).
+ * @param String $data - contents of file
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ * method - Storage method for file ("store" or "deflate")
+ *
+ * Examples:
+ *
+ * // add a file named 'foo.txt'
+ * $data = file_get_contents('foo.txt');
+ * $zip->addFile('foo.txt', $data);
+ *
+ * // add a file named 'bar.jpg' with a comment and a last-modified
+ * // time of two hours ago
+ * $data = file_get_contents('bar.jpg');
+ * $opt->setTime = time() - 2 * 3600;
+ * $opt->setComment = 'this is a comment about bar.jpg';
+ * $zip->addFile('bar.jpg', $data, $opt);
+ */
+ public function addFile(string $name, string $data, ?FileOptions $options = null): void
+ {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processData($data);
+ }
+
+ /**
+ * addFileFromPath
+ *
+ * Add a file at path to the archive.
+ *
+ * Note that large files may be compressed differently than smaller
+ * files; see the "Large File Support" section above for more
+ * information.
+ *
+ * @param String $name - name of file in archive (including directory path).
+ * @param String $path - path to file on disk (note: paths should be encoded using
+ * UNIX-style forward slashes -- e.g '/path/to/some/file').
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ * method - Storage method for file ("store" or "deflate")
+ *
+ * Examples:
+ *
+ * // add a file named 'foo.txt' from the local file '/tmp/foo.txt'
+ * $zip->addFileFromPath('foo.txt', '/tmp/foo.txt');
+ *
+ * // add a file named 'bigfile.rar' from the local file
+ * // '/usr/share/bigfile.rar' with a comment and a last-modified
+ * // time of two hours ago
+ * $path = '/usr/share/bigfile.rar';
+ * $opt->setTime = time() - 2 * 3600;
+ * $opt->setComment = 'this is a comment about bar.jpg';
+ * $zip->addFileFromPath('bigfile.rar', $path, $opt);
+ *
+ * @return void
+ * @throws \ZipStream\Exception\FileNotFoundException
+ * @throws \ZipStream\Exception\FileNotReadableException
+ */
+ public function addFileFromPath(string $name, string $path, ?FileOptions $options = null): void
+ {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processPath($path);
+ }
+
+ /**
+ * addFileFromStream
+ *
+ * Add an open stream to the archive.
+ *
+ * @param String $name - path of file in archive (including directory).
+ * @param resource $stream - contents of file as a stream resource
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ *
+ * Examples:
+ *
+ * // create a temporary file stream and write text to it
+ * $fp = tmpfile();
+ * fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
+ *
+ * // add a file named 'streamfile.txt' from the content of the stream
+ * $x->addFileFromStream('streamfile.txt', $fp);
+ *
+ * @return void
+ */
+ public function addFileFromStream(string $name, $stream, ?FileOptions $options = null): void
+ {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processStream(new Stream($stream));
+ }
+
+ /**
+ * addFileFromPsr7Stream
+ *
+ * Add an open stream to the archive.
+ *
+ * @param String $name - path of file in archive (including directory).
+ * @param StreamInterface $stream - contents of file as a stream resource
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ *
+ * Examples:
+ *
+ * $stream = $response->getBody();
+ * // add a file named 'streamfile.txt' from the content of the stream
+ * $x->addFileFromPsr7Stream('streamfile.txt', $stream);
+ *
+ * @return void
+ */
+ public function addFileFromPsr7Stream(
+ string $name,
+ StreamInterface $stream,
+ ?FileOptions $options = null
+ ): void {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processStream($stream);
+ }
+
+ /**
+ * finish
+ *
+ * Write zip footer to stream.
+ *
+ * Example:
+ *
+ * // add a list of files to the archive
+ * $files = array('foo.txt', 'bar.jpg');
+ * foreach ($files as $path)
+ * $zip->addFile($path, file_get_contents($path));
+ *
+ * // write footer to stream
+ * $zip->finish();
+ * @return void
+ *
+ * @throws OverflowException
+ */
+ public function finish(): void
+ {
+ // add trailing cdr file records
+ foreach ($this->files as $cdrFile) {
+ $this->send($cdrFile);
+ $this->cdr_ofs = $this->cdr_ofs->add(Bigint::init(strlen($cdrFile)));
+ }
+
+ // Add 64bit headers (if applicable)
+ if (count($this->files) >= 0xFFFF ||
+ $this->cdr_ofs->isOver32() ||
+ $this->ofs->isOver32()) {
+ if (!$this->opt->isEnableZip64()) {
+ throw new OverflowException();
+ }
+
+ $this->addCdr64Eof();
+ $this->addCdr64Locator();
+ }
+
+ // add trailing cdr eof record
+ $this->addCdrEof();
+
+ // The End
+ $this->clear();
+ }
+
+ /**
+ * Create a format string and argument list for pack(), then call
+ * pack() and return the result.
+ *
+ * @param array $fields
+ * @return string
+ */
+ public static function packFields(array $fields): string
+ {
+ $fmt = '';
+ $args = [];
+
+ // populate format string and argument list
+ foreach ($fields as [$format, $value]) {
+ if ($format === 'P') {
+ $fmt .= 'VV';
+ if ($value instanceof Bigint) {
+ $args[] = $value->getLow32();
+ $args[] = $value->getHigh32();
+ } else {
+ $args[] = $value;
+ $args[] = 0;
+ }
+ } else {
+ if ($value instanceof Bigint) {
+ $value = $value->getLow32();
+ }
+ $fmt .= $format;
+ $args[] = $value;
+ }
+ }
+
+ // prepend format string to argument list
+ array_unshift($args, $fmt);
+
+ // build output string from header and compressed data
+ return pack(...$args);
+ }
+
+ /**
+ * Send string, sending HTTP headers if necessary.
+ * Flush output after write if configure option is set.
+ *
+ * @param String $str
+ * @return void
+ */
+ public function send(string $str): void
+ {
+ if ($this->need_headers) {
+ $this->sendHttpHeaders();
+ }
+ $this->need_headers = false;
+
+ $outputStream = $this->opt->getOutputStream();
+
+ if ($outputStream instanceof StreamInterface) {
+ $outputStream->write($str);
+ } else {
+ fwrite($outputStream, $str);
+ }
+
+ if ($this->opt->isFlushOutput()) {
+ // flush output buffer if it is on and flushable
+ $status = ob_get_status();
+ if (isset($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
+ ob_flush();
+ }
+
+ // Flush system buffers after flushing userspace output buffer
+ flush();
+ }
+ }
+
+ /**
+ * Is this file larger than large_file_size?
+ *
+ * @param string $path
+ * @return bool
+ */
+ public function isLargeFile(string $path): bool
+ {
+ if (!$this->opt->isStatFiles()) {
+ return false;
+ }
+ $stat = stat($path);
+ return $stat['size'] > $this->opt->getLargeFileSize();
+ }
+
+ /**
+ * Save file attributes for trailing CDR record.
+ *
+ * @param File $file
+ * @return void
+ */
+ public function addToCdr(File $file): void
+ {
+ $file->ofs = $this->ofs;
+ $this->ofs = $this->ofs->add($file->getTotalLength());
+ $this->files[] = $file->getCdrFile();
+ }
+
+ /**
+ * Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
+ *
+ * @return void
+ */
+ protected function addCdr64Eof(): void
+ {
+ $num_files = count($this->files);
+ $cdr_length = $this->cdr_ofs;
+ $cdr_offset = $this->ofs;
+
+ $fields = [
+ ['V', static::ZIP64_CDR_EOF_SIGNATURE], // ZIP64 end of central file header signature
+ ['P', 44], // Length of data below this header (length of block - 12) = 44
+ ['v', static::ZIP_VERSION_MADE_BY], // Made by version
+ ['v', Version::ZIP64], // Extract by version
+ ['V', 0x00], // disk number
+ ['V', 0x00], // no of disks
+ ['P', $num_files], // no of entries on disk
+ ['P', $num_files], // no of entries in cdr
+ ['P', $cdr_length], // CDR size
+ ['P', $cdr_offset], // CDR offset
+ ];
+
+ $ret = static::packFields($fields);
+ $this->send($ret);
+ }
+
+ /**
+ * Send HTTP headers for this stream.
+ *
+ * @return void
+ */
+ protected function sendHttpHeaders(): void
+ {
+ // grab content disposition
+ $disposition = $this->opt->getContentDisposition();
+
+ if ($this->output_name) {
+ // Various different browsers dislike various characters here. Strip them all for safety.
+ $safe_output = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->output_name));
+
+ // Check if we need to UTF-8 encode the filename
+ $urlencoded = rawurlencode($safe_output);
+ $disposition .= "; filename*=UTF-8''{$urlencoded}";
+ }
+
+ $headers = [
+ 'Content-Type' => $this->opt->getContentType(),
+ 'Content-Disposition' => $disposition,
+ 'Pragma' => 'public',
+ 'Cache-Control' => 'public, must-revalidate',
+ 'Content-Transfer-Encoding' => 'binary',
+ ];
+
+ $call = $this->opt->getHttpHeaderCallback();
+ foreach ($headers as $key => $val) {
+ $call("$key: $val");
+ }
+ }
+
+ /**
+ * Send ZIP64 CDR Locator (Central Directory Record Locator) record.
+ *
+ * @return void
+ */
+ protected function addCdr64Locator(): void
+ {
+ $cdr_offset = $this->ofs->add($this->cdr_ofs);
+
+ $fields = [
+ ['V', static::ZIP64_CDR_LOCATOR_SIGNATURE], // ZIP64 end of central file header signature
+ ['V', 0x00], // Disc number containing CDR64EOF
+ ['P', $cdr_offset], // CDR offset
+ ['V', 1], // Total number of disks
+ ];
+
+ $ret = static::packFields($fields);
+ $this->send($ret);
+ }
+
+ /**
+ * Send CDR EOF (Central Directory Record End-of-File) record.
+ *
+ * @return void
+ */
+ protected function addCdrEof(): void
+ {
+ $num_files = count($this->files);
+ $cdr_length = $this->cdr_ofs;
+ $cdr_offset = $this->ofs;
+
+ // grab comment (if specified)
+ $comment = $this->opt->getComment();
+
+ $fields = [
+ ['V', static::CDR_EOF_SIGNATURE], // end of central file header signature
+ ['v', 0x00], // disk number
+ ['v', 0x00], // no of disks
+ ['v', min($num_files, 0xFFFF)], // no of entries on disk
+ ['v', min($num_files, 0xFFFF)], // no of entries in cdr
+ ['V', $cdr_length->getLowFF()], // CDR size
+ ['V', $cdr_offset->getLowFF()], // CDR offset
+ ['v', strlen($comment)], // Zip Comment size
+ ];
+
+ $ret = static::packFields($fields) . $comment;
+ $this->send($ret);
+ }
+
+ /**
+ * Clear all internal variables. Note that the stream object is not
+ * usable after this.
+ *
+ * @return void
+ */
+ protected function clear(): void
+ {
+ $this->files = [];
+ $this->ofs = new Bigint();
+ $this->cdr_ofs = new Bigint();
+ $this->opt = new ArchiveOptions();
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/BigintTest.php b/vendor/maennchen/zipstream-php/test/BigintTest.php
new file mode 100644
index 0000000..4d26fcd
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/BigintTest.php
@@ -0,0 +1,66 @@
+assertSame('0x0000000012345678', $bigint->getHex64());
+ $this->assertSame(0x12345678, $bigint->getLow32());
+ $this->assertSame(0, $bigint->getHigh32());
+ }
+
+ public function testConstructLarge(): void
+ {
+ $bigint = new Bigint(0x87654321);
+ $this->assertSame('0x0000000087654321', $bigint->getHex64());
+ $this->assertSame('87654321', bin2hex(pack('N', $bigint->getLow32())));
+ $this->assertSame(0, $bigint->getHigh32());
+ }
+
+ public function testAddSmallValue(): void
+ {
+ $bigint = new Bigint(1);
+ $bigint = $bigint->add(Bigint::init(2));
+ $this->assertSame(3, $bigint->getLow32());
+ $this->assertFalse($bigint->isOver32());
+ $this->assertTrue($bigint->isOver32(true));
+ $this->assertSame($bigint->getLowFF(), (float)$bigint->getLow32());
+ $this->assertSame($bigint->getLowFF(true), (float)0xFFFFFFFF);
+ }
+
+ public function testAddWithOverflowAtLowestByte(): void
+ {
+ $bigint = new Bigint(0xFF);
+ $bigint = $bigint->add(Bigint::init(0x01));
+ $this->assertSame(0x100, $bigint->getLow32());
+ }
+
+ public function testAddWithOverflowAtInteger32(): void
+ {
+ $bigint = new Bigint(0xFFFFFFFE);
+ $this->assertFalse($bigint->isOver32());
+ $bigint = $bigint->add(Bigint::init(0x01));
+ $this->assertTrue($bigint->isOver32());
+ $bigint = $bigint->add(Bigint::init(0x01));
+ $this->assertSame('0x0000000100000000', $bigint->getHex64());
+ $this->assertTrue($bigint->isOver32());
+ $this->assertSame((float)0xFFFFFFFF, $bigint->getLowFF());
+ }
+
+ public function testAddWithOverflowAtInteger64(): void
+ {
+ $bigint = Bigint::fromLowHigh(0xFFFFFFFF, 0xFFFFFFFF);
+ $this->assertSame('0xFFFFFFFFFFFFFFFF', $bigint->getHex64());
+ $this->expectException(OverflowException::class);
+ $bigint->add(Bigint::init(1));
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/ZipStreamTest.php b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
new file mode 100644
index 0000000..5d55bf5
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
@@ -0,0 +1,619 @@
+expectException(\ZipStream\Exception\FileNotFoundException::class);
+ // Get ZipStream Object
+ $zip = new ZipStream();
+
+ // Trigger error by adding a file which doesn't exist
+ $zip->addFileFromPath('foobar.php', '/foo/bar/foobar.php');
+ }
+
+ public function testFileNotReadableException(): void
+ {
+ // create new virtual filesystem
+ $root = vfsStream::setup('vfs');
+ // create a virtual file with no permissions
+ $file = vfsStream::newFile('foo.txt', 0)->at($root)->setContent('bar');
+ $zip = new ZipStream();
+ $this->expectException(\ZipStream\Exception\FileNotReadableException::class);
+ $zip->addFileFromPath('foo.txt', $file->url());
+ }
+
+ public function testDostime(): void
+ {
+ // Allows testing of protected method
+ $class = new ReflectionClass(File::class);
+ $method = $class->getMethod('dostime');
+ $method->setAccessible(true);
+
+ $this->assertSame($method->invoke(null, 1416246368), 1165069764);
+
+ // January 1 1980 - DOS Epoch.
+ $this->assertSame($method->invoke(null, 315532800), 2162688);
+
+ // January 1 1970 -> January 1 1980 due to minimum DOS Epoch. @todo Throw Exception?
+ $this->assertSame($method->invoke(null, 0), 2162688);
+ }
+
+ public function testAddFile(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testAddFileUtf8NameComment(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $name = 'árvíztűrő tükörfúrógép.txt';
+ $content = 'Sample String Data';
+ $comment =
+ 'Filename has every special characters ' .
+ 'from Hungarian language in lowercase. ' .
+ 'In uppercase: ÁÍŰŐÜÖÚÓÉ';
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setComment($comment);
+
+ $zip->addFile($name, $content, $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame([$name], $files);
+ $this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+ $this->assertSame($comment, $zipArch->getCommentName($name));
+ }
+
+ public function testAddFileUtf8NameNonUtfComment(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $name = 'á.txt';
+ $content = 'any';
+ $comment = mb_convert_encoding('á', 'ISO-8859-2', 'UTF-8');
+
+ // @see https://libzip.org/documentation/zip_file_get_comment.html
+ //
+ // mb_convert_encoding hasn't CP437.
+ // nearly CP850 (DOS-Latin-1)
+ $guessComment = mb_convert_encoding($comment, 'UTF-8', 'CP850');
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setComment($comment);
+
+ $zip->addFile($name, $content, $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+ $this->assertSame($guessComment, $zipArch->getCommentName($name));
+ $this->assertSame($comment, $zipArch->getCommentName($name, ZipArchive::FL_ENC_RAW));
+ }
+
+ public function testAddFileNonUtf8NameUtfComment(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $name = mb_convert_encoding('á.txt', 'ISO-8859-2', 'UTF-8');
+ $content = 'any';
+ $comment = 'á';
+
+ // @see https://libzip.org/documentation/zip_get_name.html
+ //
+ // mb_convert_encoding hasn't CP437.
+ // nearly CP850 (DOS-Latin-1)
+ $guessName = mb_convert_encoding($name, 'UTF-8', 'CP850');
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setComment($comment);
+
+ $zip->addFile($name, $content, $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertNotSame([$name], $files);
+ $this->assertSame([$guessName], $files);
+ $this->assertStringEqualsFile($tmpDir . '/' . $guessName, $content);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+ $this->assertSame($guessName, $zipArch->getNameIndex(0));
+ $this->assertSame($name, $zipArch->getNameIndex(0, ZipArchive::FL_ENC_RAW));
+ $this->assertSame($comment, $zipArch->getCommentName($guessName));
+ }
+
+ public function testAddFileWithStorageMethod(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $zip->addFile('sample.txt', 'Sample String Data', $fileOptions);
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+
+ $sample1 = $zipArch->statName('sample.txt');
+ $sample12 = $zipArch->statName('test/sample.txt');
+ $this->assertSame($sample1['comp_method'], Method::STORE);
+ $this->assertSame($sample12['comp_method'], Method::DEFLATE);
+
+ $zipArch->close();
+ }
+
+ public function testAddFileFromPath(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'Sample String Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('sample.txt', $tmpExample);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'More Simple Sample Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('test/sample.txt', $tmpExample);
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testAddFileFromPathWithStorageMethod(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'Sample String Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('sample.txt', $tmpExample, $fileOptions);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'More Simple Sample Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('test/sample.txt', $tmpExample);
+
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+
+ $sample1 = $zipArch->statName('sample.txt');
+ $this->assertSame(Method::STORE, $sample1['comp_method']);
+
+ $sample2 = $zipArch->statName('test/sample.txt');
+ $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
+
+ $zipArch->close();
+ }
+
+ public function testAddLargeFileFromPath(): void
+ {
+ $methods = [Method::DEFLATE(), Method::STORE()];
+ $falseTrue = [false, true];
+ foreach ($methods as $method) {
+ foreach ($falseTrue as $zeroHeader) {
+ foreach ($falseTrue as $zip64) {
+ if ($zeroHeader && $method->equals(Method::DEFLATE())) {
+ continue;
+ }
+ $this->addLargeFileFileFromPath($method, $zeroHeader, $zip64);
+ }
+ }
+ }
+ }
+
+ public function testAddFileFromStream(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ // In this test we can't use temporary stream to feed data
+ // because zlib.deflate filter gives empty string before PHP 7
+ // it works fine with file stream
+ $streamExample = fopen(__FILE__, 'rb');
+ $zip->addFileFromStream('sample.txt', $streamExample);
+ // fclose($streamExample);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $streamExample2 = fopen('php://temp', 'wb+');
+ fwrite($streamExample2, 'More Simple Sample Data');
+ rewind($streamExample2); // move the pointer back to the beginning of file.
+ $zip->addFileFromStream('test/sample.txt', $streamExample2, $fileOptions);
+ // fclose($streamExample2);
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile(__FILE__, file_get_contents($tmpDir . '/sample.txt'));
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testAddFileFromStreamWithStorageMethod(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $streamExample = fopen('php://temp', 'wb+');
+ fwrite($streamExample, 'Sample String Data');
+ rewind($streamExample); // move the pointer back to the beginning of file.
+ $zip->addFileFromStream('sample.txt', $streamExample, $fileOptions);
+ // fclose($streamExample);
+
+ $streamExample2 = fopen('php://temp', 'bw+');
+ fwrite($streamExample2, 'More Simple Sample Data');
+ rewind($streamExample2); // move the pointer back to the beginning of file.
+ $zip->addFileFromStream('test/sample.txt', $streamExample2);
+ // fclose($streamExample2);
+
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+
+ $sample1 = $zipArch->statName('sample.txt');
+ $this->assertSame(Method::STORE, $sample1['comp_method']);
+
+ $sample2 = $zipArch->statName('test/sample.txt');
+ $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
+
+ $zipArch->close();
+ }
+
+ public function testAddFileFromPsr7Stream(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $body = 'Sample String Data';
+ $response = new Response(200, [], $body);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testAddFileFromPsr7StreamWithOutputToPsr7Stream(): void
+ {
+ [$tmp, $resource] = $this->getTmpFileStream();
+ $psr7OutputStream = new Stream($resource);
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($psr7OutputStream);
+
+ $zip = new ZipStream(null, $options);
+
+ $body = 'Sample String Data';
+ $response = new Response(200, [], $body);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+ $zip->finish();
+ $psr7OutputStream->close();
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testAddFileFromPsr7StreamWithFileSizeSet(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $body = 'Sample String Data';
+ $fileSize = strlen($body);
+ // Add fake padding
+ $fakePadding = "\0\0\0\0\0\0";
+ $response = new Response(200, [], $body . $fakePadding);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+ $fileOptions->setSize($fileSize);
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testCreateArchiveWithFlushOptionSet(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+ $options->setFlushOutput(true);
+
+ $zip = new ZipStream(null, $options);
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testCreateArchiveWithOutputBufferingOffAndFlushOptionSet(): void
+ {
+ // WORKAROUND (1/2): remove phpunit's output buffer in order to run test without any buffering
+ ob_end_flush();
+ $this->assertSame(0, ob_get_level());
+
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+ $options->setFlushOutput(true);
+
+ $zip = new ZipStream(null, $options);
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+
+ // WORKAROUND (2/2): add back output buffering so that PHPUnit doesn't complain that it is missing
+ ob_start();
+ }
+
+ /**
+ * @return array
+ */
+ protected function getTmpFileStream(): array
+ {
+ $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+ $stream = fopen($tmp, 'wb+');
+
+ return [$tmp, $stream];
+ }
+
+ /**
+ * @param string $tmp
+ * @return string
+ */
+ protected function validateAndExtractZip($tmp): string
+ {
+ $tmpDir = $this->getTmpDir();
+
+ $zipArch = new ZipArchive();
+ $res = $zipArch->open($tmp);
+
+ if ($res !== true) {
+ $this->fail("Failed to open {$tmp}. Code: $res");
+
+ return $tmpDir;
+ }
+
+ $this->assertSame(0, $zipArch->status);
+ $this->assertSame(0, $zipArch->statusSys);
+
+ $zipArch->extractTo($tmpDir);
+ $zipArch->close();
+
+ return $tmpDir;
+ }
+
+ protected function getTmpDir(): string
+ {
+ $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+ unlink($tmp);
+ mkdir($tmp) or $this->fail('Failed to make directory');
+
+ return $tmp;
+ }
+
+ /**
+ * @param string $path
+ * @return string[]
+ */
+ protected function getRecursiveFileList(string $path): array
+ {
+ $data = [];
+ $path = (string)realpath($path);
+ $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
+
+ $pathLen = strlen($path);
+ foreach ($files as $file) {
+ $filePath = $file->getRealPath();
+ if (!is_dir($filePath)) {
+ $data[] = substr($filePath, $pathLen + 1);
+ }
+ }
+
+ sort($data);
+
+ return $data;
+ }
+
+ protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+ $options->setLargeFileMethod($method);
+ $options->setLargeFileSize(5);
+ $options->setZeroHeader($zeroHeader);
+ $options->setEnableZip64($zip64);
+
+ $zip = new ZipStream(null, $options);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ for ($i = 0; $i <= 10000; $i++) {
+ fwrite($streamExample, sha1((string)$i));
+ if ($i % 100 === 0) {
+ fwrite($streamExample, "\n");
+ }
+ }
+ fclose($streamExample);
+ $shaExample = sha1_file($tmpExample);
+ $zip->addFileFromPath('sample.txt', $tmpExample);
+ unlink($tmpExample);
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt'], $files);
+
+ $this->assertSame(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}");
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/bootstrap.php b/vendor/maennchen/zipstream-php/test/bootstrap.php
new file mode 100644
index 0000000..13c7a0e
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/bootstrap.php
@@ -0,0 +1,7 @@
+setOutputStream(fopen('php://memory', 'wb'));
+ $fileOpt->setTime(clone $expectedTime);
+
+ $zip = new ZipStream(null, $archiveOpt);
+
+ $zip->addFile('sample.txt', 'Sample', $fileOpt);
+
+ $zip->finish();
+
+ $this->assertEquals($expectedTime, $fileOpt->getTime());
+ }
+}
diff --git a/vendor/markbaker/complex/.github/workflows/main.yml b/vendor/markbaker/complex/.github/workflows/main.yml
new file mode 100644
index 0000000..8911533
--- /dev/null
+++ b/vendor/markbaker/complex/.github/workflows/main.yml
@@ -0,0 +1,153 @@
+name: main
+on: [ push, pull_request ]
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ experimental:
+ - false
+ php-version:
+ - '7.2'
+ - '7.3'
+ - '7.4'
+ - '8.0'
+ - '8.1'
+ - '8.2'
+
+ include:
+ - php-version: 'nightly'
+ experimental: true
+
+ name: PHP ${{ matrix.php-version }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+ coverage: none
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Delete composer lock file
+ id: composer-lock
+ if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' || matrix.php-version == '8.2' || matrix.php-version == 'nightly' }}
+ run: |
+ rm composer.lock
+ echo "::set-output name=flags::--ignore-platform-reqs"
+
+ - name: Install dependencies
+ run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }}
+
+ - name: Setup problem matchers for PHP
+ run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
+
+ - name: Setup problem matchers for PHPUnit
+ run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+
+ - name: "Run PHPUnit tests (Experimental: ${{ matrix.experimental }})"
+ env:
+ FAILURE_ACTION: "${{ matrix.experimental == true }}"
+ run: vendor/bin/phpunit --verbose || $FAILURE_ACTION
+
+ phpcs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 7.4
+ coverage: none
+ tools: cs2pr
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Code style with PHP_CodeSniffer
+ run: ./vendor/bin/phpcs -q --report=checkstyle classes/src/ | cs2pr
+
+ versions:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 7.4
+ coverage: none
+ tools: cs2pr
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Code Version Compatibility check with PHP_CodeSniffer
+ run: ./vendor/bin/phpcs -q --report-width=200 --report=summary,full classes/src/ --standard=PHPCompatibility --runtime-set testVersion 7.2-
+
+ coverage:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 7.4
+ coverage: pcov
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Test Coverage
+ run: ./vendor/bin/phpunit --verbose --coverage-text
diff --git a/vendor/markbaker/complex/README.md b/vendor/markbaker/complex/README.md
new file mode 100644
index 0000000..76347af
--- /dev/null
+++ b/vendor/markbaker/complex/README.md
@@ -0,0 +1,173 @@
+PHPComplex
+==========
+
+---
+
+PHP Class Library for working with Complex numbers
+
+[](https://github.com/MarkBaker/PHPComplex/actions)
+[](https://packagist.org/packages/markbaker/complex)
+[](https://packagist.org/packages/markbaker/complex)
+[](https://packagist.org/packages/markbaker/complex)
+
+
+[](https://xkcd.com/2028/)
+
+---
+
+The library currently provides the following operations:
+
+ - addition
+ - subtraction
+ - multiplication
+ - division
+ - division by
+ - division into
+
+together with functions for
+
+ - theta (polar theta angle)
+ - rho (polar distance/radius)
+ - conjugate
+ * negative
+ - inverse (1 / complex)
+ - cos (cosine)
+ - acos (inverse cosine)
+ - cosh (hyperbolic cosine)
+ - acosh (inverse hyperbolic cosine)
+ - sin (sine)
+ - asin (inverse sine)
+ - sinh (hyperbolic sine)
+ - asinh (inverse hyperbolic sine)
+ - sec (secant)
+ - asec (inverse secant)
+ - sech (hyperbolic secant)
+ - asech (inverse hyperbolic secant)
+ - csc (cosecant)
+ - acsc (inverse cosecant)
+ - csch (hyperbolic secant)
+ - acsch (inverse hyperbolic secant)
+ - tan (tangent)
+ - atan (inverse tangent)
+ - tanh (hyperbolic tangent)
+ - atanh (inverse hyperbolic tangent)
+ - cot (cotangent)
+ - acot (inverse cotangent)
+ - coth (hyperbolic cotangent)
+ - acoth (inverse hyperbolic cotangent)
+ - sqrt (square root)
+ - exp (exponential)
+ - ln (natural log)
+ - log10 (base-10 log)
+ - log2 (base-2 log)
+ - pow (raised to the power of a real number)
+
+
+---
+
+# Installation
+
+```shell
+composer require markbaker/complex:^1.0
+```
+
+# Important BC Note
+
+If you've previously been using procedural calls to functions and operations using this library, then from version 3.0 you should use [MarkBaker/PHPComplexFunctions](https://github.com/MarkBaker/PHPComplexFunctions) instead (available on packagist as [markbaker/complex-functions](https://packagist.org/packages/markbaker/complex-functions)).
+
+You'll need to replace `markbaker/complex`in your `composer.json` file with the new library, but otherwise there should be no difference in the namespacing, or in the way that you have called the Complex functions in the past, so no actual code changes are required.
+
+```shell
+composer require markbaker/complex-functions:^3.0
+```
+
+You should not reference this library (`markbaker/complex`) in your `composer.json`, composer wil take care of that for you.
+
+# Usage
+
+To create a new complex object, you can provide either the real, imaginary and suffix parts as individual values, or as an array of values passed passed to the constructor; or a string representing the value. e.g
+
+```php
+$real = 1.23;
+$imaginary = -4.56;
+$suffix = 'i';
+
+$complexObject = new Complex\Complex($real, $imaginary, $suffix);
+```
+or as an array
+```php
+$real = 1.23;
+$imaginary = -4.56;
+$suffix = 'i';
+
+$arguments = [$real, $imaginary, $suffix];
+
+$complexObject = new Complex\Complex($arguments);
+```
+or as a string
+```php
+$complexString = '1.23-4.56i';
+
+$complexObject = new Complex\Complex($complexString);
+```
+
+Complex objects are immutable: whenever you call a method or pass a complex value to a function that returns a complex value, a new Complex object will be returned, and the original will remain unchanged.
+This also allows you to chain multiple methods as you would for a fluent interface (as long as they are methods that will return a Complex result).
+
+## Performing Mathematical Operations
+
+To perform mathematical operations with Complex values, you can call the appropriate method against a complex value, passing other values as arguments
+
+```php
+$complexString1 = '1.23-4.56i';
+$complexString2 = '2.34+5.67i';
+
+$complexObject = new Complex\Complex($complexString1);
+echo $complexObject->add($complexString2);
+```
+
+or use the static Operation methods
+```php
+$complexString1 = '1.23-4.56i';
+$complexString2 = '2.34+5.67i';
+
+echo Complex\Operations::add($complexString1, $complexString2);
+```
+If you want to perform the same operation against multiple values (e.g. to add three or more complex numbers), then you can pass multiple arguments to any of the operations.
+
+You can pass these arguments as Complex objects, or as an array, or string that will parse to a complex object.
+
+## Using functions
+
+When calling any of the available functions for a complex value, you can either call the relevant method for the Complex object
+```php
+$complexString = '1.23-4.56i';
+
+$complexObject = new Complex\Complex($complexString);
+echo $complexObject->sinh();
+```
+
+or use the static Functions methods
+```php
+$complexString = '1.23-4.56i';
+
+echo Complex\Functions::sinh($complexString);
+```
+As with operations, you can pass these arguments as Complex objects, or as an array or string that will parse to a complex object.
+
+
+In the case of the `pow()` function (the only implemented function that requires an additional argument) you need to pass both arguments when calling the function
+
+```php
+$complexString = '1.23-4.56i';
+
+$complexObject = new Complex\Complex($complexString);
+echo Complex\Functions::pow($complexObject, 2);
+```
+or pass the additional argument when calling the method
+```php
+$complexString = '1.23-4.56i';
+
+$complexObject = new Complex\Complex($complexString);
+echo $complexObject->pow(2);
+```
diff --git a/vendor/markbaker/complex/classes/src/Complex.php b/vendor/markbaker/complex/classes/src/Complex.php
new file mode 100644
index 0000000..25414ee
--- /dev/null
+++ b/vendor/markbaker/complex/classes/src/Complex.php
@@ -0,0 +1,388 @@
+realPart = (float) $realPart;
+ $this->imaginaryPart = (float) $imaginaryPart;
+ $this->suffix = strtolower($suffix ?? '');
+ }
+
+ /**
+ * Gets the real part of this complex number
+ *
+ * @return Float
+ */
+ public function getReal(): float
+ {
+ return $this->realPart;
+ }
+
+ /**
+ * Gets the imaginary part of this complex number
+ *
+ * @return Float
+ */
+ public function getImaginary(): float
+ {
+ return $this->imaginaryPart;
+ }
+
+ /**
+ * Gets the suffix of this complex number
+ *
+ * @return String
+ */
+ public function getSuffix(): string
+ {
+ return $this->suffix;
+ }
+
+ /**
+ * Returns true if this is a real value, false if a complex value
+ *
+ * @return Bool
+ */
+ public function isReal(): bool
+ {
+ return $this->imaginaryPart == 0.0;
+ }
+
+ /**
+ * Returns true if this is a complex value, false if a real value
+ *
+ * @return Bool
+ */
+ public function isComplex(): bool
+ {
+ return !$this->isReal();
+ }
+
+ public function format(): string
+ {
+ $str = "";
+ if ($this->imaginaryPart != 0.0) {
+ if (\abs($this->imaginaryPart) != 1.0) {
+ $str .= $this->imaginaryPart . $this->suffix;
+ } else {
+ $str .= (($this->imaginaryPart < 0.0) ? '-' : '') . $this->suffix;
+ }
+ }
+ if ($this->realPart != 0.0) {
+ if (($str) && ($this->imaginaryPart > 0.0)) {
+ $str = "+" . $str;
+ }
+ $str = $this->realPart . $str;
+ }
+ if (!$str) {
+ $str = "0.0";
+ }
+
+ return $str;
+ }
+
+ public function __toString(): string
+ {
+ return $this->format();
+ }
+
+ /**
+ * Validates whether the argument is a valid complex number, converting scalar or array values if possible
+ *
+ * @param mixed $complex The value to validate
+ * @return Complex
+ * @throws Exception If the argument isn't a Complex number or cannot be converted to one
+ */
+ public static function validateComplexArgument($complex): Complex
+ {
+ if (is_scalar($complex) || is_array($complex)) {
+ $complex = new Complex($complex);
+ } elseif (!is_object($complex) || !($complex instanceof Complex)) {
+ throw new Exception('Value is not a valid complex number');
+ }
+
+ return $complex;
+ }
+
+ /**
+ * Returns the reverse of this complex number
+ *
+ * @return Complex
+ */
+ public function reverse(): Complex
+ {
+ return new Complex(
+ $this->imaginaryPart,
+ $this->realPart,
+ ($this->realPart == 0.0) ? null : $this->suffix
+ );
+ }
+
+ public function invertImaginary(): Complex
+ {
+ return new Complex(
+ $this->realPart,
+ $this->imaginaryPart * -1,
+ ($this->imaginaryPart == 0.0) ? null : $this->suffix
+ );
+ }
+
+ public function invertReal(): Complex
+ {
+ return new Complex(
+ $this->realPart * -1,
+ $this->imaginaryPart,
+ ($this->imaginaryPart == 0.0) ? null : $this->suffix
+ );
+ }
+
+ protected static $functions = [
+ 'abs',
+ 'acos',
+ 'acosh',
+ 'acot',
+ 'acoth',
+ 'acsc',
+ 'acsch',
+ 'argument',
+ 'asec',
+ 'asech',
+ 'asin',
+ 'asinh',
+ 'atan',
+ 'atanh',
+ 'conjugate',
+ 'cos',
+ 'cosh',
+ 'cot',
+ 'coth',
+ 'csc',
+ 'csch',
+ 'exp',
+ 'inverse',
+ 'ln',
+ 'log2',
+ 'log10',
+ 'negative',
+ 'pow',
+ 'rho',
+ 'sec',
+ 'sech',
+ 'sin',
+ 'sinh',
+ 'sqrt',
+ 'tan',
+ 'tanh',
+ 'theta',
+ ];
+
+ protected static $operations = [
+ 'add',
+ 'subtract',
+ 'multiply',
+ 'divideby',
+ 'divideinto',
+ ];
+
+ /**
+ * Returns the result of the function call or operation
+ *
+ * @return Complex|float
+ * @throws Exception|\InvalidArgumentException
+ */
+ public function __call($functionName, $arguments)
+ {
+ $functionName = strtolower(str_replace('_', '', $functionName));
+
+ // Test for function calls
+ if (in_array($functionName, self::$functions, true)) {
+ return Functions::$functionName($this, ...$arguments);
+ }
+ // Test for operation calls
+ if (in_array($functionName, self::$operations, true)) {
+ return Operations::$functionName($this, ...$arguments);
+ }
+ throw new Exception('Complex Function or Operation does not exist');
+ }
+}
diff --git a/vendor/markbaker/complex/classes/src/Exception.php b/vendor/markbaker/complex/classes/src/Exception.php
new file mode 100644
index 0000000..a2beb73
--- /dev/null
+++ b/vendor/markbaker/complex/classes/src/Exception.php
@@ -0,0 +1,13 @@
+getReal() - $invsqrt->getImaginary(),
+ $complex->getImaginary() + $invsqrt->getReal()
+ );
+ $log = self::ln($adjust);
+
+ return new Complex(
+ $log->getImaginary(),
+ -1 * $log->getReal()
+ );
+ }
+
+ /**
+ * Returns the inverse hyperbolic cosine of a complex number.
+ *
+ * Formula from Wolfram Alpha:
+ * cosh^(-1)z = ln(z + sqrt(z + 1) sqrt(z - 1)).
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic cosine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function acosh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal() && ($complex->getReal() > 1)) {
+ return new Complex(\acosh($complex->getReal()));
+ }
+
+ $acosh = self::ln(
+ Operations::add(
+ $complex,
+ Operations::multiply(
+ self::sqrt(Operations::add($complex, 1)),
+ self::sqrt(Operations::subtract($complex, 1))
+ )
+ )
+ );
+
+ return $acosh;
+ }
+
+ /**
+ * Returns the inverse cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acot($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::atan(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse hyperbolic cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acoth($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::atanh(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acsc($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::asin(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse hyperbolic cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function acsch($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::asinh(self::inverse($complex));
+ }
+
+ /**
+ * Returns the argument of a complex number.
+ * Also known as the theta of the complex number, i.e. the angle in radians
+ * from the real axis to the representation of the number in polar coordinates.
+ *
+ * This function is a synonym for theta()
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return float The argument (or theta) value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ *
+ * @see theta
+ */
+ public static function argument($complex): float
+ {
+ return self::theta($complex);
+ }
+
+ /**
+ * Returns the inverse secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function asec($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::acos(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse hyperbolic secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function asech($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::acosh(self::inverse($complex));
+ }
+
+ /**
+ * Returns the inverse sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function asin($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ $invsqrt = self::sqrt(Operations::subtract(1, Operations::multiply($complex, $complex)));
+ $adjust = new Complex(
+ $invsqrt->getReal() - $complex->getImaginary(),
+ $invsqrt->getImaginary() + $complex->getReal()
+ );
+ $log = self::ln($adjust);
+
+ return new Complex(
+ $log->getImaginary(),
+ -1 * $log->getReal()
+ );
+ }
+
+ /**
+ * Returns the inverse hyperbolic sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function asinh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal() && ($complex->getReal() > 1)) {
+ return new Complex(\asinh($complex->getReal()));
+ }
+
+ $asinh = clone $complex;
+ $asinh = $asinh->reverse()
+ ->invertReal();
+ $asinh = self::asin($asinh);
+
+ return $asinh->reverse()
+ ->invertImaginary();
+ }
+
+ /**
+ * Returns the inverse tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function atan($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\atan($complex->getReal()));
+ }
+
+ $t1Value = new Complex(-1 * $complex->getImaginary(), $complex->getReal());
+ $uValue = new Complex(1, 0);
+
+ $d1Value = clone $uValue;
+ $d1Value = Operations::subtract($d1Value, $t1Value);
+ $d2Value = Operations::add($t1Value, $uValue);
+ $uResult = $d1Value->divideBy($d2Value);
+ $uResult = self::ln($uResult);
+
+ $realMultiplier = -0.5;
+ $imaginaryMultiplier = 0.5;
+
+ if (abs($uResult->getImaginary()) === M_PI) {
+ // If we have an imaginary value at the max or min (PI or -PI), then we need to ensure
+ // that the primary is assigned for the correct quadrant.
+ $realMultiplier = (
+ ($uResult->getImaginary() === M_PI && $uResult->getReal() > 0.0) ||
+ ($uResult->getImaginary() === -M_PI && $uResult->getReal() < 0.0)
+ ) ? 0.5 : -0.5;
+ }
+
+ return new Complex(
+ $uResult->getImaginary() * $realMultiplier,
+ $uResult->getReal() * $imaginaryMultiplier,
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the inverse hyperbolic tangent of a complex number.
+ *
+ * Formula from Wolfram Alpha:
+ * tanh^(-1)z = 1/2 [ln(1 + z) - ln(1 - z)].
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse hyperbolic tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function atanh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ $real = $complex->getReal();
+ if ($real >= -1.0 && $real <= 1.0) {
+ return new Complex(\atanh($real));
+ } else {
+ return new Complex(\atanh(1 / $real), (($real < 0.0) ? M_PI_2 : -1 * M_PI_2));
+ }
+ }
+
+ $atanh = Operations::multiply(
+ Operations::subtract(
+ self::ln(Operations::add(1.0, $complex)),
+ self::ln(Operations::subtract(1.0, $complex))
+ ),
+ 0.5
+ );
+
+ return $atanh;
+ }
+
+ /**
+ * Returns the complex conjugate of a complex number
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The conjugate of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function conjugate($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return new Complex(
+ $complex->getReal(),
+ -1 * $complex->getImaginary(),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the cosine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The cosine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function cos($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\cos($complex->getReal()));
+ }
+
+ return self::conjugate(
+ new Complex(
+ \cos($complex->getReal()) * \cosh($complex->getImaginary()),
+ \sin($complex->getReal()) * \sinh($complex->getImaginary()),
+ $complex->getSuffix()
+ )
+ );
+ }
+
+ /**
+ * Returns the hyperbolic cosine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic cosine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function cosh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\cosh($complex->getReal()));
+ }
+
+ return new Complex(
+ \cosh($complex->getReal()) * \cos($complex->getImaginary()),
+ \sinh($complex->getReal()) * \sin($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function cot($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::inverse(self::tan($complex));
+ }
+
+ /**
+ * Returns the hyperbolic cotangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic cotangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function coth($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::inverse(self::tanh($complex));
+ }
+
+ /**
+ * Returns the cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function csc($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::inverse(self::sin($complex));
+ }
+
+ /**
+ * Returns the hyperbolic cosecant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic cosecant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function csch($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return new Complex(INF);
+ }
+
+ return self::inverse(self::sinh($complex));
+ }
+
+ /**
+ * Returns the exponential of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The exponential of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function exp($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && (\abs($complex->getImaginary()) == M_PI)) {
+ return new Complex(-1.0, 0.0);
+ }
+
+ $rho = \exp($complex->getReal());
+
+ return new Complex(
+ $rho * \cos($complex->getImaginary()),
+ $rho * \sin($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the inverse of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The inverse of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If function would result in a division by zero
+ */
+ public static function inverse($complex): Complex
+ {
+ $complex = clone Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ return $complex->divideInto(1.0);
+ }
+
+ /**
+ * Returns the natural logarithm of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The natural logarithm of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If the real and the imaginary parts are both zero
+ */
+ public static function ln($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
+ throw new InvalidArgumentException();
+ }
+
+ return new Complex(
+ \log(self::rho($complex)),
+ self::theta($complex),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the base-2 logarithm of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The base-2 logarithm of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If the real and the imaginary parts are both zero
+ */
+ public static function log2($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
+ throw new InvalidArgumentException();
+ } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
+ return new Complex(\log($complex->getReal(), 2), 0.0, $complex->getSuffix());
+ }
+
+ return self::ln($complex)
+ ->multiply(\log(Complex::EULER, 2));
+ }
+
+ /**
+ * Returns the common logarithm (base 10) of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The common logarithm (base 10) of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If the real and the imaginary parts are both zero
+ */
+ public static function log10($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (($complex->getReal() == 0.0) && ($complex->getImaginary() == 0.0)) {
+ throw new InvalidArgumentException();
+ } elseif (($complex->getReal() > 0.0) && ($complex->getImaginary() == 0.0)) {
+ return new Complex(\log10($complex->getReal()), 0.0, $complex->getSuffix());
+ }
+
+ return self::ln($complex)
+ ->multiply(\log10(Complex::EULER));
+ }
+
+ /**
+ * Returns the negative of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The negative value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ *
+ * @see rho
+ *
+ */
+ public static function negative($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return new Complex(
+ -1 * $complex->getReal(),
+ -1 * $complex->getImaginary(),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns a complex number raised to a power.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @param float|integer $power The power to raise this value to
+ * @return Complex The complex argument raised to the real power.
+ * @throws Exception If the power argument isn't a valid real
+ */
+ public static function pow($complex, $power): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if (!is_numeric($power)) {
+ throw new Exception('Power argument must be a real number');
+ }
+
+ if ($complex->getImaginary() == 0.0 && $complex->getReal() >= 0.0) {
+ return new Complex(\pow($complex->getReal(), $power));
+ }
+
+ $rValue = \sqrt(($complex->getReal() * $complex->getReal()) + ($complex->getImaginary() * $complex->getImaginary()));
+ $rPower = \pow($rValue, $power);
+ $theta = $complex->argument() * $power;
+ if ($theta == 0) {
+ return new Complex(1);
+ }
+
+ return new Complex($rPower * \cos($theta), $rPower * \sin($theta), $complex->getSuffix());
+ }
+
+ /**
+ * Returns the rho of a complex number.
+ * This is the distance/radius from the centrepoint to the representation of the number in polar coordinates.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return float The rho value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function rho($complex): float
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return \sqrt(
+ ($complex->getReal() * $complex->getReal()) +
+ ($complex->getImaginary() * $complex->getImaginary())
+ );
+ }
+
+ /**
+ * Returns the secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function sec($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::inverse(self::cos($complex));
+ }
+
+ /**
+ * Returns the hyperbolic secant of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic secant of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function sech($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ return self::inverse(self::cosh($complex));
+ }
+
+ /**
+ * Returns the sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function sin($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\sin($complex->getReal()));
+ }
+
+ return new Complex(
+ \sin($complex->getReal()) * \cosh($complex->getImaginary()),
+ \cos($complex->getReal()) * \sinh($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the hyperbolic sine of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic sine of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function sinh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\sinh($complex->getReal()));
+ }
+
+ return new Complex(
+ \sinh($complex->getReal()) * \cos($complex->getImaginary()),
+ \cosh($complex->getReal()) * \sin($complex->getImaginary()),
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the square root of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The Square root of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function sqrt($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ $theta = self::theta($complex);
+ $delta1 = \cos($theta / 2);
+ $delta2 = \sin($theta / 2);
+ $rho = \sqrt(self::rho($complex));
+
+ return new Complex($delta1 * $rho, $delta2 * $rho, $complex->getSuffix());
+ }
+
+ /**
+ * Returns the tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws InvalidArgumentException If function would result in a division by zero
+ */
+ public static function tan($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->isReal()) {
+ return new Complex(\tan($complex->getReal()));
+ }
+
+ $real = $complex->getReal();
+ $imaginary = $complex->getImaginary();
+ $divisor = 1 + \pow(\tan($real), 2) * \pow(\tanh($imaginary), 2);
+ if ($divisor == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ return new Complex(
+ \pow(self::sech($imaginary)->getReal(), 2) * \tan($real) / $divisor,
+ \pow(self::sec($real)->getReal(), 2) * \tanh($imaginary) / $divisor,
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the hyperbolic tangent of a complex number.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return Complex The hyperbolic tangent of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ * @throws \InvalidArgumentException If function would result in a division by zero
+ */
+ public static function tanh($complex): Complex
+ {
+ $complex = Complex::validateComplexArgument($complex);
+ $real = $complex->getReal();
+ $imaginary = $complex->getImaginary();
+ $divisor = \cos($imaginary) * \cos($imaginary) + \sinh($real) * \sinh($real);
+ if ($divisor == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ return new Complex(
+ \sinh($real) * \cosh($real) / $divisor,
+ 0.5 * \sin(2 * $imaginary) / $divisor,
+ $complex->getSuffix()
+ );
+ }
+
+ /**
+ * Returns the theta of a complex number.
+ * This is the angle in radians from the real axis to the representation of the number in polar coordinates.
+ *
+ * @param Complex|mixed $complex Complex number or a numeric value.
+ * @return float The theta value of the complex argument.
+ * @throws Exception If argument isn't a valid real or complex number.
+ */
+ public static function theta($complex): float
+ {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($complex->getReal() == 0.0) {
+ if ($complex->isReal()) {
+ return 0.0;
+ } elseif ($complex->getImaginary() < 0.0) {
+ return M_PI / -2;
+ }
+ return M_PI / 2;
+ } elseif ($complex->getReal() > 0.0) {
+ return \atan($complex->getImaginary() / $complex->getReal());
+ } elseif ($complex->getImaginary() < 0.0) {
+ return -(M_PI - \atan(\abs($complex->getImaginary()) / \abs($complex->getReal())));
+ }
+
+ return M_PI - \atan($complex->getImaginary() / \abs($complex->getReal()));
+ }
+}
diff --git a/vendor/markbaker/complex/classes/src/Operations.php b/vendor/markbaker/complex/classes/src/Operations.php
new file mode 100644
index 0000000..b13a873
--- /dev/null
+++ b/vendor/markbaker/complex/classes/src/Operations.php
@@ -0,0 +1,210 @@
+isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+
+ $real = $result->getReal() + $complex->getReal();
+ $imaginary = $result->getImaginary() + $complex->getImaginary();
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Divides two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to divide
+ * @return Complex
+ */
+ public static function divideby(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ $delta1 = ($result->getReal() * $complex->getReal()) +
+ ($result->getImaginary() * $complex->getImaginary());
+ $delta2 = ($result->getImaginary() * $complex->getReal()) -
+ ($result->getReal() * $complex->getImaginary());
+ $delta3 = ($complex->getReal() * $complex->getReal()) +
+ ($complex->getImaginary() * $complex->getImaginary());
+
+ $real = $delta1 / $delta3;
+ $imaginary = $delta2 / $delta3;
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Divides two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to divide
+ * @return Complex
+ */
+ public static function divideinto(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+ if ($result->getReal() == 0.0 && $result->getImaginary() == 0.0) {
+ throw new InvalidArgumentException('Division by zero');
+ }
+
+ $delta1 = ($complex->getReal() * $result->getReal()) +
+ ($complex->getImaginary() * $result->getImaginary());
+ $delta2 = ($complex->getImaginary() * $result->getReal()) -
+ ($complex->getReal() * $result->getImaginary());
+ $delta3 = ($result->getReal() * $result->getReal()) +
+ ($result->getImaginary() * $result->getImaginary());
+
+ $real = $delta1 / $delta3;
+ $imaginary = $delta2 / $delta3;
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Multiplies two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to multiply
+ * @return Complex
+ */
+ public static function multiply(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+
+ $real = ($result->getReal() * $complex->getReal()) -
+ ($result->getImaginary() * $complex->getImaginary());
+ $imaginary = ($result->getReal() * $complex->getImaginary()) +
+ ($result->getImaginary() * $complex->getReal());
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Subtracts two or more complex numbers
+ *
+ * @param array of string|integer|float|Complex $complexValues The numbers to subtract
+ * @return Complex
+ */
+ public static function subtract(...$complexValues): Complex
+ {
+ if (count($complexValues) < 2) {
+ throw new \Exception('This function requires at least 2 arguments');
+ }
+
+ $base = array_shift($complexValues);
+ $result = clone Complex::validateComplexArgument($base);
+
+ foreach ($complexValues as $complex) {
+ $complex = Complex::validateComplexArgument($complex);
+
+ if ($result->isComplex() && $complex->isComplex() &&
+ $result->getSuffix() !== $complex->getSuffix()) {
+ throw new Exception('Suffix Mismatch');
+ }
+
+ $real = $result->getReal() - $complex->getReal();
+ $imaginary = $result->getImaginary() - $complex->getImaginary();
+
+ $result = new Complex(
+ $real,
+ $imaginary,
+ ($imaginary == 0.0) ? null : max($result->getSuffix(), $complex->getSuffix())
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/markbaker/complex/composer.json b/vendor/markbaker/complex/composer.json
new file mode 100644
index 0000000..246683c
--- /dev/null
+++ b/vendor/markbaker/complex/composer.json
@@ -0,0 +1,40 @@
+{
+ "name": "markbaker/complex",
+ "type": "library",
+ "description": "PHP Class for working with complex numbers",
+ "keywords": ["complex", "mathematics"],
+ "homepage": "https://github.com/MarkBaker/PHPComplex",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@lange.demon.co.uk"
+ }
+ ],
+ "config": {
+ "sort-packages": true,
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "markbaker/ukraine": true
+ }
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master"
+ },
+ "autoload": {
+ "psr-4": {
+ "Complex\\": "classes/src/"
+ }
+ },
+ "scripts": {
+ "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n",
+ "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n"
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/markbaker/complex/examples/complexTest.php b/vendor/markbaker/complex/examples/complexTest.php
new file mode 100644
index 0000000..9a5e123
--- /dev/null
+++ b/vendor/markbaker/complex/examples/complexTest.php
@@ -0,0 +1,154 @@
+add(456);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456);
+$x->add(789.012);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->add(new Complex(-987.654, -32.1));
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->add(-987.654);
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->add(new Complex(0, 1));
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->add(new Complex(0, -1));
+echo $x, PHP_EOL;
+
+
+echo PHP_EOL, 'Subtract', PHP_EOL;
+
+$x = new Complex(123);
+$x->subtract(456);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456);
+$x->subtract(789.012);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->subtract(new Complex(-987.654, -32.1));
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->subtract(-987.654);
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->subtract(new Complex(0, 1));
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->subtract(new Complex(0, -1));
+echo $x, PHP_EOL;
+
+
+echo PHP_EOL, 'Multiply', PHP_EOL;
+
+$x = new Complex(123);
+$x->multiply(456);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456);
+$x->multiply(789.012);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->multiply(new Complex(-987.654, -32.1));
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->multiply(-987.654);
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->multiply(new Complex(0, 1));
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->multiply(new Complex(0, -1));
+echo $x, PHP_EOL;
+
+
+echo PHP_EOL, 'Divide By', PHP_EOL;
+
+$x = new Complex(123);
+$x->divideBy(456);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456);
+$x->divideBy(789.012);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->divideBy(new Complex(-987.654, -32.1));
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->divideBy(-987.654);
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->divideBy(new Complex(0, 1));
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->divideBy(new Complex(0, -1));
+echo $x, PHP_EOL;
+
+
+echo PHP_EOL, 'Divide Into', PHP_EOL;
+
+$x = new Complex(123);
+$x->divideInto(456);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456);
+$x->divideInto(789.012);
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->divideInto(new Complex(-987.654, -32.1));
+echo $x, PHP_EOL;
+
+$x = new Complex(123.456, 78.90);
+$x->divideInto(-987.654);
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->divideInto(new Complex(0, 1));
+echo $x, PHP_EOL;
+
+$x = new Complex(-987.654, -32.1);
+$x->divideInto(new Complex(0, -1));
+echo $x, PHP_EOL;
diff --git a/vendor/markbaker/complex/examples/testFunctions.php b/vendor/markbaker/complex/examples/testFunctions.php
new file mode 100644
index 0000000..bad1c03
--- /dev/null
+++ b/vendor/markbaker/complex/examples/testFunctions.php
@@ -0,0 +1,52 @@
+getMessage(), PHP_EOL;
+ }
+ }
+ echo PHP_EOL;
+ }
+}
diff --git a/vendor/markbaker/complex/examples/testOperations.php b/vendor/markbaker/complex/examples/testOperations.php
new file mode 100644
index 0000000..2b7e0ba
--- /dev/null
+++ b/vendor/markbaker/complex/examples/testOperations.php
@@ -0,0 +1,35 @@
+ ', $result, PHP_EOL;
+
+echo PHP_EOL;
+
+echo 'Subtraction', PHP_EOL;
+
+$result = Operations::subtract(...$values);
+echo '=> ', $result, PHP_EOL;
+
+echo PHP_EOL;
+
+echo 'Multiplication', PHP_EOL;
+
+$result = Operations::multiply(...$values);
+echo '=> ', $result, PHP_EOL;
diff --git a/vendor/markbaker/complex/license.md b/vendor/markbaker/complex/license.md
new file mode 100644
index 0000000..5b4b156
--- /dev/null
+++ b/vendor/markbaker/complex/license.md
@@ -0,0 +1,25 @@
+The MIT License (MIT)
+=====================
+
+Copyright © `2017` `Mark Baker`
+
+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.
\ No newline at end of file
diff --git a/vendor/markbaker/matrix/.github/workflows/main.yaml b/vendor/markbaker/matrix/.github/workflows/main.yaml
new file mode 100644
index 0000000..5fa1bdb
--- /dev/null
+++ b/vendor/markbaker/matrix/.github/workflows/main.yaml
@@ -0,0 +1,124 @@
+name: main
+on: [ push, pull_request ]
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-version:
+ - '7.1'
+ - '7.2'
+ - '7.3'
+ - '7.4'
+ - '8.0'
+ - '8.1'
+ - '8.2'
+
+ include:
+ - php-version: 'nightly'
+ experimental: true
+
+ name: PHP ${{ matrix.php-version }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+ extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
+ coverage: none
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Delete composer lock file
+ id: composer-lock
+ if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' || matrix.php-version == '8.2' || matrix.php-version == 'nightly' }}
+ run: |
+ rm composer.lock
+ echo "::set-output name=flags::--ignore-platform-reqs"
+
+ - name: Install dependencies
+ run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }}
+
+ - name: Setup problem matchers for PHP
+ run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
+
+ - name: Setup problem matchers for PHPUnit
+ run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+
+ - name: Test with PHPUnit
+ run: ./vendor/bin/phpunit
+
+ phpcs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 7.4
+ extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
+ coverage: none
+ tools: cs2pr
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Code style with PHP_CodeSniffer
+ run: ./vendor/bin/phpcs -q --report=checkstyle | cs2pr --graceful-warnings --colorize
+
+ coverage:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP, with composer and extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 7.4
+ extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
+ coverage: pcov
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - name: Cache composer dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+ - name: Coverage
+ run: |
+ ./vendor/bin/phpunit --coverage-text
diff --git a/vendor/markbaker/matrix/README.md b/vendor/markbaker/matrix/README.md
new file mode 100644
index 0000000..d0dc914
--- /dev/null
+++ b/vendor/markbaker/matrix/README.md
@@ -0,0 +1,215 @@
+PHPMatrix
+==========
+
+---
+
+PHP Class for handling Matrices
+
+[](https://github.com/MarkBaker/PHPMatrix/actions)
+[](https://packagist.org/packages/markbaker/matrix)
+[](https://packagist.org/packages/markbaker/matrix)
+[](https://packagist.org/packages/markbaker/matrix)
+
+
+[](https://xkcd.com/184/)
+
+Matrix Transform
+
+---
+
+This library currently provides the following operations:
+
+ - addition
+ - direct sum
+ - subtraction
+ - multiplication
+ - division (using [A].[B]-1 )
+ - division by
+ - division into
+
+together with functions for
+
+ - adjoint
+ - antidiagonal
+ - cofactors
+ - determinant
+ - diagonal
+ - identity
+ - inverse
+ - minors
+ - trace
+ - transpose
+ - solve
+
+ Given Matrices A and B, calculate X for A.X = B
+
+and classes for
+
+ - Decomposition
+ - LU Decomposition with partial row pivoting,
+
+ such that [P].[A] = [L].[U] and [A] = [P]| .[L].[U]
+ - QR Decomposition
+
+ such that [A] = [Q].[R]
+
+## TO DO
+
+ - power() function
+ - Decomposition
+ - Cholesky Decomposition
+ - EigenValue Decomposition
+ - EigenValues
+ - EigenVectors
+
+---
+
+# Installation
+
+```shell
+composer require markbaker/matrix:^3.0
+```
+
+# Important BC Note
+
+If you've previously been using procedural calls to functions and operations using this library, then from version 3.0 you should use [MarkBaker/PHPMatrixFunctions](https://github.com/MarkBaker/PHPMatrixFunctions) instead (available on packagist as [markbaker/matrix-functions](https://packagist.org/packages/markbaker/matrix-functions)).
+
+You'll need to replace `markbaker/matrix`in your `composer.json` file with the new library, but otherwise there should be no difference in the namespacing, or in the way that you have called the Matrix functions in the past, so no actual code changes are required.
+
+```shell
+composer require markbaker/matrix-functions:^1.0
+```
+
+You should not reference this library (`markbaker/matrix`) in your `composer.json`, composer wil take care of that for you.
+
+# Usage
+
+To create a new Matrix object, provide an array as the constructor argument
+
+```php
+$grid = [
+ [16, 3, 2, 13],
+ [ 5, 10, 11, 8],
+ [ 9, 6, 7, 12],
+ [ 4, 15, 14, 1],
+];
+
+$matrix = new Matrix\Matrix($grid);
+```
+The `Builder` class provides helper methods for creating specific matrices, specifically an identity matrix of a specified size; or a matrix of a specified dimensions, with every cell containing a set value.
+```php
+$matrix = Matrix\Builder::createFilledMatrix(1, 5, 3);
+```
+Will create a matrix of 5 rows and 3 columns, filled with a `1` in every cell; while
+```php
+$matrix = Matrix\Builder::createIdentityMatrix(3);
+```
+will create a 3x3 identity matrix.
+
+
+Matrix objects are immutable: whenever you call a method or pass a grid to a function that returns a matrix value, a new Matrix object will be returned, and the original will remain unchanged. This also allows you to chain multiple methods as you would for a fluent interface (as long as they are methods that will return a Matrix result).
+
+## Performing Mathematical Operations
+
+To perform mathematical operations with Matrices, you can call the appropriate method against a matrix value, passing other values as arguments
+
+```php
+$matrix1 = new Matrix\Matrix([
+ [2, 7, 6],
+ [9, 5, 1],
+ [4, 3, 8],
+]);
+$matrix2 = new Matrix\Matrix([
+ [1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+]);
+
+var_dump($matrix1->multiply($matrix2)->toArray());
+```
+or pass all values to the appropriate static method
+```php
+$matrix1 = new Matrix\Matrix([
+ [2, 7, 6],
+ [9, 5, 1],
+ [4, 3, 8],
+]);
+$matrix2 = new Matrix\Matrix([
+ [1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+]);
+
+var_dump(Matrix\Operations::multiply($matrix1, $matrix2)->toArray());
+```
+You can pass in the arguments as Matrix objects, or as arrays.
+
+If you want to perform the same operation against multiple values (e.g. to add three or more matrices), then you can pass multiple arguments to any of the operations.
+
+## Using functions
+
+When calling any of the available functions for a matrix value, you can either call the relevant method for the Matrix object
+```php
+$grid = [
+ [16, 3, 2, 13],
+ [ 5, 10, 11, 8],
+ [ 9, 6, 7, 12],
+ [ 4, 15, 14, 1],
+];
+
+$matrix = new Matrix\Matrix($grid);
+
+echo $matrix->trace();
+```
+or you can call the static method, passing the Matrix object or array as an argument
+```php
+$grid = [
+ [16, 3, 2, 13],
+ [ 5, 10, 11, 8],
+ [ 9, 6, 7, 12],
+ [ 4, 15, 14, 1],
+];
+
+$matrix = new Matrix\Matrix($grid);
+echo Matrix\Functions::trace($matrix);
+```
+```php
+$grid = [
+ [16, 3, 2, 13],
+ [ 5, 10, 11, 8],
+ [ 9, 6, 7, 12],
+ [ 4, 15, 14, 1],
+];
+
+echo Matrix\Functions::trace($grid);
+```
+
+## Decomposition
+
+The library also provides classes for matrix decomposition. You can access these using
+```php
+$grid = [
+ [1, 2],
+ [3, 4],
+];
+
+$matrix = new Matrix\Matrix($grid);
+
+$decomposition = new Matrix\Decomposition\QR($matrix);
+$Q = $decomposition->getQ();
+$R = $decomposition->getR();
+```
+
+or alternatively us the `Decomposition` factory, identifying which form of decomposition you want to use
+```php
+$grid = [
+ [1, 2],
+ [3, 4],
+];
+
+$matrix = new Matrix\Matrix($grid);
+
+$decomposition = Matrix\Decomposition\Decomposition::decomposition(Matrix\Decomposition\Decomposition::QR, $matrix);
+$Q = $decomposition->getQ();
+$R = $decomposition->getR();
+```
diff --git a/vendor/markbaker/matrix/buildPhar.php b/vendor/markbaker/matrix/buildPhar.php
new file mode 100644
index 0000000..e1b8f96
--- /dev/null
+++ b/vendor/markbaker/matrix/buildPhar.php
@@ -0,0 +1,62 @@
+ 'Mark Baker ',
+ 'Description' => 'PHP Class for working with Matrix numbers',
+ 'Copyright' => 'Mark Baker (c) 2013-' . date('Y'),
+ 'Timestamp' => time(),
+ 'Version' => '0.1.0',
+ 'Date' => date('Y-m-d')
+);
+
+// cleanup
+if (file_exists($pharName)) {
+ echo "Removed: {$pharName}\n";
+ unlink($pharName);
+}
+
+echo "Building phar file...\n";
+
+// the phar object
+$phar = new Phar($pharName, null, 'Matrix');
+$phar->buildFromDirectory($sourceDir);
+$phar->setStub(
+<<<'EOT'
+getMessage());
+ exit(1);
+ }
+
+ include 'phar://functions/sqrt.php';
+
+ __HALT_COMPILER();
+EOT
+);
+$phar->setMetadata($metaData);
+$phar->compressFiles(Phar::GZ);
+
+echo "Complete.\n";
+
+exit();
diff --git a/vendor/markbaker/matrix/classes/src/Builder.php b/vendor/markbaker/matrix/classes/src/Builder.php
new file mode 100644
index 0000000..161bb68
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Builder.php
@@ -0,0 +1,70 @@
+toArray();
+
+ for ($x = 0; $x < $dimensions; ++$x) {
+ $grid[$x][$x] = 1;
+ }
+
+ return new Matrix($grid);
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php b/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php
new file mode 100644
index 0000000..f014448
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Decomposition/Decomposition.php
@@ -0,0 +1,27 @@
+luMatrix = $matrix->toArray();
+ $this->rows = $matrix->rows;
+ $this->columns = $matrix->columns;
+
+ $this->buildPivot();
+ }
+
+ /**
+ * Get lower triangular factor.
+ *
+ * @return Matrix Lower triangular factor
+ */
+ public function getL(): Matrix
+ {
+ $lower = [];
+
+ $columns = min($this->rows, $this->columns);
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ if ($row > $column) {
+ $lower[$row][$column] = $this->luMatrix[$row][$column];
+ } elseif ($row === $column) {
+ $lower[$row][$column] = 1.0;
+ } else {
+ $lower[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ return new Matrix($lower);
+ }
+
+ /**
+ * Get upper triangular factor.
+ *
+ * @return Matrix Upper triangular factor
+ */
+ public function getU(): Matrix
+ {
+ $upper = [];
+
+ $rows = min($this->rows, $this->columns);
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ if ($row <= $column) {
+ $upper[$row][$column] = $this->luMatrix[$row][$column];
+ } else {
+ $upper[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ return new Matrix($upper);
+ }
+
+ /**
+ * Return pivot permutation vector.
+ *
+ * @return Matrix Pivot matrix
+ */
+ public function getP(): Matrix
+ {
+ $pMatrix = [];
+
+ $pivots = $this->pivot;
+ $pivotCount = count($pivots);
+ foreach ($pivots as $row => $pivot) {
+ $pMatrix[$row] = array_fill(0, $pivotCount, 0);
+ $pMatrix[$row][$pivot] = 1;
+ }
+
+ return new Matrix($pMatrix);
+ }
+
+ /**
+ * Return pivot permutation vector.
+ *
+ * @return array Pivot vector
+ */
+ public function getPivot(): array
+ {
+ return $this->pivot;
+ }
+
+ /**
+ * Is the matrix nonsingular?
+ *
+ * @return bool true if U, and hence A, is nonsingular
+ */
+ public function isNonsingular(): bool
+ {
+ for ($diagonal = 0; $diagonal < $this->columns; ++$diagonal) {
+ if ($this->luMatrix[$diagonal][$diagonal] === 0.0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function buildPivot(): void
+ {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $this->pivot[$row] = $row;
+ }
+
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $luColumn = $this->localisedReferenceColumn($column);
+
+ $this->applyTransformations($column, $luColumn);
+
+ $pivot = $this->findPivot($column, $luColumn);
+ if ($pivot !== $column) {
+ $this->pivotExchange($pivot, $column);
+ }
+
+ $this->computeMultipliers($column);
+
+ unset($luColumn);
+ }
+ }
+
+ private function localisedReferenceColumn($column): array
+ {
+ $luColumn = [];
+
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $luColumn[$row] = &$this->luMatrix[$row][$column];
+ }
+
+ return $luColumn;
+ }
+
+ private function applyTransformations($column, array $luColumn): void
+ {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $luRow = $this->luMatrix[$row];
+ // Most of the time is spent in the following dot product.
+ $kmax = min($row, $column);
+ $sValue = 0.0;
+ for ($kValue = 0; $kValue < $kmax; ++$kValue) {
+ $sValue += $luRow[$kValue] * $luColumn[$kValue];
+ }
+ $luRow[$column] = $luColumn[$row] -= $sValue;
+ }
+ }
+
+ private function findPivot($column, array $luColumn): int
+ {
+ $pivot = $column;
+ for ($row = $column + 1; $row < $this->rows; ++$row) {
+ if (abs($luColumn[$row]) > abs($luColumn[$pivot])) {
+ $pivot = $row;
+ }
+ }
+
+ return $pivot;
+ }
+
+ private function pivotExchange($pivot, $column): void
+ {
+ for ($kValue = 0; $kValue < $this->columns; ++$kValue) {
+ $tValue = $this->luMatrix[$pivot][$kValue];
+ $this->luMatrix[$pivot][$kValue] = $this->luMatrix[$column][$kValue];
+ $this->luMatrix[$column][$kValue] = $tValue;
+ }
+
+ $lValue = $this->pivot[$pivot];
+ $this->pivot[$pivot] = $this->pivot[$column];
+ $this->pivot[$column] = $lValue;
+ }
+
+ private function computeMultipliers($diagonal): void
+ {
+ if (($diagonal < $this->rows) && ($this->luMatrix[$diagonal][$diagonal] != 0.0)) {
+ for ($row = $diagonal + 1; $row < $this->rows; ++$row) {
+ $this->luMatrix[$row][$diagonal] /= $this->luMatrix[$diagonal][$diagonal];
+ }
+ }
+ }
+
+ private function pivotB(Matrix $B): array
+ {
+ $X = [];
+ foreach ($this->pivot as $rowId) {
+ $row = $B->getRows($rowId + 1)->toArray();
+ $X[] = array_pop($row);
+ }
+
+ return $X;
+ }
+
+ /**
+ * Solve A*X = B.
+ *
+ * @param Matrix $B a Matrix with as many rows as A and any number of columns
+ *
+ * @throws Exception
+ *
+ * @return Matrix X so that L*U*X = B(piv,:)
+ */
+ public function solve(Matrix $B): Matrix
+ {
+ if ($B->rows !== $this->rows) {
+ throw new Exception('Matrix row dimensions are not equal');
+ }
+
+ if ($this->rows !== $this->columns) {
+ throw new Exception('LU solve() only works on square matrices');
+ }
+
+ if (!$this->isNonsingular()) {
+ throw new Exception('Can only perform operation on singular matrix');
+ }
+
+ // Copy right hand side with pivoting
+ $nx = $B->columns;
+ $X = $this->pivotB($B);
+
+ // Solve L*Y = B(piv,:)
+ for ($k = 0; $k < $this->columns; ++$k) {
+ for ($i = $k + 1; $i < $this->columns; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k];
+ }
+ }
+ }
+
+ // Solve U*X = Y;
+ for ($k = $this->columns - 1; $k >= 0; --$k) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$k][$j] /= $this->luMatrix[$k][$k];
+ }
+ for ($i = 0; $i < $k; ++$i) {
+ for ($j = 0; $j < $nx; ++$j) {
+ $X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k];
+ }
+ }
+ }
+
+ return new Matrix($X);
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Decomposition/QR.php b/vendor/markbaker/matrix/classes/src/Decomposition/QR.php
new file mode 100644
index 0000000..4b6106f
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Decomposition/QR.php
@@ -0,0 +1,191 @@
+qrMatrix = $matrix->toArray();
+ $this->rows = $matrix->rows;
+ $this->columns = $matrix->columns;
+
+ $this->decompose();
+ }
+
+ public function getHouseholdVectors(): Matrix
+ {
+ $householdVectors = [];
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ if ($row >= $column) {
+ $householdVectors[$row][$column] = $this->qrMatrix[$row][$column];
+ } else {
+ $householdVectors[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ return new Matrix($householdVectors);
+ }
+
+ public function getQ(): Matrix
+ {
+ $qGrid = [];
+
+ $rowCount = $this->rows;
+ for ($k = $this->columns - 1; $k >= 0; --$k) {
+ for ($i = 0; $i < $this->rows; ++$i) {
+ $qGrid[$i][$k] = 0.0;
+ }
+ $qGrid[$k][$k] = 1.0;
+ if ($this->columns > $this->rows) {
+ $qGrid = array_slice($qGrid, 0, $this->rows);
+ }
+
+ for ($j = $k; $j < $this->columns; ++$j) {
+ if (isset($this->qrMatrix[$k], $this->qrMatrix[$k][$k]) && $this->qrMatrix[$k][$k] != 0.0) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $s += $this->qrMatrix[$i][$k] * $qGrid[$i][$j];
+ }
+ $s = -$s / $this->qrMatrix[$k][$k];
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $qGrid[$i][$j] += $s * $this->qrMatrix[$i][$k];
+ }
+ }
+ }
+ }
+
+ array_walk(
+ $qGrid,
+ function (&$row) use ($rowCount) {
+ $row = array_reverse($row);
+ $row = array_slice($row, 0, $rowCount);
+ }
+ );
+
+ return new Matrix($qGrid);
+ }
+
+ public function getR(): Matrix
+ {
+ $rGrid = [];
+
+ for ($row = 0; $row < $this->columns; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ if ($row < $column) {
+ $rGrid[$row][$column] = $this->qrMatrix[$row][$column] ?? 0.0;
+ } elseif ($row === $column) {
+ $rGrid[$row][$column] = $this->rDiagonal[$row] ?? 0.0;
+ } else {
+ $rGrid[$row][$column] = 0.0;
+ }
+ }
+ }
+
+ if ($this->columns > $this->rows) {
+ $rGrid = array_slice($rGrid, 0, $this->rows);
+ }
+
+ return new Matrix($rGrid);
+ }
+
+ private function hypo($a, $b): float
+ {
+ if (abs($a) > abs($b)) {
+ $r = $b / $a;
+ $r = abs($a) * sqrt(1 + $r * $r);
+ } elseif ($b != 0.0) {
+ $r = $a / $b;
+ $r = abs($b) * sqrt(1 + $r * $r);
+ } else {
+ $r = 0.0;
+ }
+
+ return $r;
+ }
+
+ /**
+ * QR Decomposition computed by Householder reflections.
+ */
+ private function decompose(): void
+ {
+ for ($k = 0; $k < $this->columns; ++$k) {
+ // Compute 2-norm of k-th column without under/overflow.
+ $norm = 0.0;
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $norm = $this->hypo($norm, $this->qrMatrix[$i][$k]);
+ }
+ if ($norm != 0.0) {
+ // Form k-th Householder vector.
+ if ($this->qrMatrix[$k][$k] < 0.0) {
+ $norm = -$norm;
+ }
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $this->qrMatrix[$i][$k] /= $norm;
+ }
+ $this->qrMatrix[$k][$k] += 1.0;
+ // Apply transformation to remaining columns.
+ for ($j = $k + 1; $j < $this->columns; ++$j) {
+ $s = 0.0;
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $s += $this->qrMatrix[$i][$k] * $this->qrMatrix[$i][$j];
+ }
+ $s = -$s / $this->qrMatrix[$k][$k];
+ for ($i = $k; $i < $this->rows; ++$i) {
+ $this->qrMatrix[$i][$j] += $s * $this->qrMatrix[$i][$k];
+ }
+ }
+ }
+ $this->rDiagonal[$k] = -$norm;
+ }
+ }
+
+ public function isFullRank(): bool
+ {
+ for ($j = 0; $j < $this->columns; ++$j) {
+ if ($this->rDiagonal[$j] == 0.0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Least squares solution of A*X = B.
+ *
+ * @param Matrix $B a Matrix with as many rows as A and any number of columns
+ *
+ * @throws Exception
+ *
+ * @return Matrix matrix that minimizes the two norm of Q*R*X-B
+ */
+ public function solve(Matrix $B): Matrix
+ {
+ if ($B->rows !== $this->rows) {
+ throw new Exception('Matrix row dimensions are not equal');
+ }
+
+ if (!$this->isFullRank()) {
+ throw new Exception('Can only perform this operation on a full-rank matrix');
+ }
+
+ // Compute Y = transpose(Q)*B
+ $Y = $this->getQ()->transpose()
+ ->multiply($B);
+ // Solve R*X = Y;
+ return $this->getR()->inverse()
+ ->multiply($Y);
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Div0Exception.php b/vendor/markbaker/matrix/classes/src/Div0Exception.php
new file mode 100644
index 0000000..eba28f8
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Div0Exception.php
@@ -0,0 +1,13 @@
+isSquare()) {
+ throw new Exception('Adjoint can only be calculated for a square matrix');
+ }
+
+ return self::getAdjoint($matrix);
+ }
+
+ /**
+ * Calculate the cofactors of the matrix
+ *
+ * @param Matrix $matrix The matrix whose cofactors we wish to calculate
+ * @return Matrix
+ *
+ * @throws Exception
+ */
+ private static function getCofactors(Matrix $matrix)
+ {
+ $cofactors = self::getMinors($matrix);
+ $dimensions = $matrix->rows;
+
+ $cof = 1;
+ for ($i = 0; $i < $dimensions; ++$i) {
+ $cofs = $cof;
+ for ($j = 0; $j < $dimensions; ++$j) {
+ $cofactors[$i][$j] *= $cofs;
+ $cofs = -$cofs;
+ }
+ $cof = -$cof;
+ }
+
+ return new Matrix($cofactors);
+ }
+
+ /**
+ * Return the cofactors of this matrix
+ *
+ * @param Matrix|array $matrix The matrix whose cofactors we wish to calculate
+ * @return Matrix
+ *
+ * @throws Exception
+ */
+ public static function cofactors($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Cofactors can only be calculated for a square matrix');
+ }
+
+ return self::getCofactors($matrix);
+ }
+
+ /**
+ * @param Matrix $matrix
+ * @param int $row
+ * @param int $column
+ * @return float
+ * @throws Exception
+ */
+ private static function getDeterminantSegment(Matrix $matrix, $row, $column)
+ {
+ $tmpMatrix = $matrix->toArray();
+ unset($tmpMatrix[$row]);
+ array_walk(
+ $tmpMatrix,
+ function (&$row) use ($column) {
+ unset($row[$column]);
+ }
+ );
+
+ return self::getDeterminant(new Matrix($tmpMatrix));
+ }
+
+ /**
+ * Calculate the determinant of the matrix
+ *
+ * @param Matrix $matrix The matrix whose determinant we wish to calculate
+ * @return float
+ *
+ * @throws Exception
+ */
+ private static function getDeterminant(Matrix $matrix)
+ {
+ $dimensions = $matrix->rows;
+ $determinant = 0;
+
+ switch ($dimensions) {
+ case 1:
+ $determinant = $matrix->getValue(1, 1);
+ break;
+ case 2:
+ $determinant = $matrix->getValue(1, 1) * $matrix->getValue(2, 2) -
+ $matrix->getValue(1, 2) * $matrix->getValue(2, 1);
+ break;
+ default:
+ for ($i = 1; $i <= $dimensions; ++$i) {
+ $det = $matrix->getValue(1, $i) * self::getDeterminantSegment($matrix, 0, $i - 1);
+ if (($i % 2) == 0) {
+ $determinant -= $det;
+ } else {
+ $determinant += $det;
+ }
+ }
+ break;
+ }
+
+ return $determinant;
+ }
+
+ /**
+ * Return the determinant of this matrix
+ *
+ * @param Matrix|array $matrix The matrix whose determinant we wish to calculate
+ * @return float
+ * @throws Exception
+ **/
+ public static function determinant($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Determinant can only be calculated for a square matrix');
+ }
+
+ return self::getDeterminant($matrix);
+ }
+
+ /**
+ * Return the diagonal of this matrix
+ *
+ * @param Matrix|array $matrix The matrix whose diagonal we wish to calculate
+ * @return Matrix
+ * @throws Exception
+ **/
+ public static function diagonal($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Diagonal can only be extracted from a square matrix');
+ }
+
+ $dimensions = $matrix->rows;
+ $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions)
+ ->toArray();
+
+ for ($i = 0; $i < $dimensions; ++$i) {
+ $grid[$i][$i] = $matrix->getValue($i + 1, $i + 1);
+ }
+
+ return new Matrix($grid);
+ }
+
+ /**
+ * Return the antidiagonal of this matrix
+ *
+ * @param Matrix|array $matrix The matrix whose antidiagonal we wish to calculate
+ * @return Matrix
+ * @throws Exception
+ **/
+ public static function antidiagonal($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Anti-Diagonal can only be extracted from a square matrix');
+ }
+
+ $dimensions = $matrix->rows;
+ $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions)
+ ->toArray();
+
+ for ($i = 0; $i < $dimensions; ++$i) {
+ $grid[$i][$dimensions - $i - 1] = $matrix->getValue($i + 1, $dimensions - $i);
+ }
+
+ return new Matrix($grid);
+ }
+
+ /**
+ * Return the identity matrix
+ * The identity matrix, or sometimes ambiguously called a unit matrix, of size n is the n × n square matrix
+ * with ones on the main diagonal and zeros elsewhere
+ *
+ * @param Matrix|array $matrix The matrix whose identity we wish to calculate
+ * @return Matrix
+ * @throws Exception
+ **/
+ public static function identity($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Identity can only be created for a square matrix');
+ }
+
+ $dimensions = $matrix->rows;
+
+ return Builder::createIdentityMatrix($dimensions);
+ }
+
+ /**
+ * Return the inverse of this matrix
+ *
+ * @param Matrix|array $matrix The matrix whose inverse we wish to calculate
+ * @return Matrix
+ * @throws Exception
+ **/
+ public static function inverse($matrix, string $type = 'inverse')
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception(ucfirst($type) . ' can only be calculated for a square matrix');
+ }
+
+ $determinant = self::getDeterminant($matrix);
+ if ($determinant == 0.0) {
+ throw new Div0Exception(ucfirst($type) . ' can only be calculated for a matrix with a non-zero determinant');
+ }
+
+ if ($matrix->rows == 1) {
+ return new Matrix([[1 / $matrix->getValue(1, 1)]]);
+ }
+
+ return self::getAdjoint($matrix)
+ ->multiply(1 / $determinant);
+ }
+
+ /**
+ * Calculate the minors of the matrix
+ *
+ * @param Matrix $matrix The matrix whose minors we wish to calculate
+ * @return array[]
+ *
+ * @throws Exception
+ */
+ protected static function getMinors(Matrix $matrix)
+ {
+ $minors = $matrix->toArray();
+ $dimensions = $matrix->rows;
+ if ($dimensions == 1) {
+ return $minors;
+ }
+
+ for ($i = 0; $i < $dimensions; ++$i) {
+ for ($j = 0; $j < $dimensions; ++$j) {
+ $minors[$i][$j] = self::getDeterminantSegment($matrix, $i, $j);
+ }
+ }
+
+ return $minors;
+ }
+
+ /**
+ * Return the minors of the matrix
+ * The minor of a matrix A is the determinant of some smaller square matrix, cut down from A by removing one or
+ * more of its rows or columns.
+ * Minors obtained by removing just one row and one column from square matrices (first minors) are required for
+ * calculating matrix cofactors, which in turn are useful for computing both the determinant and inverse of
+ * square matrices.
+ *
+ * @param Matrix|array $matrix The matrix whose minors we wish to calculate
+ * @return Matrix
+ * @throws Exception
+ **/
+ public static function minors($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Minors can only be calculated for a square matrix');
+ }
+
+ return new Matrix(self::getMinors($matrix));
+ }
+
+ /**
+ * Return the trace of this matrix
+ * The trace is defined as the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right)
+ * of the matrix
+ *
+ * @param Matrix|array $matrix The matrix whose trace we wish to calculate
+ * @return float
+ * @throws Exception
+ **/
+ public static function trace($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ if (!$matrix->isSquare()) {
+ throw new Exception('Trace can only be extracted from a square matrix');
+ }
+
+ $dimensions = $matrix->rows;
+ $result = 0;
+ for ($i = 1; $i <= $dimensions; ++$i) {
+ $result += $matrix->getValue($i, $i);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return the transpose of this matrix
+ *
+ * @param Matrix|\a $matrix The matrix whose transpose we wish to calculate
+ * @return Matrix
+ **/
+ public static function transpose($matrix)
+ {
+ $matrix = self::validateMatrix($matrix);
+
+ $array = array_values(array_merge([null], $matrix->toArray()));
+ $grid = call_user_func_array(
+ 'array_map',
+ $array
+ );
+
+ return new Matrix($grid);
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Matrix.php b/vendor/markbaker/matrix/classes/src/Matrix.php
new file mode 100644
index 0000000..95f55e7
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Matrix.php
@@ -0,0 +1,423 @@
+buildFromArray(array_values($grid));
+ }
+
+ /*
+ * Create a new Matrix object from an array of values
+ *
+ * @param array $grid
+ */
+ protected function buildFromArray(array $grid): void
+ {
+ $this->rows = count($grid);
+ $columns = array_reduce(
+ $grid,
+ function ($carry, $value) {
+ return max($carry, is_array($value) ? count($value) : 1);
+ }
+ );
+ $this->columns = $columns;
+
+ array_walk(
+ $grid,
+ function (&$value) use ($columns) {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+ $value = array_pad(array_values($value), $columns, null);
+ }
+ );
+
+ $this->grid = $grid;
+ }
+
+ /**
+ * Validate that a row number is a positive integer
+ *
+ * @param int $row
+ * @return int
+ * @throws Exception
+ */
+ public static function validateRow(int $row): int
+ {
+ if ((!is_numeric($row)) || (intval($row) < 1)) {
+ throw new Exception('Invalid Row');
+ }
+
+ return (int)$row;
+ }
+
+ /**
+ * Validate that a column number is a positive integer
+ *
+ * @param int $column
+ * @return int
+ * @throws Exception
+ */
+ public static function validateColumn(int $column): int
+ {
+ if ((!is_numeric($column)) || (intval($column) < 1)) {
+ throw new Exception('Invalid Column');
+ }
+
+ return (int)$column;
+ }
+
+ /**
+ * Validate that a row number falls within the set of rows for this matrix
+ *
+ * @param int $row
+ * @return int
+ * @throws Exception
+ */
+ protected function validateRowInRange(int $row): int
+ {
+ $row = static::validateRow($row);
+ if ($row > $this->rows) {
+ throw new Exception('Requested Row exceeds matrix size');
+ }
+
+ return $row;
+ }
+
+ /**
+ * Validate that a column number falls within the set of columns for this matrix
+ *
+ * @param int $column
+ * @return int
+ * @throws Exception
+ */
+ protected function validateColumnInRange(int $column): int
+ {
+ $column = static::validateColumn($column);
+ if ($column > $this->columns) {
+ throw new Exception('Requested Column exceeds matrix size');
+ }
+
+ return $column;
+ }
+
+ /**
+ * Return a new matrix as a subset of rows from this matrix, starting at row number $row, and $rowCount rows
+ * A $rowCount value of 0 will return all rows of the matrix from $row
+ * A negative $rowCount value will return rows until that many rows from the end of the matrix
+ *
+ * Note that row numbers start from 1, not from 0
+ *
+ * @param int $row
+ * @param int $rowCount
+ * @return static
+ * @throws Exception
+ */
+ public function getRows(int $row, int $rowCount = 1): Matrix
+ {
+ $row = $this->validateRowInRange($row);
+ if ($rowCount === 0) {
+ $rowCount = $this->rows - $row + 1;
+ }
+
+ return new static(array_slice($this->grid, $row - 1, (int)$rowCount));
+ }
+
+ /**
+ * Return a new matrix as a subset of columns from this matrix, starting at column number $column, and $columnCount columns
+ * A $columnCount value of 0 will return all columns of the matrix from $column
+ * A negative $columnCount value will return columns until that many columns from the end of the matrix
+ *
+ * Note that column numbers start from 1, not from 0
+ *
+ * @param int $column
+ * @param int $columnCount
+ * @return Matrix
+ * @throws Exception
+ */
+ public function getColumns(int $column, int $columnCount = 1): Matrix
+ {
+ $column = $this->validateColumnInRange($column);
+ if ($columnCount < 1) {
+ $columnCount = $this->columns + $columnCount - $column + 1;
+ }
+
+ $grid = [];
+ for ($i = $column - 1; $i < $column + $columnCount - 1; ++$i) {
+ $grid[] = array_column($this->grid, $i);
+ }
+
+ return (new static($grid))->transpose();
+ }
+
+ /**
+ * Return a new matrix as a subset of rows from this matrix, dropping rows starting at row number $row,
+ * and $rowCount rows
+ * A negative $rowCount value will drop rows until that many rows from the end of the matrix
+ * A $rowCount value of 0 will remove all rows of the matrix from $row
+ *
+ * Note that row numbers start from 1, not from 0
+ *
+ * @param int $row
+ * @param int $rowCount
+ * @return static
+ * @throws Exception
+ */
+ public function dropRows(int $row, int $rowCount = 1): Matrix
+ {
+ $this->validateRowInRange($row);
+ if ($rowCount === 0) {
+ $rowCount = $this->rows - $row + 1;
+ }
+
+ $grid = $this->grid;
+ array_splice($grid, $row - 1, (int)$rowCount);
+
+ return new static($grid);
+ }
+
+ /**
+ * Return a new matrix as a subset of columns from this matrix, dropping columns starting at column number $column,
+ * and $columnCount columns
+ * A negative $columnCount value will drop columns until that many columns from the end of the matrix
+ * A $columnCount value of 0 will remove all columns of the matrix from $column
+ *
+ * Note that column numbers start from 1, not from 0
+ *
+ * @param int $column
+ * @param int $columnCount
+ * @return static
+ * @throws Exception
+ */
+ public function dropColumns(int $column, int $columnCount = 1): Matrix
+ {
+ $this->validateColumnInRange($column);
+ if ($columnCount < 1) {
+ $columnCount = $this->columns + $columnCount - $column + 1;
+ }
+
+ $grid = $this->grid;
+ array_walk(
+ $grid,
+ function (&$row) use ($column, $columnCount) {
+ array_splice($row, $column - 1, (int)$columnCount);
+ }
+ );
+
+ return new static($grid);
+ }
+
+ /**
+ * Return a value from this matrix, from the "cell" identified by the row and column numbers
+ * Note that row and column numbers start from 1, not from 0
+ *
+ * @param int $row
+ * @param int $column
+ * @return mixed
+ * @throws Exception
+ */
+ public function getValue(int $row, int $column)
+ {
+ $row = $this->validateRowInRange($row);
+ $column = $this->validateColumnInRange($column);
+
+ return $this->grid[$row - 1][$column - 1];
+ }
+
+ /**
+ * Returns a Generator that will yield each row of the matrix in turn as a vector matrix
+ * or the value of each cell if the matrix is a column vector
+ *
+ * @return Generator|Matrix[]|mixed[]
+ */
+ public function rows(): Generator
+ {
+ foreach ($this->grid as $i => $row) {
+ yield $i + 1 => ($this->columns == 1)
+ ? $row[0]
+ : new static([$row]);
+ }
+ }
+
+ /**
+ * Returns a Generator that will yield each column of the matrix in turn as a vector matrix
+ * or the value of each cell if the matrix is a row vector
+ *
+ * @return Generator|Matrix[]|mixed[]
+ */
+ public function columns(): Generator
+ {
+ for ($i = 0; $i < $this->columns; ++$i) {
+ yield $i + 1 => ($this->rows == 1)
+ ? $this->grid[0][$i]
+ : new static(array_column($this->grid, $i));
+ }
+ }
+
+ /**
+ * Identify if the row and column dimensions of this matrix are equal,
+ * i.e. if it is a "square" matrix
+ *
+ * @return bool
+ */
+ public function isSquare(): bool
+ {
+ return $this->rows === $this->columns;
+ }
+
+ /**
+ * Identify if this matrix is a vector
+ * i.e. if it comprises only a single row or a single column
+ *
+ * @return bool
+ */
+ public function isVector(): bool
+ {
+ return $this->rows === 1 || $this->columns === 1;
+ }
+
+ /**
+ * Return the matrix as a 2-dimensional array
+ *
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return $this->grid;
+ }
+
+ /**
+ * Solve A*X = B.
+ *
+ * @param Matrix $B Right hand side
+ *
+ * @throws Exception
+ *
+ * @return Matrix ... Solution if A is square, least squares solution otherwise
+ */
+ public function solve(Matrix $B): Matrix
+ {
+ if ($this->columns === $this->rows) {
+ return (new LU($this))->solve($B);
+ }
+
+ return (new QR($this))->solve($B);
+ }
+
+ protected static $getters = [
+ 'rows',
+ 'columns',
+ ];
+
+ /**
+ * Access specific properties as read-only (no setters)
+ *
+ * @param string $propertyName
+ * @return mixed
+ * @throws Exception
+ */
+ public function __get(string $propertyName)
+ {
+ $propertyName = strtolower($propertyName);
+
+ // Test for function calls
+ if (in_array($propertyName, self::$getters)) {
+ return $this->$propertyName;
+ }
+
+ throw new Exception('Property does not exist');
+ }
+
+ protected static $functions = [
+ 'adjoint',
+ 'antidiagonal',
+ 'cofactors',
+ 'determinant',
+ 'diagonal',
+ 'identity',
+ 'inverse',
+ 'minors',
+ 'trace',
+ 'transpose',
+ ];
+
+ protected static $operations = [
+ 'add',
+ 'subtract',
+ 'multiply',
+ 'divideby',
+ 'divideinto',
+ 'directsum',
+ ];
+
+ /**
+ * Returns the result of the function call or operation
+ *
+ * @param string $functionName
+ * @param mixed[] $arguments
+ * @return Matrix|float
+ * @throws Exception
+ */
+ public function __call(string $functionName, $arguments)
+ {
+ $functionName = strtolower(str_replace('_', '', $functionName));
+
+ // Test for function calls
+ if (in_array($functionName, self::$functions, true)) {
+ return Functions::$functionName($this, ...$arguments);
+ }
+ // Test for operation calls
+ if (in_array($functionName, self::$operations, true)) {
+ return Operations::$functionName($this, ...$arguments);
+ }
+ throw new Exception('Function or Operation does not exist');
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operations.php b/vendor/markbaker/matrix/classes/src/Operations.php
new file mode 100644
index 0000000..e3d88d6
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operations.php
@@ -0,0 +1,157 @@
+execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function directsum(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('DirectSum operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('DirectSum arguments must be Matrix or array');
+ }
+
+ $result = new DirectSum($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function divideby(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Division operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Division arguments must be Matrix or array');
+ }
+
+ $result = new Division($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function divideinto(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Division operation requires at least 2 arguments');
+ }
+
+ $matrix = array_pop($matrixValues);
+ $matrixValues = array_reverse($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Division arguments must be Matrix or array');
+ }
+
+ $result = new Division($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function multiply(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Multiplication operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Multiplication arguments must be Matrix or array');
+ }
+
+ $result = new Multiplication($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+
+ public static function subtract(...$matrixValues): Matrix
+ {
+ if (count($matrixValues) < 2) {
+ throw new Exception('Subtraction operation requires at least 2 arguments');
+ }
+
+ $matrix = array_shift($matrixValues);
+
+ if (is_array($matrix)) {
+ $matrix = new Matrix($matrix);
+ }
+ if (!$matrix instanceof Matrix) {
+ throw new Exception('Subtraction arguments must be Matrix or array');
+ }
+
+ $result = new Subtraction($matrix);
+
+ foreach ($matrixValues as $matrix) {
+ $result->execute($matrix);
+ }
+
+ return $result->result();
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operators/Addition.php b/vendor/markbaker/matrix/classes/src/Operators/Addition.php
new file mode 100644
index 0000000..543f56e
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operators/Addition.php
@@ -0,0 +1,68 @@
+addMatrix($value);
+ } elseif (is_numeric($value)) {
+ return $this->addScalar($value);
+ }
+
+ throw new Exception('Invalid argument for addition');
+ }
+
+ /**
+ * Execute the addition for a scalar
+ *
+ * @param mixed $value The numeric value to add to the current base value
+ * @return $this The operation object, allowing multiple additions to be chained
+ **/
+ protected function addScalar($value): Operator
+ {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $this->matrix[$row][$column] += $value;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Execute the addition for a matrix
+ *
+ * @param Matrix $value The numeric value to add to the current base value
+ * @return $this The operation object, allowing multiple additions to be chained
+ * @throws Exception If the provided argument is not appropriate for the operation
+ **/
+ protected function addMatrix(Matrix $value): Operator
+ {
+ $this->validateMatchingDimensions($value);
+
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $this->matrix[$row][$column] += $value->getValue($row + 1, $column + 1);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php b/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php
new file mode 100644
index 0000000..cc51ef9
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php
@@ -0,0 +1,64 @@
+directSumMatrix($value);
+ }
+
+ throw new Exception('Invalid argument for addition');
+ }
+
+ /**
+ * Execute the direct sum for a matrix
+ *
+ * @param Matrix $value The numeric value to concatenate/direct sum with the current base value
+ * @return $this The operation object, allowing multiple additions to be chained
+ **/
+ private function directSumMatrix($value): Operator
+ {
+ $originalColumnCount = count($this->matrix[0]);
+ $originalRowCount = count($this->matrix);
+ $valColumnCount = $value->columns;
+ $valRowCount = $value->rows;
+ $value = $value->toArray();
+
+ for ($row = 0; $row < $this->rows; ++$row) {
+ $this->matrix[$row] = array_merge($this->matrix[$row], array_fill(0, $valColumnCount, 0));
+ }
+
+ $this->matrix = array_merge(
+ $this->matrix,
+ array_fill(0, $valRowCount, array_fill(0, $originalColumnCount, 0))
+ );
+
+ for ($row = $originalRowCount; $row < $originalRowCount + $valRowCount; ++$row) {
+ array_splice(
+ $this->matrix[$row],
+ $originalColumnCount,
+ $valColumnCount,
+ $value[$row - $originalRowCount]
+ );
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operators/Division.php b/vendor/markbaker/matrix/classes/src/Operators/Division.php
new file mode 100644
index 0000000..dbfec9d
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operators/Division.php
@@ -0,0 +1,35 @@
+multiplyMatrix($value, $type);
+ } elseif (is_numeric($value)) {
+ return $this->multiplyScalar(1 / $value, $type);
+ }
+
+ throw new Exception('Invalid argument for division');
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php b/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php
new file mode 100644
index 0000000..0761e46
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php
@@ -0,0 +1,86 @@
+multiplyMatrix($value, $type);
+ } elseif (is_numeric($value)) {
+ return $this->multiplyScalar($value, $type);
+ }
+
+ throw new Exception("Invalid argument for $type");
+ }
+
+ /**
+ * Execute the multiplication for a scalar
+ *
+ * @param mixed $value The numeric value to multiply with the current base value
+ * @return $this The operation object, allowing multiple mutiplications to be chained
+ **/
+ protected function multiplyScalar($value, string $type = 'multiplication'): Operator
+ {
+ try {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $this->matrix[$row][$column] *= $value;
+ }
+ }
+ } catch (Throwable $e) {
+ throw new Exception("Invalid argument for $type");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Execute the multiplication for a matrix
+ *
+ * @param Matrix $value The numeric value to multiply with the current base value
+ * @return $this The operation object, allowing multiple mutiplications to be chained
+ * @throws Exception If the provided argument is not appropriate for the operation
+ **/
+ protected function multiplyMatrix(Matrix $value, string $type = 'multiplication'): Operator
+ {
+ $this->validateReflectingDimensions($value);
+
+ $newRows = $this->rows;
+ $newColumns = $value->columns;
+ $matrix = Builder::createFilledMatrix(0, $newRows, $newColumns)
+ ->toArray();
+ try {
+ for ($row = 0; $row < $newRows; ++$row) {
+ for ($column = 0; $column < $newColumns; ++$column) {
+ $columnData = $value->getColumns($column + 1)->toArray();
+ foreach ($this->matrix[$row] as $key => $valueData) {
+ $matrix[$row][$column] += $valueData * $columnData[$key][0];
+ }
+ }
+ }
+ } catch (Throwable $e) {
+ throw new Exception("Invalid argument for $type");
+ }
+ $this->matrix = $matrix;
+
+ return $this;
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operators/Operator.php b/vendor/markbaker/matrix/classes/src/Operators/Operator.php
new file mode 100644
index 0000000..39e36c6
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operators/Operator.php
@@ -0,0 +1,78 @@
+rows = $matrix->rows;
+ $this->columns = $matrix->columns;
+ $this->matrix = $matrix->toArray();
+ }
+
+ /**
+ * Compare the dimensions of the matrices being operated on to see if they are valid for addition/subtraction
+ *
+ * @param Matrix $matrix The second Matrix object on which the operation will be performed
+ * @throws Exception
+ */
+ protected function validateMatchingDimensions(Matrix $matrix): void
+ {
+ if (($this->rows != $matrix->rows) || ($this->columns != $matrix->columns)) {
+ throw new Exception('Matrices have mismatched dimensions');
+ }
+ }
+
+ /**
+ * Compare the dimensions of the matrices being operated on to see if they are valid for multiplication/division
+ *
+ * @param Matrix $matrix The second Matrix object on which the operation will be performed
+ * @throws Exception
+ */
+ protected function validateReflectingDimensions(Matrix $matrix): void
+ {
+ if ($this->columns != $matrix->rows) {
+ throw new Exception('Matrices have mismatched dimensions');
+ }
+ }
+
+ /**
+ * Return the result of the operation
+ *
+ * @return Matrix
+ */
+ public function result(): Matrix
+ {
+ return new Matrix($this->matrix);
+ }
+}
diff --git a/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php b/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php
new file mode 100644
index 0000000..b7e14fa
--- /dev/null
+++ b/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php
@@ -0,0 +1,68 @@
+subtractMatrix($value);
+ } elseif (is_numeric($value)) {
+ return $this->subtractScalar($value);
+ }
+
+ throw new Exception('Invalid argument for subtraction');
+ }
+
+ /**
+ * Execute the subtraction for a scalar
+ *
+ * @param mixed $value The numeric value to subtracted from the current base value
+ * @return $this The operation object, allowing multiple additions to be chained
+ **/
+ protected function subtractScalar($value): Operator
+ {
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $this->matrix[$row][$column] -= $value;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Execute the subtraction for a matrix
+ *
+ * @param Matrix $value The numeric value to subtract from the current base value
+ * @return $this The operation object, allowing multiple subtractions to be chained
+ * @throws Exception If the provided argument is not appropriate for the operation
+ **/
+ protected function subtractMatrix(Matrix $value): Operator
+ {
+ $this->validateMatchingDimensions($value);
+
+ for ($row = 0; $row < $this->rows; ++$row) {
+ for ($column = 0; $column < $this->columns; ++$column) {
+ $this->matrix[$row][$column] -= $value->getValue($row + 1, $column + 1);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/markbaker/matrix/composer.json b/vendor/markbaker/matrix/composer.json
new file mode 100644
index 0000000..e86a1e0
--- /dev/null
+++ b/vendor/markbaker/matrix/composer.json
@@ -0,0 +1,52 @@
+{
+ "name": "markbaker/matrix",
+ "type": "library",
+ "description": "PHP Class for working with matrices",
+ "keywords": ["matrix", "vector", "mathematics"],
+ "homepage": "https://github.com/MarkBaker/PHPMatrix",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@demon-angel.eu"
+ }
+ ],
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "phpdocumentor/phpdocumentor": "2.*",
+ "phpmd/phpmd": "2.*",
+ "sebastian/phpcpd": "^4.0",
+ "phploc/phploc": "^4.0",
+ "squizlabs/php_codesniffer": "^3.7",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master"
+ },
+ "autoload": {
+ "psr-4": {
+ "Matrix\\": "classes/src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "MatrixTest\\": "unitTests/classes/src/"
+ }
+ },
+ "scripts": {
+ "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n",
+ "test": "phpunit -c phpunit.xml.dist",
+ "mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n",
+ "lines": "phploc classes/src/ -n",
+ "cpd": "phpcpd classes/src/ -n",
+ "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n",
+ "coverage": "phpunit -c phpunit.xml.dist --coverage-text --coverage-html ./build/coverage"
+ },
+ "minimum-stability": "dev",
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ }
+}
diff --git a/vendor/markbaker/matrix/examples/test.php b/vendor/markbaker/matrix/examples/test.php
new file mode 100644
index 0000000..071dae9
--- /dev/null
+++ b/vendor/markbaker/matrix/examples/test.php
@@ -0,0 +1,33 @@
+solve($target);
+
+echo 'X', PHP_EOL;
+var_export($X->toArray());
+echo PHP_EOL;
+
+$resolve = $matrix->multiply($X);
+
+echo 'Resolve', PHP_EOL;
+var_export($resolve->toArray());
+echo PHP_EOL;
diff --git a/vendor/markbaker/matrix/infection.json.dist b/vendor/markbaker/matrix/infection.json.dist
new file mode 100644
index 0000000..eddaa70
--- /dev/null
+++ b/vendor/markbaker/matrix/infection.json.dist
@@ -0,0 +1,17 @@
+{
+ "timeout": 1,
+ "source": {
+ "directories": [
+ "classes\/src"
+ ]
+ },
+ "logs": {
+ "text": "build/infection/text.log",
+ "summary": "build/infection/summary.log",
+ "debug": "build/infection/debug.log",
+ "perMutator": "build/infection/perMutator.md"
+ },
+ "mutators": {
+ "@default": true
+ }
+}
diff --git a/vendor/markbaker/matrix/license.md b/vendor/markbaker/matrix/license.md
new file mode 100644
index 0000000..7329058
--- /dev/null
+++ b/vendor/markbaker/matrix/license.md
@@ -0,0 +1,25 @@
+The MIT License (MIT)
+=====================
+
+Copyright © `2018` `Mark Baker`
+
+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.
\ No newline at end of file
diff --git a/vendor/markbaker/matrix/phpstan.neon b/vendor/markbaker/matrix/phpstan.neon
new file mode 100644
index 0000000..3d90d49
--- /dev/null
+++ b/vendor/markbaker/matrix/phpstan.neon
@@ -0,0 +1,6 @@
+parameters:
+ ignoreErrors:
+ - '#Property [A-Za-z\\]+::\$[A-Za-z]+ has no typehint specified#'
+ - '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has no return typehint specified#'
+ - '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has parameter \$[A-Za-z0-9]+ with no typehint specified#'
+ checkMissingIterableValueType: false
diff --git a/vendor/myclabs/php-enum/LICENSE b/vendor/myclabs/php-enum/LICENSE
new file mode 100644
index 0000000..2a8cf22
--- /dev/null
+++ b/vendor/myclabs/php-enum/LICENSE
@@ -0,0 +1,18 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 My C-Labs
+
+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.
diff --git a/vendor/myclabs/php-enum/README.md b/vendor/myclabs/php-enum/README.md
new file mode 100644
index 0000000..681d55e
--- /dev/null
+++ b/vendor/myclabs/php-enum/README.md
@@ -0,0 +1,194 @@
+# PHP Enum implementation inspired from SplEnum
+
+[![GitHub Actions][GA Image]][GA Link]
+[](https://packagist.org/packages/myclabs/php-enum)
+[](https://packagist.org/packages/myclabs/php-enum)
+[![Psalm Shepherd][Shepherd Image]][Shepherd Link]
+
+Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme).
+
+## Why?
+
+First, and mainly, `SplEnum` is not integrated to PHP, you have to install the extension separately.
+
+Using an enum instead of class constants provides the following advantages:
+
+- You can use an enum as a parameter type: `function setAction(Action $action) {`
+- You can use an enum as a return type: `function getAction() : Action {`
+- You can enrich the enum with methods (e.g. `format`, `parse`, …)
+- You can extend the enum to add new values (make your enum `final` to prevent it)
+- You can get a list of all the possible values (see below)
+
+This Enum class is not intended to replace class constants, but only to be used when it makes sense.
+
+## Installation
+
+```
+composer require myclabs/php-enum
+```
+
+## Declaration
+
+```php
+use MyCLabs\Enum\Enum;
+
+/**
+ * Action enum
+ */
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+```
+
+## Usage
+
+```php
+$action = Action::VIEW();
+
+// or with a dynamic key:
+$action = Action::$key();
+// or with a dynamic value:
+$action = Action::from($value);
+// or
+$action = new Action($value);
+```
+
+As you can see, static methods are automatically implemented to provide quick access to an enum value.
+
+One advantage over using class constants is to be able to use an enum as a parameter type:
+
+```php
+function setAction(Action $action) {
+ // ...
+}
+```
+
+## Documentation
+
+- `__construct()` The constructor checks that the value exist in the enum
+- `__toString()` You can `echo $myValue`, it will display the enum value (value of the constant)
+- `getValue()` Returns the current value of the enum
+- `getKey()` Returns the key of the current value on Enum
+- `equals()` Tests whether enum instances are equal (returns `true` if enum values are equal, `false` otherwise)
+
+Static methods:
+
+- `from()` Creates an Enum instance, checking that the value exist in the enum
+- `toArray()` method Returns all possible values as an array (constant name in key, constant value in value)
+- `keys()` Returns the names (keys) of all constants in the Enum class
+- `values()` Returns instances of the Enum class of all Enum constants (constant name in key, Enum instance in value)
+- `isValid()` Check if tested value is valid on enum set
+- `isValidKey()` Check if tested key is valid on enum set
+- `assertValidValue()` Assert the value is valid on enum set, throwing exception otherwise
+- `search()` Return key for searched value
+
+### Static methods
+
+```php
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+
+// Static method:
+$action = Action::VIEW();
+$action = Action::EDIT();
+```
+
+Static method helpers are implemented using [`__callStatic()`](http://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic).
+
+If you care about IDE autocompletion, you can either implement the static methods yourself:
+
+```php
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+
+ /**
+ * @return Action
+ */
+ public static function VIEW() {
+ return new Action(self::VIEW);
+ }
+}
+```
+
+or you can use phpdoc (this is supported in PhpStorm for example):
+
+```php
+/**
+ * @method static Action VIEW()
+ * @method static Action EDIT()
+ */
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+```
+
+## Native enums and migration
+Native enum arrived to PHP in version 8.1: https://www.php.net/enumerations
+If your project is running PHP 8.1+ or your library has it as a minimum requirement you should use it instead of this library.
+
+When migrating from `myclabs/php-enum`, the effort should be small if the usage was in the recommended way:
+- private constants
+- final classes
+- no method overridden
+
+Changes for migration:
+- Class definition should be changed from
+```php
+/**
+ * @method static Action VIEW()
+ * @method static Action EDIT()
+ */
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+```
+ to
+```php
+enum Action: string
+{
+ case VIEW = 'view';
+ case EDIT = 'edit';
+}
+```
+All places where the class was used as a type will continue to work.
+
+Usages and the change needed:
+
+| Operation | myclabs/php-enum | native enum |
+|----------------------------------------------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Obtain an instance will change from | `$enumCase = Action::VIEW()` | `$enumCase = Action::VIEW` |
+| Create an enum from a backed value | `$enumCase = new Action('view')` | `$enumCase = Action::from('view')` |
+| Get the backed value of the enum instance | `$enumCase->getValue()` | `$enumCase->value` |
+| Compare two enum instances | `$enumCase1 == $enumCase2` or `$enumCase1->equals($enumCase2)` | `$enumCase1 === $enumCase2` |
+| Get the key/name of the enum instance | `$enumCase->getKey()` | `$enumCase->name` |
+| Get a list of all the possible instances of the enum | `Action::values()` | `Action::cases()` |
+| Get a map of possible instances of the enum mapped by name | `Action::values()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), Action::cases())` or `(new ReflectionEnum(Action::class))->getConstants()` |
+| Get a list of all possible names of the enum | `Action::keys()` | `array_map(fn($case) => $case->name, Action::cases())` |
+| Get a list of all possible backed values of the enum | `Action::toArray()` | `array_map(fn($case) => $case->value, Action::cases())` |
+| Get a map of possible backed values of the enum mapped by name | `Action::toArray()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), array_map(fn($case) => $case->value, Action::cases()))` or `array_map(fn($case) => $case->value, (new ReflectionEnum(Action::class))->getConstants()))` |
+
+## Related projects
+
+- [PHP 8.1+ native enum](https://www.php.net/enumerations)
+- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type)
+- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter)
+- [PHPStan integration](https://github.com/timeweb/phpstan-enum)
+
+
+[GA Image]: https://github.com/myclabs/php-enum/workflows/CI/badge.svg
+
+[GA Link]: https://github.com/myclabs/php-enum/actions?query=workflow%3A%22CI%22+branch%3Amaster
+
+[Shepherd Image]: https://shepherd.dev/github/myclabs/php-enum/coverage.svg
+
+[Shepherd Link]: https://shepherd.dev/github/myclabs/php-enum
diff --git a/vendor/myclabs/php-enum/SECURITY.md b/vendor/myclabs/php-enum/SECURITY.md
new file mode 100644
index 0000000..84fd4e3
--- /dev/null
+++ b/vendor/myclabs/php-enum/SECURITY.md
@@ -0,0 +1,11 @@
+# Security Policy
+
+## Supported Versions
+
+Only the latest stable release is supported.
+
+## Reporting a Vulnerability
+
+To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
+
+Tidelift will coordinate the fix and disclosure.
diff --git a/vendor/myclabs/php-enum/composer.json b/vendor/myclabs/php-enum/composer.json
new file mode 100644
index 0000000..978cb19
--- /dev/null
+++ b/vendor/myclabs/php-enum/composer.json
@@ -0,0 +1,36 @@
+{
+ "name": "myclabs/php-enum",
+ "type": "library",
+ "description": "PHP Enum implementation",
+ "keywords": ["enum"],
+ "homepage": "http://github.com/myclabs/php-enum",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "MyCLabs\\Tests\\Enum\\": "tests/"
+ }
+ },
+ "require": {
+ "php": "^7.3 || ^8.0",
+ "ext-json": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2"
+ }
+}
diff --git a/vendor/myclabs/php-enum/src/Enum.php b/vendor/myclabs/php-enum/src/Enum.php
new file mode 100644
index 0000000..4c94cf6
--- /dev/null
+++ b/vendor/myclabs/php-enum/src/Enum.php
@@ -0,0 +1,318 @@
+
+ * @author Daniel Costa
+ * @author Mirosław Filip
+ *
+ * @psalm-template T
+ * @psalm-immutable
+ * @psalm-consistent-constructor
+ */
+abstract class Enum implements \JsonSerializable, \Stringable
+{
+ /**
+ * Enum value
+ *
+ * @var mixed
+ * @psalm-var T
+ */
+ protected $value;
+
+ /**
+ * Enum key, the constant name
+ *
+ * @var string
+ */
+ private $key;
+
+ /**
+ * Store existing constants in a static cache per object.
+ *
+ *
+ * @var array
+ * @psalm-var array>
+ */
+ protected static $cache = [];
+
+ /**
+ * Cache of instances of the Enum class
+ *
+ * @var array
+ * @psalm-var array>
+ */
+ protected static $instances = [];
+
+ /**
+ * Creates a new value of some type
+ *
+ * @psalm-pure
+ * @param mixed $value
+ *
+ * @psalm-param T $value
+ * @throws \UnexpectedValueException if incompatible type is given.
+ */
+ public function __construct($value)
+ {
+ if ($value instanceof static) {
+ /** @psalm-var T */
+ $value = $value->getValue();
+ }
+
+ /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
+ $this->key = static::assertValidValueReturningKey($value);
+
+ /** @psalm-var T */
+ $this->value = $value;
+ }
+
+ /**
+ * This method exists only for the compatibility reason when deserializing a previously serialized version
+ * that didn't had the key property
+ */
+ public function __wakeup()
+ {
+ /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
+ if ($this->key === null) {
+ /**
+ * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
+ * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
+ */
+ $this->key = static::search($this->value);
+ }
+ }
+
+ /**
+ * @param mixed $value
+ * @return static
+ */
+ public static function from($value): self
+ {
+ $key = static::assertValidValueReturningKey($value);
+
+ return self::__callStatic($key, []);
+ }
+
+ /**
+ * @psalm-pure
+ * @return mixed
+ * @psalm-return T
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the enum key (i.e. the constant name).
+ *
+ * @psalm-pure
+ * @return string
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-suppress InvalidCast
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string)$this->value;
+ }
+
+ /**
+ * Determines if Enum should be considered equal with the variable passed as a parameter.
+ * Returns false if an argument is an object of different class or not an object.
+ *
+ * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4
+ *
+ * @psalm-pure
+ * @psalm-param mixed $variable
+ * @return bool
+ */
+ final public function equals($variable = null): bool
+ {
+ return $variable instanceof self
+ && $this->getValue() === $variable->getValue()
+ && static::class === \get_class($variable);
+ }
+
+ /**
+ * Returns the names (keys) of all constants in the Enum class
+ *
+ * @psalm-pure
+ * @psalm-return list
+ * @return array
+ */
+ public static function keys()
+ {
+ return \array_keys(static::toArray());
+ }
+
+ /**
+ * Returns instances of the Enum class of all Enum constants
+ *
+ * @psalm-pure
+ * @psalm-return array
+ * @return static[] Constant name in key, Enum instance in value
+ */
+ public static function values()
+ {
+ $values = array();
+
+ /** @psalm-var T $value */
+ foreach (static::toArray() as $key => $value) {
+ $values[$key] = new static($value);
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns all possible values as an array
+ *
+ * @psalm-pure
+ * @psalm-suppress ImpureStaticProperty
+ *
+ * @psalm-return array
+ * @return array Constant name in key, constant value in value
+ */
+ public static function toArray()
+ {
+ $class = static::class;
+
+ if (!isset(static::$cache[$class])) {
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
+ $reflection = new \ReflectionClass($class);
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
+ static::$cache[$class] = $reflection->getConstants();
+ }
+
+ return static::$cache[$class];
+ }
+
+ /**
+ * Check if is valid enum value
+ *
+ * @param $value
+ * @psalm-param mixed $value
+ * @psalm-pure
+ * @psalm-assert-if-true T $value
+ * @return bool
+ */
+ public static function isValid($value)
+ {
+ return \in_array($value, static::toArray(), true);
+ }
+
+ /**
+ * Asserts valid enum value
+ *
+ * @psalm-pure
+ * @psalm-assert T $value
+ * @param mixed $value
+ */
+ public static function assertValidValue($value): void
+ {
+ self::assertValidValueReturningKey($value);
+ }
+
+ /**
+ * Asserts valid enum value
+ *
+ * @psalm-pure
+ * @psalm-assert T $value
+ * @param mixed $value
+ * @return string
+ */
+ private static function assertValidValueReturningKey($value): string
+ {
+ if (false === ($key = static::search($value))) {
+ throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
+ }
+
+ return $key;
+ }
+
+ /**
+ * Check if is valid enum key
+ *
+ * @param $key
+ * @psalm-param string $key
+ * @psalm-pure
+ * @return bool
+ */
+ public static function isValidKey($key)
+ {
+ $array = static::toArray();
+
+ return isset($array[$key]) || \array_key_exists($key, $array);
+ }
+
+ /**
+ * Return key for value
+ *
+ * @param mixed $value
+ *
+ * @psalm-param mixed $value
+ * @psalm-pure
+ * @return string|false
+ */
+ public static function search($value)
+ {
+ return \array_search($value, static::toArray(), true);
+ }
+
+ /**
+ * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant
+ *
+ * @param string $name
+ * @param array $arguments
+ *
+ * @return static
+ * @throws \BadMethodCallException
+ *
+ * @psalm-pure
+ */
+ public static function __callStatic($name, $arguments)
+ {
+ $class = static::class;
+ if (!isset(self::$instances[$class][$name])) {
+ $array = static::toArray();
+ if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
+ $message = "No static method or enum constant '$name' in class " . static::class;
+ throw new \BadMethodCallException($message);
+ }
+ return self::$instances[$class][$name] = new static($array[$name]);
+ }
+ return clone self::$instances[$class][$name];
+ }
+
+ /**
+ * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode()
+ * natively.
+ *
+ * @return mixed
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ * @psalm-pure
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->getValue();
+ }
+}
diff --git a/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php b/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php
new file mode 100644
index 0000000..302bf80
--- /dev/null
+++ b/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php
@@ -0,0 +1,54 @@
+register(new \MyCLabs\Enum\PHPUnit\Comparator());
+ */
+final class Comparator extends \SebastianBergmann\Comparator\Comparator
+{
+ public function accepts($expected, $actual)
+ {
+ return $expected instanceof Enum && (
+ $actual instanceof Enum || $actual === null
+ );
+ }
+
+ /**
+ * @param Enum $expected
+ * @param Enum|null $actual
+ *
+ * @return void
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ if ($expected->equals($actual)) {
+ return;
+ }
+
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->formatEnum($expected),
+ $this->formatEnum($actual),
+ false,
+ 'Failed asserting that two Enums are equal.'
+ );
+ }
+
+ private function formatEnum(Enum $enum = null)
+ {
+ if ($enum === null) {
+ return "null";
+ }
+
+ return get_class($enum)."::{$enum->getKey()}()";
+ }
+}
diff --git a/vendor/myclabs/php-enum/stubs/Stringable.php b/vendor/myclabs/php-enum/stubs/Stringable.php
new file mode 100644
index 0000000..4811af7
--- /dev/null
+++ b/vendor/myclabs/php-enum/stubs/Stringable.php
@@ -0,0 +1,11 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\MessageFormatter;
+
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+
+if (!class_exists(LazyMessageFormatter::class, false)) {
+ abstract class LazyMessageFormatter implements MessageFormatterInterface
+ {
+ public function format(string $message, string $locale, array $parameters = []): string
+ {
+ return $this->formatter->format(
+ $message,
+ $this->transformLocale($locale),
+ $parameters
+ );
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php
new file mode 100644
index 0000000..cbd890d
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\MessageFormatter;
+
+use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+
+if (!class_exists(LazyMessageFormatter::class, false)) {
+ abstract class LazyMessageFormatter implements MessageFormatterInterface, ChoiceMessageFormatterInterface
+ {
+ abstract protected function transformLocale(?string $locale): ?string;
+
+ public function format($message, $locale, array $parameters = [])
+ {
+ return $this->formatter->format(
+ $message,
+ $this->transformLocale($locale),
+ $parameters
+ );
+ }
+
+ public function choiceFormat($message, $number, $locale, array $parameters = [])
+ {
+ return $this->formatter->choiceFormat($message, $number, $locale, $parameters);
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php
new file mode 100644
index 0000000..ba7cf63
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroBuiltin.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+use PHPStan\BetterReflection\Reflection;
+use ReflectionMethod;
+
+if (!class_exists(AbstractReflectionMacro::class, false)) {
+ abstract class AbstractReflectionMacro extends AbstractMacro
+ {
+ /**
+ * {@inheritdoc}
+ */
+ public function getReflection(): ?ReflectionMethod
+ {
+ if ($this->reflectionFunction instanceof Reflection\ReflectionMethod) {
+ return new Reflection\Adapter\ReflectionMethod($this->reflectionFunction);
+ }
+
+ return $this->reflectionFunction instanceof ReflectionMethod
+ ? $this->reflectionFunction
+ : null;
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php
new file mode 100644
index 0000000..bd4c8e8
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/AbstractMacroStatic.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+use PHPStan\BetterReflection\Reflection;
+use ReflectionMethod;
+
+if (!class_exists(AbstractReflectionMacro::class, false)) {
+ abstract class AbstractReflectionMacro extends AbstractMacro
+ {
+ /**
+ * {@inheritdoc}
+ */
+ public function getReflection(): ?Reflection\Adapter\ReflectionMethod
+ {
+ if ($this->reflectionFunction instanceof Reflection\Adapter\ReflectionMethod) {
+ return $this->reflectionFunction;
+ }
+
+ if ($this->reflectionFunction instanceof Reflection\ReflectionMethod) {
+ return new Reflection\Adapter\ReflectionMethod($this->reflectionFunction);
+ }
+
+ return $this->reflectionFunction instanceof ReflectionMethod
+ ? new Reflection\Adapter\ReflectionMethod(
+ Reflection\ReflectionMethod::createFromName(
+ $this->reflectionFunction->getDeclaringClass()->getName(),
+ $this->reflectionFunction->getName()
+ )
+ )
+ : null;
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php
new file mode 100644
index 0000000..f615b3a
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroStrongType.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+if (!class_exists(LazyMacro::class, false)) {
+ abstract class LazyMacro extends AbstractReflectionMacro
+ {
+ /**
+ * {@inheritdoc}
+ */
+ public function getFileName(): ?string
+ {
+ $file = $this->reflectionFunction->getFileName();
+
+ return (($file ? realpath($file) : null) ?: $file) ?: null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStartLine(): ?int
+ {
+ return $this->reflectionFunction->getStartLine();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getEndLine(): ?int
+ {
+ return $this->reflectionFunction->getEndLine();
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php
new file mode 100644
index 0000000..bf64c1d
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/PHPStan/MacroWeakType.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+if (!class_exists(LazyMacro::class, false)) {
+ abstract class LazyMacro extends AbstractReflectionMacro
+ {
+ /**
+ * {@inheritdoc}
+ *
+ * @return string|false
+ */
+ public function getFileName()
+ {
+ $file = $this->reflectionFunction->getFileName();
+
+ return (($file ? realpath($file) : null) ?: $file) ?: null;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return int|false
+ */
+ public function getStartLine()
+ {
+ return $this->reflectionFunction->getStartLine();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return int|false
+ */
+ public function getEndLine()
+ {
+ return $this->reflectionFunction->getEndLine();
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php b/vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php
new file mode 100644
index 0000000..d35308a
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/TranslatorStrongType.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+if (!class_exists(LazyTranslator::class, false)) {
+ class LazyTranslator extends AbstractTranslator implements TranslatorStrongTypeInterface
+ {
+ public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
+ {
+ return $this->translate($id, $parameters, $domain, $locale);
+ }
+
+ public function getFromCatalogue(MessageCatalogueInterface $catalogue, string $id, string $domain = 'messages')
+ {
+ $messages = $this->getPrivateProperty($catalogue, 'messages');
+
+ if (isset($messages[$domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX][$id])) {
+ return $messages[$domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX][$id];
+ }
+
+ if (isset($messages[$domain][$id])) {
+ return $messages[$domain][$id];
+ }
+
+ $fallbackCatalogue = $this->getPrivateProperty($catalogue, 'fallbackCatalogue');
+
+ if ($fallbackCatalogue !== null) {
+ return $this->getFromCatalogue($fallbackCatalogue, $id, $domain);
+ }
+
+ return $id;
+ }
+
+ private function getPrivateProperty($instance, string $field)
+ {
+ return (function (string $field) {
+ return $this->$field;
+ })->call($instance, $field);
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php b/vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php
new file mode 100644
index 0000000..94dbdc3
--- /dev/null
+++ b/vendor/nesbot/carbon/lazy/Carbon/TranslatorWeakType.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+if (!class_exists(LazyTranslator::class, false)) {
+ class LazyTranslator extends AbstractTranslator
+ {
+ /**
+ * Returns the translation.
+ *
+ * @param string|null $id
+ * @param array $parameters
+ * @param string|null $domain
+ * @param string|null $locale
+ *
+ * @return string
+ */
+ public function trans($id, array $parameters = [], $domain = null, $locale = null)
+ {
+ return $this->translate($id, $parameters, $domain, $locale);
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/readme.md b/vendor/nesbot/carbon/readme.md
new file mode 100644
index 0000000..b8ec4bf
--- /dev/null
+++ b/vendor/nesbot/carbon/readme.md
@@ -0,0 +1,156 @@
+# Carbon
+
+[](https://packagist.org/packages/nesbot/carbon)
+[](https://packagist.org/packages/nesbot/carbon)
+[](https://github.com/briannesbitt/Carbon/actions)
+[](https://codecov.io/github/briannesbitt/Carbon?branch=master)
+[](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme)
+
+An international PHP extension for DateTime. [https://carbon.nesbot.com](https://carbon.nesbot.com)
+
+```php
+toDateTimeString());
+printf("Right now in Vancouver is %s", Carbon::now('America/Vancouver')); //implicit __toString()
+$tomorrow = Carbon::now()->addDay();
+$lastWeek = Carbon::now()->subWeek();
+$nextSummerOlympics = Carbon::createFromDate(2016)->addYears(4);
+
+$officialDate = Carbon::now()->toRfc2822String();
+
+$howOldAmI = Carbon::createFromDate(1975, 5, 21)->age;
+
+$noonTodayLondonTime = Carbon::createFromTime(12, 0, 0, 'Europe/London');
+
+$internetWillBlowUpOn = Carbon::create(2038, 01, 19, 3, 14, 7, 'GMT');
+
+// Don't really want this to happen so mock now
+Carbon::setTestNow(Carbon::createFromDate(2000, 1, 1));
+
+// comparisons are always done in UTC
+if (Carbon::now()->gte($internetWillBlowUpOn)) {
+ die();
+}
+
+// Phew! Return to normal behaviour
+Carbon::setTestNow();
+
+if (Carbon::now()->isWeekend()) {
+ echo 'Party!';
+}
+// Over 200 languages (and over 500 regional variants) supported:
+echo Carbon::now()->subMinutes(2)->diffForHumans(); // '2 minutes ago'
+echo Carbon::now()->subMinutes(2)->locale('zh_CN')->diffForHumans(); // '2分钟前'
+echo Carbon::parse('2019-07-23 14:51')->isoFormat('LLLL'); // 'Tuesday, July 23, 2019 2:51 PM'
+echo Carbon::parse('2019-07-23 14:51')->locale('fr_FR')->isoFormat('LLLL'); // 'mardi 23 juillet 2019 14:51'
+
+// ... but also does 'from now', 'after' and 'before'
+// rolling up to seconds, minutes, hours, days, months, years
+
+$daysSinceEpoch = Carbon::createFromTimestamp(0)->diffInDays();
+```
+
+[Get supported nesbot/carbon with the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme)
+
+## Installation
+
+### With Composer
+
+```
+$ composer require nesbot/carbon
+```
+
+```json
+{
+ "require": {
+ "nesbot/carbon": "^2.16"
+ }
+}
+```
+
+```php
+
+
+### Translators
+
+[Thanks to people helping us to translate Carbon in so many languages](https://carbon.nesbot.com/contribute/translators/)
+
+### Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[[Become a sponsor via OpenCollective](https://opencollective.com/Carbon#sponsor)]
+
+
+
+
+[[Become a sponsor via GitHub](https://github.com/sponsors/kylekatarnls)]
+
+### Backers
+
+Thank you to all our backers! 🙏
+
+
+
+[[Become a backer](https://opencollective.com/Carbon#backer)]
+
+## Carbon for enterprise
+
+Available as part of the Tidelift Subscription.
+
+The maintainers of ``Carbon`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/vendor/nesbot/carbon/sponsors.php b/vendor/nesbot/carbon/sponsors.php
new file mode 100644
index 0000000..4f6d994
--- /dev/null
+++ b/vendor/nesbot/carbon/sponsors.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Carbon\CarbonImmutable;
+
+require_once __DIR__.'/vendor/autoload.php';
+
+function getOpenCollectiveSponsors(): string
+{
+ $members = json_decode(file_get_contents('https://opencollective.com/carbon/members/all.json'), true);
+ $sixMonthsAgo = CarbonImmutable::parse('now - 6 months')->format('Y-m-d h:i');
+
+ $list = array_filter($members, static function ($member) use ($sixMonthsAgo) {
+ return ($member['lastTransactionAmount'] > 3 || $member['isActive']) &&
+ $member['role'] === 'BACKER' &&
+ $member['type'] !== 'USER' &&
+ ($member['totalAmountDonated'] > 100 || $member['lastTransactionAt'] > $sixMonthsAgo || $member['isActive'] && $member['lastTransactionAmount'] >= 30);
+ });
+
+ $list = array_map(static function (array $member) {
+ $createdAt = CarbonImmutable::parse($member['createdAt']);
+ $lastTransactionAt = CarbonImmutable::parse($member['lastTransactionAt']);
+
+ if ($createdAt->format('d H:i:s.u') > $lastTransactionAt->format('d H:i:s.u')) {
+ $createdAt = $createdAt
+ ->setDay($lastTransactionAt->day)
+ ->modify($lastTransactionAt->format('H:i:s.u'));
+ }
+
+ $monthlyContribution = (float) ($member['totalAmountDonated'] / ceil($createdAt->floatDiffInMonths()));
+
+ if (
+ $lastTransactionAt->isAfter('last month') &&
+ $member['lastTransactionAmount'] > $monthlyContribution
+ ) {
+ $monthlyContribution = (float) $member['lastTransactionAmount'];
+ }
+
+ $yearlyContribution = (float) ($member['totalAmountDonated'] / max(1, $createdAt->floatDiffInYears()));
+ $status = null;
+
+ if ($monthlyContribution > 29) {
+ $status = 'sponsor';
+ } elseif ($monthlyContribution > 3 || $yearlyContribution > 20) {
+ $status = 'backer';
+ } elseif ($member['totalAmountDonated'] > 0) {
+ $status = 'helper';
+ }
+
+ return array_merge($member, [
+ 'star' => ($monthlyContribution > 98 || $yearlyContribution > 500),
+ 'status' => $status,
+ 'monthlyContribution' => $monthlyContribution,
+ 'yearlyContribution' => $yearlyContribution,
+ ]);
+ }, $list);
+
+ usort($list, static function (array $a, array $b) {
+ return ($b['monthlyContribution'] <=> $a['monthlyContribution'])
+ ?: ($b['totalAmountDonated'] <=> $a['totalAmountDonated']);
+ });
+
+ return implode('', array_map(static function (array $member) {
+ $href = htmlspecialchars($member['website'] ?? $member['profile']);
+ $src = $member['image'] ?? (strtr($member['profile'], ['https://opencollective.com/' => 'https://images.opencollective.com/']).'/avatar/256.png');
+ [$x, $y] = @getimagesize($src) ?: [0, 0];
+ $validImage = ($x && $y);
+ $src = $validImage ? htmlspecialchars($src) : 'https://opencollective.com/static/images/default-guest-logo.svg';
+ $height = 64;
+ $width = $validImage ? round($x * $height / $y) : $height;
+ $href .= (strpos($href, '?') === false ? '?' : '&').'utm_source=opencollective&utm_medium=github&utm_campaign=Carbon';
+ $title = htmlspecialchars(($member['description'] ?? null) ?: $member['name']);
+ $alt = htmlspecialchars($member['name']);
+
+ return "\n".''.
+ ' '.
+ ' ';
+ }, $list))."\n";
+}
+
+file_put_contents('readme.md', preg_replace_callback(
+ '/()[\s\S]+()/',
+ static function (array $match) {
+ return $match[1].getOpenCollectiveSponsors().$match[2];
+ },
+ file_get_contents('readme.md')
+));
diff --git a/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php b/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php
new file mode 100644
index 0000000..8b8fe08
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/AbstractTranslator.php
@@ -0,0 +1,398 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\MessageFormatter\MessageFormatterMapper;
+use Closure;
+use ReflectionException;
+use ReflectionFunction;
+use Symfony\Component\Translation;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+use Symfony\Component\Translation\Loader\ArrayLoader;
+
+abstract class AbstractTranslator extends Translation\Translator
+{
+ /**
+ * Translator singletons for each language.
+ *
+ * @var array
+ */
+ protected static $singletons = [];
+
+ /**
+ * List of custom localized messages.
+ *
+ * @var array
+ */
+ protected $messages = [];
+
+ /**
+ * List of custom directories that contain translation files.
+ *
+ * @var string[]
+ */
+ protected $directories = [];
+
+ /**
+ * Set to true while constructing.
+ *
+ * @var bool
+ */
+ protected $initializing = false;
+
+ /**
+ * List of locales aliases.
+ *
+ * @var array
+ */
+ protected $aliases = [
+ 'me' => 'sr_Latn_ME',
+ 'scr' => 'sh',
+ ];
+
+ /**
+ * Return a singleton instance of Translator.
+ *
+ * @param string|null $locale optional initial locale ("en" - english by default)
+ *
+ * @return static
+ */
+ public static function get($locale = null)
+ {
+ $locale = $locale ?: 'en';
+ $key = static::class === Translator::class ? $locale : static::class.'|'.$locale;
+
+ if (!isset(static::$singletons[$key])) {
+ static::$singletons[$key] = new static($locale);
+ }
+
+ return static::$singletons[$key];
+ }
+
+ public function __construct($locale, MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false)
+ {
+ parent::setLocale($locale);
+ $this->initializing = true;
+ $this->directories = [__DIR__.'/Lang'];
+ $this->addLoader('array', new ArrayLoader());
+ parent::__construct($locale, new MessageFormatterMapper($formatter), $cacheDir, $debug);
+ $this->initializing = false;
+ }
+
+ /**
+ * Returns the list of directories translation files are searched in.
+ *
+ * @return array
+ */
+ public function getDirectories(): array
+ {
+ return $this->directories;
+ }
+
+ /**
+ * Set list of directories translation files are searched in.
+ *
+ * @param array $directories new directories list
+ *
+ * @return $this
+ */
+ public function setDirectories(array $directories)
+ {
+ $this->directories = $directories;
+
+ return $this;
+ }
+
+ /**
+ * Add a directory to the list translation files are searched in.
+ *
+ * @param string $directory new directory
+ *
+ * @return $this
+ */
+ public function addDirectory(string $directory)
+ {
+ $this->directories[] = $directory;
+
+ return $this;
+ }
+
+ /**
+ * Remove a directory from the list translation files are searched in.
+ *
+ * @param string $directory directory path
+ *
+ * @return $this
+ */
+ public function removeDirectory(string $directory)
+ {
+ $search = rtrim(strtr($directory, '\\', '/'), '/');
+
+ return $this->setDirectories(array_filter($this->getDirectories(), function ($item) use ($search) {
+ return rtrim(strtr($item, '\\', '/'), '/') !== $search;
+ }));
+ }
+
+ /**
+ * Reset messages of a locale (all locale if no locale passed).
+ * Remove custom messages and reload initial messages from matching
+ * file in Lang directory.
+ *
+ * @param string|null $locale
+ *
+ * @return bool
+ */
+ public function resetMessages($locale = null)
+ {
+ if ($locale === null) {
+ $this->messages = [];
+
+ return true;
+ }
+
+ foreach ($this->getDirectories() as $directory) {
+ $data = @include sprintf('%s/%s.php', rtrim($directory, '\\/'), $locale);
+
+ if ($data !== false) {
+ $this->messages[$locale] = $data;
+ $this->addResource('array', $this->messages[$locale], $locale);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the list of files matching a given locale prefix (or all if empty).
+ *
+ * @param string $prefix prefix required to filter result
+ *
+ * @return array
+ */
+ public function getLocalesFiles($prefix = '')
+ {
+ $files = [];
+
+ foreach ($this->getDirectories() as $directory) {
+ $directory = rtrim($directory, '\\/');
+
+ foreach (glob("$directory/$prefix*.php") as $file) {
+ $files[] = $file;
+ }
+ }
+
+ return array_unique($files);
+ }
+
+ /**
+ * Returns the list of internally available locales and already loaded custom locales.
+ * (It will ignore custom translator dynamic loading.)
+ *
+ * @param string $prefix prefix required to filter result
+ *
+ * @return array
+ */
+ public function getAvailableLocales($prefix = '')
+ {
+ $locales = [];
+ foreach ($this->getLocalesFiles($prefix) as $file) {
+ $locales[] = substr($file, strrpos($file, '/') + 1, -4);
+ }
+
+ return array_unique(array_merge($locales, array_keys($this->messages)));
+ }
+
+ protected function translate(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
+ {
+ if ($domain === null) {
+ $domain = 'messages';
+ }
+
+ $catalogue = $this->getCatalogue($locale);
+ $format = $this instanceof TranslatorStrongTypeInterface
+ ? $this->getFromCatalogue($catalogue, (string) $id, $domain)
+ : $this->getCatalogue($locale)->get((string) $id, $domain); // @codeCoverageIgnore
+
+ if ($format instanceof Closure) {
+ // @codeCoverageIgnoreStart
+ try {
+ $count = (new ReflectionFunction($format))->getNumberOfRequiredParameters();
+ } catch (ReflectionException $exception) {
+ $count = 0;
+ }
+ // @codeCoverageIgnoreEnd
+
+ return $format(
+ ...array_values($parameters),
+ ...array_fill(0, max(0, $count - \count($parameters)), null)
+ );
+ }
+
+ return parent::trans($id, $parameters, $domain, $locale);
+ }
+
+ /**
+ * Init messages language from matching file in Lang directory.
+ *
+ * @param string $locale
+ *
+ * @return bool
+ */
+ protected function loadMessagesFromFile($locale)
+ {
+ return isset($this->messages[$locale]) || $this->resetMessages($locale);
+ }
+
+ /**
+ * Set messages of a locale and take file first if present.
+ *
+ * @param string $locale
+ * @param array $messages
+ *
+ * @return $this
+ */
+ public function setMessages($locale, $messages)
+ {
+ $this->loadMessagesFromFile($locale);
+ $this->addResource('array', $messages, $locale);
+ $this->messages[$locale] = array_merge(
+ $this->messages[$locale] ?? [],
+ $messages
+ );
+
+ return $this;
+ }
+
+ /**
+ * Set messages of the current locale and take file first if present.
+ *
+ * @param array $messages
+ *
+ * @return $this
+ */
+ public function setTranslations($messages)
+ {
+ return $this->setMessages($this->getLocale(), $messages);
+ }
+
+ /**
+ * Get messages of a locale, if none given, return all the
+ * languages.
+ *
+ * @param string|null $locale
+ *
+ * @return array
+ */
+ public function getMessages($locale = null)
+ {
+ return $locale === null ? $this->messages : $this->messages[$locale];
+ }
+
+ /**
+ * Set the current translator locale and indicate if the source locale file exists
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public function setLocale($locale)
+ {
+ $locale = preg_replace_callback('/[-_]([a-z]{2,}|\d{2,})/', function ($matches) {
+ // _2-letters or YUE is a region, _3+-letters is a variant
+ $upper = strtoupper($matches[1]);
+
+ if ($upper === 'YUE' || $upper === 'ISO' || \strlen($upper) < 3) {
+ return "_$upper";
+ }
+
+ return '_'.ucfirst($matches[1]);
+ }, strtolower($locale));
+
+ $previousLocale = $this->getLocale();
+
+ if ($previousLocale === $locale && isset($this->messages[$locale])) {
+ return true;
+ }
+
+ unset(static::$singletons[$previousLocale]);
+
+ if ($locale === 'auto') {
+ $completeLocale = setlocale(LC_TIME, '0');
+ $locale = preg_replace('/^([^_.-]+).*$/', '$1', $completeLocale);
+ $locales = $this->getAvailableLocales($locale);
+
+ $completeLocaleChunks = preg_split('/[_.-]+/', $completeLocale);
+
+ $getScore = function ($language) use ($completeLocaleChunks) {
+ return self::compareChunkLists($completeLocaleChunks, preg_split('/[_.-]+/', $language));
+ };
+
+ usort($locales, function ($first, $second) use ($getScore) {
+ return $getScore($second) <=> $getScore($first);
+ });
+
+ $locale = $locales[0];
+ }
+
+ if (isset($this->aliases[$locale])) {
+ $locale = $this->aliases[$locale];
+ }
+
+ // If subtag (ex: en_CA) first load the macro (ex: en) to have a fallback
+ if (str_contains($locale, '_') &&
+ $this->loadMessagesFromFile($macroLocale = preg_replace('/^([^_]+).*$/', '$1', $locale))
+ ) {
+ parent::setLocale($macroLocale);
+ }
+
+ if (!$this->loadMessagesFromFile($locale) && !$this->initializing) {
+ return false;
+ }
+
+ parent::setLocale($locale);
+
+ return true;
+ }
+
+ /**
+ * Show locale on var_dump().
+ *
+ * @return array
+ */
+ public function __debugInfo()
+ {
+ return [
+ 'locale' => $this->getLocale(),
+ ];
+ }
+
+ private static function compareChunkLists($referenceChunks, $chunks)
+ {
+ $score = 0;
+
+ foreach ($referenceChunks as $index => $chunk) {
+ if (!isset($chunks[$index])) {
+ $score++;
+
+ continue;
+ }
+
+ if (strtolower($chunks[$index]) === strtolower($chunk)) {
+ $score += 10;
+ }
+ }
+
+ return $score;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Carbon.php b/vendor/nesbot/carbon/src/Carbon/Carbon.php
new file mode 100644
index 0000000..e32569a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Carbon.php
@@ -0,0 +1,523 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Traits\Date;
+use Carbon\Traits\DeprecatedProperties;
+use DateTime;
+use DateTimeInterface;
+use DateTimeZone;
+
+/**
+ * A simple API extension for DateTime.
+ *
+ * @mixin DeprecatedProperties
+ *
+ *
+ *
+ * @property int $year
+ * @property int $yearIso
+ * @property int $month
+ * @property int $day
+ * @property int $hour
+ * @property int $minute
+ * @property int $second
+ * @property int $micro
+ * @property int $microsecond
+ * @property int|float|string $timestamp seconds since the Unix Epoch
+ * @property string $englishDayOfWeek the day of week in English
+ * @property string $shortEnglishDayOfWeek the abbreviated day of week in English
+ * @property string $englishMonth the month in English
+ * @property string $shortEnglishMonth the abbreviated month in English
+ * @property int $milliseconds
+ * @property int $millisecond
+ * @property int $milli
+ * @property int $week 1 through 53
+ * @property int $isoWeek 1 through 53
+ * @property int $weekYear year according to week format
+ * @property int $isoWeekYear year according to ISO week format
+ * @property int $dayOfYear 1 through 366
+ * @property int $age does a diffInYears() with default parameters
+ * @property int $offset the timezone offset in seconds from UTC
+ * @property int $offsetMinutes the timezone offset in minutes from UTC
+ * @property int $offsetHours the timezone offset in hours from UTC
+ * @property CarbonTimeZone $timezone the current timezone
+ * @property CarbonTimeZone $tz alias of $timezone
+ * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday)
+ * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday)
+ * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday
+ * @property-read int $daysInMonth number of days in the given month
+ * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark)
+ * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark)
+ * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name
+ * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName
+ * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read int $noZeroHour current hour from 1 to 24
+ * @property-read int $weeksInYear 51 through 53
+ * @property-read int $isoWeeksInYear 51 through 53
+ * @property-read int $weekOfMonth 1 through 5
+ * @property-read int $weekNumberInMonth 1 through 5
+ * @property-read int $firstWeekDay 0 through 6
+ * @property-read int $lastWeekDay 0 through 6
+ * @property-read int $daysInYear 365 or 366
+ * @property-read int $quarter the quarter of this instance, 1 - 4
+ * @property-read int $decade the decade of this instance
+ * @property-read int $century the century of this instance
+ * @property-read int $millennium the millennium of this instance
+ * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise
+ * @property-read bool $local checks if the timezone is local, true if local, false otherwise
+ * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise
+ * @property-read string $timezoneName the current timezone name
+ * @property-read string $tzName alias of $timezoneName
+ * @property-read string $locale locale of the current instance
+ *
+ * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.)
+ * @method bool isLocal() Check if the current instance has non-UTC timezone.
+ * @method bool isValid() Check if the current instance is a valid date.
+ * @method bool isDST() Check if the current instance is in a daylight saving time.
+ * @method bool isSunday() Checks if the instance day is sunday.
+ * @method bool isMonday() Checks if the instance day is monday.
+ * @method bool isTuesday() Checks if the instance day is tuesday.
+ * @method bool isWednesday() Checks if the instance day is wednesday.
+ * @method bool isThursday() Checks if the instance day is thursday.
+ * @method bool isFriday() Checks if the instance day is friday.
+ * @method bool isSaturday() Checks if the instance day is saturday.
+ * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment.
+ * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year.
+ * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year.
+ * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment.
+ * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week.
+ * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week.
+ * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment.
+ * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day.
+ * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day.
+ * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment.
+ * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour.
+ * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour.
+ * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment.
+ * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute.
+ * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute.
+ * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment.
+ * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second.
+ * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second.
+ * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment.
+ * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month.
+ * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month.
+ * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment.
+ * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter.
+ * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter.
+ * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment.
+ * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade.
+ * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade.
+ * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment.
+ * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century.
+ * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century.
+ * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment.
+ * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium.
+ * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium.
+ * @method $this years(int $value) Set current instance year to the given value.
+ * @method $this year(int $value) Set current instance year to the given value.
+ * @method $this setYears(int $value) Set current instance year to the given value.
+ * @method $this setYear(int $value) Set current instance year to the given value.
+ * @method $this months(int $value) Set current instance month to the given value.
+ * @method $this month(int $value) Set current instance month to the given value.
+ * @method $this setMonths(int $value) Set current instance month to the given value.
+ * @method $this setMonth(int $value) Set current instance month to the given value.
+ * @method $this days(int $value) Set current instance day to the given value.
+ * @method $this day(int $value) Set current instance day to the given value.
+ * @method $this setDays(int $value) Set current instance day to the given value.
+ * @method $this setDay(int $value) Set current instance day to the given value.
+ * @method $this hours(int $value) Set current instance hour to the given value.
+ * @method $this hour(int $value) Set current instance hour to the given value.
+ * @method $this setHours(int $value) Set current instance hour to the given value.
+ * @method $this setHour(int $value) Set current instance hour to the given value.
+ * @method $this minutes(int $value) Set current instance minute to the given value.
+ * @method $this minute(int $value) Set current instance minute to the given value.
+ * @method $this setMinutes(int $value) Set current instance minute to the given value.
+ * @method $this setMinute(int $value) Set current instance minute to the given value.
+ * @method $this seconds(int $value) Set current instance second to the given value.
+ * @method $this second(int $value) Set current instance second to the given value.
+ * @method $this setSeconds(int $value) Set current instance second to the given value.
+ * @method $this setSecond(int $value) Set current instance second to the given value.
+ * @method $this millis(int $value) Set current instance millisecond to the given value.
+ * @method $this milli(int $value) Set current instance millisecond to the given value.
+ * @method $this setMillis(int $value) Set current instance millisecond to the given value.
+ * @method $this setMilli(int $value) Set current instance millisecond to the given value.
+ * @method $this milliseconds(int $value) Set current instance millisecond to the given value.
+ * @method $this millisecond(int $value) Set current instance millisecond to the given value.
+ * @method $this setMilliseconds(int $value) Set current instance millisecond to the given value.
+ * @method $this setMillisecond(int $value) Set current instance millisecond to the given value.
+ * @method $this micros(int $value) Set current instance microsecond to the given value.
+ * @method $this micro(int $value) Set current instance microsecond to the given value.
+ * @method $this setMicros(int $value) Set current instance microsecond to the given value.
+ * @method $this setMicro(int $value) Set current instance microsecond to the given value.
+ * @method $this microseconds(int $value) Set current instance microsecond to the given value.
+ * @method $this microsecond(int $value) Set current instance microsecond to the given value.
+ * @method $this setMicroseconds(int $value) Set current instance microsecond to the given value.
+ * @method $this setMicrosecond(int $value) Set current instance microsecond to the given value.
+ * @method $this addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval).
+ * @method $this addYear() Add one year to the instance (using date interval).
+ * @method $this subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval).
+ * @method $this subYear() Sub one year to the instance (using date interval).
+ * @method $this addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval).
+ * @method $this addMonth() Add one month to the instance (using date interval).
+ * @method $this subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval).
+ * @method $this subMonth() Sub one month to the instance (using date interval).
+ * @method $this addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval).
+ * @method $this addDay() Add one day to the instance (using date interval).
+ * @method $this subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval).
+ * @method $this subDay() Sub one day to the instance (using date interval).
+ * @method $this addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval).
+ * @method $this addHour() Add one hour to the instance (using date interval).
+ * @method $this subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval).
+ * @method $this subHour() Sub one hour to the instance (using date interval).
+ * @method $this addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval).
+ * @method $this addMinute() Add one minute to the instance (using date interval).
+ * @method $this subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval).
+ * @method $this subMinute() Sub one minute to the instance (using date interval).
+ * @method $this addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval).
+ * @method $this addSecond() Add one second to the instance (using date interval).
+ * @method $this subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval).
+ * @method $this subSecond() Sub one second to the instance (using date interval).
+ * @method $this addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this addMilli() Add one millisecond to the instance (using date interval).
+ * @method $this subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this subMilli() Sub one millisecond to the instance (using date interval).
+ * @method $this addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this addMillisecond() Add one millisecond to the instance (using date interval).
+ * @method $this subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this subMillisecond() Sub one millisecond to the instance (using date interval).
+ * @method $this addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this addMicro() Add one microsecond to the instance (using date interval).
+ * @method $this subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this subMicro() Sub one microsecond to the instance (using date interval).
+ * @method $this addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this addMicrosecond() Add one microsecond to the instance (using date interval).
+ * @method $this subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method $this subMicrosecond() Sub one microsecond to the instance (using date interval).
+ * @method $this addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval).
+ * @method $this addMillennium() Add one millennium to the instance (using date interval).
+ * @method $this subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval).
+ * @method $this subMillennium() Sub one millennium to the instance (using date interval).
+ * @method $this addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval).
+ * @method $this addCentury() Add one century to the instance (using date interval).
+ * @method $this subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval).
+ * @method $this subCentury() Sub one century to the instance (using date interval).
+ * @method $this addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval).
+ * @method $this addDecade() Add one decade to the instance (using date interval).
+ * @method $this subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval).
+ * @method $this subDecade() Sub one decade to the instance (using date interval).
+ * @method $this addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval).
+ * @method $this addQuarter() Add one quarter to the instance (using date interval).
+ * @method $this subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval).
+ * @method $this subQuarter() Sub one quarter to the instance (using date interval).
+ * @method $this addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method $this addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method $this addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval).
+ * @method $this addWeek() Add one week to the instance (using date interval).
+ * @method $this subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval).
+ * @method $this subWeek() Sub one week to the instance (using date interval).
+ * @method $this addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval).
+ * @method $this addWeekday() Add one weekday to the instance (using date interval).
+ * @method $this subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval).
+ * @method $this subWeekday() Sub one weekday to the instance (using date interval).
+ * @method $this addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMicro() Add one microsecond to the instance (using timestamp).
+ * @method $this subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMicro() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method $this addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMicrosecond() Add one microsecond to the instance (using timestamp).
+ * @method $this subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMicrosecond() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method $this addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMilli() Add one millisecond to the instance (using timestamp).
+ * @method $this subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMilli() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method $this addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMillisecond() Add one millisecond to the instance (using timestamp).
+ * @method $this subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMillisecond() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method $this addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealSecond() Add one second to the instance (using timestamp).
+ * @method $this subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealSecond() Sub one second to the instance (using timestamp).
+ * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given.
+ * @method $this addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMinute() Add one minute to the instance (using timestamp).
+ * @method $this subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMinute() Sub one minute to the instance (using timestamp).
+ * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given.
+ * @method $this addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealHour() Add one hour to the instance (using timestamp).
+ * @method $this subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealHour() Sub one hour to the instance (using timestamp).
+ * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given.
+ * @method $this addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealDay() Add one day to the instance (using timestamp).
+ * @method $this subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealDay() Sub one day to the instance (using timestamp).
+ * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given.
+ * @method $this addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealWeek() Add one week to the instance (using timestamp).
+ * @method $this subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealWeek() Sub one week to the instance (using timestamp).
+ * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given.
+ * @method $this addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMonth() Add one month to the instance (using timestamp).
+ * @method $this subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMonth() Sub one month to the instance (using timestamp).
+ * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given.
+ * @method $this addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealQuarter() Add one quarter to the instance (using timestamp).
+ * @method $this subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealQuarter() Sub one quarter to the instance (using timestamp).
+ * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given.
+ * @method $this addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealYear() Add one year to the instance (using timestamp).
+ * @method $this subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealYear() Sub one year to the instance (using timestamp).
+ * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given.
+ * @method $this addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealDecade() Add one decade to the instance (using timestamp).
+ * @method $this subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealDecade() Sub one decade to the instance (using timestamp).
+ * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given.
+ * @method $this addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealCentury() Add one century to the instance (using timestamp).
+ * @method $this subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealCentury() Sub one century to the instance (using timestamp).
+ * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given.
+ * @method $this addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp).
+ * @method $this addRealMillennium() Add one millennium to the instance (using timestamp).
+ * @method $this subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp).
+ * @method $this subRealMillennium() Sub one millennium to the instance (using timestamp).
+ * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given.
+ * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision.
+ * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision.
+ * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision.
+ * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision.
+ * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision.
+ * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision.
+ * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision.
+ * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision.
+ * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision.
+ * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision.
+ * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision.
+ * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision.
+ * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision.
+ * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision.
+ * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision.
+ * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision.
+ * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new Carbon object according to the specified format.
+ * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php
+ *
+ *
+ */
+class Carbon extends DateTime implements CarbonInterface
+{
+ use Date;
+
+ /**
+ * Returns true if the current class/instance is mutable.
+ *
+ * @return bool
+ */
+ public static function isMutable()
+ {
+ return true;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php b/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php
new file mode 100644
index 0000000..1ce967b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonConverterInterface.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use DateTimeInterface;
+
+interface CarbonConverterInterface
+{
+ public function convertDate(DateTimeInterface $dateTime, bool $negated = false): CarbonInterface;
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php b/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php
new file mode 100644
index 0000000..4c9c1cf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonImmutable.php
@@ -0,0 +1,582 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Traits\Date;
+use Carbon\Traits\DeprecatedProperties;
+use DateTimeImmutable;
+use DateTimeInterface;
+use DateTimeZone;
+
+/**
+ * A simple API extension for DateTimeImmutable.
+ *
+ * @mixin DeprecatedProperties
+ *
+ *
+ *
+ * @property int $year
+ * @property int $yearIso
+ * @property int $month
+ * @property int $day
+ * @property int $hour
+ * @property int $minute
+ * @property int $second
+ * @property int $micro
+ * @property int $microsecond
+ * @property int|float|string $timestamp seconds since the Unix Epoch
+ * @property string $englishDayOfWeek the day of week in English
+ * @property string $shortEnglishDayOfWeek the abbreviated day of week in English
+ * @property string $englishMonth the month in English
+ * @property string $shortEnglishMonth the abbreviated month in English
+ * @property int $milliseconds
+ * @property int $millisecond
+ * @property int $milli
+ * @property int $week 1 through 53
+ * @property int $isoWeek 1 through 53
+ * @property int $weekYear year according to week format
+ * @property int $isoWeekYear year according to ISO week format
+ * @property int $dayOfYear 1 through 366
+ * @property int $age does a diffInYears() with default parameters
+ * @property int $offset the timezone offset in seconds from UTC
+ * @property int $offsetMinutes the timezone offset in minutes from UTC
+ * @property int $offsetHours the timezone offset in hours from UTC
+ * @property CarbonTimeZone $timezone the current timezone
+ * @property CarbonTimeZone $tz alias of $timezone
+ * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday)
+ * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday)
+ * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday
+ * @property-read int $daysInMonth number of days in the given month
+ * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark)
+ * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark)
+ * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name
+ * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName
+ * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read int $noZeroHour current hour from 1 to 24
+ * @property-read int $weeksInYear 51 through 53
+ * @property-read int $isoWeeksInYear 51 through 53
+ * @property-read int $weekOfMonth 1 through 5
+ * @property-read int $weekNumberInMonth 1 through 5
+ * @property-read int $firstWeekDay 0 through 6
+ * @property-read int $lastWeekDay 0 through 6
+ * @property-read int $daysInYear 365 or 366
+ * @property-read int $quarter the quarter of this instance, 1 - 4
+ * @property-read int $decade the decade of this instance
+ * @property-read int $century the century of this instance
+ * @property-read int $millennium the millennium of this instance
+ * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise
+ * @property-read bool $local checks if the timezone is local, true if local, false otherwise
+ * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise
+ * @property-read string $timezoneName the current timezone name
+ * @property-read string $tzName alias of $timezoneName
+ * @property-read string $locale locale of the current instance
+ *
+ * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.)
+ * @method bool isLocal() Check if the current instance has non-UTC timezone.
+ * @method bool isValid() Check if the current instance is a valid date.
+ * @method bool isDST() Check if the current instance is in a daylight saving time.
+ * @method bool isSunday() Checks if the instance day is sunday.
+ * @method bool isMonday() Checks if the instance day is monday.
+ * @method bool isTuesday() Checks if the instance day is tuesday.
+ * @method bool isWednesday() Checks if the instance day is wednesday.
+ * @method bool isThursday() Checks if the instance day is thursday.
+ * @method bool isFriday() Checks if the instance day is friday.
+ * @method bool isSaturday() Checks if the instance day is saturday.
+ * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment.
+ * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year.
+ * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year.
+ * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment.
+ * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week.
+ * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week.
+ * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment.
+ * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day.
+ * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day.
+ * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment.
+ * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour.
+ * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour.
+ * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment.
+ * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute.
+ * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute.
+ * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment.
+ * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second.
+ * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second.
+ * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment.
+ * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month.
+ * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month.
+ * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment.
+ * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter.
+ * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter.
+ * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment.
+ * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade.
+ * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade.
+ * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment.
+ * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century.
+ * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century.
+ * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment.
+ * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium.
+ * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium.
+ * @method CarbonImmutable years(int $value) Set current instance year to the given value.
+ * @method CarbonImmutable year(int $value) Set current instance year to the given value.
+ * @method CarbonImmutable setYears(int $value) Set current instance year to the given value.
+ * @method CarbonImmutable setYear(int $value) Set current instance year to the given value.
+ * @method CarbonImmutable months(int $value) Set current instance month to the given value.
+ * @method CarbonImmutable month(int $value) Set current instance month to the given value.
+ * @method CarbonImmutable setMonths(int $value) Set current instance month to the given value.
+ * @method CarbonImmutable setMonth(int $value) Set current instance month to the given value.
+ * @method CarbonImmutable days(int $value) Set current instance day to the given value.
+ * @method CarbonImmutable day(int $value) Set current instance day to the given value.
+ * @method CarbonImmutable setDays(int $value) Set current instance day to the given value.
+ * @method CarbonImmutable setDay(int $value) Set current instance day to the given value.
+ * @method CarbonImmutable hours(int $value) Set current instance hour to the given value.
+ * @method CarbonImmutable hour(int $value) Set current instance hour to the given value.
+ * @method CarbonImmutable setHours(int $value) Set current instance hour to the given value.
+ * @method CarbonImmutable setHour(int $value) Set current instance hour to the given value.
+ * @method CarbonImmutable minutes(int $value) Set current instance minute to the given value.
+ * @method CarbonImmutable minute(int $value) Set current instance minute to the given value.
+ * @method CarbonImmutable setMinutes(int $value) Set current instance minute to the given value.
+ * @method CarbonImmutable setMinute(int $value) Set current instance minute to the given value.
+ * @method CarbonImmutable seconds(int $value) Set current instance second to the given value.
+ * @method CarbonImmutable second(int $value) Set current instance second to the given value.
+ * @method CarbonImmutable setSeconds(int $value) Set current instance second to the given value.
+ * @method CarbonImmutable setSecond(int $value) Set current instance second to the given value.
+ * @method CarbonImmutable millis(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable milli(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable setMillis(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable setMilli(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable milliseconds(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable millisecond(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable setMilliseconds(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable setMillisecond(int $value) Set current instance millisecond to the given value.
+ * @method CarbonImmutable micros(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable micro(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable setMicros(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable setMicro(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable microseconds(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable microsecond(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable setMicroseconds(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable setMicrosecond(int $value) Set current instance microsecond to the given value.
+ * @method CarbonImmutable addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addYear() Add one year to the instance (using date interval).
+ * @method CarbonImmutable subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subYear() Sub one year to the instance (using date interval).
+ * @method CarbonImmutable addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMonth() Add one month to the instance (using date interval).
+ * @method CarbonImmutable subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMonth() Sub one month to the instance (using date interval).
+ * @method CarbonImmutable addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addDay() Add one day to the instance (using date interval).
+ * @method CarbonImmutable subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subDay() Sub one day to the instance (using date interval).
+ * @method CarbonImmutable addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addHour() Add one hour to the instance (using date interval).
+ * @method CarbonImmutable subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subHour() Sub one hour to the instance (using date interval).
+ * @method CarbonImmutable addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMinute() Add one minute to the instance (using date interval).
+ * @method CarbonImmutable subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMinute() Sub one minute to the instance (using date interval).
+ * @method CarbonImmutable addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addSecond() Add one second to the instance (using date interval).
+ * @method CarbonImmutable subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subSecond() Sub one second to the instance (using date interval).
+ * @method CarbonImmutable addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMilli() Add one millisecond to the instance (using date interval).
+ * @method CarbonImmutable subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMilli() Sub one millisecond to the instance (using date interval).
+ * @method CarbonImmutable addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMillisecond() Add one millisecond to the instance (using date interval).
+ * @method CarbonImmutable subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMillisecond() Sub one millisecond to the instance (using date interval).
+ * @method CarbonImmutable addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMicro() Add one microsecond to the instance (using date interval).
+ * @method CarbonImmutable subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMicro() Sub one microsecond to the instance (using date interval).
+ * @method CarbonImmutable addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMicrosecond() Add one microsecond to the instance (using date interval).
+ * @method CarbonImmutable subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMicrosecond() Sub one microsecond to the instance (using date interval).
+ * @method CarbonImmutable addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addMillennium() Add one millennium to the instance (using date interval).
+ * @method CarbonImmutable subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subMillennium() Sub one millennium to the instance (using date interval).
+ * @method CarbonImmutable addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addCentury() Add one century to the instance (using date interval).
+ * @method CarbonImmutable subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subCentury() Sub one century to the instance (using date interval).
+ * @method CarbonImmutable addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addDecade() Add one decade to the instance (using date interval).
+ * @method CarbonImmutable subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subDecade() Sub one decade to the instance (using date interval).
+ * @method CarbonImmutable addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addQuarter() Add one quarter to the instance (using date interval).
+ * @method CarbonImmutable subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subQuarter() Sub one quarter to the instance (using date interval).
+ * @method CarbonImmutable addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonImmutable addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonImmutable addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addWeek() Add one week to the instance (using date interval).
+ * @method CarbonImmutable subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subWeek() Sub one week to the instance (using date interval).
+ * @method CarbonImmutable addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable addWeekday() Add one weekday to the instance (using date interval).
+ * @method CarbonImmutable subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval).
+ * @method CarbonImmutable subWeekday() Sub one weekday to the instance (using date interval).
+ * @method CarbonImmutable addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMicro() Add one microsecond to the instance (using timestamp).
+ * @method CarbonImmutable subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMicro() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method CarbonImmutable addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMicrosecond() Add one microsecond to the instance (using timestamp).
+ * @method CarbonImmutable subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMicrosecond() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method CarbonImmutable addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMilli() Add one millisecond to the instance (using timestamp).
+ * @method CarbonImmutable subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMilli() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method CarbonImmutable addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMillisecond() Add one millisecond to the instance (using timestamp).
+ * @method CarbonImmutable subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMillisecond() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method CarbonImmutable addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealSecond() Add one second to the instance (using timestamp).
+ * @method CarbonImmutable subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealSecond() Sub one second to the instance (using timestamp).
+ * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given.
+ * @method CarbonImmutable addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMinute() Add one minute to the instance (using timestamp).
+ * @method CarbonImmutable subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMinute() Sub one minute to the instance (using timestamp).
+ * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given.
+ * @method CarbonImmutable addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealHour() Add one hour to the instance (using timestamp).
+ * @method CarbonImmutable subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealHour() Sub one hour to the instance (using timestamp).
+ * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given.
+ * @method CarbonImmutable addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealDay() Add one day to the instance (using timestamp).
+ * @method CarbonImmutable subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealDay() Sub one day to the instance (using timestamp).
+ * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given.
+ * @method CarbonImmutable addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealWeek() Add one week to the instance (using timestamp).
+ * @method CarbonImmutable subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealWeek() Sub one week to the instance (using timestamp).
+ * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given.
+ * @method CarbonImmutable addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMonth() Add one month to the instance (using timestamp).
+ * @method CarbonImmutable subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMonth() Sub one month to the instance (using timestamp).
+ * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given.
+ * @method CarbonImmutable addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealQuarter() Add one quarter to the instance (using timestamp).
+ * @method CarbonImmutable subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealQuarter() Sub one quarter to the instance (using timestamp).
+ * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given.
+ * @method CarbonImmutable addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealYear() Add one year to the instance (using timestamp).
+ * @method CarbonImmutable subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealYear() Sub one year to the instance (using timestamp).
+ * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given.
+ * @method CarbonImmutable addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealDecade() Add one decade to the instance (using timestamp).
+ * @method CarbonImmutable subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealDecade() Sub one decade to the instance (using timestamp).
+ * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given.
+ * @method CarbonImmutable addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealCentury() Add one century to the instance (using timestamp).
+ * @method CarbonImmutable subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealCentury() Sub one century to the instance (using timestamp).
+ * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given.
+ * @method CarbonImmutable addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable addRealMillennium() Add one millennium to the instance (using timestamp).
+ * @method CarbonImmutable subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonImmutable subRealMillennium() Sub one millennium to the instance (using timestamp).
+ * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given.
+ * @method CarbonImmutable roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method CarbonImmutable roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method CarbonImmutable floorYear(float $precision = 1) Truncate the current instance year with given precision.
+ * @method CarbonImmutable floorYears(float $precision = 1) Truncate the current instance year with given precision.
+ * @method CarbonImmutable ceilYear(float $precision = 1) Ceil the current instance year with given precision.
+ * @method CarbonImmutable ceilYears(float $precision = 1) Ceil the current instance year with given precision.
+ * @method CarbonImmutable roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method CarbonImmutable roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method CarbonImmutable floorMonth(float $precision = 1) Truncate the current instance month with given precision.
+ * @method CarbonImmutable floorMonths(float $precision = 1) Truncate the current instance month with given precision.
+ * @method CarbonImmutable ceilMonth(float $precision = 1) Ceil the current instance month with given precision.
+ * @method CarbonImmutable ceilMonths(float $precision = 1) Ceil the current instance month with given precision.
+ * @method CarbonImmutable roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method CarbonImmutable roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method CarbonImmutable floorDay(float $precision = 1) Truncate the current instance day with given precision.
+ * @method CarbonImmutable floorDays(float $precision = 1) Truncate the current instance day with given precision.
+ * @method CarbonImmutable ceilDay(float $precision = 1) Ceil the current instance day with given precision.
+ * @method CarbonImmutable ceilDays(float $precision = 1) Ceil the current instance day with given precision.
+ * @method CarbonImmutable roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method CarbonImmutable roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method CarbonImmutable floorHour(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method CarbonImmutable floorHours(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method CarbonImmutable ceilHour(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method CarbonImmutable ceilHours(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method CarbonImmutable roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method CarbonImmutable roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method CarbonImmutable floorMinute(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method CarbonImmutable floorMinutes(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method CarbonImmutable ceilMinute(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method CarbonImmutable ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method CarbonImmutable roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method CarbonImmutable roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method CarbonImmutable floorSecond(float $precision = 1) Truncate the current instance second with given precision.
+ * @method CarbonImmutable floorSeconds(float $precision = 1) Truncate the current instance second with given precision.
+ * @method CarbonImmutable ceilSecond(float $precision = 1) Ceil the current instance second with given precision.
+ * @method CarbonImmutable ceilSeconds(float $precision = 1) Ceil the current instance second with given precision.
+ * @method CarbonImmutable roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method CarbonImmutable roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method CarbonImmutable floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method CarbonImmutable floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method CarbonImmutable ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method CarbonImmutable ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method CarbonImmutable roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method CarbonImmutable roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method CarbonImmutable floorCentury(float $precision = 1) Truncate the current instance century with given precision.
+ * @method CarbonImmutable floorCenturies(float $precision = 1) Truncate the current instance century with given precision.
+ * @method CarbonImmutable ceilCentury(float $precision = 1) Ceil the current instance century with given precision.
+ * @method CarbonImmutable ceilCenturies(float $precision = 1) Ceil the current instance century with given precision.
+ * @method CarbonImmutable roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method CarbonImmutable roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method CarbonImmutable floorDecade(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method CarbonImmutable floorDecades(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method CarbonImmutable ceilDecade(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method CarbonImmutable ceilDecades(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method CarbonImmutable roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method CarbonImmutable roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method CarbonImmutable floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method CarbonImmutable floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method CarbonImmutable ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method CarbonImmutable ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method CarbonImmutable roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method CarbonImmutable roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method CarbonImmutable floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method CarbonImmutable floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method CarbonImmutable ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method CarbonImmutable ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method CarbonImmutable roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method CarbonImmutable roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method CarbonImmutable floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method CarbonImmutable floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method CarbonImmutable ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method CarbonImmutable ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method static static|false createFromFormat(string $format, string $time, DateTimeZone|string|false|null $timezone = null) Parse a string into a new CarbonImmutable object according to the specified format.
+ * @method static static __set_state(array $array) https://php.net/manual/en/datetime.set-state.php
+ *
+ *
+ */
+class CarbonImmutable extends DateTimeImmutable implements CarbonInterface
+{
+ use Date {
+ __clone as dateTraitClone;
+ }
+
+ public function __clone()
+ {
+ $this->dateTraitClone();
+ $this->endOfTime = false;
+ $this->startOfTime = false;
+ }
+
+ /**
+ * Create a very old date representing start of time.
+ *
+ * @return static
+ */
+ public static function startOfTime(): self
+ {
+ $date = static::parse('0001-01-01')->years(self::getStartOfTimeYear());
+ $date->startOfTime = true;
+
+ return $date;
+ }
+
+ /**
+ * Create a very far date representing end of time.
+ *
+ * @return static
+ */
+ public static function endOfTime(): self
+ {
+ $date = static::parse('9999-12-31 23:59:59.999999')->years(self::getEndOfTimeYear());
+ $date->endOfTime = true;
+
+ return $date;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ private static function getEndOfTimeYear(): int
+ {
+ if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) {
+ return 145261681241552;
+ }
+
+ // Remove if https://bugs.php.net/bug.php?id=81107 is fixed
+ if (version_compare(PHP_VERSION, '8.1.0-dev', '>=')) {
+ return 1118290769066902787;
+ }
+
+ return PHP_INT_MAX;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ private static function getStartOfTimeYear(): int
+ {
+ if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) {
+ return -135908816449551;
+ }
+
+ // Remove if https://bugs.php.net/bug.php?id=81107 is fixed
+ if (version_compare(PHP_VERSION, '8.1.0-dev', '>=')) {
+ return -1118290769066898816;
+ }
+
+ return max(PHP_INT_MIN, -9223372036854773760);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php
new file mode 100644
index 0000000..d63c4b8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterface.php
@@ -0,0 +1,5143 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use BadMethodCallException;
+use Carbon\Exceptions\BadComparisonUnitException;
+use Carbon\Exceptions\ImmutableException;
+use Carbon\Exceptions\InvalidDateException;
+use Carbon\Exceptions\InvalidFormatException;
+use Carbon\Exceptions\UnknownGetterException;
+use Carbon\Exceptions\UnknownMethodException;
+use Carbon\Exceptions\UnknownSetterException;
+use Closure;
+use DateInterval;
+use DateTime;
+use DateTimeImmutable;
+use DateTimeInterface;
+use DateTimeZone;
+use JsonSerializable;
+use ReflectionException;
+use ReturnTypeWillChange;
+use Symfony\Component\Translation\TranslatorInterface;
+use Throwable;
+
+/**
+ * Common interface for Carbon and CarbonImmutable.
+ *
+ *
+ *
+ * @property int $year
+ * @property int $yearIso
+ * @property int $month
+ * @property int $day
+ * @property int $hour
+ * @property int $minute
+ * @property int $second
+ * @property int $micro
+ * @property int $microsecond
+ * @property int|float|string $timestamp seconds since the Unix Epoch
+ * @property string $englishDayOfWeek the day of week in English
+ * @property string $shortEnglishDayOfWeek the abbreviated day of week in English
+ * @property string $englishMonth the month in English
+ * @property string $shortEnglishMonth the abbreviated month in English
+ * @property int $milliseconds
+ * @property int $millisecond
+ * @property int $milli
+ * @property int $week 1 through 53
+ * @property int $isoWeek 1 through 53
+ * @property int $weekYear year according to week format
+ * @property int $isoWeekYear year according to ISO week format
+ * @property int $dayOfYear 1 through 366
+ * @property int $age does a diffInYears() with default parameters
+ * @property int $offset the timezone offset in seconds from UTC
+ * @property int $offsetMinutes the timezone offset in minutes from UTC
+ * @property int $offsetHours the timezone offset in hours from UTC
+ * @property CarbonTimeZone $timezone the current timezone
+ * @property CarbonTimeZone $tz alias of $timezone
+ * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday)
+ * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday)
+ * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday
+ * @property-read int $daysInMonth number of days in the given month
+ * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark)
+ * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark)
+ * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name
+ * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName
+ * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read int $noZeroHour current hour from 1 to 24
+ * @property-read int $weeksInYear 51 through 53
+ * @property-read int $isoWeeksInYear 51 through 53
+ * @property-read int $weekOfMonth 1 through 5
+ * @property-read int $weekNumberInMonth 1 through 5
+ * @property-read int $firstWeekDay 0 through 6
+ * @property-read int $lastWeekDay 0 through 6
+ * @property-read int $daysInYear 365 or 366
+ * @property-read int $quarter the quarter of this instance, 1 - 4
+ * @property-read int $decade the decade of this instance
+ * @property-read int $century the century of this instance
+ * @property-read int $millennium the millennium of this instance
+ * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise
+ * @property-read bool $local checks if the timezone is local, true if local, false otherwise
+ * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise
+ * @property-read string $timezoneName the current timezone name
+ * @property-read string $tzName alias of $timezoneName
+ * @property-read string $locale locale of the current instance
+ *
+ * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.)
+ * @method bool isLocal() Check if the current instance has non-UTC timezone.
+ * @method bool isValid() Check if the current instance is a valid date.
+ * @method bool isDST() Check if the current instance is in a daylight saving time.
+ * @method bool isSunday() Checks if the instance day is sunday.
+ * @method bool isMonday() Checks if the instance day is monday.
+ * @method bool isTuesday() Checks if the instance day is tuesday.
+ * @method bool isWednesday() Checks if the instance day is wednesday.
+ * @method bool isThursday() Checks if the instance day is thursday.
+ * @method bool isFriday() Checks if the instance day is friday.
+ * @method bool isSaturday() Checks if the instance day is saturday.
+ * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment.
+ * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year.
+ * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year.
+ * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment.
+ * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week.
+ * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week.
+ * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment.
+ * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day.
+ * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day.
+ * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment.
+ * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour.
+ * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour.
+ * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment.
+ * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute.
+ * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute.
+ * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment.
+ * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second.
+ * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second.
+ * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment.
+ * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month.
+ * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month.
+ * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment.
+ * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter.
+ * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter.
+ * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment.
+ * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade.
+ * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade.
+ * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment.
+ * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century.
+ * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century.
+ * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment.
+ * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium.
+ * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium.
+ * @method CarbonInterface years(int $value) Set current instance year to the given value.
+ * @method CarbonInterface year(int $value) Set current instance year to the given value.
+ * @method CarbonInterface setYears(int $value) Set current instance year to the given value.
+ * @method CarbonInterface setYear(int $value) Set current instance year to the given value.
+ * @method CarbonInterface months(int $value) Set current instance month to the given value.
+ * @method CarbonInterface month(int $value) Set current instance month to the given value.
+ * @method CarbonInterface setMonths(int $value) Set current instance month to the given value.
+ * @method CarbonInterface setMonth(int $value) Set current instance month to the given value.
+ * @method CarbonInterface days(int $value) Set current instance day to the given value.
+ * @method CarbonInterface day(int $value) Set current instance day to the given value.
+ * @method CarbonInterface setDays(int $value) Set current instance day to the given value.
+ * @method CarbonInterface setDay(int $value) Set current instance day to the given value.
+ * @method CarbonInterface hours(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface hour(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface setHours(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface setHour(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface minutes(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface minute(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface setMinutes(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface setMinute(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface seconds(int $value) Set current instance second to the given value.
+ * @method CarbonInterface second(int $value) Set current instance second to the given value.
+ * @method CarbonInterface setSeconds(int $value) Set current instance second to the given value.
+ * @method CarbonInterface setSecond(int $value) Set current instance second to the given value.
+ * @method CarbonInterface millis(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface milli(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMillis(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMilli(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface milliseconds(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface millisecond(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMilliseconds(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMillisecond(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface micros(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface micro(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicros(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicro(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface microseconds(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface microsecond(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicroseconds(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicrosecond(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addYear() Add one year to the instance (using date interval).
+ * @method CarbonInterface subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subYear() Sub one year to the instance (using date interval).
+ * @method CarbonInterface addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMonth() Add one month to the instance (using date interval).
+ * @method CarbonInterface subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMonth() Sub one month to the instance (using date interval).
+ * @method CarbonInterface addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addDay() Add one day to the instance (using date interval).
+ * @method CarbonInterface subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subDay() Sub one day to the instance (using date interval).
+ * @method CarbonInterface addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addHour() Add one hour to the instance (using date interval).
+ * @method CarbonInterface subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subHour() Sub one hour to the instance (using date interval).
+ * @method CarbonInterface addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMinute() Add one minute to the instance (using date interval).
+ * @method CarbonInterface subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMinute() Sub one minute to the instance (using date interval).
+ * @method CarbonInterface addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addSecond() Add one second to the instance (using date interval).
+ * @method CarbonInterface subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subSecond() Sub one second to the instance (using date interval).
+ * @method CarbonInterface addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMilli() Add one millisecond to the instance (using date interval).
+ * @method CarbonInterface subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMilli() Sub one millisecond to the instance (using date interval).
+ * @method CarbonInterface addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMillisecond() Add one millisecond to the instance (using date interval).
+ * @method CarbonInterface subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMillisecond() Sub one millisecond to the instance (using date interval).
+ * @method CarbonInterface addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMicro() Add one microsecond to the instance (using date interval).
+ * @method CarbonInterface subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMicro() Sub one microsecond to the instance (using date interval).
+ * @method CarbonInterface addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMicrosecond() Add one microsecond to the instance (using date interval).
+ * @method CarbonInterface subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMicrosecond() Sub one microsecond to the instance (using date interval).
+ * @method CarbonInterface addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMillennium() Add one millennium to the instance (using date interval).
+ * @method CarbonInterface subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMillennium() Sub one millennium to the instance (using date interval).
+ * @method CarbonInterface addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addCentury() Add one century to the instance (using date interval).
+ * @method CarbonInterface subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subCentury() Sub one century to the instance (using date interval).
+ * @method CarbonInterface addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addDecade() Add one decade to the instance (using date interval).
+ * @method CarbonInterface subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subDecade() Sub one decade to the instance (using date interval).
+ * @method CarbonInterface addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addQuarter() Add one quarter to the instance (using date interval).
+ * @method CarbonInterface subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subQuarter() Sub one quarter to the instance (using date interval).
+ * @method CarbonInterface addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addWeek() Add one week to the instance (using date interval).
+ * @method CarbonInterface subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subWeek() Sub one week to the instance (using date interval).
+ * @method CarbonInterface addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addWeekday() Add one weekday to the instance (using date interval).
+ * @method CarbonInterface subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subWeekday() Sub one weekday to the instance (using date interval).
+ * @method CarbonInterface addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMicro() Add one microsecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMicro() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method CarbonInterface addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMicrosecond() Add one microsecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMicrosecond() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method CarbonInterface addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMilli() Add one millisecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMilli() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method CarbonInterface addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMillisecond() Add one millisecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMillisecond() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method CarbonInterface addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealSecond() Add one second to the instance (using timestamp).
+ * @method CarbonInterface subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealSecond() Sub one second to the instance (using timestamp).
+ * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given.
+ * @method CarbonInterface addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMinute() Add one minute to the instance (using timestamp).
+ * @method CarbonInterface subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMinute() Sub one minute to the instance (using timestamp).
+ * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given.
+ * @method CarbonInterface addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealHour() Add one hour to the instance (using timestamp).
+ * @method CarbonInterface subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealHour() Sub one hour to the instance (using timestamp).
+ * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given.
+ * @method CarbonInterface addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealDay() Add one day to the instance (using timestamp).
+ * @method CarbonInterface subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealDay() Sub one day to the instance (using timestamp).
+ * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given.
+ * @method CarbonInterface addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealWeek() Add one week to the instance (using timestamp).
+ * @method CarbonInterface subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealWeek() Sub one week to the instance (using timestamp).
+ * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given.
+ * @method CarbonInterface addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMonth() Add one month to the instance (using timestamp).
+ * @method CarbonInterface subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMonth() Sub one month to the instance (using timestamp).
+ * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given.
+ * @method CarbonInterface addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealQuarter() Add one quarter to the instance (using timestamp).
+ * @method CarbonInterface subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealQuarter() Sub one quarter to the instance (using timestamp).
+ * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given.
+ * @method CarbonInterface addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealYear() Add one year to the instance (using timestamp).
+ * @method CarbonInterface subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealYear() Sub one year to the instance (using timestamp).
+ * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given.
+ * @method CarbonInterface addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealDecade() Add one decade to the instance (using timestamp).
+ * @method CarbonInterface subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealDecade() Sub one decade to the instance (using timestamp).
+ * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given.
+ * @method CarbonInterface addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealCentury() Add one century to the instance (using timestamp).
+ * @method CarbonInterface subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealCentury() Sub one century to the instance (using timestamp).
+ * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given.
+ * @method CarbonInterface addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMillennium() Add one millennium to the instance (using timestamp).
+ * @method CarbonInterface subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMillennium() Sub one millennium to the instance (using timestamp).
+ * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given.
+ * @method CarbonInterface roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method CarbonInterface roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method CarbonInterface floorYear(float $precision = 1) Truncate the current instance year with given precision.
+ * @method CarbonInterface floorYears(float $precision = 1) Truncate the current instance year with given precision.
+ * @method CarbonInterface ceilYear(float $precision = 1) Ceil the current instance year with given precision.
+ * @method CarbonInterface ceilYears(float $precision = 1) Ceil the current instance year with given precision.
+ * @method CarbonInterface roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method CarbonInterface roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method CarbonInterface floorMonth(float $precision = 1) Truncate the current instance month with given precision.
+ * @method CarbonInterface floorMonths(float $precision = 1) Truncate the current instance month with given precision.
+ * @method CarbonInterface ceilMonth(float $precision = 1) Ceil the current instance month with given precision.
+ * @method CarbonInterface ceilMonths(float $precision = 1) Ceil the current instance month with given precision.
+ * @method CarbonInterface roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method CarbonInterface roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method CarbonInterface floorDay(float $precision = 1) Truncate the current instance day with given precision.
+ * @method CarbonInterface floorDays(float $precision = 1) Truncate the current instance day with given precision.
+ * @method CarbonInterface ceilDay(float $precision = 1) Ceil the current instance day with given precision.
+ * @method CarbonInterface ceilDays(float $precision = 1) Ceil the current instance day with given precision.
+ * @method CarbonInterface roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method CarbonInterface roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method CarbonInterface floorHour(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method CarbonInterface floorHours(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method CarbonInterface ceilHour(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method CarbonInterface ceilHours(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method CarbonInterface roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method CarbonInterface roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method CarbonInterface floorMinute(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method CarbonInterface floorMinutes(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method CarbonInterface ceilMinute(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method CarbonInterface ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method CarbonInterface roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method CarbonInterface roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method CarbonInterface floorSecond(float $precision = 1) Truncate the current instance second with given precision.
+ * @method CarbonInterface floorSeconds(float $precision = 1) Truncate the current instance second with given precision.
+ * @method CarbonInterface ceilSecond(float $precision = 1) Ceil the current instance second with given precision.
+ * @method CarbonInterface ceilSeconds(float $precision = 1) Ceil the current instance second with given precision.
+ * @method CarbonInterface roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method CarbonInterface roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method CarbonInterface floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method CarbonInterface floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method CarbonInterface ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method CarbonInterface ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method CarbonInterface roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method CarbonInterface roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method CarbonInterface floorCentury(float $precision = 1) Truncate the current instance century with given precision.
+ * @method CarbonInterface floorCenturies(float $precision = 1) Truncate the current instance century with given precision.
+ * @method CarbonInterface ceilCentury(float $precision = 1) Ceil the current instance century with given precision.
+ * @method CarbonInterface ceilCenturies(float $precision = 1) Ceil the current instance century with given precision.
+ * @method CarbonInterface roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method CarbonInterface roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method CarbonInterface floorDecade(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method CarbonInterface floorDecades(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method CarbonInterface ceilDecade(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method CarbonInterface ceilDecades(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method CarbonInterface roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method CarbonInterface roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method CarbonInterface floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method CarbonInterface floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method CarbonInterface ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method CarbonInterface ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method CarbonInterface roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method CarbonInterface roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method CarbonInterface floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method CarbonInterface floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method CarbonInterface ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method CarbonInterface ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method CarbonInterface roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method CarbonInterface roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method CarbonInterface floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method CarbonInterface floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method CarbonInterface ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method CarbonInterface ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ *
+ *
+ */
+interface CarbonInterface extends DateTimeInterface, JsonSerializable
+{
+ /**
+ * Diff wording options(expressed in octal).
+ */
+ public const NO_ZERO_DIFF = 01;
+ public const JUST_NOW = 02;
+ public const ONE_DAY_WORDS = 04;
+ public const TWO_DAY_WORDS = 010;
+ public const SEQUENTIAL_PARTS_ONLY = 020;
+ public const ROUND = 040;
+ public const FLOOR = 0100;
+ public const CEIL = 0200;
+
+ /**
+ * Diff syntax options.
+ */
+ public const DIFF_ABSOLUTE = 1; // backward compatibility with true
+ public const DIFF_RELATIVE_AUTO = 0; // backward compatibility with false
+ public const DIFF_RELATIVE_TO_NOW = 2;
+ public const DIFF_RELATIVE_TO_OTHER = 3;
+
+ /**
+ * Translate string options.
+ */
+ public const TRANSLATE_MONTHS = 1;
+ public const TRANSLATE_DAYS = 2;
+ public const TRANSLATE_UNITS = 4;
+ public const TRANSLATE_MERIDIEM = 8;
+ public const TRANSLATE_DIFF = 0x10;
+ public const TRANSLATE_ALL = self::TRANSLATE_MONTHS | self::TRANSLATE_DAYS | self::TRANSLATE_UNITS | self::TRANSLATE_MERIDIEM | self::TRANSLATE_DIFF;
+
+ /**
+ * The day constants.
+ */
+ public const SUNDAY = 0;
+ public const MONDAY = 1;
+ public const TUESDAY = 2;
+ public const WEDNESDAY = 3;
+ public const THURSDAY = 4;
+ public const FRIDAY = 5;
+ public const SATURDAY = 6;
+
+ /**
+ * The month constants.
+ * These aren't used by Carbon itself but exist for
+ * convenience sake alone.
+ */
+ public const JANUARY = 1;
+ public const FEBRUARY = 2;
+ public const MARCH = 3;
+ public const APRIL = 4;
+ public const MAY = 5;
+ public const JUNE = 6;
+ public const JULY = 7;
+ public const AUGUST = 8;
+ public const SEPTEMBER = 9;
+ public const OCTOBER = 10;
+ public const NOVEMBER = 11;
+ public const DECEMBER = 12;
+
+ /**
+ * Number of X in Y.
+ */
+ public const YEARS_PER_MILLENNIUM = 1000;
+ public const YEARS_PER_CENTURY = 100;
+ public const YEARS_PER_DECADE = 10;
+ public const MONTHS_PER_YEAR = 12;
+ public const MONTHS_PER_QUARTER = 3;
+ public const QUARTERS_PER_YEAR = 4;
+ public const WEEKS_PER_YEAR = 52;
+ public const WEEKS_PER_MONTH = 4;
+ public const DAYS_PER_YEAR = 365;
+ public const DAYS_PER_WEEK = 7;
+ public const HOURS_PER_DAY = 24;
+ public const MINUTES_PER_HOUR = 60;
+ public const SECONDS_PER_MINUTE = 60;
+ public const MILLISECONDS_PER_SECOND = 1000;
+ public const MICROSECONDS_PER_MILLISECOND = 1000;
+ public const MICROSECONDS_PER_SECOND = 1000000;
+
+ /**
+ * Special settings to get the start of week from current locale culture.
+ */
+ public const WEEK_DAY_AUTO = 'auto';
+
+ /**
+ * RFC7231 DateTime format.
+ *
+ * @var string
+ */
+ public const RFC7231_FORMAT = 'D, d M Y H:i:s \G\M\T';
+
+ /**
+ * Default format to use for __toString method when type juggling occurs.
+ *
+ * @var string
+ */
+ public const DEFAULT_TO_STRING_FORMAT = 'Y-m-d H:i:s';
+
+ /**
+ * Format for converting mocked time, includes microseconds.
+ *
+ * @var string
+ */
+ public const MOCK_DATETIME_FORMAT = 'Y-m-d H:i:s.u';
+
+ /**
+ * Pattern detection for ->isoFormat and ::createFromIsoFormat.
+ *
+ * @var string
+ */
+ public const ISO_FORMAT_REGEXP = '(O[YMDHhms]|[Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY?|g{1,5}|G{1,5}|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?)';
+
+ //
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method magic method name called
+ * @param array $parameters parameters list
+ *
+ * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable
+ *
+ * @return mixed
+ */
+ public function __call($method, $parameters);
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method magic method name called
+ * @param array $parameters parameters list
+ *
+ * @throws BadMethodCallException
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $parameters);
+
+ /**
+ * Update constructedObjectId on cloned.
+ */
+ public function __clone();
+
+ /**
+ * Create a new Carbon instance.
+ *
+ * Please see the testing aids section (specifically static::setTestNow())
+ * for more on the possibility of this constructor returning a test instance.
+ *
+ * @param DateTimeInterface|string|null $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ */
+ public function __construct($time = null, $tz = null);
+
+ /**
+ * Show truthy properties on var_dump().
+ *
+ * @return array
+ */
+ public function __debugInfo();
+
+ /**
+ * Get a part of the Carbon object
+ *
+ * @param string $name
+ *
+ * @throws UnknownGetterException
+ *
+ * @return string|int|bool|DateTimeZone|null
+ */
+ public function __get($name);
+
+ /**
+ * Check if an attribute exists on the object
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function __isset($name);
+
+ /**
+ * Set a part of the Carbon object
+ *
+ * @param string $name
+ * @param string|int|DateTimeZone $value
+ *
+ * @throws UnknownSetterException|ReflectionException
+ *
+ * @return void
+ */
+ public function __set($name, $value);
+
+ /**
+ * The __set_state handler.
+ *
+ * @param string|array $dump
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public static function __set_state($dump);
+
+ /**
+ * Returns the list of properties to dump on serialize() called on.
+ *
+ * Only used by PHP < 7.4.
+ *
+ * @return array
+ */
+ public function __sleep();
+
+ /**
+ * Format the instance as a string using the set format
+ *
+ * @example
+ * ```
+ * echo Carbon::now(); // Carbon instances can be cast to string
+ * ```
+ *
+ * @return string
+ */
+ public function __toString();
+
+ /**
+ * Add given units or interval to the current instance.
+ *
+ * @example $date->add('hour', 3)
+ * @example $date->add(15, 'days')
+ * @example $date->add(CarbonInterval::days(4))
+ *
+ * @param string|DateInterval|Closure|CarbonConverterInterface $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function add($unit, $value = 1, $overflow = null);
+
+ /**
+ * Add seconds to the instance using timestamp. Positive $value travels
+ * forward while negative $value travels into the past.
+ *
+ * @param string $unit
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealUnit($unit, $value = 1);
+
+ /**
+ * Add given units to the current instance.
+ *
+ * @param string $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ public function addUnit($unit, $value = 1, $overflow = null);
+
+ /**
+ * Add any unit to a new value without overflowing current other unit given.
+ *
+ * @param string $valueUnit unit name to modify
+ * @param int $value amount to add to the input unit
+ * @param string $overflowUnit unit name to not overflow
+ *
+ * @return static
+ */
+ public function addUnitNoOverflow($valueUnit, $value, $overflowUnit);
+
+ /**
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given to now
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single part)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function ago($syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Modify the current instance to the average of a given instance (default now) and the current instance
+ * (second-precision).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date
+ *
+ * @return static
+ */
+ public function average($date = null);
+
+ /**
+ * Clone the current instance if it's mutable.
+ *
+ * This method is convenient to ensure you don't mutate the initial object
+ * but avoid to make a useless copy of it if it's already immutable.
+ *
+ * @return static
+ */
+ public function avoidMutation();
+
+ /**
+ * Determines if the instance is between two others.
+ *
+ * The third argument allow you to specify if bounds are included or not (true by default)
+ * but for when you including/excluding bounds may produce different results in your application,
+ * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->between('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->between('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function between($date1, $date2, $equal = true): bool;
+
+ /**
+ * Determines if the instance is between two others, bounds excluded.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return bool
+ */
+ public function betweenExcluded($date1, $date2): bool;
+
+ /**
+ * Determines if the instance is between two others, bounds included.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return bool
+ */
+ public function betweenIncluded($date1, $date2): bool;
+
+ /**
+ * Returns either day of week + time (e.g. "Last Friday at 3:30 PM") if reference time is within 7 days,
+ * or a calendar date (e.g. "10/29/2017") otherwise.
+ *
+ * Language, date and time formats will change according to the current locale.
+ *
+ * @param Carbon|\DateTimeInterface|string|null $referenceTime
+ * @param array $formats
+ *
+ * @return string
+ */
+ public function calendar($referenceTime = null, array $formats = []);
+
+ /**
+ * Checks if the (date)time string is in a given format and valid to create a
+ * new instance.
+ *
+ * @example
+ * ```
+ * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true
+ * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function canBeCreatedFromFormat($date, $format);
+
+ /**
+ * Return the Carbon instance passed through, a now instance in the same timezone
+ * if null given or parse the input if string given.
+ *
+ * @param Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ public function carbonize($date = null);
+
+ /**
+ * Cast the current instance into the given class.
+ *
+ * @param string $className The $className::instance() method will be called to cast the current object.
+ *
+ * @return DateTimeInterface
+ */
+ public function cast(string $className);
+
+ /**
+ * Ceil the current instance second with given precision if specified.
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return CarbonInterface
+ */
+ public function ceil($precision = 1);
+
+ /**
+ * Ceil the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int $precision
+ *
+ * @return CarbonInterface
+ */
+ public function ceilUnit($unit, $precision = 1);
+
+ /**
+ * Ceil the current instance week.
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return CarbonInterface
+ */
+ public function ceilWeek($weekStartsAt = null);
+
+ /**
+ * Similar to native modify() method of DateTime but can handle more grammars.
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->change('next 2pm');
+ * ```
+ *
+ * @link https://php.net/manual/en/datetime.modify.php
+ *
+ * @param string $modifier
+ *
+ * @return static|false
+ */
+ public function change($modifier);
+
+ /**
+ * Cleanup properties attached to the public scope of DateTime when a dump of the date is requested.
+ * foreach ($date as $_) {}
+ * serializer($date)
+ * var_export($date)
+ * get_object_vars($date)
+ */
+ public function cleanupDumpProperties();
+
+ /**
+ * @alias copy
+ *
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function clone();
+
+ /**
+ * Get the closest date from the instance (second-precision).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return static
+ */
+ public function closest($date1, $date2);
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy();
+
+ /**
+ * Create a new Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * @param DateTimeInterface|int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null);
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to now.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createFromDate($year = null, $month = null, $day = null, $tz = null);
+
+ /**
+ * Create a Carbon instance from a specific format.
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ #[ReturnTypeWillChange]
+ public static function createFromFormat($format, $time, $tz = null);
+
+ /**
+ * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()).
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz optional timezone
+ * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use)
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null);
+
+ /**
+ * Create a Carbon instance from a specific format and a string in a given language.
+ *
+ * @param string $format Datetime format
+ * @param string $locale
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function createFromLocaleFormat($format, $locale, $time, $tz = null);
+
+ /**
+ * Create a Carbon instance from a specific ISO format and a string in a given language.
+ *
+ * @param string $format Datetime ISO format
+ * @param string $locale
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null);
+
+ /**
+ * Create a Carbon instance from just a time. The date portion is set to today.
+ *
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null);
+
+ /**
+ * Create a Carbon instance from a time string. The date portion is set to today.
+ *
+ * @param string $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createFromTimeString($time, $tz = null);
+
+ /**
+ * Create a Carbon instance from a timestamp and set the timezone (use default one if not specified).
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestamp($timestamp, $tz = null);
+
+ /**
+ * Create a Carbon instance from a timestamp in milliseconds.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestampMs($timestamp, $tz = null);
+
+ /**
+ * Create a Carbon instance from a timestamp in milliseconds.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ *
+ * @return static
+ */
+ public static function createFromTimestampMsUTC($timestamp);
+
+ /**
+ * Create a Carbon instance from an timestamp keeping the timezone to UTC.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ *
+ * @return static
+ */
+ public static function createFromTimestampUTC($timestamp);
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to midnight.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null);
+
+ /**
+ * Create a new safe Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * If one of the set values is not valid, an InvalidDateException
+ * will be thrown.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidDateException
+ *
+ * @return static|false
+ */
+ public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null);
+
+ /**
+ * Create a new Carbon instance from a specific date and time using strict validation.
+ *
+ * @see create()
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null);
+
+ /**
+ * Get/set the day of year.
+ *
+ * @param int|null $value new value for day of year if using as setter.
+ *
+ * @return static|int
+ */
+ public function dayOfYear($value = null);
+
+ /**
+ * Get the difference as a CarbonInterval instance.
+ * Return relative interval (negative if $absolute flag is not set to true and the given date is before
+ * current one).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return CarbonInterval
+ */
+ public function diffAsCarbonInterval($date = null, $absolute = true, array $skip = []);
+
+ /**
+ * Get the difference by the given interval using a filter closure.
+ *
+ * @param CarbonInterval $ci An interval to traverse by
+ * @param Closure $callback
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true);
+
+ /**
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ *
+ * @example
+ * ```
+ * echo Carbon::tomorrow()->diffForHumans() . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(['parts' => 2]) . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(['parts' => 3, 'join' => true]) . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday()) . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n";
+ * ```
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'skip' entry, list of units to skip (array of strings or a single string,
+ * ` it can be the unit name (singular or plural) or its shortcut
+ * ` (y, m, w, d, h, min, s, ms, µs).
+ * - 'aUnit' entry, prefer "an hour" over "1 hour" if true
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * - 'minimumUnit' entry determines the smallest unit of time to display can be long or
+ * ` short form of the units, e.g. 'hour' or 'h' (default value: s)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Get the difference in days rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDays($date = null, $absolute = true);
+
+ /**
+ * Get the difference in days using a filter closure rounded down.
+ *
+ * @param Closure $callback
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true);
+
+ /**
+ * Get the difference in hours rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHours($date = null, $absolute = true);
+
+ /**
+ * Get the difference in hours using a filter closure rounded down.
+ *
+ * @param Closure $callback
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true);
+
+ /**
+ * Get the difference in microseconds.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMicroseconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in milliseconds rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMilliseconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in minutes rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMinutes($date = null, $absolute = true);
+
+ /**
+ * Get the difference in months rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMonths($date = null, $absolute = true);
+
+ /**
+ * Get the difference in quarters rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInQuarters($date = null, $absolute = true);
+
+ /**
+ * Get the difference in hours rounded down using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealHours($date = null, $absolute = true);
+
+ /**
+ * Get the difference in microseconds using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMicroseconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in milliseconds rounded down using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMilliseconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in minutes rounded down using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMinutes($date = null, $absolute = true);
+
+ /**
+ * Get the difference in seconds using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealSeconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in seconds rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInSeconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in weekdays rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekdays($date = null, $absolute = true);
+
+ /**
+ * Get the difference in weekend days using a filter rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekendDays($date = null, $absolute = true);
+
+ /**
+ * Get the difference in weeks rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeeks($date = null, $absolute = true);
+
+ /**
+ * Get the difference in years
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInYears($date = null, $absolute = true);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * @param int $humanDiffOption
+ */
+ public static function disableHumanDiffOption($humanDiffOption);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * @param int $humanDiffOption
+ */
+ public static function enableHumanDiffOption($humanDiffOption);
+
+ /**
+ * Modify to end of current given unit.
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->startOf('month')
+ * ->endOf('week', Carbon::FRIDAY);
+ * ```
+ *
+ * @param string $unit
+ * @param array $params
+ *
+ * @return static
+ */
+ public function endOf($unit, ...$params);
+
+ /**
+ * Resets the date to end of the century and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfCentury();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfCentury();
+
+ /**
+ * Resets the time to 23:59:59.999999 end of day
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfDay();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfDay();
+
+ /**
+ * Resets the date to end of the decade and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfDecade();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfDecade();
+
+ /**
+ * Modify to end of current hour, minutes and seconds become 59
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfHour();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfHour();
+
+ /**
+ * Resets the date to end of the millennium and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfMillennium();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfMillennium();
+
+ /**
+ * Modify to end of current minute, seconds become 59
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfMinute();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfMinute();
+
+ /**
+ * Resets the date to end of the month and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfMonth();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfMonth();
+
+ /**
+ * Resets the date to end of the quarter and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfQuarter();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfQuarter();
+
+ /**
+ * Modify to end of current second, microseconds become 999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->endOfSecond()
+ * ->format('H:i:s.u');
+ * ```
+ *
+ * @return static
+ */
+ public function endOfSecond();
+
+ /**
+ * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->endOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek(Carbon::SATURDAY) . "\n";
+ * ```
+ *
+ * @param int $weekEndsAt optional start allow you to specify the day of week to use to end the week
+ *
+ * @return static
+ */
+ public function endOfWeek($weekEndsAt = null);
+
+ /**
+ * Resets the date to end of the year and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfYear();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfYear();
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->eq(Carbon::parse('2018-07-25 12:45:16')); // true
+ * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see equalTo()
+ *
+ * @return bool
+ */
+ public function eq($date): bool;
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true
+ * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function equalTo($date): bool;
+
+ /**
+ * Set the current locale to the given, execute the passed function, reset the locale to previous one,
+ * then return the result of the closure (or null if the closure was void).
+ *
+ * @param string $locale locale ex. en
+ * @param callable $func
+ *
+ * @return mixed
+ */
+ public static function executeWithLocale($locale, $func);
+
+ /**
+ * Get the farthest date from the instance (second-precision).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return static
+ */
+ public function farthest($date1, $date2);
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * first day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function firstOfMonth($dayOfWeek = null);
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * first day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function firstOfQuarter($dayOfWeek = null);
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * first day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function firstOfYear($dayOfWeek = null);
+
+ /**
+ * Get the difference in days as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInDays($date = null, $absolute = true);
+
+ /**
+ * Get the difference in hours as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInHours($date = null, $absolute = true);
+
+ /**
+ * Get the difference in minutes as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInMinutes($date = null, $absolute = true);
+
+ /**
+ * Get the difference in months as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInMonths($date = null, $absolute = true);
+
+ /**
+ * Get the difference in days as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealDays($date = null, $absolute = true);
+
+ /**
+ * Get the difference in hours as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealHours($date = null, $absolute = true);
+
+ /**
+ * Get the difference in minutes as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealMinutes($date = null, $absolute = true);
+
+ /**
+ * Get the difference in months as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealMonths($date = null, $absolute = true);
+
+ /**
+ * Get the difference in seconds as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealSeconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in weeks as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealWeeks($date = null, $absolute = true);
+
+ /**
+ * Get the difference in year as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealYears($date = null, $absolute = true);
+
+ /**
+ * Get the difference in seconds as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInSeconds($date = null, $absolute = true);
+
+ /**
+ * Get the difference in weeks as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInWeeks($date = null, $absolute = true);
+
+ /**
+ * Get the difference in year as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInYears($date = null, $absolute = true);
+
+ /**
+ * Round the current instance second with given precision if specified.
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return CarbonInterface
+ */
+ public function floor($precision = 1);
+
+ /**
+ * Truncate the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int $precision
+ *
+ * @return CarbonInterface
+ */
+ public function floorUnit($unit, $precision = 1);
+
+ /**
+ * Truncate the current instance week.
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return CarbonInterface
+ */
+ public function floorWeek($weekStartsAt = null);
+
+ /**
+ * Format the instance with the current locale. You can set the current
+ * locale using setlocale() https://php.net/setlocale.
+ *
+ * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ * Use ->isoFormat() instead.
+ * Deprecated since 2.55.0
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function formatLocalized($format);
+
+ /**
+ * @alias diffForHumans
+ *
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function from($other = null, $syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Get the difference in a human readable format in the current locale from current
+ * instance to now.
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function fromNow($syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Create an instance from a serialized string.
+ *
+ * @param string $value
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function fromSerialized($value);
+
+ /**
+ * Register a custom macro.
+ *
+ * @param object|callable $macro
+ * @param int $priority marco with higher priority is tried first
+ *
+ * @return void
+ */
+ public static function genericMacro($macro, $priority = 0);
+
+ /**
+ * Get a part of the Carbon object
+ *
+ * @param string $name
+ *
+ * @throws UnknownGetterException
+ *
+ * @return string|int|bool|DateTimeZone|null
+ */
+ public function get($name);
+
+ /**
+ * Returns the alternative number for a given date property if available in the current locale.
+ *
+ * @param string $key date property
+ *
+ * @return string
+ */
+ public function getAltNumber(string $key): string;
+
+ /**
+ * Returns the list of internally available locales and already loaded custom locales.
+ * (It will ignore custom translator dynamic loading.)
+ *
+ * @return array
+ */
+ public static function getAvailableLocales();
+
+ /**
+ * Returns list of Language object for each available locale. This object allow you to get the ISO name, native
+ * name, region and variant of the locale.
+ *
+ * @return Language[]
+ */
+ public static function getAvailableLocalesInfo();
+
+ /**
+ * Returns list of calendar formats for ISO formatting.
+ *
+ * @param string|null $locale current locale used if null
+ *
+ * @return array
+ */
+ public function getCalendarFormats($locale = null);
+
+ /**
+ * Get the days of the week
+ *
+ * @return array
+ */
+ public static function getDays();
+
+ /**
+ * Return the number of days since the start of the week (using the current locale or the first parameter
+ * if explicitly given).
+ *
+ * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week,
+ * if not provided, start of week is inferred from the locale
+ * (Sunday for en_US, Monday for de_DE, etc.)
+ *
+ * @return int
+ */
+ public function getDaysFromStartOfWeek(?int $weekStartsAt = null): int;
+
+ /**
+ * Get the fallback locale.
+ *
+ * @see https://symfony.com/doc/current/components/translation.html#fallback-locales
+ *
+ * @return string|null
+ */
+ public static function getFallbackLocale();
+
+ /**
+ * List of replacements from date() format to isoFormat().
+ *
+ * @return array
+ */
+ public static function getFormatsToIsoReplacements();
+
+ /**
+ * Return default humanDiff() options (merged flags as integer).
+ *
+ * @return int
+ */
+ public static function getHumanDiffOptions();
+
+ /**
+ * Returns list of locale formats for ISO formatting.
+ *
+ * @param string|null $locale current locale used if null
+ *
+ * @return array
+ */
+ public function getIsoFormats($locale = null);
+
+ /**
+ * Returns list of locale units for ISO formatting.
+ *
+ * @return array
+ */
+ public static function getIsoUnits();
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return array
+ */
+ #[ReturnTypeWillChange]
+ public static function getLastErrors();
+
+ /**
+ * Get the raw callable macro registered globally or locally for a given name.
+ *
+ * @param string $name
+ *
+ * @return callable|null
+ */
+ public function getLocalMacro($name);
+
+ /**
+ * Get the translator of the current instance or the default if none set.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public function getLocalTranslator();
+
+ /**
+ * Get the current translator locale.
+ *
+ * @return string
+ */
+ public static function getLocale();
+
+ /**
+ * Get the raw callable macro registered globally for a given name.
+ *
+ * @param string $name
+ *
+ * @return callable|null
+ */
+ public static function getMacro($name);
+
+ /**
+ * get midday/noon hour
+ *
+ * @return int
+ */
+ public static function getMidDayAt();
+
+ /**
+ * Returns the offset hour and minute formatted with +/- and a given separator (":" by default).
+ * For example, if the time zone is 9 hours 30 minutes, you'll get "+09:30", with "@@" as first
+ * argument, "+09@@30", with "" as first argument, "+0930". Negative offset will return something
+ * like "-12:00".
+ *
+ * @param string $separator string to place between hours and minutes (":" by default)
+ *
+ * @return string
+ */
+ public function getOffsetString($separator = ':');
+
+ /**
+ * Returns a unit of the instance padded with 0 by default or any other string if specified.
+ *
+ * @param string $unit Carbon unit name
+ * @param int $length Length of the output (2 by default)
+ * @param string $padString String to use for padding ("0" by default)
+ * @param int $padType Side(s) to pad (STR_PAD_LEFT by default)
+ *
+ * @return string
+ */
+ public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = 0);
+
+ /**
+ * Returns a timestamp rounded with the given precision (6 by default).
+ *
+ * @example getPreciseTimestamp() 1532087464437474 (microsecond maximum precision)
+ * @example getPreciseTimestamp(6) 1532087464437474
+ * @example getPreciseTimestamp(5) 153208746443747 (1/100000 second precision)
+ * @example getPreciseTimestamp(4) 15320874644375 (1/10000 second precision)
+ * @example getPreciseTimestamp(3) 1532087464437 (millisecond precision)
+ * @example getPreciseTimestamp(2) 153208746444 (1/100 second precision)
+ * @example getPreciseTimestamp(1) 15320874644 (1/10 second precision)
+ * @example getPreciseTimestamp(0) 1532087464 (second precision)
+ * @example getPreciseTimestamp(-1) 153208746 (10 second precision)
+ * @example getPreciseTimestamp(-2) 15320875 (100 second precision)
+ *
+ * @param int $precision
+ *
+ * @return float
+ */
+ public function getPreciseTimestamp($precision = 6);
+
+ /**
+ * Returns current local settings.
+ *
+ * @return array
+ */
+ public function getSettings();
+
+ /**
+ * Get the Carbon instance (real or mock) to be returned when a "now"
+ * instance is created.
+ *
+ * @return Closure|static the current instance used for testing
+ */
+ public static function getTestNow();
+
+ /**
+ * Return a format from H:i to H:i:s.u according to given unit precision.
+ *
+ * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond"
+ *
+ * @return string
+ */
+ public static function getTimeFormatByPrecision($unitPrecision);
+
+ /**
+ * Returns the timestamp with millisecond precision.
+ *
+ * @return int
+ */
+ public function getTimestampMs();
+
+ /**
+ * Get the translation of the current week day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ * @param string $keySuffix "", "_short" or "_min"
+ * @param string|null $defaultValue default value if translation missing
+ *
+ * @return string
+ */
+ public function getTranslatedDayName($context = null, $keySuffix = '', $defaultValue = null);
+
+ /**
+ * Get the translation of the current abbreviated week day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ *
+ * @return string
+ */
+ public function getTranslatedMinDayName($context = null);
+
+ /**
+ * Get the translation of the current month day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ * @param string $keySuffix "" or "_short"
+ * @param string|null $defaultValue default value if translation missing
+ *
+ * @return string
+ */
+ public function getTranslatedMonthName($context = null, $keySuffix = '', $defaultValue = null);
+
+ /**
+ * Get the translation of the current short week day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ *
+ * @return string
+ */
+ public function getTranslatedShortDayName($context = null);
+
+ /**
+ * Get the translation of the current short month day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ *
+ * @return string
+ */
+ public function getTranslatedShortMonthName($context = null);
+
+ /**
+ * Returns raw translation message for a given key.
+ *
+ * @param string $key key to find
+ * @param string|null $locale current locale used if null
+ * @param string|null $default default value if translation returns the key
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use
+ *
+ * @return string
+ */
+ public function getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null);
+
+ /**
+ * Returns raw translation message for a given key.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use
+ * @param string $key key to find
+ * @param string|null $locale current locale used if null
+ * @param string|null $default default value if translation returns the key
+ *
+ * @return string
+ */
+ public static function getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null);
+
+ /**
+ * Get the default translator instance in use.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public static function getTranslator();
+
+ /**
+ * Get the last day of week
+ *
+ * @return int
+ */
+ public static function getWeekEndsAt();
+
+ /**
+ * Get the first day of week
+ *
+ * @return int
+ */
+ public static function getWeekStartsAt();
+
+ /**
+ * Get weekend days
+ *
+ * @return array
+ */
+ public static function getWeekendDays();
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function greaterThan($date): bool;
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function greaterThanOrEqualTo($date): bool;
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see greaterThan()
+ *
+ * @return bool
+ */
+ public function gt($date): bool;
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see greaterThanOrEqualTo()
+ *
+ * @return bool
+ */
+ public function gte($date): bool;
+
+ /**
+ * Checks if the (date)time string is in a given format.
+ *
+ * @example
+ * ```
+ * Carbon::hasFormat('11:12:45', 'h:i:s'); // true
+ * Carbon::hasFormat('13:12:45', 'h:i:s'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function hasFormat($date, $format);
+
+ /**
+ * Checks if the (date)time string is in a given format.
+ *
+ * @example
+ * ```
+ * Carbon::hasFormatWithModifiers('31/08/2015', 'd#m#Y'); // true
+ * Carbon::hasFormatWithModifiers('31/08/2015', 'm#d#Y'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function hasFormatWithModifiers($date, $format): bool;
+
+ /**
+ * Checks if macro is registered globally or locally.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasLocalMacro($name);
+
+ /**
+ * Return true if the current instance has its own translator.
+ *
+ * @return bool
+ */
+ public function hasLocalTranslator();
+
+ /**
+ * Checks if macro is registered globally.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name);
+
+ /**
+ * Determine if a time string will produce a relative date.
+ *
+ * @param string $time
+ *
+ * @return bool true if time match a relative date, false if absolute or invalid time string
+ */
+ public static function hasRelativeKeywords($time);
+
+ /**
+ * Determine if there is a valid test instance set. A valid test instance
+ * is anything that is not null.
+ *
+ * @return bool true if there is a test instance, otherwise false
+ */
+ public static function hasTestNow();
+
+ /**
+ * Create a Carbon instance from a DateTime one.
+ *
+ * @param DateTimeInterface $date
+ *
+ * @return static
+ */
+ public static function instance($date);
+
+ /**
+ * Returns true if the current date matches the given string.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2018')); // false
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('06-02')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06-02')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('Sunday')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('June')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:45')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:00')); // false
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12h')); // true
+ * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3pm')); // true
+ * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3am')); // false
+ * ```
+ *
+ * @param string $tester day name, month name, hour, date, etc. as string
+ *
+ * @return bool
+ */
+ public function is(string $tester);
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see greaterThan()
+ *
+ * @return bool
+ */
+ public function isAfter($date): bool;
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lessThan()
+ *
+ * @return bool
+ */
+ public function isBefore($date): bool;
+
+ /**
+ * Determines if the instance is between two others
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->isBetween('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->isBetween('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function isBetween($date1, $date2, $equal = true): bool;
+
+ /**
+ * Check if its the birthday. Compares the date/month values of the two dates.
+ *
+ * @example
+ * ```
+ * Carbon::now()->subYears(5)->isBirthday(); // true
+ * Carbon::now()->subYears(5)->subDay()->isBirthday(); // false
+ * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-05')); // true
+ * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isBirthday($date = null);
+
+ /**
+ * Determines if the instance is in the current unit given.
+ *
+ * @example
+ * ```
+ * Carbon::now()->isCurrentUnit('hour'); // true
+ * Carbon::now()->subHours(2)->isCurrentUnit('hour'); // false
+ * ```
+ *
+ * @param string $unit The unit to test.
+ *
+ * @throws BadMethodCallException
+ *
+ * @return bool
+ */
+ public function isCurrentUnit($unit);
+
+ /**
+ * Checks if this day is a specific day of the week.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::WEDNESDAY); // true
+ * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::FRIDAY); // false
+ * Carbon::parse('2019-07-17')->isDayOfWeek('Wednesday'); // true
+ * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false
+ * ```
+ *
+ * @param int $dayOfWeek
+ *
+ * @return bool
+ */
+ public function isDayOfWeek($dayOfWeek);
+
+ /**
+ * Check if the instance is end of day.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(); // true
+ * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(); // true
+ * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(); // true
+ * Carbon::parse('2019-02-28 23:59:58.999999')->isEndOfDay(); // false
+ * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(true); // true
+ * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(true); // false
+ * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false
+ * ```
+ *
+ * @param bool $checkMicroseconds check time at microseconds precision
+ *
+ * @return bool
+ */
+ public function isEndOfDay($checkMicroseconds = false);
+
+ /**
+ * Returns true if the date was created using CarbonImmutable::endOfTime()
+ *
+ * @return bool
+ */
+ public function isEndOfTime(): bool;
+
+ /**
+ * Determines if the instance is in the future, ie. greater (after) than now.
+ *
+ * @example
+ * ```
+ * Carbon::now()->addHours(5)->isFuture(); // true
+ * Carbon::now()->subHours(5)->isFuture(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isFuture();
+
+ /**
+ * Returns true if the current class/instance is immutable.
+ *
+ * @return bool
+ */
+ public static function isImmutable();
+
+ /**
+ * Check if today is the last day of the Month
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28')->isLastOfMonth(); // true
+ * Carbon::parse('2019-03-28')->isLastOfMonth(); // false
+ * Carbon::parse('2019-03-30')->isLastOfMonth(); // false
+ * Carbon::parse('2019-03-31')->isLastOfMonth(); // true
+ * Carbon::parse('2019-04-30')->isLastOfMonth(); // true
+ * ```
+ *
+ * @return bool
+ */
+ public function isLastOfMonth();
+
+ /**
+ * Determines if the instance is a leap year.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2020-01-01')->isLeapYear(); // true
+ * Carbon::parse('2019-01-01')->isLeapYear(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isLeapYear();
+
+ /**
+ * Determines if the instance is a long year (using ISO 8601 year).
+ *
+ * @example
+ * ```
+ * Carbon::parse('2015-01-01')->isLongIsoYear(); // true
+ * Carbon::parse('2016-01-01')->isLongIsoYear(); // true
+ * Carbon::parse('2016-01-03')->isLongIsoYear(); // false
+ * Carbon::parse('2019-12-29')->isLongIsoYear(); // false
+ * Carbon::parse('2019-12-30')->isLongIsoYear(); // true
+ * ```
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
+ *
+ * @return bool
+ */
+ public function isLongIsoYear();
+
+ /**
+ * Determines if the instance is a long year (using calendar year).
+ *
+ * ⚠️ This method completely ignores month and day to use the numeric year number,
+ * it's not correct if the exact date matters. For instance as `2019-12-30` is already
+ * in the first week of the 2020 year, if you want to know from this date if ISO week
+ * year 2020 is a long year, use `isLongIsoYear` instead.
+ *
+ * @example
+ * ```
+ * Carbon::create(2015)->isLongYear(); // true
+ * Carbon::create(2016)->isLongYear(); // false
+ * ```
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
+ *
+ * @return bool
+ */
+ public function isLongYear();
+
+ /**
+ * Check if the instance is midday.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 11:59:59.999999')->isMidday(); // false
+ * Carbon::parse('2019-02-28 12:00:00')->isMidday(); // true
+ * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true
+ * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isMidday();
+
+ /**
+ * Check if the instance is start of day / midnight.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 00:00:00')->isMidnight(); // true
+ * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true
+ * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isMidnight();
+
+ /**
+ * Returns true if a property can be changed via setter.
+ *
+ * @param string $unit
+ *
+ * @return bool
+ */
+ public static function isModifiableUnit($unit);
+
+ /**
+ * Returns true if the current class/instance is mutable.
+ *
+ * @return bool
+ */
+ public static function isMutable();
+
+ /**
+ * Determines if the instance is in the past, ie. less (before) than now.
+ *
+ * @example
+ * ```
+ * Carbon::now()->subHours(5)->isPast(); // true
+ * Carbon::now()->addHours(5)->isPast(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isPast();
+
+ /**
+ * Compares the formatted values of the two dates.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-12-13')); // true
+ * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false
+ * ```
+ *
+ * @param string $format date formats to compare.
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isSameAs($format, $date = null);
+
+ /**
+ * Checks if the passed in date is in the same month as the instance´s month.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-01-01')); // true
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-02-01')); // false
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01')); // false
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameMonth($date = null, $ofSameYear = true);
+
+ /**
+ * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed).
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-03-01')); // true
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-04-01')); // false
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01')); // false
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameQuarter($date = null, $ofSameYear = true);
+
+ /**
+ * Determines if the instance is in the current unit given.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-01-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // true
+ * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false
+ * ```
+ *
+ * @param string $unit singular unit string
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day.
+ *
+ * @throws BadComparisonUnitException
+ *
+ * @return bool
+ */
+ public function isSameUnit($unit, $date = null);
+
+ /**
+ * Check if the instance is start of day / midnight.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 00:00:00')->isStartOfDay(); // true
+ * Carbon::parse('2019-02-28 00:00:00.999999')->isStartOfDay(); // true
+ * Carbon::parse('2019-02-28 00:00:01')->isStartOfDay(); // false
+ * Carbon::parse('2019-02-28 00:00:00.000000')->isStartOfDay(true); // true
+ * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false
+ * ```
+ *
+ * @param bool $checkMicroseconds check time at microseconds precision
+ *
+ * @return bool
+ */
+ public function isStartOfDay($checkMicroseconds = false);
+
+ /**
+ * Returns true if the date was created using CarbonImmutable::startOfTime()
+ *
+ * @return bool
+ */
+ public function isStartOfTime(): bool;
+
+ /**
+ * Returns true if the strict mode is globally in use, false else.
+ * (It can be overridden in specific instances.)
+ *
+ * @return bool
+ */
+ public static function isStrictModeEnabled();
+
+ /**
+ * Determines if the instance is today.
+ *
+ * @example
+ * ```
+ * Carbon::today()->isToday(); // true
+ * Carbon::tomorrow()->isToday(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isToday();
+
+ /**
+ * Determines if the instance is tomorrow.
+ *
+ * @example
+ * ```
+ * Carbon::tomorrow()->isTomorrow(); // true
+ * Carbon::yesterday()->isTomorrow(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isTomorrow();
+
+ /**
+ * Determines if the instance is a weekday.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-07-14')->isWeekday(); // false
+ * Carbon::parse('2019-07-15')->isWeekday(); // true
+ * ```
+ *
+ * @return bool
+ */
+ public function isWeekday();
+
+ /**
+ * Determines if the instance is a weekend day.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-07-14')->isWeekend(); // true
+ * Carbon::parse('2019-07-15')->isWeekend(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isWeekend();
+
+ /**
+ * Determines if the instance is yesterday.
+ *
+ * @example
+ * ```
+ * Carbon::yesterday()->isYesterday(); // true
+ * Carbon::tomorrow()->isYesterday(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isYesterday();
+
+ /**
+ * Format in the current language using ISO replacement patterns.
+ *
+ * @param string $format
+ * @param string|null $originalFormat provide context if a chunk has been passed alone
+ *
+ * @return string
+ */
+ public function isoFormat(string $format, ?string $originalFormat = null): string;
+
+ /**
+ * Get/set the week number using given first day of week and first
+ * day of year included in the first week. Or use ISO format if no settings
+ * given.
+ *
+ * @param int|null $week
+ * @param int|null $dayOfWeek
+ * @param int|null $dayOfYear
+ *
+ * @return int|static
+ */
+ public function isoWeek($week = null, $dayOfWeek = null, $dayOfYear = null);
+
+ /**
+ * Set/get the week number of year using given first day of week and first
+ * day of year included in the first week. Or use ISO format if no settings
+ * given.
+ *
+ * @param int|null $year if null, act as a getter, if not null, set the year and return current instance.
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int|static
+ */
+ public function isoWeekYear($year = null, $dayOfWeek = null, $dayOfYear = null);
+
+ /**
+ * Get/set the ISO weekday from 1 (Monday) to 7 (Sunday).
+ *
+ * @param int|null $value new value for weekday if using as setter.
+ *
+ * @return static|int
+ */
+ public function isoWeekday($value = null);
+
+ /**
+ * Get the number of weeks of the current week-year using given first day of week and first
+ * day of year included in the first week. Or use ISO format if no settings
+ * given.
+ *
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int
+ */
+ public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null);
+
+ /**
+ * Prepare the object for JSON serialization.
+ *
+ * @return array|string
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize();
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * last day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function lastOfMonth($dayOfWeek = null);
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * last day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function lastOfQuarter($dayOfWeek = null);
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * last day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function lastOfYear($dayOfWeek = null);
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function lessThan($date): bool;
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function lessThanOrEqualTo($date): bool;
+
+ /**
+ * Get/set the locale for the current instance.
+ *
+ * @param string|null $locale
+ * @param string ...$fallbackLocales
+ *
+ * @return $this|string
+ */
+ public function locale(?string $locale = null, ...$fallbackLocales);
+
+ /**
+ * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow).
+ * Support is considered enabled if the 3 words are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffOneDayWords($locale);
+
+ /**
+ * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffSyntax($locale);
+
+ /**
+ * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow).
+ * Support is considered enabled if the 2 words are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffTwoDayWords($locale);
+
+ /**
+ * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasPeriodSyntax($locale);
+
+ /**
+ * Returns true if the given locale is internally supported and has short-units support.
+ * Support is considered enabled if either year, day or hour has a short variant translated.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasShortUnits($locale);
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lessThan()
+ *
+ * @return bool
+ */
+ public function lt($date): bool;
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lessThanOrEqualTo()
+ *
+ * @return bool
+ */
+ public function lte($date): bool;
+
+ /**
+ * Register a custom macro.
+ *
+ * @example
+ * ```
+ * $userSettings = [
+ * 'locale' => 'pt',
+ * 'timezone' => 'America/Sao_Paulo',
+ * ];
+ * Carbon::macro('userFormat', function () use ($userSettings) {
+ * return $this->copy()->locale($userSettings['locale'])->tz($userSettings['timezone'])->calendar();
+ * });
+ * echo Carbon::yesterday()->hours(11)->userFormat();
+ * ```
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro);
+
+ /**
+ * Make a Carbon instance from given variable if possible.
+ *
+ * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ *
+ * @param mixed $var
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|null
+ */
+ public static function make($var);
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return static
+ */
+ public function max($date = null);
+
+ /**
+ * Create a Carbon instance for the greatest supported date.
+ *
+ * @return static
+ */
+ public static function maxValue();
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see max()
+ *
+ * @return static
+ */
+ public function maximum($date = null);
+
+ /**
+ * Return the meridiem of the current time in the current locale.
+ *
+ * @param bool $isLower if true, returns lowercase variant if available in the current locale.
+ *
+ * @return string
+ */
+ public function meridiem(bool $isLower = false): string;
+
+ /**
+ * Modify to midday, default to self::$midDayAt
+ *
+ * @return static
+ */
+ public function midDay();
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return static
+ */
+ public function min($date = null);
+
+ /**
+ * Create a Carbon instance for the lowest supported date.
+ *
+ * @return static
+ */
+ public static function minValue();
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see min()
+ *
+ * @return static
+ */
+ public function minimum($date = null);
+
+ /**
+ * Mix another object into the class.
+ *
+ * @example
+ * ```
+ * Carbon::mixin(new class {
+ * public function addMoon() {
+ * return function () {
+ * return $this->addDays(30);
+ * };
+ * }
+ * public function subMoon() {
+ * return function () {
+ * return $this->subDays(30);
+ * };
+ * }
+ * });
+ * $fullMoon = Carbon::create('2018-12-22');
+ * $nextFullMoon = $fullMoon->addMoon();
+ * $blackMoon = Carbon::create('2019-01-06');
+ * $previousBlackMoon = $blackMoon->subMoon();
+ * echo "$nextFullMoon\n";
+ * echo "$previousBlackMoon\n";
+ * ```
+ *
+ * @param object|string $mixin
+ *
+ * @throws ReflectionException
+ *
+ * @return void
+ */
+ public static function mixin($mixin);
+
+ /**
+ * Calls \DateTime::modify if mutable or \DateTimeImmutable::modify else.
+ *
+ * @see https://php.net/manual/en/datetime.modify.php
+ *
+ * @return static|false
+ */
+ #[ReturnTypeWillChange]
+ public function modify($modify);
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->ne(Carbon::parse('2018-07-25 12:45:16')); // false
+ * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see notEqualTo()
+ *
+ * @return bool
+ */
+ public function ne($date): bool;
+
+ /**
+ * Modify to the next occurrence of a given modifier such as a day of
+ * the week. If no modifier is provided, modify to the next occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param string|int|null $modifier
+ *
+ * @return static|false
+ */
+ public function next($modifier = null);
+
+ /**
+ * Go forward to the next weekday.
+ *
+ * @return static
+ */
+ public function nextWeekday();
+
+ /**
+ * Go forward to the next weekend day.
+ *
+ * @return static
+ */
+ public function nextWeekendDay();
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false
+ * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function notEqualTo($date): bool;
+
+ /**
+ * Get a Carbon instance for the current date and time.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function now($tz = null);
+
+ /**
+ * Returns a present instance in the same timezone.
+ *
+ * @return static
+ */
+ public function nowWithSameTz();
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current month. If the calculated occurrence is outside the scope
+ * of the current month, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfMonth($nth, $dayOfWeek);
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current quarter. If the calculated occurrence is outside the scope
+ * of the current quarter, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfQuarter($nth, $dayOfWeek);
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current year. If the calculated occurrence is outside the scope
+ * of the current year, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfYear($nth, $dayOfWeek);
+
+ /**
+ * Return a property with its ordinal.
+ *
+ * @param string $key
+ * @param string|null $period
+ *
+ * @return string
+ */
+ public function ordinal(string $key, ?string $period = null): string;
+
+ /**
+ * Create a carbon instance from a string.
+ *
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ *
+ * @param string|DateTimeInterface|null $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function parse($time = null, $tz = null);
+
+ /**
+ * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.).
+ *
+ * @param string $time date/time string in the given language (may also contain English).
+ * @param string|null $locale if locale is null or not specified, current global locale will be
+ * used instead.
+ * @param DateTimeZone|string|null $tz optional timezone for the new instance.
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function parseFromLocale($time, $locale = null, $tz = null);
+
+ /**
+ * Returns standardized plural of a given singular/plural unit name (in English).
+ *
+ * @param string $unit
+ *
+ * @return string
+ */
+ public static function pluralUnit(string $unit): string;
+
+ /**
+ * Modify to the previous occurrence of a given modifier such as a day of
+ * the week. If no dayOfWeek is provided, modify to the previous occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param string|int|null $modifier
+ *
+ * @return static|false
+ */
+ public function previous($modifier = null);
+
+ /**
+ * Go backward to the previous weekday.
+ *
+ * @return static
+ */
+ public function previousWeekday();
+
+ /**
+ * Go backward to the previous weekend day.
+ *
+ * @return static
+ */
+ public function previousWeekendDay();
+
+ /**
+ * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval).
+ *
+ * @param \DateTimeInterface|Carbon|CarbonImmutable|null $end period end date
+ * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit
+ * @param string|null $unit if specified, $interval must be an integer
+ *
+ * @return CarbonPeriod
+ */
+ public function range($end = null, $interval = null, $unit = null);
+
+ /**
+ * Call native PHP DateTime/DateTimeImmutable add() method.
+ *
+ * @param DateInterval $interval
+ *
+ * @return static
+ */
+ public function rawAdd(DateInterval $interval);
+
+ /**
+ * Create a Carbon instance from a specific format.
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function rawCreateFromFormat($format, $time, $tz = null);
+
+ /**
+ * @see https://php.net/manual/en/datetime.format.php
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function rawFormat($format);
+
+ /**
+ * Create a carbon instance from a string.
+ *
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ *
+ * @param string|DateTimeInterface|null $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function rawParse($time = null, $tz = null);
+
+ /**
+ * Call native PHP DateTime/DateTimeImmutable sub() method.
+ *
+ * @param DateInterval $interval
+ *
+ * @return static
+ */
+ public function rawSub(DateInterval $interval);
+
+ /**
+ * Remove all macros and generic macros.
+ */
+ public static function resetMacros();
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetMonthsOverflow();
+
+ /**
+ * Reset the format used to the default when type juggling a Carbon instance to a string
+ *
+ * @return void
+ */
+ public static function resetToStringFormat();
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetYearsOverflow();
+
+ /**
+ * Round the current instance second with given precision if specified.
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ * @param string $function
+ *
+ * @return CarbonInterface
+ */
+ public function round($precision = 1, $function = 'round');
+
+ /**
+ * Round the current instance at the given unit with given precision if specified and the given function.
+ *
+ * @param string $unit
+ * @param float|int $precision
+ * @param string $function
+ *
+ * @return CarbonInterface
+ */
+ public function roundUnit($unit, $precision = 1, $function = 'round');
+
+ /**
+ * Round the current instance week.
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return CarbonInterface
+ */
+ public function roundWeek($weekStartsAt = null);
+
+ /**
+ * The number of seconds since midnight.
+ *
+ * @return int
+ */
+ public function secondsSinceMidnight();
+
+ /**
+ * The number of seconds until 23:59:59.
+ *
+ * @return int
+ */
+ public function secondsUntilEndOfDay();
+
+ /**
+ * Return a serialized string of the instance.
+ *
+ * @return string
+ */
+ public function serialize();
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather transform Carbon object before the serialization.
+ *
+ * JSON serialize all Carbon instances using the given callback.
+ *
+ * @param callable $callback
+ *
+ * @return void
+ */
+ public static function serializeUsing($callback);
+
+ /**
+ * Set a part of the Carbon object
+ *
+ * @param string|array $name
+ * @param string|int|DateTimeZone $value
+ *
+ * @throws ImmutableException|UnknownSetterException
+ *
+ * @return $this
+ */
+ public function set($name, $value = null);
+
+ /**
+ * Set the date with gregorian year, month and day numbers.
+ *
+ * @see https://php.net/manual/en/datetime.setdate.php
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setDate($year, $month, $day);
+
+ /**
+ * Set the year, month, and date for this instance to that of the passed instance.
+ *
+ * @param Carbon|DateTimeInterface $date now if null
+ *
+ * @return static
+ */
+ public function setDateFrom($date = null);
+
+ /**
+ * Set the date and time all together.
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ * @param int $hour
+ * @param int $minute
+ * @param int $second
+ * @param int $microseconds
+ *
+ * @return static
+ */
+ public function setDateTime($year, $month, $day, $hour, $minute, $second = 0, $microseconds = 0);
+
+ /**
+ * Set the date and time for this instance to that of the passed instance.
+ *
+ * @param Carbon|DateTimeInterface $date
+ *
+ * @return static
+ */
+ public function setDateTimeFrom($date = null);
+
+ /**
+ * Set the day (keeping the current time) to the start of the week + the number of days passed as the first
+ * parameter. First day of week is driven by the locale unless explicitly set with the second parameter.
+ *
+ * @param int $numberOfDays number of days to add after the start of the current week
+ * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week,
+ * if not provided, start of week is inferred from the locale
+ * (Sunday for en_US, Monday for de_DE, etc.)
+ *
+ * @return static
+ */
+ public function setDaysFromStartOfWeek(int $numberOfDays, ?int $weekStartsAt = null);
+
+ /**
+ * Set the fallback locale.
+ *
+ * @see https://symfony.com/doc/current/components/translation.html#fallback-locales
+ *
+ * @param string $locale
+ */
+ public static function setFallbackLocale($locale);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * @param int $humanDiffOptions
+ */
+ public static function setHumanDiffOptions($humanDiffOptions);
+
+ /**
+ * Set a date according to the ISO 8601 standard - using weeks and day offsets rather than specific dates.
+ *
+ * @see https://php.net/manual/en/datetime.setisodate.php
+ *
+ * @param int $year
+ * @param int $week
+ * @param int $day
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setISODate($year, $week, $day = 1);
+
+ /**
+ * Set the translator for the current instance.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ *
+ * @return $this
+ */
+ public function setLocalTranslator(TranslatorInterface $translator);
+
+ /**
+ * Set the current translator locale and indicate if the source locale file exists.
+ * Pass 'auto' as locale to use closest language from the current LC_TIME locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function setLocale($locale);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider mid-day is always 12pm, then if you need to test if it's an other
+ * hour, test it explicitly:
+ * $date->format('G') == 13
+ * or to set explicitly to a given hour:
+ * $date->setTime(13, 0, 0, 0)
+ *
+ * Set midday/noon hour
+ *
+ * @param int $hour midday hour
+ *
+ * @return void
+ */
+ public static function setMidDayAt($hour);
+
+ /**
+ * Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ *
+ * Note the timezone parameter was left out of the examples above and
+ * has no affect as the mock value will be returned regardless of its value.
+ *
+ * Only the moment is mocked with setTestNow(), the timezone will still be the one passed
+ * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()).
+ *
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ *
+ * /!\ Use this method for unit tests only.
+ *
+ * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance
+ */
+ public static function setTestNow($testNow = null);
+
+ /**
+ * Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ *
+ * It will also align default timezone (e.g. call date_default_timezone_set()) with
+ * the second argument or if null, with the timezone of the given date object.
+ *
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ *
+ * /!\ Use this method for unit tests only.
+ *
+ * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance
+ */
+ public static function setTestNowAndTimezone($testNow = null, $tz = null);
+
+ /**
+ * Resets the current time of the DateTime object to a different time.
+ *
+ * @see https://php.net/manual/en/datetime.settime.php
+ *
+ * @param int $hour
+ * @param int $minute
+ * @param int $second
+ * @param int $microseconds
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setTime($hour, $minute, $second = 0, $microseconds = 0);
+
+ /**
+ * Set the hour, minute, second and microseconds for this instance to that of the passed instance.
+ *
+ * @param Carbon|DateTimeInterface $date now if null
+ *
+ * @return static
+ */
+ public function setTimeFrom($date = null);
+
+ /**
+ * Set the time by time string.
+ *
+ * @param string $time
+ *
+ * @return static
+ */
+ public function setTimeFromTimeString($time);
+
+ /**
+ * Set the instance's timestamp.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $unixTimestamp
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setTimestamp($unixTimestamp);
+
+ /**
+ * Set the instance's timezone from a string or object.
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setTimezone($value);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and
+ * use other method or custom format passed to format() method if you need to dump another string
+ * format.
+ *
+ * Set the default format used when type juggling a Carbon instance to a string.
+ *
+ * @param string|Closure|null $format
+ *
+ * @return void
+ */
+ public static function setToStringFormat($format);
+
+ /**
+ * Set the default translator instance to use.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ *
+ * @return void
+ */
+ public static function setTranslator(TranslatorInterface $translator);
+
+ /**
+ * Set specified unit to new given value.
+ *
+ * @param string $unit year, month, day, hour, minute, second or microsecond
+ * @param int $value new value for given unit
+ *
+ * @return static
+ */
+ public function setUnit($unit, $value = null);
+
+ /**
+ * Set any unit to a new value without overflowing current other unit given.
+ *
+ * @param string $valueUnit unit name to modify
+ * @param int $value new value for the input unit
+ * @param string $overflowUnit unit name to not overflow
+ *
+ * @return static
+ */
+ public function setUnitNoOverflow($valueUnit, $value, $overflowUnit);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use UTF-8 language packages on every machine.
+ *
+ * Set if UTF8 will be used for localized date/time.
+ *
+ * @param bool $utf8
+ */
+ public static function setUtf8($utf8);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek
+ * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the
+ * start of week according to current locale selected and implicitly the end of week.
+ *
+ * Set the last day of week
+ *
+ * @param int|string $day week end day (or 'auto' to get the day before the first day of week
+ * from Carbon::getLocale() culture).
+ *
+ * @return void
+ */
+ public static function setWeekEndsAt($day);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the
+ * 'first_day_of_week' locale setting to change the start of week according to current locale
+ * selected and implicitly the end of week.
+ *
+ * Set the first day of week
+ *
+ * @param int|string $day week start day (or 'auto' to get the first day of week from Carbon::getLocale() culture).
+ *
+ * @return void
+ */
+ public static function setWeekStartsAt($day);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider week-end is always saturday and sunday, and if you have some custom
+ * week-end days to handle, give to those days an other name and create a macro for them:
+ *
+ * ```
+ * Carbon::macro('isDayOff', function ($date) {
+ * return $date->isSunday() || $date->isMonday();
+ * });
+ * Carbon::macro('isNotDayOff', function ($date) {
+ * return !$date->isDayOff();
+ * });
+ * if ($someDate->isDayOff()) ...
+ * if ($someDate->isNotDayOff()) ...
+ * // Add 5 not-off days
+ * $count = 5;
+ * while ($someDate->isDayOff() || ($count-- > 0)) {
+ * $someDate->addDay();
+ * }
+ * ```
+ *
+ * Set weekend days
+ *
+ * @param array $days
+ *
+ * @return void
+ */
+ public static function setWeekendDays($days);
+
+ /**
+ * Set specific options.
+ * - strictMode: true|false|null
+ * - monthOverflow: true|false|null
+ * - yearOverflow: true|false|null
+ * - humanDiffOptions: int|null
+ * - toStringFormat: string|Closure|null
+ * - toJsonFormat: string|Closure|null
+ * - locale: string|null
+ * - timezone: \DateTimeZone|string|int|null
+ * - macros: array|null
+ * - genericMacros: array|null
+ *
+ * @param array $settings
+ *
+ * @return $this|static
+ */
+ public function settings(array $settings);
+
+ /**
+ * Set the instance's timezone from a string or object and add/subtract the offset difference.
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function shiftTimezone($value);
+
+ /**
+ * Get the month overflow global behavior (can be overridden in specific instances).
+ *
+ * @return bool
+ */
+ public static function shouldOverflowMonths();
+
+ /**
+ * Get the month overflow global behavior (can be overridden in specific instances).
+ *
+ * @return bool
+ */
+ public static function shouldOverflowYears();
+
+ /**
+ * @alias diffForHumans
+ *
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ */
+ public function since($other = null, $syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Returns standardized singular of a given singular/plural unit name (in English).
+ *
+ * @param string $unit
+ *
+ * @return string
+ */
+ public static function singularUnit(string $unit): string;
+
+ /**
+ * Modify to start of current given unit.
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->startOf('month')
+ * ->endOf('week', Carbon::FRIDAY);
+ * ```
+ *
+ * @param string $unit
+ * @param array $params
+ *
+ * @return static
+ */
+ public function startOf($unit, ...$params);
+
+ /**
+ * Resets the date to the first day of the century and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfCentury();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfCentury();
+
+ /**
+ * Resets the time to 00:00:00 start of day
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfDay();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfDay();
+
+ /**
+ * Resets the date to the first day of the decade and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfDecade();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfDecade();
+
+ /**
+ * Modify to start of current hour, minutes and seconds become 0
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfHour();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfHour();
+
+ /**
+ * Resets the date to the first day of the millennium and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfMillennium();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfMillennium();
+
+ /**
+ * Modify to start of current minute, seconds become 0
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfMinute();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfMinute();
+
+ /**
+ * Resets the date to the first day of the month and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfMonth();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfMonth();
+
+ /**
+ * Resets the date to the first day of the quarter and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfQuarter();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfQuarter();
+
+ /**
+ * Modify to start of current second, microseconds become 0
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->startOfSecond()
+ * ->format('H:i:s.u');
+ * ```
+ *
+ * @return static
+ */
+ public function startOfSecond();
+
+ /**
+ * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->startOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek(Carbon::SUNDAY) . "\n";
+ * ```
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return static
+ */
+ public function startOfWeek($weekStartsAt = null);
+
+ /**
+ * Resets the date to the first day of the year and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfYear();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfYear();
+
+ /**
+ * Subtract given units or interval to the current instance.
+ *
+ * @example $date->sub('hour', 3)
+ * @example $date->sub(15, 'days')
+ * @example $date->sub(CarbonInterval::days(4))
+ *
+ * @param string|DateInterval|Closure|CarbonConverterInterface $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function sub($unit, $value = 1, $overflow = null);
+
+ public function subRealUnit($unit, $value = 1);
+
+ /**
+ * Subtract given units to the current instance.
+ *
+ * @param string $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ public function subUnit($unit, $value = 1, $overflow = null);
+
+ /**
+ * Subtract any unit to a new value without overflowing current other unit given.
+ *
+ * @param string $valueUnit unit name to modify
+ * @param int $value amount to subtract to the input unit
+ * @param string $overflowUnit unit name to not overflow
+ *
+ * @return static
+ */
+ public function subUnitNoOverflow($valueUnit, $value, $overflowUnit);
+
+ /**
+ * Subtract given units or interval to the current instance.
+ *
+ * @see sub()
+ *
+ * @param string|DateInterval $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ public function subtract($unit, $value = 1, $overflow = null);
+
+ /**
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ *
+ * @return string
+ */
+ public function timespan($other = null, $timezone = null);
+
+ /**
+ * Set the instance's timestamp.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $unixTimestamp
+ *
+ * @return static
+ */
+ public function timestamp($unixTimestamp);
+
+ /**
+ * @alias setTimezone
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function timezone($value);
+
+ /**
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given (or now if null given) to current instance.
+ *
+ * When comparing a value in the past to default now:
+ * 1 hour from now
+ * 5 months from now
+ *
+ * When comparing a value in the future to default now:
+ * 1 hour ago
+ * 5 months ago
+ *
+ * When comparing a value in the past to another value:
+ * 1 hour after
+ * 5 months after
+ *
+ * When comparing a value in the future to another value:
+ * 1 hour before
+ * 5 months before
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function to($other = null, $syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Get default array representation.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toArray());
+ * ```
+ *
+ * @return array
+ */
+ public function toArray();
+
+ /**
+ * Format the instance as ATOM
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toAtomString();
+ * ```
+ *
+ * @return string
+ */
+ public function toAtomString();
+
+ /**
+ * Format the instance as COOKIE
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toCookieString();
+ * ```
+ *
+ * @return string
+ */
+ public function toCookieString();
+
+ /**
+ * @alias toDateTime
+ *
+ * Return native DateTime PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toDate());
+ * ```
+ *
+ * @return DateTime
+ */
+ public function toDate();
+
+ /**
+ * Format the instance as date
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDateString();
+ * ```
+ *
+ * @return string
+ */
+ public function toDateString();
+
+ /**
+ * Return native DateTime PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toDateTime());
+ * ```
+ *
+ * @return DateTime
+ */
+ public function toDateTime();
+
+ /**
+ * Return native toDateTimeImmutable PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toDateTimeImmutable());
+ * ```
+ *
+ * @return DateTimeImmutable
+ */
+ public function toDateTimeImmutable();
+
+ /**
+ * Format the instance as date and time T-separated with no timezone
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDateTimeLocalString();
+ * echo "\n";
+ * echo Carbon::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toDateTimeLocalString($unitPrecision = 'second');
+
+ /**
+ * Format the instance as date and time
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDateTimeString();
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toDateTimeString($unitPrecision = 'second');
+
+ /**
+ * Format the instance with day, date and time
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDayDateTimeString();
+ * ```
+ *
+ * @return string
+ */
+ public function toDayDateTimeString();
+
+ /**
+ * Format the instance as a readable date
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toFormattedDateString();
+ * ```
+ *
+ * @return string
+ */
+ public function toFormattedDateString();
+
+ /**
+ * Format the instance with the day, and a readable date
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toFormattedDayDateString();
+ * ```
+ *
+ * @return string
+ */
+ public function toFormattedDayDateString(): string;
+
+ /**
+ * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z, if $keepOffset truthy, offset will be kept:
+ * 1977-04-22T01:00:00-05:00).
+ *
+ * @example
+ * ```
+ * echo Carbon::now('America/Toronto')->toISOString() . "\n";
+ * echo Carbon::now('America/Toronto')->toISOString(true) . "\n";
+ * ```
+ *
+ * @param bool $keepOffset Pass true to keep the date offset. Else forced to UTC.
+ *
+ * @return null|string
+ */
+ public function toISOString($keepOffset = false);
+
+ /**
+ * Return a immutable copy of the instance.
+ *
+ * @return CarbonImmutable
+ */
+ public function toImmutable();
+
+ /**
+ * Format the instance as ISO8601
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toIso8601String();
+ * ```
+ *
+ * @return string
+ */
+ public function toIso8601String();
+
+ /**
+ * Convert the instance to UTC and return as Zulu ISO8601
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toIso8601ZuluString();
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toIso8601ZuluString($unitPrecision = 'second');
+
+ /**
+ * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z) with UTC timezone.
+ *
+ * @example
+ * ```
+ * echo Carbon::now('America/Toronto')->toJSON();
+ * ```
+ *
+ * @return null|string
+ */
+ public function toJSON();
+
+ /**
+ * Return a mutable copy of the instance.
+ *
+ * @return Carbon
+ */
+ public function toMutable();
+
+ /**
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given to now
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single part)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function toNow($syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * Get default object representation.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toObject());
+ * ```
+ *
+ * @return object
+ */
+ public function toObject();
+
+ /**
+ * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval).
+ *
+ * @param \DateTimeInterface|Carbon|CarbonImmutable|int|null $end period end date or recurrences count if int
+ * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit
+ * @param string|null $unit if specified, $interval must be an integer
+ *
+ * @return CarbonPeriod
+ */
+ public function toPeriod($end = null, $interval = null, $unit = null);
+
+ /**
+ * Format the instance as RFC1036
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc1036String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc1036String();
+
+ /**
+ * Format the instance as RFC1123
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc1123String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc1123String();
+
+ /**
+ * Format the instance as RFC2822
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc2822String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc2822String();
+
+ /**
+ * Format the instance as RFC3339
+ *
+ * @param bool $extended
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc3339String() . "\n";
+ * echo Carbon::now()->toRfc3339String(true) . "\n";
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc3339String($extended = false);
+
+ /**
+ * Format the instance as RFC7231
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc7231String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc7231String();
+
+ /**
+ * Format the instance as RFC822
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc822String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc822String();
+
+ /**
+ * Format the instance as RFC850
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc850String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc850String();
+
+ /**
+ * Format the instance as RSS
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRssString();
+ * ```
+ *
+ * @return string
+ */
+ public function toRssString();
+
+ /**
+ * Returns english human readable complete date string.
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toString();
+ * ```
+ *
+ * @return string
+ */
+ public function toString();
+
+ /**
+ * Format the instance as time
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toTimeString();
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toTimeString($unitPrecision = 'second');
+
+ /**
+ * Format the instance as W3C
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toW3cString();
+ * ```
+ *
+ * @return string
+ */
+ public function toW3cString();
+
+ /**
+ * Create a Carbon instance for today.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function today($tz = null);
+
+ /**
+ * Create a Carbon instance for tomorrow.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function tomorrow($tz = null);
+
+ /**
+ * Translate using translation string or callback available.
+ *
+ * @param string $key
+ * @param array $parameters
+ * @param string|int|float|null $number
+ * @param \Symfony\Component\Translation\TranslatorInterface|null $translator
+ * @param bool $altNumbers
+ *
+ * @return string
+ */
+ public function translate(string $key, array $parameters = [], $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string;
+
+ /**
+ * Returns the alternative number for a given integer if available in the current locale.
+ *
+ * @param int $number
+ *
+ * @return string
+ */
+ public function translateNumber(int $number): string;
+
+ /**
+ * Translate a time string from a locale to an other.
+ *
+ * @param string $timeString date/time/duration string to translate (may also contain English)
+ * @param string|null $from input locale of the $timeString parameter (`Carbon::getLocale()` by default)
+ * @param string|null $to output locale of the result returned (`"en"` by default)
+ * @param int $mode specify what to translate with options:
+ * - self::TRANSLATE_ALL (default)
+ * - CarbonInterface::TRANSLATE_MONTHS
+ * - CarbonInterface::TRANSLATE_DAYS
+ * - CarbonInterface::TRANSLATE_UNITS
+ * - CarbonInterface::TRANSLATE_MERIDIEM
+ * You can use pipe to group: CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS
+ *
+ * @return string
+ */
+ public static function translateTimeString($timeString, $from = null, $to = null, $mode = self::TRANSLATE_ALL);
+
+ /**
+ * Translate a time string from the current locale (`$date->locale()`) to an other.
+ *
+ * @param string $timeString time string to translate
+ * @param string|null $to output locale of the result returned ("en" by default)
+ *
+ * @return string
+ */
+ public function translateTimeStringTo($timeString, $to = null);
+
+ /**
+ * Translate using translation string or callback available.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ * @param string $key
+ * @param array $parameters
+ * @param null $number
+ *
+ * @return string
+ */
+ public static function translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null): string;
+
+ /**
+ * Format as ->format() do (using date replacements patterns from https://php.net/manual/en/function.date.php)
+ * but translate words whenever possible (months, day names, etc.) using the current locale.
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function translatedFormat(string $format): string;
+
+ /**
+ * Set the timezone or returns the timezone name if no arguments passed.
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static|string
+ */
+ public function tz($value = null);
+
+ /**
+ * @alias getTimestamp
+ *
+ * Returns the UNIX timestamp for the current date.
+ *
+ * @return int
+ */
+ public function unix();
+
+ /**
+ * @alias to
+ *
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given (or now if null given) to current instance.
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function until($other = null, $syntax = null, $short = false, $parts = 1, $options = null);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Indicates if months should be calculated with overflow.
+ *
+ * @param bool $monthsOverflow
+ *
+ * @return void
+ */
+ public static function useMonthsOverflow($monthsOverflow = true);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * Enable the strict mode (or disable with passing false).
+ *
+ * @param bool $strictModeEnabled
+ */
+ public static function useStrictMode($strictModeEnabled = true);
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Indicates if years should be calculated with overflow.
+ *
+ * @param bool $yearsOverflow
+ *
+ * @return void
+ */
+ public static function useYearsOverflow($yearsOverflow = true);
+
+ /**
+ * Set the instance's timezone to UTC.
+ *
+ * @return static
+ */
+ public function utc();
+
+ /**
+ * Returns the minutes offset to UTC if no arguments passed, else set the timezone with given minutes shift passed.
+ *
+ * @param int|null $minuteOffset
+ *
+ * @return int|static
+ */
+ public function utcOffset(?int $minuteOffset = null);
+
+ /**
+ * Returns the milliseconds timestamps used amongst other by Date javascript objects.
+ *
+ * @return float
+ */
+ public function valueOf();
+
+ /**
+ * Get/set the week number using given first day of week and first
+ * day of year included in the first week. Or use US format if no settings
+ * given (Sunday / Jan 6).
+ *
+ * @param int|null $week
+ * @param int|null $dayOfWeek
+ * @param int|null $dayOfYear
+ *
+ * @return int|static
+ */
+ public function week($week = null, $dayOfWeek = null, $dayOfYear = null);
+
+ /**
+ * Set/get the week number of year using given first day of week and first
+ * day of year included in the first week. Or use US format if no settings
+ * given (Sunday / Jan 6).
+ *
+ * @param int|null $year if null, act as a getter, if not null, set the year and return current instance.
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int|static
+ */
+ public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null);
+
+ /**
+ * Get/set the weekday from 0 (Sunday) to 6 (Saturday).
+ *
+ * @param int|null $value new value for weekday if using as setter.
+ *
+ * @return static|int
+ */
+ public function weekday($value = null);
+
+ /**
+ * Get the number of weeks of the current week-year using given first day of week and first
+ * day of year included in the first week. Or use US format if no settings
+ * given (Sunday / Jan 6).
+ *
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int
+ */
+ public function weeksInYear($dayOfWeek = null, $dayOfYear = null);
+
+ /**
+ * Temporarily sets a static date to be used within the callback.
+ * Using setTestNow to set the date, executing the callback, then
+ * clearing the test instance.
+ *
+ * /!\ Use this method for unit tests only.
+ *
+ * @template T
+ *
+ * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance
+ * @param Closure(): T $callback
+ *
+ * @return mixed
+ * @phpstan-return T
+ */
+ public static function withTestNow($testNow, $callback);
+
+ /**
+ * Create a Carbon instance for yesterday.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function yesterday($tz = null);
+
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php
new file mode 100644
index 0000000..8437c54
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonInterval.php
@@ -0,0 +1,3054 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Exceptions\BadFluentConstructorException;
+use Carbon\Exceptions\BadFluentSetterException;
+use Carbon\Exceptions\InvalidCastException;
+use Carbon\Exceptions\InvalidIntervalException;
+use Carbon\Exceptions\OutOfRangeException;
+use Carbon\Exceptions\ParseErrorException;
+use Carbon\Exceptions\UnitNotConfiguredException;
+use Carbon\Exceptions\UnknownGetterException;
+use Carbon\Exceptions\UnknownSetterException;
+use Carbon\Exceptions\UnknownUnitException;
+use Carbon\Traits\IntervalRounding;
+use Carbon\Traits\IntervalStep;
+use Carbon\Traits\MagicParameter;
+use Carbon\Traits\Mixin;
+use Carbon\Traits\Options;
+use Carbon\Traits\ToStringFormat;
+use Closure;
+use DateInterval;
+use DateMalformedIntervalStringException;
+use DateTimeInterface;
+use DateTimeZone;
+use Exception;
+use InvalidArgumentException;
+use ReflectionException;
+use ReturnTypeWillChange;
+use RuntimeException;
+use Throwable;
+
+/**
+ * A simple API extension for DateInterval.
+ * The implementation provides helpers to handle weeks but only days are saved.
+ * Weeks are calculated based on the total days of the current instance.
+ *
+ * @property int $years Total years of the current interval.
+ * @property int $months Total months of the current interval.
+ * @property int $weeks Total weeks of the current interval calculated from the days.
+ * @property int $dayz Total days of the current interval (weeks * 7 + days).
+ * @property int $hours Total hours of the current interval.
+ * @property int $minutes Total minutes of the current interval.
+ * @property int $seconds Total seconds of the current interval.
+ * @property int $microseconds Total microseconds of the current interval.
+ * @property int $milliseconds Total milliseconds of the current interval.
+ * @property int $microExcludeMilli Remaining microseconds without the milliseconds.
+ * @property int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
+ * @property int $daysExcludeWeeks alias of dayzExcludeWeeks
+ * @property-read float $totalYears Number of years equivalent to the interval.
+ * @property-read float $totalMonths Number of months equivalent to the interval.
+ * @property-read float $totalWeeks Number of weeks equivalent to the interval.
+ * @property-read float $totalDays Number of days equivalent to the interval.
+ * @property-read float $totalDayz Alias for totalDays.
+ * @property-read float $totalHours Number of hours equivalent to the interval.
+ * @property-read float $totalMinutes Number of minutes equivalent to the interval.
+ * @property-read float $totalSeconds Number of seconds equivalent to the interval.
+ * @property-read float $totalMilliseconds Number of milliseconds equivalent to the interval.
+ * @property-read float $totalMicroseconds Number of microseconds equivalent to the interval.
+ * @property-read string $locale locale of the current instance
+ *
+ * @method static CarbonInterval years($years = 1) Create instance specifying a number of years or modify the number of years if called on an instance.
+ * @method static CarbonInterval year($years = 1) Alias for years()
+ * @method static CarbonInterval months($months = 1) Create instance specifying a number of months or modify the number of months if called on an instance.
+ * @method static CarbonInterval month($months = 1) Alias for months()
+ * @method static CarbonInterval weeks($weeks = 1) Create instance specifying a number of weeks or modify the number of weeks if called on an instance.
+ * @method static CarbonInterval week($weeks = 1) Alias for weeks()
+ * @method static CarbonInterval days($days = 1) Create instance specifying a number of days or modify the number of days if called on an instance.
+ * @method static CarbonInterval dayz($days = 1) Alias for days()
+ * @method static CarbonInterval daysExcludeWeeks($days = 1) Create instance specifying a number of days or modify the number of days (keeping the current number of weeks) if called on an instance.
+ * @method static CarbonInterval dayzExcludeWeeks($days = 1) Alias for daysExcludeWeeks()
+ * @method static CarbonInterval day($days = 1) Alias for days()
+ * @method static CarbonInterval hours($hours = 1) Create instance specifying a number of hours or modify the number of hours if called on an instance.
+ * @method static CarbonInterval hour($hours = 1) Alias for hours()
+ * @method static CarbonInterval minutes($minutes = 1) Create instance specifying a number of minutes or modify the number of minutes if called on an instance.
+ * @method static CarbonInterval minute($minutes = 1) Alias for minutes()
+ * @method static CarbonInterval seconds($seconds = 1) Create instance specifying a number of seconds or modify the number of seconds if called on an instance.
+ * @method static CarbonInterval second($seconds = 1) Alias for seconds()
+ * @method static CarbonInterval milliseconds($milliseconds = 1) Create instance specifying a number of milliseconds or modify the number of milliseconds if called on an instance.
+ * @method static CarbonInterval millisecond($milliseconds = 1) Alias for milliseconds()
+ * @method static CarbonInterval microseconds($microseconds = 1) Create instance specifying a number of microseconds or modify the number of microseconds if called on an instance.
+ * @method static CarbonInterval microsecond($microseconds = 1) Alias for microseconds()
+ * @method $this addYears(int $years) Add given number of years to the current interval
+ * @method $this subYears(int $years) Subtract given number of years to the current interval
+ * @method $this addMonths(int $months) Add given number of months to the current interval
+ * @method $this subMonths(int $months) Subtract given number of months to the current interval
+ * @method $this addWeeks(int|float $weeks) Add given number of weeks to the current interval
+ * @method $this subWeeks(int|float $weeks) Subtract given number of weeks to the current interval
+ * @method $this addDays(int|float $days) Add given number of days to the current interval
+ * @method $this subDays(int|float $days) Subtract given number of days to the current interval
+ * @method $this addHours(int|float $hours) Add given number of hours to the current interval
+ * @method $this subHours(int|float $hours) Subtract given number of hours to the current interval
+ * @method $this addMinutes(int|float $minutes) Add given number of minutes to the current interval
+ * @method $this subMinutes(int|float $minutes) Subtract given number of minutes to the current interval
+ * @method $this addSeconds(int|float $seconds) Add given number of seconds to the current interval
+ * @method $this subSeconds(int|float $seconds) Subtract given number of seconds to the current interval
+ * @method $this addMilliseconds(int|float $milliseconds) Add given number of milliseconds to the current interval
+ * @method $this subMilliseconds(int|float $milliseconds) Subtract given number of milliseconds to the current interval
+ * @method $this addMicroseconds(int|float $microseconds) Add given number of microseconds to the current interval
+ * @method $this subMicroseconds(int|float $microseconds) Subtract given number of microseconds to the current interval
+ * @method $this roundYear(int|float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method $this roundYears(int|float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method $this floorYear(int|float $precision = 1) Truncate the current instance year with given precision.
+ * @method $this floorYears(int|float $precision = 1) Truncate the current instance year with given precision.
+ * @method $this ceilYear(int|float $precision = 1) Ceil the current instance year with given precision.
+ * @method $this ceilYears(int|float $precision = 1) Ceil the current instance year with given precision.
+ * @method $this roundMonth(int|float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method $this roundMonths(int|float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method $this floorMonth(int|float $precision = 1) Truncate the current instance month with given precision.
+ * @method $this floorMonths(int|float $precision = 1) Truncate the current instance month with given precision.
+ * @method $this ceilMonth(int|float $precision = 1) Ceil the current instance month with given precision.
+ * @method $this ceilMonths(int|float $precision = 1) Ceil the current instance month with given precision.
+ * @method $this roundWeek(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this roundWeeks(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this floorWeek(int|float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this floorWeeks(int|float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this ceilWeek(int|float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this ceilWeeks(int|float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this roundDay(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this roundDays(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this floorDay(int|float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this floorDays(int|float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this ceilDay(int|float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this ceilDays(int|float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this roundHour(int|float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method $this roundHours(int|float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method $this floorHour(int|float $precision = 1) Truncate the current instance hour with given precision.
+ * @method $this floorHours(int|float $precision = 1) Truncate the current instance hour with given precision.
+ * @method $this ceilHour(int|float $precision = 1) Ceil the current instance hour with given precision.
+ * @method $this ceilHours(int|float $precision = 1) Ceil the current instance hour with given precision.
+ * @method $this roundMinute(int|float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method $this roundMinutes(int|float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method $this floorMinute(int|float $precision = 1) Truncate the current instance minute with given precision.
+ * @method $this floorMinutes(int|float $precision = 1) Truncate the current instance minute with given precision.
+ * @method $this ceilMinute(int|float $precision = 1) Ceil the current instance minute with given precision.
+ * @method $this ceilMinutes(int|float $precision = 1) Ceil the current instance minute with given precision.
+ * @method $this roundSecond(int|float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method $this roundSeconds(int|float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method $this floorSecond(int|float $precision = 1) Truncate the current instance second with given precision.
+ * @method $this floorSeconds(int|float $precision = 1) Truncate the current instance second with given precision.
+ * @method $this ceilSecond(int|float $precision = 1) Ceil the current instance second with given precision.
+ * @method $this ceilSeconds(int|float $precision = 1) Ceil the current instance second with given precision.
+ * @method $this roundMillennium(int|float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method $this roundMillennia(int|float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method $this floorMillennium(int|float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method $this floorMillennia(int|float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method $this ceilMillennium(int|float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method $this ceilMillennia(int|float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method $this roundCentury(int|float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method $this roundCenturies(int|float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method $this floorCentury(int|float $precision = 1) Truncate the current instance century with given precision.
+ * @method $this floorCenturies(int|float $precision = 1) Truncate the current instance century with given precision.
+ * @method $this ceilCentury(int|float $precision = 1) Ceil the current instance century with given precision.
+ * @method $this ceilCenturies(int|float $precision = 1) Ceil the current instance century with given precision.
+ * @method $this roundDecade(int|float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method $this roundDecades(int|float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method $this floorDecade(int|float $precision = 1) Truncate the current instance decade with given precision.
+ * @method $this floorDecades(int|float $precision = 1) Truncate the current instance decade with given precision.
+ * @method $this ceilDecade(int|float $precision = 1) Ceil the current instance decade with given precision.
+ * @method $this ceilDecades(int|float $precision = 1) Ceil the current instance decade with given precision.
+ * @method $this roundQuarter(int|float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method $this roundQuarters(int|float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method $this floorQuarter(int|float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method $this floorQuarters(int|float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method $this ceilQuarter(int|float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method $this ceilQuarters(int|float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method $this roundMillisecond(int|float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method $this roundMilliseconds(int|float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method $this floorMillisecond(int|float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method $this floorMilliseconds(int|float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method $this ceilMillisecond(int|float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method $this ceilMilliseconds(int|float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method $this roundMicrosecond(int|float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method $this roundMicroseconds(int|float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method $this floorMicrosecond(int|float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method $this floorMicroseconds(int|float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method $this ceilMicrosecond(int|float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method $this ceilMicroseconds(int|float $precision = 1) Ceil the current instance microsecond with given precision.
+ */
+class CarbonInterval extends DateInterval implements CarbonConverterInterface
+{
+ use IntervalRounding;
+ use IntervalStep;
+ use MagicParameter;
+ use Mixin {
+ Mixin::mixin as baseMixin;
+ }
+ use Options;
+ use ToStringFormat;
+
+ /**
+ * Interval spec period designators
+ */
+ public const PERIOD_PREFIX = 'P';
+ public const PERIOD_YEARS = 'Y';
+ public const PERIOD_MONTHS = 'M';
+ public const PERIOD_DAYS = 'D';
+ public const PERIOD_TIME_PREFIX = 'T';
+ public const PERIOD_HOURS = 'H';
+ public const PERIOD_MINUTES = 'M';
+ public const PERIOD_SECONDS = 'S';
+
+ /**
+ * A translator to ... er ... translate stuff
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static $translator;
+
+ /**
+ * @var array|null
+ */
+ protected static $cascadeFactors;
+
+ /**
+ * @var array
+ */
+ protected static $formats = [
+ 'y' => 'y',
+ 'Y' => 'y',
+ 'o' => 'y',
+ 'm' => 'm',
+ 'n' => 'm',
+ 'W' => 'weeks',
+ 'd' => 'd',
+ 'j' => 'd',
+ 'z' => 'd',
+ 'h' => 'h',
+ 'g' => 'h',
+ 'H' => 'h',
+ 'G' => 'h',
+ 'i' => 'i',
+ 's' => 's',
+ 'u' => 'micro',
+ 'v' => 'milli',
+ ];
+
+ /**
+ * @var array|null
+ */
+ private static $flipCascadeFactors;
+
+ /**
+ * @var bool
+ */
+ private static $floatSettersEnabled = false;
+
+ /**
+ * The registered macros.
+ *
+ * @var array
+ */
+ protected static $macros = [];
+
+ /**
+ * Timezone handler for settings() method.
+ *
+ * @var mixed
+ */
+ protected $tzName;
+
+ /**
+ * Set the instance's timezone from a string or object.
+ *
+ * @param \DateTimeZone|string $tzName
+ *
+ * @return static
+ */
+ public function setTimezone($tzName)
+ {
+ $this->tzName = $tzName;
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ *
+ * Set the instance's timezone from a string or object and add/subtract the offset difference.
+ *
+ * @param \DateTimeZone|string $tzName
+ *
+ * @return static
+ */
+ public function shiftTimezone($tzName)
+ {
+ $this->tzName = $tzName;
+
+ return $this;
+ }
+
+ /**
+ * Mapping of units and factors for cascading.
+ *
+ * Should only be modified by changing the factors or referenced constants.
+ *
+ * @return array
+ */
+ public static function getCascadeFactors()
+ {
+ return static::$cascadeFactors ?: static::getDefaultCascadeFactors();
+ }
+
+ protected static function getDefaultCascadeFactors(): array
+ {
+ return [
+ 'milliseconds' => [Carbon::MICROSECONDS_PER_MILLISECOND, 'microseconds'],
+ 'seconds' => [Carbon::MILLISECONDS_PER_SECOND, 'milliseconds'],
+ 'minutes' => [Carbon::SECONDS_PER_MINUTE, 'seconds'],
+ 'hours' => [Carbon::MINUTES_PER_HOUR, 'minutes'],
+ 'dayz' => [Carbon::HOURS_PER_DAY, 'hours'],
+ 'weeks' => [Carbon::DAYS_PER_WEEK, 'dayz'],
+ 'months' => [Carbon::WEEKS_PER_MONTH, 'weeks'],
+ 'years' => [Carbon::MONTHS_PER_YEAR, 'months'],
+ ];
+ }
+
+ private static function standardizeUnit($unit)
+ {
+ $unit = rtrim($unit, 'sz').'s';
+
+ return $unit === 'days' ? 'dayz' : $unit;
+ }
+
+ private static function getFlipCascadeFactors()
+ {
+ if (!self::$flipCascadeFactors) {
+ self::$flipCascadeFactors = [];
+
+ foreach (static::getCascadeFactors() as $to => [$factor, $from]) {
+ self::$flipCascadeFactors[self::standardizeUnit($from)] = [self::standardizeUnit($to), $factor];
+ }
+ }
+
+ return self::$flipCascadeFactors;
+ }
+
+ /**
+ * Set default cascading factors for ->cascade() method.
+ *
+ * @param array $cascadeFactors
+ */
+ public static function setCascadeFactors(array $cascadeFactors)
+ {
+ self::$flipCascadeFactors = null;
+ static::$cascadeFactors = $cascadeFactors;
+ }
+
+ /**
+ * This option allow you to opt-in for the Carbon 3 behavior where float
+ * values will no longer be cast to integer (so truncated).
+ *
+ * ⚠️ This settings will be applied globally, which mean your whole application
+ * code including the third-party dependencies that also may use Carbon will
+ * adopt the new behavior.
+ */
+ public static function enableFloatSetters(bool $floatSettersEnabled = true): void
+ {
+ self::$floatSettersEnabled = $floatSettersEnabled;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //////////////////////////// CONSTRUCTORS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Create a new CarbonInterval instance.
+ *
+ * @param Closure|DateInterval|string|int|null $years
+ * @param int|float|null $months
+ * @param int|float|null $weeks
+ * @param int|float|null $days
+ * @param int|float|null $hours
+ * @param int|float|null $minutes
+ * @param int|float|null $seconds
+ * @param int|float|null $microseconds
+ *
+ * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval.
+ */
+ public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null)
+ {
+ if ($years instanceof Closure) {
+ $this->step = $years;
+ $years = null;
+ }
+
+ if ($years instanceof DateInterval) {
+ parent::__construct(static::getDateIntervalSpec($years));
+ $this->f = $years->f;
+ self::copyNegativeUnits($years, $this);
+
+ return;
+ }
+
+ $spec = $years;
+ $isStringSpec = (\is_string($spec) && !preg_match('/^[\d.]/', $spec));
+
+ if (!$isStringSpec || (float) $years) {
+ $spec = static::PERIOD_PREFIX;
+
+ $spec .= $years > 0 ? $years.static::PERIOD_YEARS : '';
+ $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : '';
+
+ $specDays = 0;
+ $specDays += $weeks > 0 ? $weeks * static::getDaysPerWeek() : 0;
+ $specDays += $days > 0 ? $days : 0;
+
+ $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : '';
+
+ if ($hours > 0 || $minutes > 0 || $seconds > 0) {
+ $spec .= static::PERIOD_TIME_PREFIX;
+ $spec .= $hours > 0 ? $hours.static::PERIOD_HOURS : '';
+ $spec .= $minutes > 0 ? $minutes.static::PERIOD_MINUTES : '';
+ $spec .= $seconds > 0 ? $seconds.static::PERIOD_SECONDS : '';
+ }
+
+ if ($spec === static::PERIOD_PREFIX) {
+ // Allow the zero interval.
+ $spec .= '0'.static::PERIOD_YEARS;
+ }
+ }
+
+ try {
+ parent::__construct($spec);
+ } catch (Throwable $exception) {
+ try {
+ parent::__construct('PT0S');
+
+ if ($isStringSpec) {
+ if (!preg_match('/^P
+ (?:(?[+-]?\d*(?:\.\d+)?)Y)?
+ (?:(?[+-]?\d*(?:\.\d+)?)M)?
+ (?:(?[+-]?\d*(?:\.\d+)?)W)?
+ (?:(?[+-]?\d*(?:\.\d+)?)D)?
+ (?:T
+ (?:(?[+-]?\d*(?:\.\d+)?)H)?
+ (?:(?[+-]?\d*(?:\.\d+)?)M)?
+ (?:(?[+-]?\d*(?:\.\d+)?)S)?
+ )?
+ $/x', $spec, $match)) {
+ throw new InvalidArgumentException("Invalid duration: $spec");
+ }
+
+ $years = (float) ($match['year'] ?? 0);
+ $this->assertSafeForInteger('year', $years);
+ $months = (float) ($match['month'] ?? 0);
+ $this->assertSafeForInteger('month', $months);
+ $weeks = (float) ($match['week'] ?? 0);
+ $this->assertSafeForInteger('week', $weeks);
+ $days = (float) ($match['day'] ?? 0);
+ $this->assertSafeForInteger('day', $days);
+ $hours = (float) ($match['hour'] ?? 0);
+ $this->assertSafeForInteger('hour', $hours);
+ $minutes = (float) ($match['minute'] ?? 0);
+ $this->assertSafeForInteger('minute', $minutes);
+ $seconds = (float) ($match['second'] ?? 0);
+ $this->assertSafeForInteger('second', $seconds);
+ }
+
+ $totalDays = (($weeks * static::getDaysPerWeek()) + $days);
+ $this->assertSafeForInteger('days total (including weeks)', $totalDays);
+
+ $this->y = (int) $years;
+ $this->m = (int) $months;
+ $this->d = (int) $totalDays;
+ $this->h = (int) $hours;
+ $this->i = (int) $minutes;
+ $this->s = (int) $seconds;
+
+ if (
+ ((float) $this->y) !== $years ||
+ ((float) $this->m) !== $months ||
+ ((float) $this->d) !== $totalDays ||
+ ((float) $this->h) !== $hours ||
+ ((float) $this->i) !== $minutes ||
+ ((float) $this->s) !== $seconds
+ ) {
+ $this->add(static::fromString(
+ ($years - $this->y).' years '.
+ ($months - $this->m).' months '.
+ ($totalDays - $this->d).' days '.
+ ($hours - $this->h).' hours '.
+ ($minutes - $this->i).' minutes '.
+ ($seconds - $this->s).' seconds '
+ ));
+ }
+ } catch (Throwable $secondException) {
+ throw $secondException instanceof OutOfRangeException ? $secondException : $exception;
+ }
+ }
+
+ if ($microseconds !== null) {
+ $this->f = $microseconds / Carbon::MICROSECONDS_PER_SECOND;
+ }
+ }
+
+ /**
+ * Returns the factor for a given source-to-target couple.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return int|float|null
+ */
+ public static function getFactor($source, $target)
+ {
+ $source = self::standardizeUnit($source);
+ $target = self::standardizeUnit($target);
+ $factors = self::getFlipCascadeFactors();
+
+ if (isset($factors[$source])) {
+ [$to, $factor] = $factors[$source];
+
+ if ($to === $target) {
+ return $factor;
+ }
+
+ return $factor * static::getFactor($to, $target);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the factor for a given source-to-target couple if set,
+ * else try to find the appropriate constant as the factor, such as Carbon::DAYS_PER_WEEK.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return int|float|null
+ */
+ public static function getFactorWithDefault($source, $target)
+ {
+ $factor = self::getFactor($source, $target);
+
+ if ($factor) {
+ return $factor;
+ }
+
+ static $defaults = [
+ 'month' => ['year' => Carbon::MONTHS_PER_YEAR],
+ 'week' => ['month' => Carbon::WEEKS_PER_MONTH],
+ 'day' => ['week' => Carbon::DAYS_PER_WEEK],
+ 'hour' => ['day' => Carbon::HOURS_PER_DAY],
+ 'minute' => ['hour' => Carbon::MINUTES_PER_HOUR],
+ 'second' => ['minute' => Carbon::SECONDS_PER_MINUTE],
+ 'millisecond' => ['second' => Carbon::MILLISECONDS_PER_SECOND],
+ 'microsecond' => ['millisecond' => Carbon::MICROSECONDS_PER_MILLISECOND],
+ ];
+
+ return $defaults[$source][$target] ?? null;
+ }
+
+ /**
+ * Returns current config for days per week.
+ *
+ * @return int|float
+ */
+ public static function getDaysPerWeek()
+ {
+ return static::getFactor('dayz', 'weeks') ?: Carbon::DAYS_PER_WEEK;
+ }
+
+ /**
+ * Returns current config for hours per day.
+ *
+ * @return int|float
+ */
+ public static function getHoursPerDay()
+ {
+ return static::getFactor('hours', 'dayz') ?: Carbon::HOURS_PER_DAY;
+ }
+
+ /**
+ * Returns current config for minutes per hour.
+ *
+ * @return int|float
+ */
+ public static function getMinutesPerHour()
+ {
+ return static::getFactor('minutes', 'hours') ?: Carbon::MINUTES_PER_HOUR;
+ }
+
+ /**
+ * Returns current config for seconds per minute.
+ *
+ * @return int|float
+ */
+ public static function getSecondsPerMinute()
+ {
+ return static::getFactor('seconds', 'minutes') ?: Carbon::SECONDS_PER_MINUTE;
+ }
+
+ /**
+ * Returns current config for microseconds per second.
+ *
+ * @return int|float
+ */
+ public static function getMillisecondsPerSecond()
+ {
+ return static::getFactor('milliseconds', 'seconds') ?: Carbon::MILLISECONDS_PER_SECOND;
+ }
+
+ /**
+ * Returns current config for microseconds per second.
+ *
+ * @return int|float
+ */
+ public static function getMicrosecondsPerMillisecond()
+ {
+ return static::getFactor('microseconds', 'milliseconds') ?: Carbon::MICROSECONDS_PER_MILLISECOND;
+ }
+
+ /**
+ * Create a new CarbonInterval instance from specific values.
+ * This is an alias for the constructor that allows better fluent
+ * syntax as it allows you to do CarbonInterval::create(1)->fn() rather than
+ * (new CarbonInterval(1))->fn().
+ *
+ * @param int $years
+ * @param int $months
+ * @param int $weeks
+ * @param int $days
+ * @param int $hours
+ * @param int $minutes
+ * @param int $seconds
+ * @param int $microseconds
+ *
+ * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval.
+ *
+ * @return static
+ */
+ public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null)
+ {
+ return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $microseconds);
+ }
+
+ /**
+ * Parse a string into a new CarbonInterval object according to the specified format.
+ *
+ * @example
+ * ```
+ * echo Carboninterval::createFromFormat('H:i', '1:30');
+ * ```
+ *
+ * @param string $format Format of the $interval input string
+ * @param string|null $interval Input string to convert into an interval
+ *
+ * @throws \Carbon\Exceptions\ParseErrorException when the $interval cannot be parsed as an interval.
+ *
+ * @return static
+ */
+ public static function createFromFormat(string $format, ?string $interval)
+ {
+ $instance = new static(0);
+ $length = mb_strlen($format);
+
+ if (preg_match('/s([,.])([uv])$/', $format, $match)) {
+ $interval = explode($match[1], $interval);
+ $index = \count($interval) - 1;
+ $interval[$index] = str_pad($interval[$index], $match[2] === 'v' ? 3 : 6, '0');
+ $interval = implode($match[1], $interval);
+ }
+
+ $interval = $interval ?? '';
+
+ for ($index = 0; $index < $length; $index++) {
+ $expected = mb_substr($format, $index, 1);
+ $nextCharacter = mb_substr($interval, 0, 1);
+ $unit = static::$formats[$expected] ?? null;
+
+ if ($unit) {
+ if (!preg_match('/^-?\d+/', $interval, $match)) {
+ throw new ParseErrorException('number', $nextCharacter);
+ }
+
+ $interval = mb_substr($interval, mb_strlen($match[0]));
+ $instance->$unit += (int) ($match[0]);
+
+ continue;
+ }
+
+ if ($nextCharacter !== $expected) {
+ throw new ParseErrorException(
+ "'$expected'",
+ $nextCharacter,
+ 'Allowed substitutes for interval formats are '.implode(', ', array_keys(static::$formats))."\n".
+ 'See https://php.net/manual/en/function.date.php for their meaning'
+ );
+ }
+
+ $interval = mb_substr($interval, 1);
+ }
+
+ if ($interval !== '') {
+ throw new ParseErrorException(
+ 'end of string',
+ $interval
+ );
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy()
+ {
+ $date = new static(0);
+ $date->copyProperties($this);
+ $date->step = $this->step;
+
+ return $date;
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function clone()
+ {
+ return $this->copy();
+ }
+
+ /**
+ * Provide static helpers to create instances. Allows CarbonInterval::years(3).
+ *
+ * Note: This is done using the magic method to allow static and instance methods to
+ * have the same names.
+ *
+ * @param string $method magic method name called
+ * @param array $parameters parameters list
+ *
+ * @return static|null
+ */
+ public static function __callStatic($method, $parameters)
+ {
+ try {
+ $interval = new static(0);
+ $localStrictModeEnabled = $interval->localStrictModeEnabled;
+ $interval->localStrictModeEnabled = true;
+
+ $result = static::hasMacro($method)
+ ? static::bindMacroContext(null, function () use (&$method, &$parameters, &$interval) {
+ return $interval->callMacro($method, $parameters);
+ })
+ : $interval->$method(...$parameters);
+
+ $interval->localStrictModeEnabled = $localStrictModeEnabled;
+
+ return $result;
+ } catch (BadFluentSetterException $exception) {
+ if (Carbon::isStrictModeEnabled()) {
+ throw new BadFluentConstructorException($method, 0, $exception);
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Evaluate the PHP generated by var_export() and recreate the exported CarbonInterval instance.
+ *
+ * @param array $dump data as exported by var_export()
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public static function __set_state($dump)
+ {
+ /** @noinspection PhpVoidFunctionResultUsedInspection */
+ /** @var DateInterval $dateInterval */
+ $dateInterval = parent::__set_state($dump);
+
+ return static::instance($dateInterval);
+ }
+
+ /**
+ * Return the current context from inside a macro callee or a new one if static.
+ *
+ * @return static
+ */
+ protected static function this()
+ {
+ return end(static::$macroContextStack) ?: new static(0);
+ }
+
+ /**
+ * Creates a CarbonInterval from string.
+ *
+ * Format:
+ *
+ * Suffix | Unit | Example | DateInterval expression
+ * -------|---------|---------|------------------------
+ * y | years | 1y | P1Y
+ * mo | months | 3mo | P3M
+ * w | weeks | 2w | P2W
+ * d | days | 28d | P28D
+ * h | hours | 4h | PT4H
+ * m | minutes | 12m | PT12M
+ * s | seconds | 59s | PT59S
+ *
+ * e. g. `1w 3d 4h 32m 23s` is converted to 10 days 4 hours 32 minutes and 23 seconds.
+ *
+ * Special cases:
+ * - An empty string will return a zero interval
+ * - Fractions are allowed for weeks, days, hours and minutes and will be converted
+ * and rounded to the next smaller value (caution: 0.5w = 4d)
+ *
+ * @param string $intervalDefinition
+ *
+ * @return static
+ */
+ public static function fromString($intervalDefinition)
+ {
+ if (empty($intervalDefinition)) {
+ return new static(0);
+ }
+
+ $years = 0;
+ $months = 0;
+ $weeks = 0;
+ $days = 0;
+ $hours = 0;
+ $minutes = 0;
+ $seconds = 0;
+ $milliseconds = 0;
+ $microseconds = 0;
+
+ $pattern = '/(\d+(?:\.\d+)?)\h*([^\d\h]*)/i';
+ preg_match_all($pattern, $intervalDefinition, $parts, PREG_SET_ORDER);
+
+ while ([$part, $value, $unit] = array_shift($parts)) {
+ $intValue = (int) $value;
+ $fraction = (float) $value - $intValue;
+
+ // Fix calculation precision
+ switch (round($fraction, 6)) {
+ case 1:
+ $fraction = 0;
+ $intValue++;
+
+ break;
+ case 0:
+ $fraction = 0;
+
+ break;
+ }
+
+ switch ($unit === 'µs' ? 'µs' : strtolower($unit)) {
+ case 'millennia':
+ case 'millennium':
+ $years += $intValue * CarbonInterface::YEARS_PER_MILLENNIUM;
+
+ break;
+
+ case 'century':
+ case 'centuries':
+ $years += $intValue * CarbonInterface::YEARS_PER_CENTURY;
+
+ break;
+
+ case 'decade':
+ case 'decades':
+ $years += $intValue * CarbonInterface::YEARS_PER_DECADE;
+
+ break;
+
+ case 'year':
+ case 'years':
+ case 'y':
+ case 'yr':
+ case 'yrs':
+ $years += $intValue;
+
+ break;
+
+ case 'quarter':
+ case 'quarters':
+ $months += $intValue * CarbonInterface::MONTHS_PER_QUARTER;
+
+ break;
+
+ case 'month':
+ case 'months':
+ case 'mo':
+ case 'mos':
+ $months += $intValue;
+
+ break;
+
+ case 'week':
+ case 'weeks':
+ case 'w':
+ $weeks += $intValue;
+
+ if ($fraction) {
+ $parts[] = [null, $fraction * static::getDaysPerWeek(), 'd'];
+ }
+
+ break;
+
+ case 'day':
+ case 'days':
+ case 'd':
+ $days += $intValue;
+
+ if ($fraction) {
+ $parts[] = [null, $fraction * static::getHoursPerDay(), 'h'];
+ }
+
+ break;
+
+ case 'hour':
+ case 'hours':
+ case 'h':
+ $hours += $intValue;
+
+ if ($fraction) {
+ $parts[] = [null, $fraction * static::getMinutesPerHour(), 'm'];
+ }
+
+ break;
+
+ case 'minute':
+ case 'minutes':
+ case 'm':
+ $minutes += $intValue;
+
+ if ($fraction) {
+ $parts[] = [null, $fraction * static::getSecondsPerMinute(), 's'];
+ }
+
+ break;
+
+ case 'second':
+ case 'seconds':
+ case 's':
+ $seconds += $intValue;
+
+ if ($fraction) {
+ $parts[] = [null, $fraction * static::getMillisecondsPerSecond(), 'ms'];
+ }
+
+ break;
+
+ case 'millisecond':
+ case 'milliseconds':
+ case 'milli':
+ case 'ms':
+ $milliseconds += $intValue;
+
+ if ($fraction) {
+ $microseconds += round($fraction * static::getMicrosecondsPerMillisecond());
+ }
+
+ break;
+
+ case 'microsecond':
+ case 'microseconds':
+ case 'micro':
+ case 'µs':
+ $microseconds += $intValue;
+
+ break;
+
+ default:
+ throw new InvalidIntervalException(
+ sprintf('Invalid part %s in definition %s', $part, $intervalDefinition)
+ );
+ }
+ }
+
+ return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $milliseconds * Carbon::MICROSECONDS_PER_MILLISECOND + $microseconds);
+ }
+
+ /**
+ * Creates a CarbonInterval from string using a different locale.
+ *
+ * @param string $interval interval string in the given language (may also contain English).
+ * @param string|null $locale if locale is null or not specified, current global locale will be used instead.
+ *
+ * @return static
+ */
+ public static function parseFromLocale($interval, $locale = null)
+ {
+ return static::fromString(Carbon::translateTimeString($interval, $locale ?: static::getLocale(), 'en'));
+ }
+
+ private static function castIntervalToClass(DateInterval $interval, string $className, array $skip = [])
+ {
+ $mainClass = DateInterval::class;
+
+ if (!is_a($className, $mainClass, true)) {
+ throw new InvalidCastException("$className is not a sub-class of $mainClass.");
+ }
+
+ $microseconds = $interval->f;
+ $instance = new $className(static::getDateIntervalSpec($interval, false, $skip));
+
+ if ($microseconds) {
+ $instance->f = $microseconds;
+ }
+
+ if ($interval instanceof self && is_a($className, self::class, true)) {
+ self::copyStep($interval, $instance);
+ }
+
+ self::copyNegativeUnits($interval, $instance);
+
+ return $instance;
+ }
+
+ private static function copyNegativeUnits(DateInterval $from, DateInterval $to): void
+ {
+ $to->invert = $from->invert;
+
+ foreach (['y', 'm', 'd', 'h', 'i', 's'] as $unit) {
+ if ($from->$unit < 0) {
+ $to->$unit *= -1;
+ }
+ }
+ }
+
+ private static function copyStep(self $from, self $to): void
+ {
+ $to->setStep($from->getStep());
+ }
+
+ /**
+ * Cast the current instance into the given class.
+ *
+ * @param string $className The $className::instance() method will be called to cast the current object.
+ *
+ * @return DateInterval
+ */
+ public function cast(string $className)
+ {
+ return self::castIntervalToClass($this, $className);
+ }
+
+ /**
+ * Create a CarbonInterval instance from a DateInterval one. Can not instance
+ * DateInterval objects created from DateTime::diff() as you can't externally
+ * set the $days field.
+ *
+ * @param DateInterval $interval
+ * @param bool $skipCopy set to true to return the passed object
+ * (without copying it) if it's already of the
+ * current class
+ *
+ * @return static
+ */
+ public static function instance(DateInterval $interval, array $skip = [], bool $skipCopy = false)
+ {
+ if ($skipCopy && $interval instanceof static) {
+ return $interval;
+ }
+
+ return self::castIntervalToClass($interval, static::class, $skip);
+ }
+
+ /**
+ * Make a CarbonInterval instance from given variable if possible.
+ *
+ * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ *
+ * @param mixed|int|DateInterval|string|Closure|null $interval interval or number of the given $unit
+ * @param string|null $unit if specified, $interval must be an integer
+ * @param bool $skipCopy set to true to return the passed object
+ * (without copying it) if it's already of the
+ * current class
+ *
+ * @return static|null
+ */
+ public static function make($interval, $unit = null, bool $skipCopy = false)
+ {
+ if ($unit) {
+ $interval = "$interval ".Carbon::pluralUnit($unit);
+ }
+
+ if ($interval instanceof DateInterval) {
+ return static::instance($interval, [], $skipCopy);
+ }
+
+ if ($interval instanceof Closure) {
+ return new static($interval);
+ }
+
+ if (!\is_string($interval)) {
+ return null;
+ }
+
+ return static::makeFromString($interval);
+ }
+
+ protected static function makeFromString(string $interval)
+ {
+ $interval = preg_replace('/\s+/', ' ', trim($interval));
+
+ if (preg_match('/^P[T\d]/', $interval)) {
+ return new static($interval);
+ }
+
+ if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $interval)) {
+ return static::fromString($interval);
+ }
+
+ // @codeCoverageIgnoreStart
+ try {
+ /** @var static $interval */
+ $interval = static::createFromDateString($interval);
+ } catch (DateMalformedIntervalStringException $e) {
+ return null;
+ }
+ // @codeCoverageIgnoreEnd
+
+ return !$interval || $interval->isEmpty() ? null : $interval;
+ }
+
+ protected function resolveInterval($interval)
+ {
+ if (!($interval instanceof self)) {
+ return self::make($interval);
+ }
+
+ return $interval;
+ }
+
+ /**
+ * Sets up a DateInterval from the relative parts of the string.
+ *
+ * @param string $time
+ *
+ * @return static
+ *
+ * @link https://php.net/manual/en/dateinterval.createfromdatestring.php
+ */
+ #[ReturnTypeWillChange]
+ public static function createFromDateString($time)
+ {
+ $interval = @parent::createFromDateString(strtr($time, [
+ ',' => ' ',
+ ' and ' => ' ',
+ ]));
+
+ if ($interval instanceof DateInterval) {
+ $interval = static::instance($interval);
+ }
+
+ return $interval;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// GETTERS AND SETTERS /////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a part of the CarbonInterval object.
+ *
+ * @param string $name
+ *
+ * @throws UnknownGetterException
+ *
+ * @return int|float|string
+ */
+ public function get($name)
+ {
+ if (str_starts_with($name, 'total')) {
+ return $this->total(substr($name, 5));
+ }
+
+ switch ($name) {
+ case 'years':
+ return $this->y;
+
+ case 'months':
+ return $this->m;
+
+ case 'dayz':
+ return $this->d;
+
+ case 'hours':
+ return $this->h;
+
+ case 'minutes':
+ return $this->i;
+
+ case 'seconds':
+ return $this->s;
+
+ case 'milli':
+ case 'milliseconds':
+ return (int) (round($this->f * Carbon::MICROSECONDS_PER_SECOND) / Carbon::MICROSECONDS_PER_MILLISECOND);
+
+ case 'micro':
+ case 'microseconds':
+ return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND);
+
+ case 'microExcludeMilli':
+ return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND) % Carbon::MICROSECONDS_PER_MILLISECOND;
+
+ case 'weeks':
+ return (int) ($this->d / (int) static::getDaysPerWeek());
+
+ case 'daysExcludeWeeks':
+ case 'dayzExcludeWeeks':
+ return $this->d % (int) static::getDaysPerWeek();
+
+ case 'locale':
+ return $this->getTranslatorLocale();
+
+ default:
+ throw new UnknownGetterException($name);
+ }
+ }
+
+ /**
+ * Get a part of the CarbonInterval object.
+ *
+ * @param string $name
+ *
+ * @throws UnknownGetterException
+ *
+ * @return int|float|string
+ */
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * Set a part of the CarbonInterval object.
+ *
+ * @param string|array $name
+ * @param int $value
+ *
+ * @throws UnknownSetterException
+ *
+ * @return $this
+ */
+ public function set($name, $value = null)
+ {
+ $properties = \is_array($name) ? $name : [$name => $value];
+
+ foreach ($properties as $key => $value) {
+ switch (Carbon::singularUnit(rtrim($key, 'z'))) {
+ case 'year':
+ $this->checkIntegerValue($key, $value);
+ $this->y = $value;
+ $this->handleDecimalPart('year', $value, $this->y);
+
+ break;
+
+ case 'month':
+ $this->checkIntegerValue($key, $value);
+ $this->m = $value;
+ $this->handleDecimalPart('month', $value, $this->m);
+
+ break;
+
+ case 'week':
+ $this->checkIntegerValue($key, $value);
+ $days = $value * (int) static::getDaysPerWeek();
+ $this->assertSafeForInteger('days total (including weeks)', $days);
+ $this->d = $days;
+ $this->handleDecimalPart('day', $days, $this->d);
+
+ break;
+
+ case 'day':
+ $this->checkIntegerValue($key, $value);
+ $this->d = $value;
+ $this->handleDecimalPart('day', $value, $this->d);
+
+ break;
+
+ case 'daysexcludeweek':
+ case 'dayzexcludeweek':
+ $this->checkIntegerValue($key, $value);
+ $days = $this->weeks * (int) static::getDaysPerWeek() + $value;
+ $this->assertSafeForInteger('days total (including weeks)', $days);
+ $this->d = $days;
+ $this->handleDecimalPart('day', $days, $this->d);
+
+ break;
+
+ case 'hour':
+ $this->checkIntegerValue($key, $value);
+ $this->h = $value;
+ $this->handleDecimalPart('hour', $value, $this->h);
+
+ break;
+
+ case 'minute':
+ $this->checkIntegerValue($key, $value);
+ $this->i = $value;
+ $this->handleDecimalPart('minute', $value, $this->i);
+
+ break;
+
+ case 'second':
+ $this->checkIntegerValue($key, $value);
+ $this->s = $value;
+ $this->handleDecimalPart('second', $value, $this->s);
+
+ break;
+
+ case 'milli':
+ case 'millisecond':
+ $this->microseconds = $value * Carbon::MICROSECONDS_PER_MILLISECOND + $this->microseconds % Carbon::MICROSECONDS_PER_MILLISECOND;
+
+ break;
+
+ case 'micro':
+ case 'microsecond':
+ $this->f = $value / Carbon::MICROSECONDS_PER_SECOND;
+
+ break;
+
+ default:
+ if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) {
+ throw new UnknownSetterException($key);
+ }
+
+ $this->$key = $value;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set a part of the CarbonInterval object.
+ *
+ * @param string $name
+ * @param int $value
+ *
+ * @throws UnknownSetterException
+ */
+ public function __set($name, $value)
+ {
+ $this->set($name, $value);
+ }
+
+ /**
+ * Allow setting of weeks and days to be cumulative.
+ *
+ * @param int $weeks Number of weeks to set
+ * @param int $days Number of days to set
+ *
+ * @return static
+ */
+ public function weeksAndDays($weeks, $days)
+ {
+ $this->dayz = ($weeks * static::getDaysPerWeek()) + $days;
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the interval is empty for each unit.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->years === 0 &&
+ $this->months === 0 &&
+ $this->dayz === 0 &&
+ !$this->days &&
+ $this->hours === 0 &&
+ $this->minutes === 0 &&
+ $this->seconds === 0 &&
+ $this->microseconds === 0;
+ }
+
+ /**
+ * Register a custom macro.
+ *
+ * @example
+ * ```
+ * CarbonInterval::macro('twice', function () {
+ * return $this->times(2);
+ * });
+ * echo CarbonInterval::hours(2)->twice();
+ * ```
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro)
+ {
+ static::$macros[$name] = $macro;
+ }
+
+ /**
+ * Register macros from a mixin object.
+ *
+ * @example
+ * ```
+ * CarbonInterval::mixin(new class {
+ * public function daysToHours() {
+ * return function () {
+ * $this->hours += $this->days;
+ * $this->days = 0;
+ *
+ * return $this;
+ * };
+ * }
+ * public function hoursToDays() {
+ * return function () {
+ * $this->days += $this->hours;
+ * $this->hours = 0;
+ *
+ * return $this;
+ * };
+ * }
+ * });
+ * echo CarbonInterval::hours(5)->hoursToDays() . "\n";
+ * echo CarbonInterval::days(5)->daysToHours() . "\n";
+ * ```
+ *
+ * @param object|string $mixin
+ *
+ * @throws ReflectionException
+ *
+ * @return void
+ */
+ public static function mixin($mixin)
+ {
+ static::baseMixin($mixin);
+ }
+
+ /**
+ * Check if macro is registered.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$macros[$name]);
+ }
+
+ /**
+ * Call given macro.
+ *
+ * @param string $name
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ protected function callMacro($name, $parameters)
+ {
+ $macro = static::$macros[$name];
+
+ if ($macro instanceof Closure) {
+ $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class);
+
+ return ($boundMacro ?: $macro)(...$parameters);
+ }
+
+ return $macro(...$parameters);
+ }
+
+ /**
+ * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day().
+ *
+ * Note: This is done using the magic method to allow static and instance methods to
+ * have the same names.
+ *
+ * @param string $method magic method name called
+ * @param array $parameters parameters list
+ *
+ * @throws BadFluentSetterException|Throwable
+ *
+ * @return static
+ */
+ public function __call($method, $parameters)
+ {
+ if (static::hasMacro($method)) {
+ return static::bindMacroContext($this, function () use (&$method, &$parameters) {
+ return $this->callMacro($method, $parameters);
+ });
+ }
+
+ $roundedValue = $this->callRoundMethod($method, $parameters);
+
+ if ($roundedValue !== null) {
+ return $roundedValue;
+ }
+
+ if (preg_match('/^(?add|sub)(?[A-Z].*)$/', $method, $match)) {
+ $value = $this->getMagicParameter($parameters, 0, Carbon::pluralUnit($match['unit']), 0);
+
+ return $this->{$match['method']}($value, $match['unit']);
+ }
+
+ $value = $this->getMagicParameter($parameters, 0, Carbon::pluralUnit($method), 1);
+
+ try {
+ $this->set($method, $value);
+ } catch (UnknownSetterException $exception) {
+ if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) {
+ throw new BadFluentSetterException($method, 0, $exception);
+ }
+ }
+
+ return $this;
+ }
+
+ protected function getForHumansInitialVariables($syntax, $short)
+ {
+ if (\is_array($syntax)) {
+ return $syntax;
+ }
+
+ if (\is_int($short)) {
+ return [
+ 'parts' => $short,
+ 'short' => false,
+ ];
+ }
+
+ if (\is_bool($syntax)) {
+ return [
+ 'short' => $syntax,
+ 'syntax' => CarbonInterface::DIFF_ABSOLUTE,
+ ];
+ }
+
+ return [];
+ }
+
+ /**
+ * @param mixed $syntax
+ * @param mixed $short
+ * @param mixed $parts
+ * @param mixed $options
+ *
+ * @return array
+ */
+ protected function getForHumansParameters($syntax = null, $short = false, $parts = -1, $options = null)
+ {
+ $optionalSpace = ' ';
+ $default = $this->getTranslationMessage('list.0') ?? $this->getTranslationMessage('list') ?? ' ';
+ $join = $default === '' ? '' : ' ';
+ $altNumbers = false;
+ $aUnit = false;
+ $minimumUnit = 's';
+ $skip = [];
+ extract($this->getForHumansInitialVariables($syntax, $short));
+ $skip = array_map('strtolower', array_filter((array) $skip, static function ($value) {
+ return \is_string($value) && $value !== '';
+ }));
+
+ if ($syntax === null) {
+ $syntax = CarbonInterface::DIFF_ABSOLUTE;
+ }
+
+ if ($parts === -1) {
+ $parts = INF;
+ }
+
+ if ($options === null) {
+ $options = static::getHumanDiffOptions();
+ }
+
+ if ($join === false) {
+ $join = ' ';
+ } elseif ($join === true) {
+ $join = [
+ $default,
+ $this->getTranslationMessage('list.1') ?? $default,
+ ];
+ }
+
+ if ($altNumbers && $altNumbers !== true) {
+ $language = new Language($this->locale);
+ $altNumbers = \in_array($language->getCode(), (array) $altNumbers, true);
+ }
+
+ if (\is_array($join)) {
+ [$default, $last] = $join;
+
+ if ($default !== ' ') {
+ $optionalSpace = '';
+ }
+
+ $join = function ($list) use ($default, $last) {
+ if (\count($list) < 2) {
+ return implode('', $list);
+ }
+
+ $end = array_pop($list);
+
+ return implode($default, $list).$last.$end;
+ };
+ }
+
+ if (\is_string($join)) {
+ if ($join !== ' ') {
+ $optionalSpace = '';
+ }
+
+ $glue = $join;
+ $join = function ($list) use ($glue) {
+ return implode($glue, $list);
+ };
+ }
+
+ $interpolations = [
+ ':optional-space' => $optionalSpace,
+ ];
+
+ return [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip];
+ }
+
+ protected static function getRoundingMethodFromOptions(int $options): ?string
+ {
+ if ($options & CarbonInterface::ROUND) {
+ return 'round';
+ }
+
+ if ($options & CarbonInterface::CEIL) {
+ return 'ceil';
+ }
+
+ if ($options & CarbonInterface::FLOOR) {
+ return 'floor';
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns interval values as an array where key are the unit names and values the counts.
+ *
+ * @return int[]
+ */
+ public function toArray()
+ {
+ return [
+ 'years' => $this->years,
+ 'months' => $this->months,
+ 'weeks' => $this->weeks,
+ 'days' => $this->daysExcludeWeeks,
+ 'hours' => $this->hours,
+ 'minutes' => $this->minutes,
+ 'seconds' => $this->seconds,
+ 'microseconds' => $this->microseconds,
+ ];
+ }
+
+ /**
+ * Returns interval non-zero values as an array where key are the unit names and values the counts.
+ *
+ * @return int[]
+ */
+ public function getNonZeroValues()
+ {
+ return array_filter($this->toArray(), 'intval');
+ }
+
+ /**
+ * Returns interval values as an array where key are the unit names and values the counts
+ * from the biggest non-zero one the the smallest non-zero one.
+ *
+ * @return int[]
+ */
+ public function getValuesSequence()
+ {
+ $nonZeroValues = $this->getNonZeroValues();
+
+ if ($nonZeroValues === []) {
+ return [];
+ }
+
+ $keys = array_keys($nonZeroValues);
+ $firstKey = $keys[0];
+ $lastKey = $keys[\count($keys) - 1];
+ $values = [];
+ $record = false;
+
+ foreach ($this->toArray() as $unit => $count) {
+ if ($unit === $firstKey) {
+ $record = true;
+ }
+
+ if ($record) {
+ $values[$unit] = $count;
+ }
+
+ if ($unit === $lastKey) {
+ $record = false;
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Get the current interval in a human readable format in the current locale.
+ *
+ * @example
+ * ```
+ * echo CarbonInterval::fromString('4d 3h 40m')->forHumans() . "\n";
+ * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['parts' => 2]) . "\n";
+ * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['parts' => 3, 'join' => true]) . "\n";
+ * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['short' => true]) . "\n";
+ * echo CarbonInterval::fromString('1d 24h')->forHumans(['join' => ' or ']) . "\n";
+ * echo CarbonInterval::fromString('1d 24h')->forHumans(['minimumUnit' => 'hour']) . "\n";
+ * ```
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'skip' entry, list of units to skip (array of strings or a single string,
+ * ` it can be the unit name (singular or plural) or its shortcut
+ * ` (y, m, w, d, h, min, s, ms, µs).
+ * - 'aUnit' entry, prefer "an hour" over "1 hour" if true
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'minimumUnit' entry determines the smallest unit of time to display can be long or
+ * ` short form of the units, e.g. 'hour' or 'h' (default value: s)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: -1: no limits)
+ * @param int $options human diff options
+ *
+ * @throws Exception
+ *
+ * @return string
+ */
+ public function forHumans($syntax = null, $short = false, $parts = -1, $options = null)
+ {
+ [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip] = $this
+ ->getForHumansParameters($syntax, $short, $parts, $options);
+
+ $interval = [];
+
+ $syntax = (int) ($syntax ?? CarbonInterface::DIFF_ABSOLUTE);
+ $absolute = $syntax === CarbonInterface::DIFF_ABSOLUTE;
+ $relativeToNow = $syntax === CarbonInterface::DIFF_RELATIVE_TO_NOW;
+ $count = 1;
+ $unit = $short ? 's' : 'second';
+ $isFuture = $this->invert === 1;
+ $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
+ $declensionMode = null;
+
+ /** @var \Symfony\Component\Translation\Translator $translator */
+ $translator = $this->getLocalTranslator();
+
+ $handleDeclensions = function ($unit, $count, $index = 0, $parts = 1) use ($interpolations, $transId, $translator, $altNumbers, $absolute, &$declensionMode) {
+ if (!$absolute) {
+ $declensionMode = $declensionMode ?? $this->translate($transId.'_mode');
+
+ if ($this->needsDeclension($declensionMode, $index, $parts)) {
+ // Some languages have special pluralization for past and future tense.
+ $key = $unit.'_'.$transId;
+ $result = $this->translate($key, $interpolations, $count, $translator, $altNumbers);
+
+ if ($result !== $key) {
+ return $result;
+ }
+ }
+ }
+
+ $result = $this->translate($unit, $interpolations, $count, $translator, $altNumbers);
+
+ if ($result !== $unit) {
+ return $result;
+ }
+
+ return null;
+ };
+
+ $intervalValues = $this;
+ $method = static::getRoundingMethodFromOptions($options);
+
+ if ($method) {
+ $previousCount = INF;
+
+ while (
+ \count($intervalValues->getNonZeroValues()) > $parts &&
+ ($count = \count($keys = array_keys($intervalValues->getValuesSequence()))) > 1
+ ) {
+ $index = min($count, $previousCount - 1) - 2;
+
+ if ($index < 0) {
+ break;
+ }
+
+ $intervalValues = $this->copy()->roundUnit(
+ $keys[$index],
+ 1,
+ $method
+ );
+ $previousCount = $count;
+ }
+ }
+
+ $diffIntervalArray = [
+ ['value' => $intervalValues->years, 'unit' => 'year', 'unitShort' => 'y'],
+ ['value' => $intervalValues->months, 'unit' => 'month', 'unitShort' => 'm'],
+ ['value' => $intervalValues->weeks, 'unit' => 'week', 'unitShort' => 'w'],
+ ['value' => $intervalValues->daysExcludeWeeks, 'unit' => 'day', 'unitShort' => 'd'],
+ ['value' => $intervalValues->hours, 'unit' => 'hour', 'unitShort' => 'h'],
+ ['value' => $intervalValues->minutes, 'unit' => 'minute', 'unitShort' => 'min'],
+ ['value' => $intervalValues->seconds, 'unit' => 'second', 'unitShort' => 's'],
+ ['value' => $intervalValues->milliseconds, 'unit' => 'millisecond', 'unitShort' => 'ms'],
+ ['value' => $intervalValues->microExcludeMilli, 'unit' => 'microsecond', 'unitShort' => 'µs'],
+ ];
+
+ if (!empty($skip)) {
+ foreach ($diffIntervalArray as $index => &$unitData) {
+ $nextIndex = $index + 1;
+
+ if ($unitData['value'] &&
+ isset($diffIntervalArray[$nextIndex]) &&
+ \count(array_intersect([$unitData['unit'], $unitData['unit'].'s', $unitData['unitShort']], $skip))
+ ) {
+ $diffIntervalArray[$nextIndex]['value'] += $unitData['value'] *
+ self::getFactorWithDefault($diffIntervalArray[$nextIndex]['unit'], $unitData['unit']);
+ $unitData['value'] = 0;
+ }
+ }
+ }
+
+ $transChoice = function ($short, $unitData, $index, $parts) use ($absolute, $handleDeclensions, $translator, $aUnit, $altNumbers, $interpolations) {
+ $count = $unitData['value'];
+
+ if ($short) {
+ $result = $handleDeclensions($unitData['unitShort'], $count, $index, $parts);
+
+ if ($result !== null) {
+ return $result;
+ }
+ } elseif ($aUnit) {
+ $result = $handleDeclensions('a_'.$unitData['unit'], $count, $index, $parts);
+
+ if ($result !== null) {
+ return $result;
+ }
+ }
+
+ if (!$absolute) {
+ return $handleDeclensions($unitData['unit'], $count, $index, $parts);
+ }
+
+ return $this->translate($unitData['unit'], $interpolations, $count, $translator, $altNumbers);
+ };
+
+ $fallbackUnit = ['second', 's'];
+
+ foreach ($diffIntervalArray as $diffIntervalData) {
+ if ($diffIntervalData['value'] > 0) {
+ $unit = $short ? $diffIntervalData['unitShort'] : $diffIntervalData['unit'];
+ $count = $diffIntervalData['value'];
+ $interval[] = [$short, $diffIntervalData];
+ } elseif ($options & CarbonInterface::SEQUENTIAL_PARTS_ONLY && \count($interval) > 0) {
+ break;
+ }
+
+ // break the loop after we get the required number of parts in array
+ if (\count($interval) >= $parts) {
+ break;
+ }
+
+ // break the loop after we have reached the minimum unit
+ if (\in_array($minimumUnit, [$diffIntervalData['unit'], $diffIntervalData['unitShort']], true)) {
+ $fallbackUnit = [$diffIntervalData['unit'], $diffIntervalData['unitShort']];
+
+ break;
+ }
+ }
+
+ $actualParts = \count($interval);
+
+ foreach ($interval as $index => &$item) {
+ $item = $transChoice($item[0], $item[1], $index, $actualParts);
+ }
+
+ if (\count($interval) === 0) {
+ if ($relativeToNow && $options & CarbonInterface::JUST_NOW) {
+ $key = 'diff_now';
+ $translation = $this->translate($key, $interpolations, null, $translator);
+
+ if ($translation !== $key) {
+ return $translation;
+ }
+ }
+
+ $count = $options & CarbonInterface::NO_ZERO_DIFF ? 1 : 0;
+ $unit = $fallbackUnit[$short ? 1 : 0];
+ $interval[] = $this->translate($unit, $interpolations, $count, $translator, $altNumbers);
+ }
+
+ // join the interval parts by a space
+ $time = $join($interval);
+
+ unset($diffIntervalArray, $interval);
+
+ if ($absolute) {
+ return $time;
+ }
+
+ $isFuture = $this->invert === 1;
+
+ $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
+
+ if ($parts === 1) {
+ if ($relativeToNow && $unit === 'day') {
+ if ($count === 1 && $options & CarbonInterface::ONE_DAY_WORDS) {
+ $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday';
+ $translation = $this->translate($key, $interpolations, null, $translator);
+
+ if ($translation !== $key) {
+ return $translation;
+ }
+ }
+
+ if ($count === 2 && $options & CarbonInterface::TWO_DAY_WORDS) {
+ $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday';
+ $translation = $this->translate($key, $interpolations, null, $translator);
+
+ if ($translation !== $key) {
+ return $translation;
+ }
+ }
+ }
+
+ $aTime = $aUnit ? $handleDeclensions('a_'.$unit, $count) : null;
+
+ $time = $aTime ?: $handleDeclensions($unit, $count) ?: $time;
+ }
+
+ $time = [':time' => $time];
+
+ return $this->translate($transId, array_merge($time, $interpolations, $time), null, $translator);
+ }
+
+ /**
+ * Format the instance as a string using the forHumans() function.
+ *
+ * @throws Exception
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $format = $this->localToStringFormat ?? static::$toStringFormat;
+
+ if (!$format) {
+ return $this->forHumans();
+ }
+
+ if ($format instanceof Closure) {
+ return $format($this);
+ }
+
+ return $this->format($format);
+ }
+
+ /**
+ * Return native DateInterval PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(CarbonInterval::hours(2)->toDateInterval());
+ * ```
+ *
+ * @return DateInterval
+ */
+ public function toDateInterval()
+ {
+ return self::castIntervalToClass($this, DateInterval::class);
+ }
+
+ /**
+ * Convert the interval to a CarbonPeriod.
+ *
+ * @param DateTimeInterface|string|int ...$params Start date, [end date or recurrences] and optional settings.
+ *
+ * @return CarbonPeriod
+ */
+ public function toPeriod(...$params)
+ {
+ if ($this->tzName) {
+ $tz = \is_string($this->tzName) ? new DateTimeZone($this->tzName) : $this->tzName;
+
+ if ($tz instanceof DateTimeZone) {
+ array_unshift($params, $tz);
+ }
+ }
+
+ return CarbonPeriod::create($this, ...$params);
+ }
+
+ /**
+ * Invert the interval.
+ *
+ * @param bool|int $inverted if a parameter is passed, the passed value cast as 1 or 0 is used
+ * as the new value of the ->invert property.
+ *
+ * @return $this
+ */
+ public function invert($inverted = null)
+ {
+ $this->invert = (\func_num_args() === 0 ? !$this->invert : $inverted) ? 1 : 0;
+
+ return $this;
+ }
+
+ protected function solveNegativeInterval()
+ {
+ if (!$this->isEmpty() && $this->years <= 0 && $this->months <= 0 && $this->dayz <= 0 && $this->hours <= 0 && $this->minutes <= 0 && $this->seconds <= 0 && $this->microseconds <= 0) {
+ $this->years *= -1;
+ $this->months *= -1;
+ $this->dayz *= -1;
+ $this->hours *= -1;
+ $this->minutes *= -1;
+ $this->seconds *= -1;
+ $this->microseconds *= -1;
+ $this->invert();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add the passed interval to the current instance.
+ *
+ * @param string|DateInterval $unit
+ * @param int|float $value
+ *
+ * @return $this
+ */
+ public function add($unit, $value = 1)
+ {
+ if (is_numeric($unit)) {
+ [$value, $unit] = [$unit, $value];
+ }
+
+ if (\is_string($unit) && !preg_match('/^\s*\d/', $unit)) {
+ $unit = "$value $unit";
+ $value = 1;
+ }
+
+ $interval = static::make($unit);
+
+ if (!$interval) {
+ throw new InvalidIntervalException('This type of data cannot be added/subtracted.');
+ }
+
+ if ($value !== 1) {
+ $interval->times($value);
+ }
+
+ $sign = ($this->invert === 1) !== ($interval->invert === 1) ? -1 : 1;
+ $this->years += $interval->y * $sign;
+ $this->months += $interval->m * $sign;
+ $this->dayz += ($interval->days === false ? $interval->d : $interval->days) * $sign;
+ $this->hours += $interval->h * $sign;
+ $this->minutes += $interval->i * $sign;
+ $this->seconds += $interval->s * $sign;
+ $this->microseconds += $interval->microseconds * $sign;
+
+ $this->solveNegativeInterval();
+
+ return $this;
+ }
+
+ /**
+ * Subtract the passed interval to the current instance.
+ *
+ * @param string|DateInterval $unit
+ * @param int|float $value
+ *
+ * @return $this
+ */
+ public function sub($unit, $value = 1)
+ {
+ if (is_numeric($unit)) {
+ [$value, $unit] = [$unit, $value];
+ }
+
+ return $this->add($unit, -(float) $value);
+ }
+
+ /**
+ * Subtract the passed interval to the current instance.
+ *
+ * @param string|DateInterval $unit
+ * @param int|float $value
+ *
+ * @return $this
+ */
+ public function subtract($unit, $value = 1)
+ {
+ return $this->sub($unit, $value);
+ }
+
+ /**
+ * Add given parameters to the current interval.
+ *
+ * @param int $years
+ * @param int $months
+ * @param int|float $weeks
+ * @param int|float $days
+ * @param int|float $hours
+ * @param int|float $minutes
+ * @param int|float $seconds
+ * @param int|float $microseconds
+ *
+ * @return $this
+ */
+ public function plus(
+ $years = 0,
+ $months = 0,
+ $weeks = 0,
+ $days = 0,
+ $hours = 0,
+ $minutes = 0,
+ $seconds = 0,
+ $microseconds = 0
+ ): self {
+ return $this->add("
+ $years years $months months $weeks weeks $days days
+ $hours hours $minutes minutes $seconds seconds $microseconds microseconds
+ ");
+ }
+
+ /**
+ * Add given parameters to the current interval.
+ *
+ * @param int $years
+ * @param int $months
+ * @param int|float $weeks
+ * @param int|float $days
+ * @param int|float $hours
+ * @param int|float $minutes
+ * @param int|float $seconds
+ * @param int|float $microseconds
+ *
+ * @return $this
+ */
+ public function minus(
+ $years = 0,
+ $months = 0,
+ $weeks = 0,
+ $days = 0,
+ $hours = 0,
+ $minutes = 0,
+ $seconds = 0,
+ $microseconds = 0
+ ): self {
+ return $this->sub("
+ $years years $months months $weeks weeks $days days
+ $hours hours $minutes minutes $seconds seconds $microseconds microseconds
+ ");
+ }
+
+ /**
+ * Multiply current instance given number of times. times() is naive, it multiplies each unit
+ * (so day can be greater than 31, hour can be greater than 23, etc.) and the result is rounded
+ * separately for each unit.
+ *
+ * Use times() when you want a fast and approximated calculation that does not cascade units.
+ *
+ * For a precise and cascaded calculation,
+ *
+ * @see multiply()
+ *
+ * @param float|int $factor
+ *
+ * @return $this
+ */
+ public function times($factor)
+ {
+ if ($factor < 0) {
+ $this->invert = $this->invert ? 0 : 1;
+ $factor = -$factor;
+ }
+
+ $this->years = (int) round($this->years * $factor);
+ $this->months = (int) round($this->months * $factor);
+ $this->dayz = (int) round($this->dayz * $factor);
+ $this->hours = (int) round($this->hours * $factor);
+ $this->minutes = (int) round($this->minutes * $factor);
+ $this->seconds = (int) round($this->seconds * $factor);
+ $this->microseconds = (int) round($this->microseconds * $factor);
+
+ return $this;
+ }
+
+ /**
+ * Divide current instance by a given divider. shares() is naive, it divides each unit separately
+ * and the result is rounded for each unit. So 5 hours and 20 minutes shared by 3 becomes 2 hours
+ * and 7 minutes.
+ *
+ * Use shares() when you want a fast and approximated calculation that does not cascade units.
+ *
+ * For a precise and cascaded calculation,
+ *
+ * @see divide()
+ *
+ * @param float|int $divider
+ *
+ * @return $this
+ */
+ public function shares($divider)
+ {
+ return $this->times(1 / $divider);
+ }
+
+ protected function copyProperties(self $interval, $ignoreSign = false)
+ {
+ $this->years = $interval->years;
+ $this->months = $interval->months;
+ $this->dayz = $interval->dayz;
+ $this->hours = $interval->hours;
+ $this->minutes = $interval->minutes;
+ $this->seconds = $interval->seconds;
+ $this->microseconds = $interval->microseconds;
+
+ if (!$ignoreSign) {
+ $this->invert = $interval->invert;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Multiply and cascade current instance by a given factor.
+ *
+ * @param float|int $factor
+ *
+ * @return $this
+ */
+ public function multiply($factor)
+ {
+ if ($factor < 0) {
+ $this->invert = $this->invert ? 0 : 1;
+ $factor = -$factor;
+ }
+
+ $yearPart = (int) floor($this->years * $factor); // Split calculation to prevent imprecision
+
+ if ($yearPart) {
+ $this->years -= $yearPart / $factor;
+ }
+
+ return $this->copyProperties(
+ static::create($yearPart)
+ ->microseconds(abs($this->totalMicroseconds) * $factor)
+ ->cascade(),
+ true
+ );
+ }
+
+ /**
+ * Divide and cascade current instance by a given divider.
+ *
+ * @param float|int $divider
+ *
+ * @return $this
+ */
+ public function divide($divider)
+ {
+ return $this->multiply(1 / $divider);
+ }
+
+ /**
+ * Get the interval_spec string of a date interval.
+ *
+ * @param DateInterval $interval
+ *
+ * @return string
+ */
+ public static function getDateIntervalSpec(DateInterval $interval, bool $microseconds = false, array $skip = [])
+ {
+ $date = array_filter([
+ static::PERIOD_YEARS => abs($interval->y),
+ static::PERIOD_MONTHS => abs($interval->m),
+ static::PERIOD_DAYS => abs($interval->d),
+ ]);
+
+ if (
+ $interval->days >= CarbonInterface::DAYS_PER_WEEK * CarbonInterface::WEEKS_PER_MONTH &&
+ (!isset($date[static::PERIOD_YEARS]) || \count(array_intersect(['y', 'year', 'years'], $skip))) &&
+ (!isset($date[static::PERIOD_MONTHS]) || \count(array_intersect(['m', 'month', 'months'], $skip)))
+ ) {
+ $date = [
+ static::PERIOD_DAYS => abs($interval->days),
+ ];
+ }
+
+ $seconds = abs($interval->s);
+ if ($microseconds && $interval->f > 0) {
+ $seconds = sprintf('%d.%06d', $seconds, abs($interval->f) * 1000000);
+ }
+
+ $time = array_filter([
+ static::PERIOD_HOURS => abs($interval->h),
+ static::PERIOD_MINUTES => abs($interval->i),
+ static::PERIOD_SECONDS => $seconds,
+ ]);
+
+ $specString = static::PERIOD_PREFIX;
+
+ foreach ($date as $key => $value) {
+ $specString .= $value.$key;
+ }
+
+ if (\count($time) > 0) {
+ $specString .= static::PERIOD_TIME_PREFIX;
+ foreach ($time as $key => $value) {
+ $specString .= $value.$key;
+ }
+ }
+
+ return $specString === static::PERIOD_PREFIX ? 'PT0S' : $specString;
+ }
+
+ /**
+ * Get the interval_spec string.
+ *
+ * @return string
+ */
+ public function spec(bool $microseconds = false)
+ {
+ return static::getDateIntervalSpec($this, $microseconds);
+ }
+
+ /**
+ * Comparing 2 date intervals.
+ *
+ * @param DateInterval $first
+ * @param DateInterval $second
+ *
+ * @return int
+ */
+ public static function compareDateIntervals(DateInterval $first, DateInterval $second)
+ {
+ $current = Carbon::now();
+ $passed = $current->avoidMutation()->add($second);
+ $current->add($first);
+
+ if ($current < $passed) {
+ return -1;
+ }
+ if ($current > $passed) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Comparing with passed interval.
+ *
+ * @param DateInterval $interval
+ *
+ * @return int
+ */
+ public function compare(DateInterval $interval)
+ {
+ return static::compareDateIntervals($this, $interval);
+ }
+
+ private function invertCascade(array $values)
+ {
+ return $this->set(array_map(function ($value) {
+ return -$value;
+ }, $values))->doCascade(true)->invert();
+ }
+
+ private function doCascade(bool $deep)
+ {
+ $originalData = $this->toArray();
+ $originalData['milliseconds'] = (int) ($originalData['microseconds'] / static::getMicrosecondsPerMillisecond());
+ $originalData['microseconds'] = $originalData['microseconds'] % static::getMicrosecondsPerMillisecond();
+ $originalData['weeks'] = (int) ($this->d / static::getDaysPerWeek());
+ $originalData['daysExcludeWeeks'] = fmod($this->d, static::getDaysPerWeek());
+ unset($originalData['days']);
+ $newData = $originalData;
+ $previous = [];
+
+ foreach (self::getFlipCascadeFactors() as $source => [$target, $factor]) {
+ foreach (['source', 'target'] as $key) {
+ if ($$key === 'dayz') {
+ $$key = 'daysExcludeWeeks';
+ }
+ }
+
+ $value = $newData[$source];
+ $modulo = fmod($factor + fmod($value, $factor), $factor);
+ $newData[$source] = $modulo;
+ $newData[$target] += ($value - $modulo) / $factor;
+
+ $decimalPart = fmod($newData[$source], 1);
+
+ if ($decimalPart !== 0.0) {
+ $unit = $source;
+
+ foreach ($previous as [$subUnit, $subFactor]) {
+ $newData[$unit] -= $decimalPart;
+ $newData[$subUnit] += $decimalPart * $subFactor;
+ $decimalPart = fmod($newData[$subUnit], 1);
+
+ if ($decimalPart === 0.0) {
+ break;
+ }
+
+ $unit = $subUnit;
+ }
+ }
+
+ array_unshift($previous, [$source, $factor]);
+ }
+
+ $positive = null;
+
+ if (!$deep) {
+ foreach ($newData as $value) {
+ if ($value) {
+ if ($positive === null) {
+ $positive = ($value > 0);
+
+ continue;
+ }
+
+ if (($value > 0) !== $positive) {
+ return $this->invertCascade($originalData)
+ ->solveNegativeInterval();
+ }
+ }
+ }
+ }
+
+ return $this->set($newData)
+ ->solveNegativeInterval();
+ }
+
+ /**
+ * Convert overflowed values into bigger units.
+ *
+ * @return $this
+ */
+ public function cascade()
+ {
+ return $this->doCascade(false);
+ }
+
+ public function hasNegativeValues(): bool
+ {
+ foreach ($this->toArray() as $value) {
+ if ($value < 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function hasPositiveValues(): bool
+ {
+ foreach ($this->toArray() as $value) {
+ if ($value > 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get amount of given unit equivalent to the interval.
+ *
+ * @param string $unit
+ *
+ * @throws UnknownUnitException|UnitNotConfiguredException
+ *
+ * @return float
+ */
+ public function total($unit)
+ {
+ $realUnit = $unit = strtolower($unit);
+
+ if (\in_array($unit, ['days', 'weeks'])) {
+ $realUnit = 'dayz';
+ } elseif (!\in_array($unit, ['microseconds', 'milliseconds', 'seconds', 'minutes', 'hours', 'dayz', 'months', 'years'])) {
+ throw new UnknownUnitException($unit);
+ }
+
+ $result = 0;
+ $cumulativeFactor = 0;
+ $unitFound = false;
+ $factors = self::getFlipCascadeFactors();
+ $daysPerWeek = (int) static::getDaysPerWeek();
+
+ $values = [
+ 'years' => $this->years,
+ 'months' => $this->months,
+ 'weeks' => (int) ($this->d / $daysPerWeek),
+ 'dayz' => fmod($this->d, $daysPerWeek),
+ 'hours' => $this->hours,
+ 'minutes' => $this->minutes,
+ 'seconds' => $this->seconds,
+ 'milliseconds' => (int) ($this->microseconds / Carbon::MICROSECONDS_PER_MILLISECOND),
+ 'microseconds' => $this->microseconds % Carbon::MICROSECONDS_PER_MILLISECOND,
+ ];
+
+ if (isset($factors['dayz']) && $factors['dayz'][0] !== 'weeks') {
+ $values['dayz'] += $values['weeks'] * $daysPerWeek;
+ $values['weeks'] = 0;
+ }
+
+ foreach ($factors as $source => [$target, $factor]) {
+ if ($source === $realUnit) {
+ $unitFound = true;
+ $value = $values[$source];
+ $result += $value;
+ $cumulativeFactor = 1;
+ }
+
+ if ($factor === false) {
+ if ($unitFound) {
+ break;
+ }
+
+ $result = 0;
+ $cumulativeFactor = 0;
+
+ continue;
+ }
+
+ if ($target === $realUnit) {
+ $unitFound = true;
+ }
+
+ if ($cumulativeFactor) {
+ $cumulativeFactor *= $factor;
+ $result += $values[$target] * $cumulativeFactor;
+
+ continue;
+ }
+
+ $value = $values[$source];
+
+ $result = ($result + $value) / $factor;
+ }
+
+ if (isset($target) && !$cumulativeFactor) {
+ $result += $values[$target];
+ }
+
+ if (!$unitFound) {
+ throw new UnitNotConfiguredException($unit);
+ }
+
+ if ($this->invert) {
+ $result *= -1;
+ }
+
+ if ($unit === 'weeks') {
+ $result /= $daysPerWeek;
+ }
+
+ // Cast as int numbers with no decimal part
+ return fmod($result, 1) === 0.0 ? (int) $result : $result;
+ }
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @see equalTo()
+ *
+ * @return bool
+ */
+ public function eq($interval): bool
+ {
+ return $this->equalTo($interval);
+ }
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @return bool
+ */
+ public function equalTo($interval): bool
+ {
+ $interval = $this->resolveInterval($interval);
+
+ return $interval !== null && $this->totalMicroseconds === $interval->totalMicroseconds;
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @see notEqualTo()
+ *
+ * @return bool
+ */
+ public function ne($interval): bool
+ {
+ return $this->notEqualTo($interval);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @return bool
+ */
+ public function notEqualTo($interval): bool
+ {
+ return !$this->eq($interval);
+ }
+
+ /**
+ * Determines if the instance is greater (longer) than another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @see greaterThan()
+ *
+ * @return bool
+ */
+ public function gt($interval): bool
+ {
+ return $this->greaterThan($interval);
+ }
+
+ /**
+ * Determines if the instance is greater (longer) than another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @return bool
+ */
+ public function greaterThan($interval): bool
+ {
+ $interval = $this->resolveInterval($interval);
+
+ return $interval === null || $this->totalMicroseconds > $interval->totalMicroseconds;
+ }
+
+ /**
+ * Determines if the instance is greater (longer) than or equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @see greaterThanOrEqualTo()
+ *
+ * @return bool
+ */
+ public function gte($interval): bool
+ {
+ return $this->greaterThanOrEqualTo($interval);
+ }
+
+ /**
+ * Determines if the instance is greater (longer) than or equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @return bool
+ */
+ public function greaterThanOrEqualTo($interval): bool
+ {
+ return $this->greaterThan($interval) || $this->equalTo($interval);
+ }
+
+ /**
+ * Determines if the instance is less (shorter) than another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @see lessThan()
+ *
+ * @return bool
+ */
+ public function lt($interval): bool
+ {
+ return $this->lessThan($interval);
+ }
+
+ /**
+ * Determines if the instance is less (shorter) than another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @return bool
+ */
+ public function lessThan($interval): bool
+ {
+ $interval = $this->resolveInterval($interval);
+
+ return $interval !== null && $this->totalMicroseconds < $interval->totalMicroseconds;
+ }
+
+ /**
+ * Determines if the instance is less (shorter) than or equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @see lessThanOrEqualTo()
+ *
+ * @return bool
+ */
+ public function lte($interval): bool
+ {
+ return $this->lessThanOrEqualTo($interval);
+ }
+
+ /**
+ * Determines if the instance is less (shorter) than or equal to another
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval
+ *
+ * @return bool
+ */
+ public function lessThanOrEqualTo($interval): bool
+ {
+ return $this->lessThan($interval) || $this->equalTo($interval);
+ }
+
+ /**
+ * Determines if the instance is between two others.
+ *
+ * The third argument allow you to specify if bounds are included or not (true by default)
+ * but for when you including/excluding bounds may produce different results in your application,
+ * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead.
+ *
+ * @example
+ * ```
+ * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(3)); // true
+ * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::hours(36)); // false
+ * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(2)); // true
+ * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(2), false); // false
+ * ```
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval1
+ * @param CarbonInterval|DateInterval|mixed $interval2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function between($interval1, $interval2, $equal = true): bool
+ {
+ return $equal
+ ? $this->greaterThanOrEqualTo($interval1) && $this->lessThanOrEqualTo($interval2)
+ : $this->greaterThan($interval1) && $this->lessThan($interval2);
+ }
+
+ /**
+ * Determines if the instance is between two others, bounds excluded.
+ *
+ * @example
+ * ```
+ * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(3)); // true
+ * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::hours(36)); // false
+ * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(2)); // true
+ * ```
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval1
+ * @param CarbonInterval|DateInterval|mixed $interval2
+ *
+ * @return bool
+ */
+ public function betweenIncluded($interval1, $interval2): bool
+ {
+ return $this->between($interval1, $interval2, true);
+ }
+
+ /**
+ * Determines if the instance is between two others, bounds excluded.
+ *
+ * @example
+ * ```
+ * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(3)); // true
+ * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::hours(36)); // false
+ * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(2)); // false
+ * ```
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval1
+ * @param CarbonInterval|DateInterval|mixed $interval2
+ *
+ * @return bool
+ */
+ public function betweenExcluded($interval1, $interval2): bool
+ {
+ return $this->between($interval1, $interval2, false);
+ }
+
+ /**
+ * Determines if the instance is between two others
+ *
+ * @example
+ * ```
+ * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(3)); // true
+ * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::hours(36)); // false
+ * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(2)); // true
+ * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(2), false); // false
+ * ```
+ *
+ * @param CarbonInterval|DateInterval|mixed $interval1
+ * @param CarbonInterval|DateInterval|mixed $interval2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function isBetween($interval1, $interval2, $equal = true): bool
+ {
+ return $this->between($interval1, $interval2, $equal);
+ }
+
+ /**
+ * Round the current instance at the given unit with given precision if specified and the given function.
+ *
+ * @param string $unit
+ * @param float|int|string|DateInterval|null $precision
+ * @param string $function
+ *
+ * @throws Exception
+ *
+ * @return $this
+ */
+ public function roundUnit($unit, $precision = 1, $function = 'round')
+ {
+ if (static::getCascadeFactors() !== static::getDefaultCascadeFactors()) {
+ $value = $function($this->total($unit) / $precision) * $precision;
+ $inverted = $value < 0;
+
+ return $this->copyProperties(self::fromString(
+ number_format(abs($value), 12, '.', '').' '.$unit
+ )->invert($inverted)->cascade());
+ }
+
+ $base = CarbonImmutable::parse('2000-01-01 00:00:00', 'UTC')
+ ->roundUnit($unit, $precision, $function);
+ $next = $base->add($this);
+ $inverted = $next < $base;
+
+ if ($inverted) {
+ $next = $base->sub($this);
+ }
+
+ $this->copyProperties(
+ $next
+ ->roundUnit($unit, $precision, $function)
+ ->diffAsCarbonInterval($base)
+ );
+
+ return $this->invert($inverted);
+ }
+
+ /**
+ * Truncate the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int|string|DateInterval|null $precision
+ *
+ * @throws Exception
+ *
+ * @return $this
+ */
+ public function floorUnit($unit, $precision = 1)
+ {
+ return $this->roundUnit($unit, $precision, 'floor');
+ }
+
+ /**
+ * Ceil the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int|string|DateInterval|null $precision
+ *
+ * @throws Exception
+ *
+ * @return $this
+ */
+ public function ceilUnit($unit, $precision = 1)
+ {
+ return $this->roundUnit($unit, $precision, 'ceil');
+ }
+
+ /**
+ * Round the current instance second with given precision if specified.
+ *
+ * @param float|int|string|DateInterval|null $precision
+ * @param string $function
+ *
+ * @throws Exception
+ *
+ * @return $this
+ */
+ public function round($precision = 1, $function = 'round')
+ {
+ return $this->roundWith($precision, $function);
+ }
+
+ /**
+ * Round the current instance second with given precision if specified.
+ *
+ * @param float|int|string|DateInterval|null $precision
+ *
+ * @throws Exception
+ *
+ * @return $this
+ */
+ public function floor($precision = 1)
+ {
+ return $this->round($precision, 'floor');
+ }
+
+ /**
+ * Ceil the current instance second with given precision if specified.
+ *
+ * @param float|int|string|DateInterval|null $precision
+ *
+ * @throws Exception
+ *
+ * @return $this
+ */
+ public function ceil($precision = 1)
+ {
+ return $this->round($precision, 'ceil');
+ }
+
+ private function needsDeclension(string $mode, int $index, int $parts): bool
+ {
+ switch ($mode) {
+ case 'last':
+ return $index === $parts - 1;
+ default:
+ return true;
+ }
+ }
+
+ private function checkIntegerValue(string $name, $value)
+ {
+ if (\is_int($value)) {
+ return;
+ }
+
+ $this->assertSafeForInteger($name, $value);
+
+ if (\is_float($value) && (((float) (int) $value) === $value)) {
+ return;
+ }
+
+ if (!self::$floatSettersEnabled) {
+ $type = \gettype($value);
+ @trigger_error(
+ "Since 2.70.0, it's deprecated to pass $type value for $name.\n".
+ "It's truncated when stored as an integer interval unit.\n".
+ "From 3.0.0, decimal part will no longer be truncated and will be cascaded to smaller units.\n".
+ "- To maintain the current behavior, use explicit cast: $name((int) \$value)\n".
+ "- To adopt the new behavior globally, call CarbonInterval::enableFloatSetters()\n",
+ \E_USER_DEPRECATED
+ );
+ }
+ }
+
+ /**
+ * Throw an exception if precision loss when storing the given value as an integer would be >= 1.0.
+ */
+ private function assertSafeForInteger(string $name, $value)
+ {
+ if ($value && !\is_int($value) && ($value >= 0x7fffffffffffffff || $value <= -0x7fffffffffffffff)) {
+ throw new OutOfRangeException($name, -0x7fffffffffffffff, 0x7fffffffffffffff, $value);
+ }
+ }
+
+ private function handleDecimalPart(string $unit, $value, $integerValue)
+ {
+ if (self::$floatSettersEnabled) {
+ $floatValue = (float) $value;
+ $base = (float) $integerValue;
+
+ if ($floatValue === $base) {
+ return;
+ }
+
+ $units = [
+ 'y' => 'year',
+ 'm' => 'month',
+ 'd' => 'day',
+ 'h' => 'hour',
+ 'i' => 'minute',
+ 's' => 'second',
+ ];
+ $upper = true;
+
+ foreach ($units as $property => $name) {
+ if ($name === $unit) {
+ $upper = false;
+
+ continue;
+ }
+
+ if (!$upper && $this->$property !== 0) {
+ throw new RuntimeException(
+ "You cannot set $unit to a float value as $name would be overridden, ".
+ 'set it first to 0 explicitly if you really want to erase its value'
+ );
+ }
+ }
+
+ $this->add($unit, $floatValue - $base);
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php
new file mode 100644
index 0000000..9633d2b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriod.php
@@ -0,0 +1,2729 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Exceptions\EndLessPeriodException;
+use Carbon\Exceptions\InvalidCastException;
+use Carbon\Exceptions\InvalidIntervalException;
+use Carbon\Exceptions\InvalidPeriodDateException;
+use Carbon\Exceptions\InvalidPeriodParameterException;
+use Carbon\Exceptions\NotACarbonClassException;
+use Carbon\Exceptions\NotAPeriodException;
+use Carbon\Exceptions\UnknownGetterException;
+use Carbon\Exceptions\UnknownMethodException;
+use Carbon\Exceptions\UnreachableException;
+use Carbon\Traits\IntervalRounding;
+use Carbon\Traits\Mixin;
+use Carbon\Traits\Options;
+use Carbon\Traits\ToStringFormat;
+use Closure;
+use Countable;
+use DateInterval;
+use DatePeriod;
+use DateTime;
+use DateTimeImmutable;
+use DateTimeInterface;
+use DateTimeZone;
+use InvalidArgumentException;
+use Iterator;
+use JsonSerializable;
+use ReflectionException;
+use ReturnTypeWillChange;
+use RuntimeException;
+
+/**
+ * Substitution of DatePeriod with some modifications and many more features.
+ *
+ * @property-read int|float $recurrences number of recurrences (if end not set).
+ * @property-read bool $include_start_date rather the start date is included in the iteration.
+ * @property-read bool $include_end_date rather the end date is included in the iteration (if recurrences not set).
+ * @property-read CarbonInterface $start Period start date.
+ * @property-read CarbonInterface $current Current date from the iteration.
+ * @property-read CarbonInterface $end Period end date.
+ * @property-read CarbonInterval $interval Underlying date interval instance. Always present, one day by default.
+ *
+ * @method static static start($date, $inclusive = null) Create instance specifying start date or modify the start date if called on an instance.
+ * @method static static since($date, $inclusive = null) Alias for start().
+ * @method static static sinceNow($inclusive = null) Create instance with start date set to now or set the start date to now if called on an instance.
+ * @method static static end($date = null, $inclusive = null) Create instance specifying end date or modify the end date if called on an instance.
+ * @method static static until($date = null, $inclusive = null) Alias for end().
+ * @method static static untilNow($inclusive = null) Create instance with end date set to now or set the end date to now if called on an instance.
+ * @method static static dates($start, $end = null) Create instance with start and end dates or modify the start and end dates if called on an instance.
+ * @method static static between($start, $end = null) Create instance with start and end dates or modify the start and end dates if called on an instance.
+ * @method static static recurrences($recurrences = null) Create instance with maximum number of recurrences or modify the number of recurrences if called on an instance.
+ * @method static static times($recurrences = null) Alias for recurrences().
+ * @method static static options($options = null) Create instance with options or modify the options if called on an instance.
+ * @method static static toggle($options, $state = null) Create instance with options toggled on or off, or toggle options if called on an instance.
+ * @method static static filter($callback, $name = null) Create instance with filter added to the stack or append a filter if called on an instance.
+ * @method static static push($callback, $name = null) Alias for filter().
+ * @method static static prepend($callback, $name = null) Create instance with filter prepended to the stack or prepend a filter if called on an instance.
+ * @method static static filters(array $filters = []) Create instance with filters stack or replace the whole filters stack if called on an instance.
+ * @method static static interval($interval) Create instance with given date interval or modify the interval if called on an instance.
+ * @method static static each($interval) Create instance with given date interval or modify the interval if called on an instance.
+ * @method static static every($interval) Create instance with given date interval or modify the interval if called on an instance.
+ * @method static static step($interval) Create instance with given date interval or modify the interval if called on an instance.
+ * @method static static stepBy($interval) Create instance with given date interval or modify the interval if called on an instance.
+ * @method static static invert() Create instance with inverted date interval or invert the interval if called on an instance.
+ * @method static static years($years = 1) Create instance specifying a number of years for date interval or replace the interval by the given a number of years if called on an instance.
+ * @method static static year($years = 1) Alias for years().
+ * @method static static months($months = 1) Create instance specifying a number of months for date interval or replace the interval by the given a number of months if called on an instance.
+ * @method static static month($months = 1) Alias for months().
+ * @method static static weeks($weeks = 1) Create instance specifying a number of weeks for date interval or replace the interval by the given a number of weeks if called on an instance.
+ * @method static static week($weeks = 1) Alias for weeks().
+ * @method static static days($days = 1) Create instance specifying a number of days for date interval or replace the interval by the given a number of days if called on an instance.
+ * @method static static dayz($days = 1) Alias for days().
+ * @method static static day($days = 1) Alias for days().
+ * @method static static hours($hours = 1) Create instance specifying a number of hours for date interval or replace the interval by the given a number of hours if called on an instance.
+ * @method static static hour($hours = 1) Alias for hours().
+ * @method static static minutes($minutes = 1) Create instance specifying a number of minutes for date interval or replace the interval by the given a number of minutes if called on an instance.
+ * @method static static minute($minutes = 1) Alias for minutes().
+ * @method static static seconds($seconds = 1) Create instance specifying a number of seconds for date interval or replace the interval by the given a number of seconds if called on an instance.
+ * @method static static second($seconds = 1) Alias for seconds().
+ * @method static static milliseconds($milliseconds = 1) Create instance specifying a number of milliseconds for date interval or replace the interval by the given a number of milliseconds if called on an instance.
+ * @method static static millisecond($milliseconds = 1) Alias for milliseconds().
+ * @method static static microseconds($microseconds = 1) Create instance specifying a number of microseconds for date interval or replace the interval by the given a number of microseconds if called on an instance.
+ * @method static static microsecond($microseconds = 1) Alias for microseconds().
+ * @method $this roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method $this roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method $this floorYear(float $precision = 1) Truncate the current instance year with given precision.
+ * @method $this floorYears(float $precision = 1) Truncate the current instance year with given precision.
+ * @method $this ceilYear(float $precision = 1) Ceil the current instance year with given precision.
+ * @method $this ceilYears(float $precision = 1) Ceil the current instance year with given precision.
+ * @method $this roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method $this roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method $this floorMonth(float $precision = 1) Truncate the current instance month with given precision.
+ * @method $this floorMonths(float $precision = 1) Truncate the current instance month with given precision.
+ * @method $this ceilMonth(float $precision = 1) Ceil the current instance month with given precision.
+ * @method $this ceilMonths(float $precision = 1) Ceil the current instance month with given precision.
+ * @method $this roundWeek(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this roundWeeks(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this floorWeek(float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this floorWeeks(float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this ceilWeek(float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this ceilWeeks(float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method $this floorDay(float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this floorDays(float $precision = 1) Truncate the current instance day with given precision.
+ * @method $this ceilDay(float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this ceilDays(float $precision = 1) Ceil the current instance day with given precision.
+ * @method $this roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method $this roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method $this floorHour(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method $this floorHours(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method $this ceilHour(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method $this ceilHours(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method $this roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method $this roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method $this floorMinute(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method $this floorMinutes(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method $this ceilMinute(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method $this ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method $this roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method $this roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method $this floorSecond(float $precision = 1) Truncate the current instance second with given precision.
+ * @method $this floorSeconds(float $precision = 1) Truncate the current instance second with given precision.
+ * @method $this ceilSecond(float $precision = 1) Ceil the current instance second with given precision.
+ * @method $this ceilSeconds(float $precision = 1) Ceil the current instance second with given precision.
+ * @method $this roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method $this roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method $this floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method $this floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method $this ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method $this ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method $this roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method $this roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method $this floorCentury(float $precision = 1) Truncate the current instance century with given precision.
+ * @method $this floorCenturies(float $precision = 1) Truncate the current instance century with given precision.
+ * @method $this ceilCentury(float $precision = 1) Ceil the current instance century with given precision.
+ * @method $this ceilCenturies(float $precision = 1) Ceil the current instance century with given precision.
+ * @method $this roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method $this roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method $this floorDecade(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method $this floorDecades(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method $this ceilDecade(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method $this ceilDecades(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method $this roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method $this roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method $this floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method $this floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method $this ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method $this ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method $this roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method $this roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method $this floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method $this floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method $this ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method $this ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method $this roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method $this roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method $this floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method $this floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method $this ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method $this ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision.
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class CarbonPeriod implements Iterator, Countable, JsonSerializable
+{
+ use IntervalRounding;
+ use Mixin {
+ Mixin::mixin as baseMixin;
+ }
+ use Options;
+ use ToStringFormat;
+
+ /**
+ * Built-in filter for limit by recurrences.
+ *
+ * @var callable
+ */
+ public const RECURRENCES_FILTER = [self::class, 'filterRecurrences'];
+
+ /**
+ * Built-in filter for limit to an end.
+ *
+ * @var callable
+ */
+ public const END_DATE_FILTER = [self::class, 'filterEndDate'];
+
+ /**
+ * Special value which can be returned by filters to end iteration. Also a filter.
+ *
+ * @var callable
+ */
+ public const END_ITERATION = [self::class, 'endIteration'];
+
+ /**
+ * Exclude start date from iteration.
+ *
+ * @var int
+ */
+ public const EXCLUDE_START_DATE = 1;
+
+ /**
+ * Exclude end date from iteration.
+ *
+ * @var int
+ */
+ public const EXCLUDE_END_DATE = 2;
+
+ /**
+ * Yield CarbonImmutable instances.
+ *
+ * @var int
+ */
+ public const IMMUTABLE = 4;
+
+ /**
+ * Number of maximum attempts before giving up on finding next valid date.
+ *
+ * @var int
+ */
+ public const NEXT_MAX_ATTEMPTS = 1000;
+
+ /**
+ * Number of maximum attempts before giving up on finding end date.
+ *
+ * @var int
+ */
+ public const END_MAX_ATTEMPTS = 10000;
+
+ /**
+ * The registered macros.
+ *
+ * @var array
+ */
+ protected static $macros = [];
+
+ /**
+ * Date class of iteration items.
+ *
+ * @var string
+ */
+ protected $dateClass = Carbon::class;
+
+ /**
+ * Underlying date interval instance. Always present, one day by default.
+ *
+ * @var CarbonInterval
+ */
+ protected $dateInterval;
+
+ /**
+ * True once __construct is finished.
+ *
+ * @var bool
+ */
+ protected $constructed = false;
+
+ /**
+ * Whether current date interval was set by default.
+ *
+ * @var bool
+ */
+ protected $isDefaultInterval;
+
+ /**
+ * The filters stack.
+ *
+ * @var array
+ */
+ protected $filters = [];
+
+ /**
+ * Period start date. Applied on rewind. Always present, now by default.
+ *
+ * @var CarbonInterface
+ */
+ protected $startDate;
+
+ /**
+ * Period end date. For inverted interval should be before the start date. Applied via a filter.
+ *
+ * @var CarbonInterface|null
+ */
+ protected $endDate;
+
+ /**
+ * Limit for number of recurrences. Applied via a filter.
+ *
+ * @var int|null
+ */
+ protected $recurrences;
+
+ /**
+ * Iteration options.
+ *
+ * @var int
+ */
+ protected $options;
+
+ /**
+ * Index of current date. Always sequential, even if some dates are skipped by filters.
+ * Equal to null only before the first iteration.
+ *
+ * @var int
+ */
+ protected $key;
+
+ /**
+ * Current date. May temporarily hold unaccepted value when looking for a next valid date.
+ * Equal to null only before the first iteration.
+ *
+ * @var CarbonInterface
+ */
+ protected $current;
+
+ /**
+ * Timezone of current date. Taken from the start date.
+ *
+ * @var \DateTimeZone|null
+ */
+ protected $timezone;
+
+ /**
+ * The cached validation result for current date.
+ *
+ * @var bool|string|null
+ */
+ protected $validationResult;
+
+ /**
+ * Timezone handler for settings() method.
+ *
+ * @var mixed
+ */
+ protected $tzName;
+
+ /**
+ * Make a CarbonPeriod instance from given variable if possible.
+ *
+ * @param mixed $var
+ *
+ * @return static|null
+ */
+ public static function make($var)
+ {
+ try {
+ return static::instance($var);
+ } catch (NotAPeriodException $e) {
+ return static::create($var);
+ }
+ }
+
+ /**
+ * Create a new instance from a DatePeriod or CarbonPeriod object.
+ *
+ * @param CarbonPeriod|DatePeriod $period
+ *
+ * @return static
+ */
+ public static function instance($period)
+ {
+ if ($period instanceof static) {
+ return $period->copy();
+ }
+
+ if ($period instanceof self) {
+ return new static(
+ $period->getStartDate(),
+ $period->getEndDate() ?: $period->getRecurrences(),
+ $period->getDateInterval(),
+ $period->getOptions()
+ );
+ }
+
+ if ($period instanceof DatePeriod) {
+ return new static(
+ $period->start,
+ $period->end ?: ($period->recurrences - 1),
+ $period->interval,
+ $period->include_start_date ? 0 : static::EXCLUDE_START_DATE
+ );
+ }
+
+ $class = static::class;
+ $type = \gettype($period);
+
+ throw new NotAPeriodException(
+ 'Argument 1 passed to '.$class.'::'.__METHOD__.'() '.
+ 'must be an instance of DatePeriod or '.$class.', '.
+ ($type === 'object' ? 'instance of '.\get_class($period) : $type).' given.'
+ );
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @return static
+ */
+ public static function create(...$params)
+ {
+ return static::createFromArray($params);
+ }
+
+ /**
+ * Create a new instance from an array of parameters.
+ *
+ * @param array $params
+ *
+ * @return static
+ */
+ public static function createFromArray(array $params)
+ {
+ return new static(...$params);
+ }
+
+ /**
+ * Create CarbonPeriod from ISO 8601 string.
+ *
+ * @param string $iso
+ * @param int|null $options
+ *
+ * @return static
+ */
+ public static function createFromIso($iso, $options = null)
+ {
+ $params = static::parseIso8601($iso);
+
+ $instance = static::createFromArray($params);
+
+ if ($options !== null) {
+ $instance->setOptions($options);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Return whether given interval contains non zero value of any time unit.
+ *
+ * @param \DateInterval $interval
+ *
+ * @return bool
+ */
+ protected static function intervalHasTime(DateInterval $interval)
+ {
+ return $interval->h || $interval->i || $interval->s || $interval->f;
+ }
+
+ /**
+ * Return whether given variable is an ISO 8601 specification.
+ *
+ * Note: Check is very basic, as actual validation will be done later when parsing.
+ * We just want to ensure that variable is not any other type of a valid parameter.
+ *
+ * @param mixed $var
+ *
+ * @return bool
+ */
+ protected static function isIso8601($var)
+ {
+ if (!\is_string($var)) {
+ return false;
+ }
+
+ // Match slash but not within a timezone name.
+ $part = '[a-z]+(?:[_-][a-z]+)*';
+
+ preg_match("#\b$part/$part\b|(/)#i", $var, $match);
+
+ return isset($match[1]);
+ }
+
+ /**
+ * Parse given ISO 8601 string into an array of arguments.
+ *
+ * @SuppressWarnings(PHPMD.ElseExpression)
+ *
+ * @param string $iso
+ *
+ * @return array
+ */
+ protected static function parseIso8601($iso)
+ {
+ $result = [];
+
+ $interval = null;
+ $start = null;
+ $end = null;
+
+ foreach (explode('/', $iso) as $key => $part) {
+ if ($key === 0 && preg_match('/^R(\d*|INF)$/', $part, $match)) {
+ $parsed = \strlen($match[1]) ? (($match[1] !== 'INF') ? (int) $match[1] : INF) : null;
+ } elseif ($interval === null && $parsed = CarbonInterval::make($part)) {
+ $interval = $part;
+ } elseif ($start === null && $parsed = Carbon::make($part)) {
+ $start = $part;
+ } elseif ($end === null && $parsed = Carbon::make(static::addMissingParts($start ?? '', $part))) {
+ $end = $part;
+ } else {
+ throw new InvalidPeriodParameterException("Invalid ISO 8601 specification: $iso.");
+ }
+
+ $result[] = $parsed;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Add missing parts of the target date from the soure date.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return string
+ */
+ protected static function addMissingParts($source, $target)
+ {
+ $pattern = '/'.preg_replace('/\d+/', '[0-9]+', preg_quote($target, '/')).'$/';
+
+ $result = preg_replace($pattern, $target, $source, 1, $count);
+
+ return $count ? $result : $target;
+ }
+
+ /**
+ * Register a custom macro.
+ *
+ * @example
+ * ```
+ * CarbonPeriod::macro('middle', function () {
+ * return $this->getStartDate()->average($this->getEndDate());
+ * });
+ * echo CarbonPeriod::since('2011-05-12')->until('2011-06-03')->middle();
+ * ```
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro)
+ {
+ static::$macros[$name] = $macro;
+ }
+
+ /**
+ * Register macros from a mixin object.
+ *
+ * @example
+ * ```
+ * CarbonPeriod::mixin(new class {
+ * public function addDays() {
+ * return function ($count = 1) {
+ * return $this->setStartDate(
+ * $this->getStartDate()->addDays($count)
+ * )->setEndDate(
+ * $this->getEndDate()->addDays($count)
+ * );
+ * };
+ * }
+ * public function subDays() {
+ * return function ($count = 1) {
+ * return $this->setStartDate(
+ * $this->getStartDate()->subDays($count)
+ * )->setEndDate(
+ * $this->getEndDate()->subDays($count)
+ * );
+ * };
+ * }
+ * });
+ * echo CarbonPeriod::create('2000-01-01', '2000-02-01')->addDays(5)->subDays(3);
+ * ```
+ *
+ * @param object|string $mixin
+ *
+ * @throws ReflectionException
+ *
+ * @return void
+ */
+ public static function mixin($mixin)
+ {
+ static::baseMixin($mixin);
+ }
+
+ /**
+ * Check if macro is registered.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$macros[$name]);
+ }
+
+ /**
+ * Provide static proxy for instance aliases.
+ *
+ * @param string $method
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $parameters)
+ {
+ $date = new static();
+
+ if (static::hasMacro($method)) {
+ return static::bindMacroContext(null, function () use (&$method, &$parameters, &$date) {
+ return $date->callMacro($method, $parameters);
+ });
+ }
+
+ return $date->$method(...$parameters);
+ }
+
+ /**
+ * CarbonPeriod constructor.
+ *
+ * @SuppressWarnings(PHPMD.ElseExpression)
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct(...$arguments)
+ {
+ if (is_a($this->dateClass, DateTimeImmutable::class, true)) {
+ $this->options = static::IMMUTABLE;
+ }
+
+ // Parse and assign arguments one by one. First argument may be an ISO 8601 spec,
+ // which will be first parsed into parts and then processed the same way.
+
+ $argumentsCount = \count($arguments);
+
+ if ($argumentsCount && static::isIso8601($iso = $arguments[0])) {
+ array_splice($arguments, 0, 1, static::parseIso8601($iso));
+ }
+
+ if ($argumentsCount === 1) {
+ if ($arguments[0] instanceof DatePeriod) {
+ $arguments = [
+ $arguments[0]->start,
+ $arguments[0]->end ?: ($arguments[0]->recurrences - 1),
+ $arguments[0]->interval,
+ $arguments[0]->include_start_date ? 0 : static::EXCLUDE_START_DATE,
+ ];
+ } elseif ($arguments[0] instanceof self) {
+ $arguments = [
+ $arguments[0]->getStartDate(),
+ $arguments[0]->getEndDate() ?: $arguments[0]->getRecurrences(),
+ $arguments[0]->getDateInterval(),
+ $arguments[0]->getOptions(),
+ ];
+ }
+ }
+
+ $optionsSet = false;
+
+ foreach ($arguments as $argument) {
+ $parsedDate = null;
+
+ if ($argument instanceof DateTimeZone) {
+ $this->setTimezone($argument);
+ } elseif ($this->dateInterval === null &&
+ (
+ (\is_string($argument) && preg_match(
+ '/^(-?\d(\d(?![\/-])|[^\d\/-]([\/-])?)*|P[T\d].*|(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+)$/i',
+ $argument
+ )) ||
+ $argument instanceof DateInterval ||
+ $argument instanceof Closure
+ ) &&
+ $parsedInterval = @CarbonInterval::make($argument)
+ ) {
+ $this->setDateInterval($parsedInterval);
+ } elseif ($this->startDate === null && $parsedDate = $this->makeDateTime($argument)) {
+ $this->setStartDate($parsedDate);
+ } elseif ($this->endDate === null && ($parsedDate = $parsedDate ?? $this->makeDateTime($argument))) {
+ $this->setEndDate($parsedDate);
+ } elseif ($this->recurrences === null && $this->endDate === null && is_numeric($argument)) {
+ $this->setRecurrences($argument);
+ } elseif (!$optionsSet && (\is_int($argument) || $argument === null)) {
+ $optionsSet = true;
+ $this->setOptions(((int) $this->options) | ((int) $argument));
+ } else {
+ throw new InvalidPeriodParameterException('Invalid constructor parameters.');
+ }
+ }
+
+ if ($this->startDate === null) {
+ $this->setStartDate(Carbon::now());
+ }
+
+ if ($this->dateInterval === null) {
+ $this->setDateInterval(CarbonInterval::day());
+
+ $this->isDefaultInterval = true;
+ }
+
+ if ($this->options === null) {
+ $this->setOptions(0);
+ }
+
+ $this->constructed = true;
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy()
+ {
+ return clone $this;
+ }
+
+ /**
+ * Prepare the instance to be set (self if mutable to be mutated,
+ * copy if immutable to generate a new instance).
+ *
+ * @return static
+ */
+ protected function copyIfImmutable()
+ {
+ return $this;
+ }
+
+ /**
+ * Get the getter for a property allowing both `DatePeriod` snakeCase and camelCase names.
+ *
+ * @param string $name
+ *
+ * @return callable|null
+ */
+ protected function getGetter(string $name)
+ {
+ switch (strtolower(preg_replace('/[A-Z]/', '_$0', $name))) {
+ case 'start':
+ case 'start_date':
+ return [$this, 'getStartDate'];
+ case 'end':
+ case 'end_date':
+ return [$this, 'getEndDate'];
+ case 'interval':
+ case 'date_interval':
+ return [$this, 'getDateInterval'];
+ case 'recurrences':
+ return [$this, 'getRecurrences'];
+ case 'include_start_date':
+ return [$this, 'isStartIncluded'];
+ case 'include_end_date':
+ return [$this, 'isEndIncluded'];
+ case 'current':
+ return [$this, 'current'];
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get a property allowing both `DatePeriod` snakeCase and camelCase names.
+ *
+ * @param string $name
+ *
+ * @return bool|CarbonInterface|CarbonInterval|int|null
+ */
+ public function get(string $name)
+ {
+ $getter = $this->getGetter($name);
+
+ if ($getter) {
+ return $getter();
+ }
+
+ throw new UnknownGetterException($name);
+ }
+
+ /**
+ * Get a property allowing both `DatePeriod` snakeCase and camelCase names.
+ *
+ * @param string $name
+ *
+ * @return bool|CarbonInterface|CarbonInterval|int|null
+ */
+ public function __get(string $name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * Check if an attribute exists on the object
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function __isset(string $name): bool
+ {
+ return $this->getGetter($name) !== null;
+ }
+
+ /**
+ * @alias copy
+ *
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function clone()
+ {
+ return clone $this;
+ }
+
+ /**
+ * Set the iteration item class.
+ *
+ * @param string $dateClass
+ *
+ * @return static
+ */
+ public function setDateClass(string $dateClass)
+ {
+ if (!is_a($dateClass, CarbonInterface::class, true)) {
+ throw new NotACarbonClassException($dateClass);
+ }
+
+ $self = $this->copyIfImmutable();
+ $self->dateClass = $dateClass;
+
+ if (is_a($dateClass, Carbon::class, true)) {
+ $self->options = $self->options & ~static::IMMUTABLE;
+ } elseif (is_a($dateClass, CarbonImmutable::class, true)) {
+ $self->options = $self->options | static::IMMUTABLE;
+ }
+
+ return $self;
+ }
+
+ /**
+ * Returns iteration item date class.
+ *
+ * @return string
+ */
+ public function getDateClass(): string
+ {
+ return $this->dateClass;
+ }
+
+ /**
+ * Change the period date interval.
+ *
+ * @param DateInterval|string $interval
+ *
+ * @throws InvalidIntervalException
+ *
+ * @return static
+ */
+ public function setDateInterval($interval)
+ {
+ if (!$interval = CarbonInterval::make($interval)) {
+ throw new InvalidIntervalException('Invalid interval.');
+ }
+
+ if ($interval->spec() === 'PT0S' && !$interval->f && !$interval->getStep()) {
+ throw new InvalidIntervalException('Empty interval is not accepted.');
+ }
+
+ $self = $this->copyIfImmutable();
+ $self->dateInterval = $interval;
+
+ $self->isDefaultInterval = false;
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Invert the period date interval.
+ *
+ * @return static
+ */
+ public function invertDateInterval()
+ {
+ return $this->setDateInterval($this->dateInterval->invert());
+ }
+
+ /**
+ * Set start and end date.
+ *
+ * @param DateTime|DateTimeInterface|string $start
+ * @param DateTime|DateTimeInterface|string|null $end
+ *
+ * @return static
+ */
+ public function setDates($start, $end)
+ {
+ return $this->setStartDate($start)->setEndDate($end);
+ }
+
+ /**
+ * Change the period options.
+ *
+ * @param int|null $options
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return static
+ */
+ public function setOptions($options)
+ {
+ if (!\is_int($options) && $options !== null) {
+ throw new InvalidPeriodParameterException('Invalid options.');
+ }
+
+ $self = $this->copyIfImmutable();
+ $self->options = $options ?: 0;
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Get the period options.
+ *
+ * @return int
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Toggle given options on or off.
+ *
+ * @param int $options
+ * @param bool|null $state
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public function toggleOptions($options, $state = null)
+ {
+ if ($state === null) {
+ $state = ($this->options & $options) !== $options;
+ }
+
+ return $this->setOptions(
+ $state ?
+ $this->options | $options :
+ $this->options & ~$options
+ );
+ }
+
+ /**
+ * Toggle EXCLUDE_START_DATE option.
+ *
+ * @param bool $state
+ *
+ * @return static
+ */
+ public function excludeStartDate($state = true)
+ {
+ return $this->toggleOptions(static::EXCLUDE_START_DATE, $state);
+ }
+
+ /**
+ * Toggle EXCLUDE_END_DATE option.
+ *
+ * @param bool $state
+ *
+ * @return static
+ */
+ public function excludeEndDate($state = true)
+ {
+ return $this->toggleOptions(static::EXCLUDE_END_DATE, $state);
+ }
+
+ /**
+ * Get the underlying date interval.
+ *
+ * @return CarbonInterval
+ */
+ public function getDateInterval()
+ {
+ return $this->dateInterval->copy();
+ }
+
+ /**
+ * Get start date of the period.
+ *
+ * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval.
+ *
+ * @return CarbonInterface
+ */
+ public function getStartDate(string $rounding = null)
+ {
+ $date = $this->startDate->avoidMutation();
+
+ return $rounding ? $date->round($this->getDateInterval(), $rounding) : $date;
+ }
+
+ /**
+ * Get end date of the period.
+ *
+ * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval.
+ *
+ * @return CarbonInterface|null
+ */
+ public function getEndDate(string $rounding = null)
+ {
+ if (!$this->endDate) {
+ return null;
+ }
+
+ $date = $this->endDate->avoidMutation();
+
+ return $rounding ? $date->round($this->getDateInterval(), $rounding) : $date;
+ }
+
+ /**
+ * Get number of recurrences.
+ *
+ * @return int|float|null
+ */
+ public function getRecurrences()
+ {
+ return $this->recurrences;
+ }
+
+ /**
+ * Returns true if the start date should be excluded.
+ *
+ * @return bool
+ */
+ public function isStartExcluded()
+ {
+ return ($this->options & static::EXCLUDE_START_DATE) !== 0;
+ }
+
+ /**
+ * Returns true if the end date should be excluded.
+ *
+ * @return bool
+ */
+ public function isEndExcluded()
+ {
+ return ($this->options & static::EXCLUDE_END_DATE) !== 0;
+ }
+
+ /**
+ * Returns true if the start date should be included.
+ *
+ * @return bool
+ */
+ public function isStartIncluded()
+ {
+ return !$this->isStartExcluded();
+ }
+
+ /**
+ * Returns true if the end date should be included.
+ *
+ * @return bool
+ */
+ public function isEndIncluded()
+ {
+ return !$this->isEndExcluded();
+ }
+
+ /**
+ * Return the start if it's included by option, else return the start + 1 period interval.
+ *
+ * @return CarbonInterface
+ */
+ public function getIncludedStartDate()
+ {
+ $start = $this->getStartDate();
+
+ if ($this->isStartExcluded()) {
+ return $start->add($this->getDateInterval());
+ }
+
+ return $start;
+ }
+
+ /**
+ * Return the end if it's included by option, else return the end - 1 period interval.
+ * Warning: if the period has no fixed end, this method will iterate the period to calculate it.
+ *
+ * @return CarbonInterface
+ */
+ public function getIncludedEndDate()
+ {
+ $end = $this->getEndDate();
+
+ if (!$end) {
+ return $this->calculateEnd();
+ }
+
+ if ($this->isEndExcluded()) {
+ return $end->sub($this->getDateInterval());
+ }
+
+ return $end;
+ }
+
+ /**
+ * Add a filter to the stack.
+ *
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ *
+ * @param callable $callback
+ * @param string $name
+ *
+ * @return static
+ */
+ public function addFilter($callback, $name = null)
+ {
+ $self = $this->copyIfImmutable();
+ $tuple = $self->createFilterTuple(\func_get_args());
+
+ $self->filters[] = $tuple;
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Prepend a filter to the stack.
+ *
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ *
+ * @param callable $callback
+ * @param string $name
+ *
+ * @return static
+ */
+ public function prependFilter($callback, $name = null)
+ {
+ $self = $this->copyIfImmutable();
+ $tuple = $self->createFilterTuple(\func_get_args());
+
+ array_unshift($self->filters, $tuple);
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Remove a filter by instance or name.
+ *
+ * @param callable|string $filter
+ *
+ * @return static
+ */
+ public function removeFilter($filter)
+ {
+ $self = $this->copyIfImmutable();
+ $key = \is_callable($filter) ? 0 : 1;
+
+ $self->filters = array_values(array_filter(
+ $this->filters,
+ function ($tuple) use ($key, $filter) {
+ return $tuple[$key] !== $filter;
+ }
+ ));
+
+ $self->updateInternalState();
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Return whether given instance or name is in the filter stack.
+ *
+ * @param callable|string $filter
+ *
+ * @return bool
+ */
+ public function hasFilter($filter)
+ {
+ $key = \is_callable($filter) ? 0 : 1;
+
+ foreach ($this->filters as $tuple) {
+ if ($tuple[$key] === $filter) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get filters stack.
+ *
+ * @return array
+ */
+ public function getFilters()
+ {
+ return $this->filters;
+ }
+
+ /**
+ * Set filters stack.
+ *
+ * @param array $filters
+ *
+ * @return static
+ */
+ public function setFilters(array $filters)
+ {
+ $self = $this->copyIfImmutable();
+ $self->filters = $filters;
+
+ $self->updateInternalState();
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Reset filters stack.
+ *
+ * @return static
+ */
+ public function resetFilters()
+ {
+ $self = $this->copyIfImmutable();
+ $self->filters = [];
+
+ if ($self->endDate !== null) {
+ $self->filters[] = [static::END_DATE_FILTER, null];
+ }
+
+ if ($self->recurrences !== null) {
+ $self->filters[] = [static::RECURRENCES_FILTER, null];
+ }
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Add a recurrences filter (set maximum number of recurrences).
+ *
+ * @param int|float|null $recurrences
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return static
+ */
+ public function setRecurrences($recurrences)
+ {
+ if ((!is_numeric($recurrences) && $recurrences !== null) || $recurrences < 0) {
+ throw new InvalidPeriodParameterException('Invalid number of recurrences.');
+ }
+
+ if ($recurrences === null) {
+ return $this->removeFilter(static::RECURRENCES_FILTER);
+ }
+
+ /** @var self $self */
+ $self = $this->copyIfImmutable();
+ $self->recurrences = $recurrences === INF ? INF : (int) $recurrences;
+
+ if (!$self->hasFilter(static::RECURRENCES_FILTER)) {
+ return $self->addFilter(static::RECURRENCES_FILTER);
+ }
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Change the period start date.
+ *
+ * @param DateTime|DateTimeInterface|string $date
+ * @param bool|null $inclusive
+ *
+ * @throws InvalidPeriodDateException
+ *
+ * @return static
+ */
+ public function setStartDate($date, $inclusive = null)
+ {
+ if (!$this->isInfiniteDate($date) && !($date = ([$this->dateClass, 'make'])($date))) {
+ throw new InvalidPeriodDateException('Invalid start date.');
+ }
+
+ $self = $this->copyIfImmutable();
+ $self->startDate = $date;
+
+ if ($inclusive !== null) {
+ $self = $self->toggleOptions(static::EXCLUDE_START_DATE, !$inclusive);
+ }
+
+ return $self;
+ }
+
+ /**
+ * Change the period end date.
+ *
+ * @param DateTime|DateTimeInterface|string|null $date
+ * @param bool|null $inclusive
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return static
+ */
+ public function setEndDate($date, $inclusive = null)
+ {
+ if ($date !== null && !$this->isInfiniteDate($date) && !$date = ([$this->dateClass, 'make'])($date)) {
+ throw new InvalidPeriodDateException('Invalid end date.');
+ }
+
+ if (!$date) {
+ return $this->removeFilter(static::END_DATE_FILTER);
+ }
+
+ $self = $this->copyIfImmutable();
+ $self->endDate = $date;
+
+ if ($inclusive !== null) {
+ $self = $self->toggleOptions(static::EXCLUDE_END_DATE, !$inclusive);
+ }
+
+ if (!$self->hasFilter(static::END_DATE_FILTER)) {
+ return $self->addFilter(static::END_DATE_FILTER);
+ }
+
+ $self->handleChangedParameters();
+
+ return $self;
+ }
+
+ /**
+ * Check if the current position is valid.
+ *
+ * @return bool
+ */
+ #[ReturnTypeWillChange]
+ public function valid()
+ {
+ return $this->validateCurrentDate() === true;
+ }
+
+ /**
+ * Return the current key.
+ *
+ * @return int|null
+ */
+ #[ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->valid()
+ ? $this->key
+ : null;
+ }
+
+ /**
+ * Return the current date.
+ *
+ * @return CarbonInterface|null
+ */
+ #[ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->valid()
+ ? $this->prepareForReturn($this->current)
+ : null;
+ }
+
+ /**
+ * Move forward to the next date.
+ *
+ * @throws RuntimeException
+ *
+ * @return void
+ */
+ #[ReturnTypeWillChange]
+ public function next()
+ {
+ if ($this->current === null) {
+ $this->rewind();
+ }
+
+ if ($this->validationResult !== static::END_ITERATION) {
+ $this->key++;
+
+ $this->incrementCurrentDateUntilValid();
+ }
+ }
+
+ /**
+ * Rewind to the start date.
+ *
+ * Iterating over a date in the UTC timezone avoids bug during backward DST change.
+ *
+ * @see https://bugs.php.net/bug.php?id=72255
+ * @see https://bugs.php.net/bug.php?id=74274
+ * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time
+ *
+ * @throws RuntimeException
+ *
+ * @return void
+ */
+ #[ReturnTypeWillChange]
+ public function rewind()
+ {
+ $this->key = 0;
+ $this->current = ([$this->dateClass, 'make'])($this->startDate);
+ $settings = $this->getSettings();
+
+ if ($this->hasLocalTranslator()) {
+ $settings['locale'] = $this->getTranslatorLocale();
+ }
+
+ $this->current->settings($settings);
+ $this->timezone = static::intervalHasTime($this->dateInterval) ? $this->current->getTimezone() : null;
+
+ if ($this->timezone) {
+ $this->current = $this->current->utc();
+ }
+
+ $this->validationResult = null;
+
+ if ($this->isStartExcluded() || $this->validateCurrentDate() === false) {
+ $this->incrementCurrentDateUntilValid();
+ }
+ }
+
+ /**
+ * Skip iterations and returns iteration state (false if ended, true if still valid).
+ *
+ * @param int $count steps number to skip (1 by default)
+ *
+ * @return bool
+ */
+ public function skip($count = 1)
+ {
+ for ($i = $count; $this->valid() && $i > 0; $i--) {
+ $this->next();
+ }
+
+ return $this->valid();
+ }
+
+ /**
+ * Format the date period as ISO 8601.
+ *
+ * @return string
+ */
+ public function toIso8601String()
+ {
+ $parts = [];
+
+ if ($this->recurrences !== null) {
+ $parts[] = 'R'.$this->recurrences;
+ }
+
+ $parts[] = $this->startDate->toIso8601String();
+
+ $parts[] = $this->dateInterval->spec();
+
+ if ($this->endDate !== null) {
+ $parts[] = $this->endDate->toIso8601String();
+ }
+
+ return implode('/', $parts);
+ }
+
+ /**
+ * Convert the date period into a string.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $format = $this->localToStringFormat ?? static::$toStringFormat;
+
+ if ($format instanceof Closure) {
+ return $format($this);
+ }
+
+ $translator = ([$this->dateClass, 'getTranslator'])();
+
+ $parts = [];
+
+ $format = $format ?? (
+ !$this->startDate->isStartOfDay() || ($this->endDate && !$this->endDate->isStartOfDay())
+ ? 'Y-m-d H:i:s'
+ : 'Y-m-d'
+ );
+
+ if ($this->recurrences !== null) {
+ $parts[] = $this->translate('period_recurrences', [], $this->recurrences, $translator);
+ }
+
+ $parts[] = $this->translate('period_interval', [':interval' => $this->dateInterval->forHumans([
+ 'join' => true,
+ ])], null, $translator);
+
+ $parts[] = $this->translate('period_start_date', [':date' => $this->startDate->rawFormat($format)], null, $translator);
+
+ if ($this->endDate !== null) {
+ $parts[] = $this->translate('period_end_date', [':date' => $this->endDate->rawFormat($format)], null, $translator);
+ }
+
+ $result = implode(' ', $parts);
+
+ return mb_strtoupper(mb_substr($result, 0, 1)).mb_substr($result, 1);
+ }
+
+ /**
+ * Format the date period as ISO 8601.
+ *
+ * @return string
+ */
+ public function spec()
+ {
+ return $this->toIso8601String();
+ }
+
+ /**
+ * Cast the current instance into the given class.
+ *
+ * @param string $className The $className::instance() method will be called to cast the current object.
+ *
+ * @return DatePeriod
+ */
+ public function cast(string $className)
+ {
+ if (!method_exists($className, 'instance')) {
+ if (is_a($className, DatePeriod::class, true)) {
+ return new $className(
+ $this->rawDate($this->getStartDate()),
+ $this->getDateInterval(),
+ $this->getEndDate() ? $this->rawDate($this->getIncludedEndDate()) : $this->getRecurrences(),
+ $this->isStartExcluded() ? DatePeriod::EXCLUDE_START_DATE : 0
+ );
+ }
+
+ throw new InvalidCastException("$className has not the instance() method needed to cast the date.");
+ }
+
+ return $className::instance($this);
+ }
+
+ /**
+ * Return native DatePeriod PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(CarbonPeriod::create('2021-01-05', '2021-02-15')->toDatePeriod());
+ * ```
+ *
+ * @return DatePeriod
+ */
+ public function toDatePeriod()
+ {
+ return $this->cast(DatePeriod::class);
+ }
+
+ /**
+ * Return `true` if the period has no custom filter and is guaranteed to be endless.
+ *
+ * Note that we can't check if a period is endless as soon as it has custom filters
+ * because filters can emit `CarbonPeriod::END_ITERATION` to stop the iteration in
+ * a way we can't predict without actually iterating the period.
+ */
+ public function isUnfilteredAndEndLess(): bool
+ {
+ foreach ($this->filters as $filter) {
+ switch ($filter) {
+ case [static::RECURRENCES_FILTER, null]:
+ if ($this->recurrences !== null && is_finite($this->recurrences)) {
+ return false;
+ }
+
+ break;
+
+ case [static::END_DATE_FILTER, null]:
+ if ($this->endDate !== null && !$this->endDate->isEndOfTime()) {
+ return false;
+ }
+
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Convert the date period into an array without changing current iteration state.
+ *
+ * @return CarbonInterface[]
+ */
+ public function toArray()
+ {
+ if ($this->isUnfilteredAndEndLess()) {
+ throw new EndLessPeriodException("Endless period can't be converted to array nor counted.");
+ }
+
+ $state = [
+ $this->key,
+ $this->current ? $this->current->avoidMutation() : null,
+ $this->validationResult,
+ ];
+
+ $result = iterator_to_array($this);
+
+ [$this->key, $this->current, $this->validationResult] = $state;
+
+ return $result;
+ }
+
+ /**
+ * Count dates in the date period.
+ *
+ * @return int
+ */
+ #[ReturnTypeWillChange]
+ public function count()
+ {
+ return \count($this->toArray());
+ }
+
+ /**
+ * Return the first date in the date period.
+ *
+ * @return CarbonInterface|null
+ */
+ public function first()
+ {
+ if ($this->isUnfilteredAndEndLess()) {
+ foreach ($this as $date) {
+ $this->rewind();
+
+ return $date;
+ }
+
+ return null;
+ }
+
+ return ($this->toArray() ?: [])[0] ?? null;
+ }
+
+ /**
+ * Return the last date in the date period.
+ *
+ * @return CarbonInterface|null
+ */
+ public function last()
+ {
+ $array = $this->toArray();
+
+ return $array ? $array[\count($array) - 1] : null;
+ }
+
+ /**
+ * Convert the date period into a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Add aliases for setters.
+ *
+ * CarbonPeriod::days(3)->hours(5)->invert()
+ * ->sinceNow()->until('2010-01-10')
+ * ->filter(...)
+ * ->count()
+ *
+ * Note: We use magic method to let static and instance aliases with the same names.
+ *
+ * @param string $method
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (static::hasMacro($method)) {
+ return static::bindMacroContext($this, function () use (&$method, &$parameters) {
+ return $this->callMacro($method, $parameters);
+ });
+ }
+
+ $roundedValue = $this->callRoundMethod($method, $parameters);
+
+ if ($roundedValue !== null) {
+ return $roundedValue;
+ }
+
+ switch ($method) {
+ case 'start':
+ case 'since':
+ self::setDefaultParameters($parameters, [
+ [0, 'date', null],
+ ]);
+
+ return $this->setStartDate(...$parameters);
+
+ case 'sinceNow':
+ return $this->setStartDate(new Carbon(), ...$parameters);
+
+ case 'end':
+ case 'until':
+ self::setDefaultParameters($parameters, [
+ [0, 'date', null],
+ ]);
+
+ return $this->setEndDate(...$parameters);
+
+ case 'untilNow':
+ return $this->setEndDate(new Carbon(), ...$parameters);
+
+ case 'dates':
+ case 'between':
+ self::setDefaultParameters($parameters, [
+ [0, 'start', null],
+ [1, 'end', null],
+ ]);
+
+ return $this->setDates(...$parameters);
+
+ case 'recurrences':
+ case 'times':
+ self::setDefaultParameters($parameters, [
+ [0, 'recurrences', null],
+ ]);
+
+ return $this->setRecurrences(...$parameters);
+
+ case 'options':
+ self::setDefaultParameters($parameters, [
+ [0, 'options', null],
+ ]);
+
+ return $this->setOptions(...$parameters);
+
+ case 'toggle':
+ self::setDefaultParameters($parameters, [
+ [0, 'options', null],
+ ]);
+
+ return $this->toggleOptions(...$parameters);
+
+ case 'filter':
+ case 'push':
+ return $this->addFilter(...$parameters);
+
+ case 'prepend':
+ return $this->prependFilter(...$parameters);
+
+ case 'filters':
+ self::setDefaultParameters($parameters, [
+ [0, 'filters', []],
+ ]);
+
+ return $this->setFilters(...$parameters);
+
+ case 'interval':
+ case 'each':
+ case 'every':
+ case 'step':
+ case 'stepBy':
+ return $this->setDateInterval(...$parameters);
+
+ case 'invert':
+ return $this->invertDateInterval();
+
+ case 'years':
+ case 'year':
+ case 'months':
+ case 'month':
+ case 'weeks':
+ case 'week':
+ case 'days':
+ case 'dayz':
+ case 'day':
+ case 'hours':
+ case 'hour':
+ case 'minutes':
+ case 'minute':
+ case 'seconds':
+ case 'second':
+ case 'milliseconds':
+ case 'millisecond':
+ case 'microseconds':
+ case 'microsecond':
+ return $this->setDateInterval((
+ // Override default P1D when instantiating via fluent setters.
+ [$this->isDefaultInterval ? new CarbonInterval('PT0S') : $this->dateInterval, $method]
+ )(...$parameters));
+ }
+
+ if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) {
+ throw new UnknownMethodException($method);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the instance's timezone from a string or object and apply it to start/end.
+ *
+ * @param \DateTimeZone|string $timezone
+ *
+ * @return static
+ */
+ public function setTimezone($timezone)
+ {
+ $self = $this->copyIfImmutable();
+ $self->tzName = $timezone;
+ $self->timezone = $timezone;
+
+ if ($self->startDate) {
+ $self = $self->setStartDate($self->startDate->setTimezone($timezone));
+ }
+
+ if ($self->endDate) {
+ $self = $self->setEndDate($self->endDate->setTimezone($timezone));
+ }
+
+ return $self;
+ }
+
+ /**
+ * Set the instance's timezone from a string or object and add/subtract the offset difference to start/end.
+ *
+ * @param \DateTimeZone|string $timezone
+ *
+ * @return static
+ */
+ public function shiftTimezone($timezone)
+ {
+ $self = $this->copyIfImmutable();
+ $self->tzName = $timezone;
+ $self->timezone = $timezone;
+
+ if ($self->startDate) {
+ $self = $self->setStartDate($self->startDate->shiftTimezone($timezone));
+ }
+
+ if ($self->endDate) {
+ $self = $self->setEndDate($self->endDate->shiftTimezone($timezone));
+ }
+
+ return $self;
+ }
+
+ /**
+ * Returns the end is set, else calculated from start an recurrences.
+ *
+ * @param string|null $rounding Optional rounding 'floor', 'ceil', 'round' using the period interval.
+ *
+ * @return CarbonInterface
+ */
+ public function calculateEnd(string $rounding = null)
+ {
+ if ($end = $this->getEndDate($rounding)) {
+ return $end;
+ }
+
+ if ($this->dateInterval->isEmpty()) {
+ return $this->getStartDate($rounding);
+ }
+
+ $date = $this->getEndFromRecurrences() ?? $this->iterateUntilEnd();
+
+ if ($date && $rounding) {
+ $date = $date->avoidMutation()->round($this->getDateInterval(), $rounding);
+ }
+
+ return $date;
+ }
+
+ /**
+ * @return CarbonInterface|null
+ */
+ private function getEndFromRecurrences()
+ {
+ if ($this->recurrences === null) {
+ throw new UnreachableException(
+ "Could not calculate period end without either explicit end or recurrences.\n".
+ "If you're looking for a forever-period, use ->setRecurrences(INF)."
+ );
+ }
+
+ if ($this->recurrences === INF) {
+ $start = $this->getStartDate();
+
+ return $start < $start->avoidMutation()->add($this->getDateInterval())
+ ? CarbonImmutable::endOfTime()
+ : CarbonImmutable::startOfTime();
+ }
+
+ if ($this->filters === [[static::RECURRENCES_FILTER, null]]) {
+ return $this->getStartDate()->avoidMutation()->add(
+ $this->getDateInterval()->times(
+ $this->recurrences - ($this->isStartExcluded() ? 0 : 1)
+ )
+ );
+ }
+
+ return null;
+ }
+
+ /**
+ * @return CarbonInterface|null
+ */
+ private function iterateUntilEnd()
+ {
+ $attempts = 0;
+ $date = null;
+
+ foreach ($this as $date) {
+ if (++$attempts > static::END_MAX_ATTEMPTS) {
+ throw new UnreachableException(
+ 'Could not calculate period end after iterating '.static::END_MAX_ATTEMPTS.' times.'
+ );
+ }
+ }
+
+ return $date;
+ }
+
+ /**
+ * Returns true if the current period overlaps the given one (if 1 parameter passed)
+ * or the period between 2 dates (if 2 parameters passed).
+ *
+ * @param CarbonPeriod|\DateTimeInterface|Carbon|CarbonImmutable|string $rangeOrRangeStart
+ * @param \DateTimeInterface|Carbon|CarbonImmutable|string|null $rangeEnd
+ *
+ * @return bool
+ */
+ public function overlaps($rangeOrRangeStart, $rangeEnd = null)
+ {
+ $range = $rangeEnd ? static::create($rangeOrRangeStart, $rangeEnd) : $rangeOrRangeStart;
+
+ if (!($range instanceof self)) {
+ $range = static::create($range);
+ }
+
+ [$start, $end] = $this->orderCouple($this->getStartDate(), $this->calculateEnd());
+ [$rangeStart, $rangeEnd] = $this->orderCouple($range->getStartDate(), $range->calculateEnd());
+
+ return $end > $rangeStart && $rangeEnd > $start;
+ }
+
+ /**
+ * Execute a given function on each date of the period.
+ *
+ * @example
+ * ```
+ * Carbon::create('2020-11-29')->daysUntil('2020-12-24')->forEach(function (Carbon $date) {
+ * echo $date->diffInDays('2020-12-25')." days before Christmas!\n";
+ * });
+ * ```
+ *
+ * @param callable $callback
+ */
+ public function forEach(callable $callback)
+ {
+ foreach ($this as $date) {
+ $callback($date);
+ }
+ }
+
+ /**
+ * Execute a given function on each date of the period and yield the result of this function.
+ *
+ * @example
+ * ```
+ * $period = Carbon::create('2020-11-29')->daysUntil('2020-12-24');
+ * echo implode("\n", iterator_to_array($period->map(function (Carbon $date) {
+ * return $date->diffInDays('2020-12-25').' days before Christmas!';
+ * })));
+ * ```
+ *
+ * @param callable $callback
+ *
+ * @return \Generator
+ */
+ public function map(callable $callback)
+ {
+ foreach ($this as $date) {
+ yield $callback($date);
+ }
+ }
+
+ /**
+ * Determines if the instance is equal to another.
+ * Warning: if options differ, instances wil never be equal.
+ *
+ * @param mixed $period
+ *
+ * @see equalTo()
+ *
+ * @return bool
+ */
+ public function eq($period): bool
+ {
+ return $this->equalTo($period);
+ }
+
+ /**
+ * Determines if the instance is equal to another.
+ * Warning: if options differ, instances wil never be equal.
+ *
+ * @param mixed $period
+ *
+ * @return bool
+ */
+ public function equalTo($period): bool
+ {
+ if (!($period instanceof self)) {
+ $period = self::make($period);
+ }
+
+ $end = $this->getEndDate();
+
+ return $period !== null
+ && $this->getDateInterval()->eq($period->getDateInterval())
+ && $this->getStartDate()->eq($period->getStartDate())
+ && ($end ? $end->eq($period->getEndDate()) : $this->getRecurrences() === $period->getRecurrences())
+ && ($this->getOptions() & (~static::IMMUTABLE)) === ($period->getOptions() & (~static::IMMUTABLE));
+ }
+
+ /**
+ * Determines if the instance is not equal to another.
+ * Warning: if options differ, instances wil never be equal.
+ *
+ * @param mixed $period
+ *
+ * @see notEqualTo()
+ *
+ * @return bool
+ */
+ public function ne($period): bool
+ {
+ return $this->notEqualTo($period);
+ }
+
+ /**
+ * Determines if the instance is not equal to another.
+ * Warning: if options differ, instances wil never be equal.
+ *
+ * @param mixed $period
+ *
+ * @return bool
+ */
+ public function notEqualTo($period): bool
+ {
+ return !$this->eq($period);
+ }
+
+ /**
+ * Determines if the start date is before an other given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function startsBefore($date = null): bool
+ {
+ return $this->getStartDate()->lessThan($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the start date is before or the same as a given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function startsBeforeOrAt($date = null): bool
+ {
+ return $this->getStartDate()->lessThanOrEqualTo($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the start date is after an other given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function startsAfter($date = null): bool
+ {
+ return $this->getStartDate()->greaterThan($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the start date is after or the same as a given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function startsAfterOrAt($date = null): bool
+ {
+ return $this->getStartDate()->greaterThanOrEqualTo($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the start date is the same as a given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function startsAt($date = null): bool
+ {
+ return $this->getStartDate()->equalTo($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the end date is before an other given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function endsBefore($date = null): bool
+ {
+ return $this->calculateEnd()->lessThan($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the end date is before or the same as a given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function endsBeforeOrAt($date = null): bool
+ {
+ return $this->calculateEnd()->lessThanOrEqualTo($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the end date is after an other given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function endsAfter($date = null): bool
+ {
+ return $this->calculateEnd()->greaterThan($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the end date is after or the same as a given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function endsAfterOrAt($date = null): bool
+ {
+ return $this->calculateEnd()->greaterThanOrEqualTo($this->resolveCarbon($date));
+ }
+
+ /**
+ * Determines if the end date is the same as a given date.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @param mixed $date
+ *
+ * @return bool
+ */
+ public function endsAt($date = null): bool
+ {
+ return $this->calculateEnd()->equalTo($this->resolveCarbon($date));
+ }
+
+ /**
+ * Return true if start date is now or later.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @return bool
+ */
+ public function isStarted(): bool
+ {
+ return $this->startsBeforeOrAt();
+ }
+
+ /**
+ * Return true if end date is now or later.
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @return bool
+ */
+ public function isEnded(): bool
+ {
+ return $this->endsBeforeOrAt();
+ }
+
+ /**
+ * Return true if now is between start date (included) and end date (excluded).
+ * (Rather start/end are included by options is ignored.)
+ *
+ * @return bool
+ */
+ public function isInProgress(): bool
+ {
+ return $this->isStarted() && !$this->isEnded();
+ }
+
+ /**
+ * Round the current instance at the given unit with given precision if specified and the given function.
+ *
+ * @param string $unit
+ * @param float|int|string|\DateInterval|null $precision
+ * @param string $function
+ *
+ * @return static
+ */
+ public function roundUnit($unit, $precision = 1, $function = 'round')
+ {
+ $self = $this->copyIfImmutable();
+ $self = $self->setStartDate($self->getStartDate()->roundUnit($unit, $precision, $function));
+
+ if ($self->endDate) {
+ $self = $self->setEndDate($self->getEndDate()->roundUnit($unit, $precision, $function));
+ }
+
+ return $self->setDateInterval($self->getDateInterval()->roundUnit($unit, $precision, $function));
+ }
+
+ /**
+ * Truncate the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return static
+ */
+ public function floorUnit($unit, $precision = 1)
+ {
+ return $this->roundUnit($unit, $precision, 'floor');
+ }
+
+ /**
+ * Ceil the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return static
+ */
+ public function ceilUnit($unit, $precision = 1)
+ {
+ return $this->roundUnit($unit, $precision, 'ceil');
+ }
+
+ /**
+ * Round the current instance second with given precision if specified (else period interval is used).
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ * @param string $function
+ *
+ * @return static
+ */
+ public function round($precision = null, $function = 'round')
+ {
+ return $this->roundWith(
+ $precision ?? $this->getDateInterval()->setLocalTranslator(TranslatorImmutable::get('en'))->forHumans(),
+ $function
+ );
+ }
+
+ /**
+ * Round the current instance second with given precision if specified (else period interval is used).
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return static
+ */
+ public function floor($precision = null)
+ {
+ return $this->round($precision, 'floor');
+ }
+
+ /**
+ * Ceil the current instance second with given precision if specified (else period interval is used).
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return static
+ */
+ public function ceil($precision = null)
+ {
+ return $this->round($precision, 'ceil');
+ }
+
+ /**
+ * Specify data which should be serialized to JSON.
+ *
+ * @link https://php.net/manual/en/jsonserializable.jsonserialize.php
+ *
+ * @return CarbonInterface[]
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Return true if the given date is between start and end.
+ *
+ * @param \Carbon\Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|\DateTimeInterface|string|null $date
+ *
+ * @return bool
+ */
+ public function contains($date = null): bool
+ {
+ $startMethod = 'startsBefore'.($this->isStartIncluded() ? 'OrAt' : '');
+ $endMethod = 'endsAfter'.($this->isEndIncluded() ? 'OrAt' : '');
+
+ return $this->$startMethod($date) && $this->$endMethod($date);
+ }
+
+ /**
+ * Return true if the current period follows a given other period (with no overlap).
+ * For instance, [2019-08-01 -> 2019-08-12] follows [2019-07-29 -> 2019-07-31]
+ * Note than in this example, follows() would be false if 2019-08-01 or 2019-07-31 was excluded by options.
+ *
+ * @param \Carbon\CarbonPeriod|\DatePeriod|string $period
+ *
+ * @return bool
+ */
+ public function follows($period, ...$arguments): bool
+ {
+ $period = $this->resolveCarbonPeriod($period, ...$arguments);
+
+ return $this->getIncludedStartDate()->equalTo($period->getIncludedEndDate()->add($period->getDateInterval()));
+ }
+
+ /**
+ * Return true if the given other period follows the current one (with no overlap).
+ * For instance, [2019-07-29 -> 2019-07-31] is followed by [2019-08-01 -> 2019-08-12]
+ * Note than in this example, isFollowedBy() would be false if 2019-08-01 or 2019-07-31 was excluded by options.
+ *
+ * @param \Carbon\CarbonPeriod|\DatePeriod|string $period
+ *
+ * @return bool
+ */
+ public function isFollowedBy($period, ...$arguments): bool
+ {
+ $period = $this->resolveCarbonPeriod($period, ...$arguments);
+
+ return $period->follows($this);
+ }
+
+ /**
+ * Return true if the given period either follows or is followed by the current one.
+ *
+ * @see follows()
+ * @see isFollowedBy()
+ *
+ * @param \Carbon\CarbonPeriod|\DatePeriod|string $period
+ *
+ * @return bool
+ */
+ public function isConsecutiveWith($period, ...$arguments): bool
+ {
+ return $this->follows($period, ...$arguments) || $this->isFollowedBy($period, ...$arguments);
+ }
+
+ /**
+ * Update properties after removing built-in filters.
+ *
+ * @return void
+ */
+ protected function updateInternalState()
+ {
+ if (!$this->hasFilter(static::END_DATE_FILTER)) {
+ $this->endDate = null;
+ }
+
+ if (!$this->hasFilter(static::RECURRENCES_FILTER)) {
+ $this->recurrences = null;
+ }
+ }
+
+ /**
+ * Create a filter tuple from raw parameters.
+ *
+ * Will create an automatic filter callback for one of Carbon's is* methods.
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ protected function createFilterTuple(array $parameters)
+ {
+ $method = array_shift($parameters);
+
+ if (!$this->isCarbonPredicateMethod($method)) {
+ return [$method, array_shift($parameters)];
+ }
+
+ return [function ($date) use ($method, $parameters) {
+ return ([$date, $method])(...$parameters);
+ }, $method];
+ }
+
+ /**
+ * Return whether given callable is a string pointing to one of Carbon's is* methods
+ * and should be automatically converted to a filter callback.
+ *
+ * @param callable $callable
+ *
+ * @return bool
+ */
+ protected function isCarbonPredicateMethod($callable)
+ {
+ return \is_string($callable) && str_starts_with($callable, 'is') &&
+ (method_exists($this->dateClass, $callable) || ([$this->dateClass, 'hasMacro'])($callable));
+ }
+
+ /**
+ * Recurrences filter callback (limits number of recurrences).
+ *
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ *
+ * @param \Carbon\Carbon $current
+ * @param int $key
+ *
+ * @return bool|string
+ */
+ protected function filterRecurrences($current, $key)
+ {
+ if ($key < $this->recurrences) {
+ return true;
+ }
+
+ return static::END_ITERATION;
+ }
+
+ /**
+ * End date filter callback.
+ *
+ * @param \Carbon\Carbon $current
+ *
+ * @return bool|string
+ */
+ protected function filterEndDate($current)
+ {
+ if (!$this->isEndExcluded() && $current == $this->endDate) {
+ return true;
+ }
+
+ if ($this->dateInterval->invert ? $current > $this->endDate : $current < $this->endDate) {
+ return true;
+ }
+
+ return static::END_ITERATION;
+ }
+
+ /**
+ * End iteration filter callback.
+ *
+ * @return string
+ */
+ protected function endIteration()
+ {
+ return static::END_ITERATION;
+ }
+
+ /**
+ * Handle change of the parameters.
+ */
+ protected function handleChangedParameters()
+ {
+ if (($this->getOptions() & static::IMMUTABLE) && $this->dateClass === Carbon::class) {
+ $this->dateClass = CarbonImmutable::class;
+ } elseif (!($this->getOptions() & static::IMMUTABLE) && $this->dateClass === CarbonImmutable::class) {
+ $this->dateClass = Carbon::class;
+ }
+
+ $this->validationResult = null;
+ }
+
+ /**
+ * Validate current date and stop iteration when necessary.
+ *
+ * Returns true when current date is valid, false if it is not, or static::END_ITERATION
+ * when iteration should be stopped.
+ *
+ * @return bool|string
+ */
+ protected function validateCurrentDate()
+ {
+ if ($this->current === null) {
+ $this->rewind();
+ }
+
+ // Check after the first rewind to avoid repeating the initial validation.
+ return $this->validationResult ?? ($this->validationResult = $this->checkFilters());
+ }
+
+ /**
+ * Check whether current value and key pass all the filters.
+ *
+ * @return bool|string
+ */
+ protected function checkFilters()
+ {
+ $current = $this->prepareForReturn($this->current);
+
+ foreach ($this->filters as $tuple) {
+ $result = \call_user_func(
+ $tuple[0],
+ $current->avoidMutation(),
+ $this->key,
+ $this
+ );
+
+ if ($result === static::END_ITERATION) {
+ return static::END_ITERATION;
+ }
+
+ if (!$result) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Prepare given date to be returned to the external logic.
+ *
+ * @param CarbonInterface $date
+ *
+ * @return CarbonInterface
+ */
+ protected function prepareForReturn(CarbonInterface $date)
+ {
+ $date = ([$this->dateClass, 'make'])($date);
+
+ if ($this->timezone) {
+ $date = $date->setTimezone($this->timezone);
+ }
+
+ return $date;
+ }
+
+ /**
+ * Keep incrementing the current date until a valid date is found or the iteration is ended.
+ *
+ * @throws RuntimeException
+ *
+ * @return void
+ */
+ protected function incrementCurrentDateUntilValid()
+ {
+ $attempts = 0;
+
+ do {
+ $this->current = $this->current->add($this->dateInterval);
+
+ $this->validationResult = null;
+
+ if (++$attempts > static::NEXT_MAX_ATTEMPTS) {
+ throw new UnreachableException('Could not find next valid date.');
+ }
+ } while ($this->validateCurrentDate() === false);
+ }
+
+ /**
+ * Call given macro.
+ *
+ * @param string $name
+ * @param array $parameters
+ *
+ * @return mixed
+ */
+ protected function callMacro($name, $parameters)
+ {
+ $macro = static::$macros[$name];
+
+ if ($macro instanceof Closure) {
+ $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class);
+
+ return ($boundMacro ?: $macro)(...$parameters);
+ }
+
+ return $macro(...$parameters);
+ }
+
+ /**
+ * Return the Carbon instance passed through, a now instance in the same timezone
+ * if null given or parse the input if string given.
+ *
+ * @param \Carbon\Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|\DateTimeInterface|string|null $date
+ *
+ * @return \Carbon\CarbonInterface
+ */
+ protected function resolveCarbon($date = null)
+ {
+ return $this->getStartDate()->nowWithSameTz()->carbonize($date);
+ }
+
+ /**
+ * Resolve passed arguments or DatePeriod to a CarbonPeriod object.
+ *
+ * @param mixed $period
+ * @param mixed ...$arguments
+ *
+ * @return static
+ */
+ protected function resolveCarbonPeriod($period, ...$arguments)
+ {
+ if ($period instanceof self) {
+ return $period;
+ }
+
+ return $period instanceof DatePeriod
+ ? static::instance($period)
+ : static::create($period, ...$arguments);
+ }
+
+ private function orderCouple($first, $second): array
+ {
+ return $first > $second ? [$second, $first] : [$first, $second];
+ }
+
+ private function makeDateTime($value): ?DateTimeInterface
+ {
+ if ($value instanceof DateTimeInterface) {
+ return $value;
+ }
+
+ if (\is_string($value)) {
+ $value = trim($value);
+
+ if (!preg_match('/^P[\dT]/', $value) &&
+ !preg_match('/^R\d/', $value) &&
+ preg_match('/[a-z\d]/i', $value)
+ ) {
+ return Carbon::parse($value, $this->tzName);
+ }
+ }
+
+ return null;
+ }
+
+ private function isInfiniteDate($date): bool
+ {
+ return $date instanceof CarbonInterface && ($date->isEndOfTime() || $date->isStartOfTime());
+ }
+
+ private function rawDate($date): ?DateTimeInterface
+ {
+ if ($date === false || $date === null) {
+ return null;
+ }
+
+ if ($date instanceof CarbonInterface) {
+ return $date->isMutable()
+ ? $date->toDateTime()
+ : $date->toDateTimeImmutable();
+ }
+
+ if (\in_array(\get_class($date), [DateTime::class, DateTimeImmutable::class], true)) {
+ return $date;
+ }
+
+ $class = $date instanceof DateTime ? DateTime::class : DateTimeImmutable::class;
+
+ return new $class($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
+ }
+
+ private static function setDefaultParameters(array &$parameters, array $defaults): void
+ {
+ foreach ($defaults as [$index, $name, $value]) {
+ if (!\array_key_exists($index, $parameters) && !\array_key_exists($name, $parameters)) {
+ $parameters[$index] = $value;
+ }
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php b/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php
new file mode 100644
index 0000000..cda0733
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+class CarbonPeriodImmutable extends CarbonPeriod
+{
+ /**
+ * Date class of iteration items.
+ *
+ * @var string
+ */
+ protected $dateClass = CarbonImmutable::class;
+
+ /**
+ * Prepare the instance to be set (self if mutable to be mutated,
+ * copy if immutable to generate a new instance).
+ *
+ * @return static
+ */
+ protected function copyIfImmutable()
+ {
+ return $this->constructed ? clone $this : $this;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php b/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php
new file mode 100644
index 0000000..c81899f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/CarbonTimeZone.php
@@ -0,0 +1,320 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Exceptions\InvalidCastException;
+use Carbon\Exceptions\InvalidTimeZoneException;
+use DateTimeInterface;
+use DateTimeZone;
+use Throwable;
+
+class CarbonTimeZone extends DateTimeZone
+{
+ public function __construct($timezone = null)
+ {
+ parent::__construct(static::getDateTimeZoneNameFromMixed($timezone));
+ }
+
+ protected static function parseNumericTimezone($timezone)
+ {
+ if ($timezone <= -100 || $timezone >= 100) {
+ throw new InvalidTimeZoneException('Absolute timezone offset cannot be greater than 100.');
+ }
+
+ return ($timezone >= 0 ? '+' : '').ltrim($timezone, '+').':00';
+ }
+
+ protected static function getDateTimeZoneNameFromMixed($timezone)
+ {
+ if ($timezone === null) {
+ return date_default_timezone_get();
+ }
+
+ if (\is_string($timezone)) {
+ $timezone = preg_replace('/^\s*([+-]\d+)(\d{2})\s*$/', '$1:$2', $timezone);
+ }
+
+ if (is_numeric($timezone)) {
+ return static::parseNumericTimezone($timezone);
+ }
+
+ return $timezone;
+ }
+
+ protected static function getDateTimeZoneFromName(&$name)
+ {
+ return @timezone_open($name = (string) static::getDateTimeZoneNameFromMixed($name));
+ }
+
+ /**
+ * Cast the current instance into the given class.
+ *
+ * @param string $className The $className::instance() method will be called to cast the current object.
+ *
+ * @return DateTimeZone
+ */
+ public function cast(string $className)
+ {
+ if (!method_exists($className, 'instance')) {
+ if (is_a($className, DateTimeZone::class, true)) {
+ return new $className($this->getName());
+ }
+
+ throw new InvalidCastException("$className has not the instance() method needed to cast the date.");
+ }
+
+ return $className::instance($this);
+ }
+
+ /**
+ * Create a CarbonTimeZone from mixed input.
+ *
+ * @param DateTimeZone|string|int|null $object original value to get CarbonTimeZone from it.
+ * @param DateTimeZone|string|int|null $objectDump dump of the object for error messages.
+ *
+ * @throws InvalidTimeZoneException
+ *
+ * @return false|static
+ */
+ public static function instance($object = null, $objectDump = null)
+ {
+ $tz = $object;
+
+ if ($tz instanceof static) {
+ return $tz;
+ }
+
+ if ($tz === null) {
+ return new static();
+ }
+
+ if (!$tz instanceof DateTimeZone) {
+ $tz = static::getDateTimeZoneFromName($object);
+ }
+
+ if ($tz !== false) {
+ return new static($tz->getName());
+ }
+
+ if (Carbon::isStrictModeEnabled()) {
+ throw new InvalidTimeZoneException('Unknown or bad timezone ('.($objectDump ?: $object).')');
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns abbreviated name of the current timezone according to DST setting.
+ *
+ * @param bool $dst
+ *
+ * @return string
+ */
+ public function getAbbreviatedName($dst = false)
+ {
+ $name = $this->getName();
+
+ foreach ($this->listAbbreviations() as $abbreviation => $zones) {
+ foreach ($zones as $zone) {
+ if ($zone['timezone_id'] === $name && $zone['dst'] == $dst) {
+ return $abbreviation;
+ }
+ }
+ }
+
+ return 'unknown';
+ }
+
+ /**
+ * @alias getAbbreviatedName
+ *
+ * Returns abbreviated name of the current timezone according to DST setting.
+ *
+ * @param bool $dst
+ *
+ * @return string
+ */
+ public function getAbbr($dst = false)
+ {
+ return $this->getAbbreviatedName($dst);
+ }
+
+ /**
+ * Get the offset as string "sHH:MM" (such as "+00:00" or "-12:30").
+ *
+ * @param DateTimeInterface|null $date
+ *
+ * @return string
+ */
+ public function toOffsetName(DateTimeInterface $date = null)
+ {
+ return static::getOffsetNameFromMinuteOffset(
+ $this->getOffset($date ?: Carbon::now($this)) / 60
+ );
+ }
+
+ /**
+ * Returns a new CarbonTimeZone object using the offset string instead of region string.
+ *
+ * @param DateTimeInterface|null $date
+ *
+ * @return CarbonTimeZone
+ */
+ public function toOffsetTimeZone(DateTimeInterface $date = null)
+ {
+ return new static($this->toOffsetName($date));
+ }
+
+ /**
+ * Returns the first region string (such as "America/Toronto") that matches the current timezone or
+ * false if no match is found.
+ *
+ * @see timezone_name_from_abbr native PHP function.
+ *
+ * @param DateTimeInterface|null $date
+ * @param int $isDst
+ *
+ * @return string|false
+ */
+ public function toRegionName(DateTimeInterface $date = null, $isDst = 1)
+ {
+ $name = $this->getName();
+ $firstChar = substr($name, 0, 1);
+
+ if ($firstChar !== '+' && $firstChar !== '-') {
+ return $name;
+ }
+
+ $date = $date ?: Carbon::now($this);
+
+ // Integer construction no longer supported since PHP 8
+ // @codeCoverageIgnoreStart
+ try {
+ $offset = @$this->getOffset($date) ?: 0;
+ } catch (Throwable $e) {
+ $offset = 0;
+ }
+ // @codeCoverageIgnoreEnd
+
+ $name = @timezone_name_from_abbr('', $offset, $isDst);
+
+ if ($name) {
+ return $name;
+ }
+
+ foreach (timezone_identifiers_list() as $timezone) {
+ if (Carbon::instance($date)->tz($timezone)->getOffset() === $offset) {
+ return $timezone;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a new CarbonTimeZone object using the region string instead of offset string.
+ *
+ * @param DateTimeInterface|null $date
+ *
+ * @return CarbonTimeZone|false
+ */
+ public function toRegionTimeZone(DateTimeInterface $date = null)
+ {
+ $tz = $this->toRegionName($date);
+
+ if ($tz !== false) {
+ return new static($tz);
+ }
+
+ if (Carbon::isStrictModeEnabled()) {
+ throw new InvalidTimeZoneException('Unknown timezone for offset '.$this->getOffset($date ?: Carbon::now($this)).' seconds.');
+ }
+
+ return false;
+ }
+
+ /**
+ * Cast to string (get timezone name).
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getName();
+ }
+
+ /**
+ * Return the type number:
+ *
+ * Type 1; A UTC offset, such as -0300
+ * Type 2; A timezone abbreviation, such as GMT
+ * Type 3: A timezone identifier, such as Europe/London
+ */
+ public function getType(): int
+ {
+ return preg_match('/"timezone_type";i:(\d)/', serialize($this), $match) ? (int) $match[1] : 3;
+ }
+
+ /**
+ * Create a CarbonTimeZone from mixed input.
+ *
+ * @param DateTimeZone|string|int|null $object
+ *
+ * @return false|static
+ */
+ public static function create($object = null)
+ {
+ return static::instance($object);
+ }
+
+ /**
+ * Create a CarbonTimeZone from int/float hour offset.
+ *
+ * @param float $hourOffset number of hour of the timezone shift (can be decimal).
+ *
+ * @return false|static
+ */
+ public static function createFromHourOffset(float $hourOffset)
+ {
+ return static::createFromMinuteOffset($hourOffset * Carbon::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Create a CarbonTimeZone from int/float minute offset.
+ *
+ * @param float $minuteOffset number of total minutes of the timezone shift.
+ *
+ * @return false|static
+ */
+ public static function createFromMinuteOffset(float $minuteOffset)
+ {
+ return static::instance(static::getOffsetNameFromMinuteOffset($minuteOffset));
+ }
+
+ /**
+ * Convert a total minutes offset into a standardized timezone offset string.
+ *
+ * @param float $minutes number of total minutes of the timezone shift.
+ *
+ * @return string
+ */
+ public static function getOffsetNameFromMinuteOffset(float $minutes): string
+ {
+ $minutes = round($minutes);
+ $unsignedMinutes = abs($minutes);
+
+ return ($minutes < 0 ? '-' : '+').
+ str_pad((string) floor($unsignedMinutes / 60), 2, '0', STR_PAD_LEFT).
+ ':'.
+ str_pad((string) ($unsignedMinutes % 60), 2, '0', STR_PAD_LEFT);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php b/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php
new file mode 100644
index 0000000..4f35d6c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Cli/Invoker.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Cli;
+
+class Invoker
+{
+ public const CLI_CLASS_NAME = 'Carbon\\Cli';
+
+ protected function runWithCli(string $className, array $parameters): bool
+ {
+ $cli = new $className();
+
+ return $cli(...$parameters);
+ }
+
+ public function __invoke(...$parameters): bool
+ {
+ if (class_exists(self::CLI_CLASS_NAME)) {
+ return $this->runWithCli(self::CLI_CLASS_NAME, $parameters);
+ }
+
+ $function = (($parameters[1] ?? '') === 'install' ? ($parameters[2] ?? null) : null) ?: 'shell_exec';
+ $function('composer require carbon-cli/carbon-cli --no-interaction');
+
+ echo 'Installation succeeded.';
+
+ return true;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonDoctrineType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonDoctrineType.php
new file mode 100644
index 0000000..ccc457f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonDoctrineType.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Doctrine;
+
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+
+interface CarbonDoctrineType
+{
+ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform);
+
+ public function convertToPHPValue($value, AbstractPlatform $platform);
+
+ public function convertToDatabaseValue($value, AbstractPlatform $platform);
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php
new file mode 100644
index 0000000..bf476a7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonImmutableType.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Doctrine;
+
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+
+class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'carbon_immutable';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function requiresSQLCommentHint(AbstractPlatform $platform)
+ {
+ return true;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php
new file mode 100644
index 0000000..9289d84
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonType.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Doctrine;
+
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+
+class CarbonType extends DateTimeType implements CarbonDoctrineType
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'carbon';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function requiresSQLCommentHint(AbstractPlatform $platform)
+ {
+ return true;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php
new file mode 100644
index 0000000..ecfe17e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php
@@ -0,0 +1,123 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Doctrine;
+
+use Carbon\Carbon;
+use Carbon\CarbonInterface;
+use DateTimeInterface;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\ConversionException;
+use Exception;
+
+/**
+ * @template T of CarbonInterface
+ */
+trait CarbonTypeConverter
+{
+ /**
+ * @return class-string
+ */
+ protected function getCarbonClassName(): string
+ {
+ return Carbon::class;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
+ {
+ $precision = $fieldDeclaration['precision'] ?: 10;
+
+ if ($fieldDeclaration['secondPrecision'] ?? false) {
+ $precision = 0;
+ }
+
+ if ($precision === 10) {
+ $precision = DateTimeDefaultPrecision::get();
+ }
+
+ $type = parent::getSQLDeclaration($fieldDeclaration, $platform);
+
+ if (!$precision) {
+ return $type;
+ }
+
+ if (str_contains($type, '(')) {
+ return preg_replace('/\(\d+\)/', "($precision)", $type);
+ }
+
+ [$before, $after] = explode(' ', "$type ");
+
+ return trim("$before($precision) $after");
+ }
+
+ /**
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ *
+ * @return T|null
+ */
+ public function convertToPHPValue($value, AbstractPlatform $platform)
+ {
+ $class = $this->getCarbonClassName();
+
+ if ($value === null || is_a($value, $class)) {
+ return $value;
+ }
+
+ if ($value instanceof DateTimeInterface) {
+ return $class::instance($value);
+ }
+
+ $date = null;
+ $error = null;
+
+ try {
+ $date = $class::parse($value);
+ } catch (Exception $exception) {
+ $error = $exception;
+ }
+
+ if (!$date) {
+ throw ConversionException::conversionFailedFormat(
+ $value,
+ $this->getName(),
+ 'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
+ $error
+ );
+ }
+
+ return $date;
+ }
+
+ /**
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ *
+ * @return string|null
+ */
+ public function convertToDatabaseValue($value, AbstractPlatform $platform)
+ {
+ if ($value === null) {
+ return $value;
+ }
+
+ if ($value instanceof DateTimeInterface) {
+ return $value->format('Y-m-d H:i:s.u');
+ }
+
+ throw ConversionException::conversionFailedInvalidType(
+ $value,
+ $this->getName(),
+ ['null', 'DateTime', 'Carbon']
+ );
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
new file mode 100644
index 0000000..642fd41
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Doctrine;
+
+class DateTimeDefaultPrecision
+{
+ private static $precision = 6;
+
+ /**
+ * Change the default Doctrine datetime and datetime_immutable precision.
+ *
+ * @param int $precision
+ */
+ public static function set(int $precision): void
+ {
+ self::$precision = $precision;
+ }
+
+ /**
+ * Get the default Doctrine datetime and datetime_immutable precision.
+ *
+ * @return int
+ */
+ public static function get(): int
+ {
+ return self::$precision;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php
new file mode 100644
index 0000000..4992710
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php
@@ -0,0 +1,24 @@
+ */
+ use CarbonTypeConverter;
+
+ /**
+ * @return class-string
+ */
+ protected function getCarbonClassName(): string
+ {
+ return CarbonImmutable::class;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeType.php b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeType.php
new file mode 100644
index 0000000..29b0bb9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeType.php
@@ -0,0 +1,16 @@
+ */
+ use CarbonTypeConverter;
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php
new file mode 100644
index 0000000..3ca8837
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use Throwable;
+
+class BadComparisonUnitException extends UnitException
+{
+ /**
+ * The unit.
+ *
+ * @var string
+ */
+ protected $unit;
+
+ /**
+ * Constructor.
+ *
+ * @param string $unit
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($unit, $code = 0, Throwable $previous = null)
+ {
+ $this->unit = $unit;
+
+ parent::__construct("Bad comparison unit: '$unit'", $code, $previous);
+ }
+
+ /**
+ * Get the unit.
+ *
+ * @return string
+ */
+ public function getUnit(): string
+ {
+ return $this->unit;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php
new file mode 100644
index 0000000..2e222e5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use BadMethodCallException as BaseBadMethodCallException;
+use Throwable;
+
+class BadFluentConstructorException extends BaseBadMethodCallException implements BadMethodCallException
+{
+ /**
+ * The method.
+ *
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * Constructor.
+ *
+ * @param string $method
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($method, $code = 0, Throwable $previous = null)
+ {
+ $this->method = $method;
+
+ parent::__construct(sprintf("Unknown fluent constructor '%s'.", $method), $code, $previous);
+ }
+
+ /**
+ * Get the method.
+ *
+ * @return string
+ */
+ public function getMethod(): string
+ {
+ return $this->method;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php
new file mode 100644
index 0000000..4ceaa2e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use BadMethodCallException as BaseBadMethodCallException;
+use Throwable;
+
+class BadFluentSetterException extends BaseBadMethodCallException implements BadMethodCallException
+{
+ /**
+ * The setter.
+ *
+ * @var string
+ */
+ protected $setter;
+
+ /**
+ * Constructor.
+ *
+ * @param string $setter
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($setter, $code = 0, Throwable $previous = null)
+ {
+ $this->setter = $setter;
+
+ parent::__construct(sprintf("Unknown fluent setter '%s'", $setter), $code, $previous);
+ }
+
+ /**
+ * Get the setter.
+ *
+ * @return string
+ */
+ public function getSetter(): string
+ {
+ return $this->setter;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php
new file mode 100644
index 0000000..108206d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+interface BadMethodCallException extends Exception
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php
new file mode 100644
index 0000000..e104926
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use RuntimeException as BaseRuntimeException;
+
+final class EndLessPeriodException extends BaseRuntimeException implements RuntimeException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php
new file mode 100644
index 0000000..8ad747e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/Exception.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+interface Exception
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php
new file mode 100644
index 0000000..db334c6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use RuntimeException as BaseRuntimeException;
+use Throwable;
+
+class ImmutableException extends BaseRuntimeException implements RuntimeException
+{
+ /**
+ * The value.
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Constructor.
+ *
+ * @param string $value the immutable type/value
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($value, $code = 0, Throwable $previous = null)
+ {
+ $this->value = $value;
+ parent::__construct("$value is immutable.", $code, $previous);
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return string
+ */
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php
new file mode 100644
index 0000000..5b013cd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+interface InvalidArgumentException extends Exception
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php
new file mode 100644
index 0000000..a421401
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidCastException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php
new file mode 100644
index 0000000..c9ecb6b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+class InvalidDateException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ /**
+ * The invalid field.
+ *
+ * @var string
+ */
+ private $field;
+
+ /**
+ * The invalid value.
+ *
+ * @var mixed
+ */
+ private $value;
+
+ /**
+ * Constructor.
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($field, $value, $code = 0, Throwable $previous = null)
+ {
+ $this->field = $field;
+ $this->value = $value;
+ parent::__construct($field.' : '.$value.' is not a valid value.', $code, $previous);
+ }
+
+ /**
+ * Get the invalid field.
+ *
+ * @return string
+ */
+ public function getField()
+ {
+ return $this->field;
+ }
+
+ /**
+ * Get the invalid value.
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php
new file mode 100644
index 0000000..92d55fe
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidFormatException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php
new file mode 100644
index 0000000..69cf412
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidIntervalException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php
new file mode 100644
index 0000000..9bd84a9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidPeriodDateException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php
new file mode 100644
index 0000000..cf2c902
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidPeriodParameterException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php
new file mode 100644
index 0000000..f725955
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidTimeZoneException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php
new file mode 100644
index 0000000..2c8ec9b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class InvalidTypeException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php
new file mode 100644
index 0000000..7a87632
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use Carbon\CarbonInterface;
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+class NotACarbonClassException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ /**
+ * The className.
+ *
+ * @var string
+ */
+ protected $className;
+
+ /**
+ * Constructor.
+ *
+ * @param string $className
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($className, $code = 0, Throwable $previous = null)
+ {
+ $this->className = $className;
+
+ parent::__construct(sprintf('Given class does not implement %s: %s', CarbonInterface::class, $className), $code, $previous);
+ }
+
+ /**
+ * Get the className.
+ *
+ * @return string
+ */
+ public function getClassName(): string
+ {
+ return $this->className;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php
new file mode 100644
index 0000000..4edd7a4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class NotAPeriodException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php
new file mode 100644
index 0000000..f2c5468
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+class NotLocaleAwareException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ /**
+ * Constructor.
+ *
+ * @param mixed $object
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($object, $code = 0, Throwable $previous = null)
+ {
+ $dump = \is_object($object) ? \get_class($object) : \gettype($object);
+
+ parent::__construct("$dump does neither implements Symfony\Contracts\Translation\LocaleAwareInterface nor getLocale() method.", $code, $previous);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php
new file mode 100644
index 0000000..2c586d0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php
@@ -0,0 +1,101 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+// This will extends OutOfRangeException instead of InvalidArgumentException since 3.0.0
+// use OutOfRangeException as BaseOutOfRangeException;
+
+class OutOfRangeException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ /**
+ * The unit or name of the value.
+ *
+ * @var string
+ */
+ private $unit;
+
+ /**
+ * The range minimum.
+ *
+ * @var mixed
+ */
+ private $min;
+
+ /**
+ * The range maximum.
+ *
+ * @var mixed
+ */
+ private $max;
+
+ /**
+ * The invalid value.
+ *
+ * @var mixed
+ */
+ private $value;
+
+ /**
+ * Constructor.
+ *
+ * @param string $unit
+ * @param mixed $min
+ * @param mixed $max
+ * @param mixed $value
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($unit, $min, $max, $value, $code = 0, Throwable $previous = null)
+ {
+ $this->unit = $unit;
+ $this->min = $min;
+ $this->max = $max;
+ $this->value = $value;
+
+ parent::__construct("$unit must be between $min and $max, $value given", $code, $previous);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMax()
+ {
+ return $this->max;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMin()
+ {
+ return $this->min;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getUnit()
+ {
+ return $this->unit;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php
new file mode 100644
index 0000000..5416fd1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+class ParseErrorException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ /**
+ * The expected.
+ *
+ * @var string
+ */
+ protected $expected;
+
+ /**
+ * The actual.
+ *
+ * @var string
+ */
+ protected $actual;
+
+ /**
+ * The help message.
+ *
+ * @var string
+ */
+ protected $help;
+
+ /**
+ * Constructor.
+ *
+ * @param string $expected
+ * @param string $actual
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($expected, $actual, $help = '', $code = 0, Throwable $previous = null)
+ {
+ $this->expected = $expected;
+ $this->actual = $actual;
+ $this->help = $help;
+
+ $actual = $actual === '' ? 'data is missing' : "get '$actual'";
+
+ parent::__construct(trim("Format expected $expected but $actual\n$help"), $code, $previous);
+ }
+
+ /**
+ * Get the expected.
+ *
+ * @return string
+ */
+ public function getExpected(): string
+ {
+ return $this->expected;
+ }
+
+ /**
+ * Get the actual.
+ *
+ * @return string
+ */
+ public function getActual(): string
+ {
+ return $this->actual;
+ }
+
+ /**
+ * Get the help message.
+ *
+ * @return string
+ */
+ public function getHelp(): string
+ {
+ return $this->help;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php
new file mode 100644
index 0000000..ad196f7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+interface RuntimeException extends Exception
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php
new file mode 100644
index 0000000..ee99953
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+
+class UnitException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php
new file mode 100644
index 0000000..0e72305
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use Throwable;
+
+class UnitNotConfiguredException extends UnitException
+{
+ /**
+ * The unit.
+ *
+ * @var string
+ */
+ protected $unit;
+
+ /**
+ * Constructor.
+ *
+ * @param string $unit
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($unit, $code = 0, Throwable $previous = null)
+ {
+ $this->unit = $unit;
+
+ parent::__construct("Unit $unit have no configuration to get total from other units.", $code, $previous);
+ }
+
+ /**
+ * Get the unit.
+ *
+ * @return string
+ */
+ public function getUnit(): string
+ {
+ return $this->unit;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php
new file mode 100644
index 0000000..5c50497
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+class UnknownGetterException extends BaseInvalidArgumentException implements InvalidArgumentException
+{
+ /**
+ * The getter.
+ *
+ * @var string
+ */
+ protected $getter;
+
+ /**
+ * Constructor.
+ *
+ * @param string $getter getter name
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($getter, $code = 0, Throwable $previous = null)
+ {
+ $this->getter = $getter;
+
+ parent::__construct("Unknown getter '$getter'", $code, $previous);
+ }
+
+ /**
+ * Get the getter.
+ *
+ * @return string
+ */
+ public function getGetter(): string
+ {
+ return $this->getter;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php
new file mode 100644
index 0000000..75273a7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use BadMethodCallException as BaseBadMethodCallException;
+use Throwable;
+
+class UnknownMethodException extends BaseBadMethodCallException implements BadMethodCallException
+{
+ /**
+ * The method.
+ *
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * Constructor.
+ *
+ * @param string $method
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($method, $code = 0, Throwable $previous = null)
+ {
+ $this->method = $method;
+
+ parent::__construct("Method $method does not exist.", $code, $previous);
+ }
+
+ /**
+ * Get the method.
+ *
+ * @return string
+ */
+ public function getMethod(): string
+ {
+ return $this->method;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php
new file mode 100644
index 0000000..a795f5d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use Throwable;
+
+class UnknownSetterException extends BaseInvalidArgumentException implements BadMethodCallException
+{
+ /**
+ * The setter.
+ *
+ * @var string
+ */
+ protected $setter;
+
+ /**
+ * Constructor.
+ *
+ * @param string $setter setter name
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($setter, $code = 0, Throwable $previous = null)
+ {
+ $this->setter = $setter;
+
+ parent::__construct("Unknown setter '$setter'", $code, $previous);
+ }
+
+ /**
+ * Get the setter.
+ *
+ * @return string
+ */
+ public function getSetter(): string
+ {
+ return $this->setter;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php
new file mode 100644
index 0000000..ecd7f7a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use Throwable;
+
+class UnknownUnitException extends UnitException
+{
+ /**
+ * The unit.
+ *
+ * @var string
+ */
+ protected $unit;
+
+ /**
+ * Constructor.
+ *
+ * @param string $unit
+ * @param int $code
+ * @param Throwable|null $previous
+ */
+ public function __construct($unit, $code = 0, Throwable $previous = null)
+ {
+ $this->unit = $unit;
+
+ parent::__construct("Unknown unit '$unit'.", $code, $previous);
+ }
+
+ /**
+ * Get the unit.
+ *
+ * @return string
+ */
+ public function getUnit(): string
+ {
+ return $this->unit;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php
new file mode 100644
index 0000000..1654ab1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Exceptions;
+
+use RuntimeException as BaseRuntimeException;
+
+class UnreachableException extends BaseRuntimeException implements RuntimeException
+{
+ //
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Factory.php b/vendor/nesbot/carbon/src/Carbon/Factory.php
new file mode 100644
index 0000000..d497535
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Factory.php
@@ -0,0 +1,326 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Closure;
+use DateTimeInterface;
+use ReflectionMethod;
+
+/**
+ * A factory to generate Carbon instances with common settings.
+ *
+ *
+ *
+ * @method bool canBeCreatedFromFormat($date, $format) Checks if the (date)time string is in a given format and valid to create a
+ * new instance.
+ * @method Carbon|false create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) Create a new Carbon instance from a specific date and time.
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ * @method Carbon createFromDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to now.
+ * @method Carbon|false createFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format.
+ * @method Carbon|false createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()).
+ * @method Carbon|false createFromLocaleFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific format and a string in a given language.
+ * @method Carbon|false createFromLocaleIsoFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific ISO format and a string in a given language.
+ * @method Carbon createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) Create a Carbon instance from just a time. The date portion is set to today.
+ * @method Carbon createFromTimeString($time, $tz = null) Create a Carbon instance from a time string. The date portion is set to today.
+ * @method Carbon createFromTimestamp($timestamp, $tz = null) Create a Carbon instance from a timestamp and set the timezone (use default one if not specified).
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method Carbon createFromTimestampMs($timestamp, $tz = null) Create a Carbon instance from a timestamp in milliseconds.
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method Carbon createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds.
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method Carbon createFromTimestampUTC($timestamp) Create a Carbon instance from an timestamp keeping the timezone to UTC.
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method Carbon createMidnightDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to midnight.
+ * @method Carbon|false createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) Create a new safe Carbon instance from a specific date and time.
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ * If one of the set values is not valid, an InvalidDateException
+ * will be thrown.
+ * @method CarbonInterface createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null) Create a new Carbon instance from a specific date and time using strict validation.
+ * @method Carbon disableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method Carbon enableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method mixed executeWithLocale($locale, $func) Set the current locale to the given, execute the passed function, reset the locale to previous one,
+ * then return the result of the closure (or null if the closure was void).
+ * @method Carbon fromSerialized($value) Create an instance from a serialized string.
+ * @method void genericMacro($macro, $priority = 0) Register a custom macro.
+ * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales.
+ * (It will ignore custom translator dynamic loading.)
+ * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native
+ * name, region and variant of the locale.
+ * @method array getDays() Get the days of the week
+ * @method string|null getFallbackLocale() Get the fallback locale.
+ * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat().
+ * @method int getHumanDiffOptions() Return default humanDiff() options (merged flags as integer).
+ * @method array getIsoUnits() Returns list of locale units for ISO formatting.
+ * @method array getLastErrors() {@inheritdoc}
+ * @method string getLocale() Get the current translator locale.
+ * @method callable|null getMacro($name) Get the raw callable macro registered globally for a given name.
+ * @method int getMidDayAt() get midday/noon hour
+ * @method Closure|Carbon getTestNow() Get the Carbon instance (real or mock) to be returned when a "now"
+ * instance is created.
+ * @method string getTimeFormatByPrecision($unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision.
+ * @method string getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key.
+ * @method \Symfony\Component\Translation\TranslatorInterface getTranslator() Get the default translator instance in use.
+ * @method int getWeekEndsAt() Get the last day of week
+ * @method int getWeekStartsAt() Get the first day of week
+ * @method array getWeekendDays() Get weekend days
+ * @method bool hasFormat($date, $format) Checks if the (date)time string is in a given format.
+ * @method bool hasFormatWithModifiers($date, $format) Checks if the (date)time string is in a given format.
+ * @method bool hasMacro($name) Checks if macro is registered globally.
+ * @method bool hasRelativeKeywords($time) Determine if a time string will produce a relative date.
+ * @method bool hasTestNow() Determine if there is a valid test instance set. A valid test instance
+ * is anything that is not null.
+ * @method Carbon instance($date) Create a Carbon instance from a DateTime one.
+ * @method bool isImmutable() Returns true if the current class/instance is immutable.
+ * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter.
+ * @method bool isMutable() Returns true if the current class/instance is mutable.
+ * @method bool isStrictModeEnabled() Returns true if the strict mode is globally in use, false else.
+ * (It can be overridden in specific instances.)
+ * @method bool localeHasDiffOneDayWords($locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow).
+ * Support is considered enabled if the 3 words are translated in the given locale.
+ * @method bool localeHasDiffSyntax($locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ * @method bool localeHasDiffTwoDayWords($locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow).
+ * Support is considered enabled if the 2 words are translated in the given locale.
+ * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ * @method bool localeHasShortUnits($locale) Returns true if the given locale is internally supported and has short-units support.
+ * Support is considered enabled if either year, day or hour has a short variant translated.
+ * @method void macro($name, $macro) Register a custom macro.
+ * @method Carbon|null make($var) Make a Carbon instance from given variable if possible.
+ * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ * @method Carbon maxValue() Create a Carbon instance for the greatest supported date.
+ * @method Carbon minValue() Create a Carbon instance for the lowest supported date.
+ * @method void mixin($mixin) Mix another object into the class.
+ * @method Carbon now($tz = null) Get a Carbon instance for the current date and time.
+ * @method Carbon parse($time = null, $tz = null) Create a carbon instance from a string.
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ * @method Carbon parseFromLocale($time, $locale = null, $tz = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.).
+ * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English).
+ * @method Carbon|false rawCreateFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format.
+ * @method Carbon rawParse($time = null, $tz = null) Create a carbon instance from a string.
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ * @method Carbon resetMacros() Remove all macros and generic macros.
+ * @method void resetMonthsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method void resetToStringFormat() Reset the format used to the default when type juggling a Carbon instance to a string
+ * @method void resetYearsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method void serializeUsing($callback) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather transform Carbon object before the serialization.
+ * JSON serialize all Carbon instances using the given callback.
+ * @method Carbon setFallbackLocale($locale) Set the fallback locale.
+ * @method Carbon setHumanDiffOptions($humanDiffOptions) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method bool setLocale($locale) Set the current translator locale and indicate if the source locale file exists.
+ * Pass 'auto' as locale to use closest language from the current LC_TIME locale.
+ * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider mid-day is always 12pm, then if you need to test if it's an other
+ * hour, test it explicitly:
+ * $date->format('G') == 13
+ * or to set explicitly to a given hour:
+ * $date->setTime(13, 0, 0, 0)
+ * Set midday/noon hour
+ * @method Carbon setTestNow($testNow = null) Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ * Note the timezone parameter was left out of the examples above and
+ * has no affect as the mock value will be returned regardless of its value.
+ * Only the moment is mocked with setTestNow(), the timezone will still be the one passed
+ * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()).
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ * /!\ Use this method for unit tests only.
+ * @method Carbon setTestNowAndTimezone($testNow = null, $tz = null) Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ * It will also align default timezone (e.g. call date_default_timezone_set()) with
+ * the second argument or if null, with the timezone of the given date object.
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ * /!\ Use this method for unit tests only.
+ * @method void setToStringFormat($format) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and
+ * use other method or custom format passed to format() method if you need to dump another string
+ * format.
+ * Set the default format used when type juggling a Carbon instance to a string.
+ * @method void setTranslator(TranslatorInterface $translator) Set the default translator instance to use.
+ * @method Carbon setUtf8($utf8) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use UTF-8 language packages on every machine.
+ * Set if UTF8 will be used for localized date/time.
+ * @method void setWeekEndsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek
+ * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the
+ * start of week according to current locale selected and implicitly the end of week.
+ * Set the last day of week
+ * @method void setWeekStartsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the
+ * 'first_day_of_week' locale setting to change the start of week according to current locale
+ * selected and implicitly the end of week.
+ * Set the first day of week
+ * @method void setWeekendDays($days) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider week-end is always saturday and sunday, and if you have some custom
+ * week-end days to handle, give to those days an other name and create a macro for them:
+ * ```
+ * Carbon::macro('isDayOff', function ($date) {
+ * return $date->isSunday() || $date->isMonday();
+ * });
+ * Carbon::macro('isNotDayOff', function ($date) {
+ * return !$date->isDayOff();
+ * });
+ * if ($someDate->isDayOff()) ...
+ * if ($someDate->isNotDayOff()) ...
+ * // Add 5 not-off days
+ * $count = 5;
+ * while ($someDate->isDayOff() || ($count-- > 0)) {
+ * $someDate->addDay();
+ * }
+ * ```
+ * Set weekend days
+ * @method bool shouldOverflowMonths() Get the month overflow global behavior (can be overridden in specific instances).
+ * @method bool shouldOverflowYears() Get the month overflow global behavior (can be overridden in specific instances).
+ * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English).
+ * @method Carbon today($tz = null) Create a Carbon instance for today.
+ * @method Carbon tomorrow($tz = null) Create a Carbon instance for tomorrow.
+ * @method string translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other.
+ * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available.
+ * @method void useMonthsOverflow($monthsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method Carbon useStrictMode($strictModeEnabled = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method void useYearsOverflow($yearsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method mixed withTestNow($testNow, $callback) Temporarily sets a static date to be used within the callback.
+ * Using setTestNow to set the date, executing the callback, then
+ * clearing the test instance.
+ * /!\ Use this method for unit tests only.
+ * @method Carbon yesterday($tz = null) Create a Carbon instance for yesterday.
+ *
+ *
+ */
+class Factory
+{
+ protected $className = Carbon::class;
+
+ protected $settings = [];
+
+ public function __construct(array $settings = [], ?string $className = null)
+ {
+ if ($className) {
+ $this->className = $className;
+ }
+
+ $this->settings = $settings;
+ }
+
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ public function setClassName(string $className)
+ {
+ $this->className = $className;
+
+ return $this;
+ }
+
+ public function className(string $className = null)
+ {
+ return $className === null ? $this->getClassName() : $this->setClassName($className);
+ }
+
+ public function getSettings()
+ {
+ return $this->settings;
+ }
+
+ public function setSettings(array $settings)
+ {
+ $this->settings = $settings;
+
+ return $this;
+ }
+
+ public function settings(array $settings = null)
+ {
+ return $settings === null ? $this->getSettings() : $this->setSettings($settings);
+ }
+
+ public function mergeSettings(array $settings)
+ {
+ $this->settings = array_merge($this->settings, $settings);
+
+ return $this;
+ }
+
+ public function __call($name, $arguments)
+ {
+ $method = new ReflectionMethod($this->className, $name);
+ $settings = $this->settings;
+
+ if ($settings && isset($settings['timezone'])) {
+ $tzParameters = array_filter($method->getParameters(), function ($parameter) {
+ return \in_array($parameter->getName(), ['tz', 'timezone'], true);
+ });
+
+ if (isset($arguments[0]) && \in_array($name, ['instance', 'make', 'create', 'parse'], true)) {
+ if ($arguments[0] instanceof DateTimeInterface) {
+ $settings['innerTimezone'] = $settings['timezone'];
+ } elseif (\is_string($arguments[0]) && date_parse($arguments[0])['is_localtime']) {
+ unset($settings['timezone'], $settings['innerTimezone']);
+ }
+ } elseif (\count($tzParameters)) {
+ array_splice($arguments, key($tzParameters), 0, [$settings['timezone']]);
+ unset($settings['timezone']);
+ }
+ }
+
+ $result = $this->className::$name(...$arguments);
+
+ return $result instanceof CarbonInterface && !empty($settings)
+ ? $result->settings($settings)
+ : $result;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php b/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php
new file mode 100644
index 0000000..d88a1cf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/FactoryImmutable.php
@@ -0,0 +1,259 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Closure;
+use DateTimeImmutable;
+use DateTimeZone;
+use Psr\Clock\ClockInterface;
+
+/**
+ * A factory to generate CarbonImmutable instances with common settings.
+ *
+ *
+ *
+ * @method bool canBeCreatedFromFormat($date, $format) Checks if the (date)time string is in a given format and valid to create a
+ * new instance.
+ * @method CarbonImmutable|false create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null) Create a new Carbon instance from a specific date and time.
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ * @method CarbonImmutable createFromDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to now.
+ * @method CarbonImmutable|false createFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format.
+ * @method CarbonImmutable|false createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null) Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()).
+ * @method CarbonImmutable|false createFromLocaleFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific format and a string in a given language.
+ * @method CarbonImmutable|false createFromLocaleIsoFormat($format, $locale, $time, $tz = null) Create a Carbon instance from a specific ISO format and a string in a given language.
+ * @method CarbonImmutable createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null) Create a Carbon instance from just a time. The date portion is set to today.
+ * @method CarbonImmutable createFromTimeString($time, $tz = null) Create a Carbon instance from a time string. The date portion is set to today.
+ * @method CarbonImmutable createFromTimestamp($timestamp, $tz = null) Create a Carbon instance from a timestamp and set the timezone (use default one if not specified).
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method CarbonImmutable createFromTimestampMs($timestamp, $tz = null) Create a Carbon instance from a timestamp in milliseconds.
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method CarbonImmutable createFromTimestampMsUTC($timestamp) Create a Carbon instance from a timestamp in milliseconds.
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method CarbonImmutable createFromTimestampUTC($timestamp) Create a Carbon instance from an timestamp keeping the timezone to UTC.
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ * @method CarbonImmutable createMidnightDate($year = null, $month = null, $day = null, $tz = null) Create a Carbon instance from just a date. The time portion is set to midnight.
+ * @method CarbonImmutable|false createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null) Create a new safe Carbon instance from a specific date and time.
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ * If one of the set values is not valid, an InvalidDateException
+ * will be thrown.
+ * @method CarbonInterface createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null) Create a new Carbon instance from a specific date and time using strict validation.
+ * @method CarbonImmutable disableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method CarbonImmutable enableHumanDiffOption($humanDiffOption) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method mixed executeWithLocale($locale, $func) Set the current locale to the given, execute the passed function, reset the locale to previous one,
+ * then return the result of the closure (or null if the closure was void).
+ * @method CarbonImmutable fromSerialized($value) Create an instance from a serialized string.
+ * @method void genericMacro($macro, $priority = 0) Register a custom macro.
+ * @method array getAvailableLocales() Returns the list of internally available locales and already loaded custom locales.
+ * (It will ignore custom translator dynamic loading.)
+ * @method Language[] getAvailableLocalesInfo() Returns list of Language object for each available locale. This object allow you to get the ISO name, native
+ * name, region and variant of the locale.
+ * @method array getDays() Get the days of the week
+ * @method string|null getFallbackLocale() Get the fallback locale.
+ * @method array getFormatsToIsoReplacements() List of replacements from date() format to isoFormat().
+ * @method int getHumanDiffOptions() Return default humanDiff() options (merged flags as integer).
+ * @method array getIsoUnits() Returns list of locale units for ISO formatting.
+ * @method array getLastErrors() {@inheritdoc}
+ * @method string getLocale() Get the current translator locale.
+ * @method callable|null getMacro($name) Get the raw callable macro registered globally for a given name.
+ * @method int getMidDayAt() get midday/noon hour
+ * @method Closure|CarbonImmutable getTestNow() Get the Carbon instance (real or mock) to be returned when a "now"
+ * instance is created.
+ * @method string getTimeFormatByPrecision($unitPrecision) Return a format from H:i to H:i:s.u according to given unit precision.
+ * @method string getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null) Returns raw translation message for a given key.
+ * @method \Symfony\Component\Translation\TranslatorInterface getTranslator() Get the default translator instance in use.
+ * @method int getWeekEndsAt() Get the last day of week
+ * @method int getWeekStartsAt() Get the first day of week
+ * @method array getWeekendDays() Get weekend days
+ * @method bool hasFormat($date, $format) Checks if the (date)time string is in a given format.
+ * @method bool hasFormatWithModifiers($date, $format) Checks if the (date)time string is in a given format.
+ * @method bool hasMacro($name) Checks if macro is registered globally.
+ * @method bool hasRelativeKeywords($time) Determine if a time string will produce a relative date.
+ * @method bool hasTestNow() Determine if there is a valid test instance set. A valid test instance
+ * is anything that is not null.
+ * @method CarbonImmutable instance($date) Create a Carbon instance from a DateTime one.
+ * @method bool isImmutable() Returns true if the current class/instance is immutable.
+ * @method bool isModifiableUnit($unit) Returns true if a property can be changed via setter.
+ * @method bool isMutable() Returns true if the current class/instance is mutable.
+ * @method bool isStrictModeEnabled() Returns true if the strict mode is globally in use, false else.
+ * (It can be overridden in specific instances.)
+ * @method bool localeHasDiffOneDayWords($locale) Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow).
+ * Support is considered enabled if the 3 words are translated in the given locale.
+ * @method bool localeHasDiffSyntax($locale) Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ * @method bool localeHasDiffTwoDayWords($locale) Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow).
+ * Support is considered enabled if the 2 words are translated in the given locale.
+ * @method bool localeHasPeriodSyntax($locale) Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ * @method bool localeHasShortUnits($locale) Returns true if the given locale is internally supported and has short-units support.
+ * Support is considered enabled if either year, day or hour has a short variant translated.
+ * @method void macro($name, $macro) Register a custom macro.
+ * @method CarbonImmutable|null make($var) Make a Carbon instance from given variable if possible.
+ * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ * @method CarbonImmutable maxValue() Create a Carbon instance for the greatest supported date.
+ * @method CarbonImmutable minValue() Create a Carbon instance for the lowest supported date.
+ * @method void mixin($mixin) Mix another object into the class.
+ * @method CarbonImmutable parse($time = null, $tz = null) Create a carbon instance from a string.
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ * @method CarbonImmutable parseFromLocale($time, $locale = null, $tz = null) Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.).
+ * @method string pluralUnit(string $unit) Returns standardized plural of a given singular/plural unit name (in English).
+ * @method CarbonImmutable|false rawCreateFromFormat($format, $time, $tz = null) Create a Carbon instance from a specific format.
+ * @method CarbonImmutable rawParse($time = null, $tz = null) Create a carbon instance from a string.
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ * @method CarbonImmutable resetMacros() Remove all macros and generic macros.
+ * @method void resetMonthsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method void resetToStringFormat() Reset the format used to the default when type juggling a Carbon instance to a string
+ * @method void resetYearsOverflow() @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method void serializeUsing($callback) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather transform Carbon object before the serialization.
+ * JSON serialize all Carbon instances using the given callback.
+ * @method CarbonImmutable setFallbackLocale($locale) Set the fallback locale.
+ * @method CarbonImmutable setHumanDiffOptions($humanDiffOptions) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method bool setLocale($locale) Set the current translator locale and indicate if the source locale file exists.
+ * Pass 'auto' as locale to use closest language from the current LC_TIME locale.
+ * @method void setMidDayAt($hour) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider mid-day is always 12pm, then if you need to test if it's an other
+ * hour, test it explicitly:
+ * $date->format('G') == 13
+ * or to set explicitly to a given hour:
+ * $date->setTime(13, 0, 0, 0)
+ * Set midday/noon hour
+ * @method CarbonImmutable setTestNow($testNow = null) Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ * Note the timezone parameter was left out of the examples above and
+ * has no affect as the mock value will be returned regardless of its value.
+ * Only the moment is mocked with setTestNow(), the timezone will still be the one passed
+ * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()).
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ * /!\ Use this method for unit tests only.
+ * @method CarbonImmutable setTestNowAndTimezone($testNow = null, $tz = null) Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ * It will also align default timezone (e.g. call date_default_timezone_set()) with
+ * the second argument or if null, with the timezone of the given date object.
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ * /!\ Use this method for unit tests only.
+ * @method void setToStringFormat($format) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and
+ * use other method or custom format passed to format() method if you need to dump another string
+ * format.
+ * Set the default format used when type juggling a Carbon instance to a string.
+ * @method void setTranslator(TranslatorInterface $translator) Set the default translator instance to use.
+ * @method CarbonImmutable setUtf8($utf8) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use UTF-8 language packages on every machine.
+ * Set if UTF8 will be used for localized date/time.
+ * @method void setWeekEndsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek
+ * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the
+ * start of week according to current locale selected and implicitly the end of week.
+ * Set the last day of week
+ * @method void setWeekStartsAt($day) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the
+ * 'first_day_of_week' locale setting to change the start of week according to current locale
+ * selected and implicitly the end of week.
+ * Set the first day of week
+ * @method void setWeekendDays($days) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider week-end is always saturday and sunday, and if you have some custom
+ * week-end days to handle, give to those days an other name and create a macro for them:
+ * ```
+ * Carbon::macro('isDayOff', function ($date) {
+ * return $date->isSunday() || $date->isMonday();
+ * });
+ * Carbon::macro('isNotDayOff', function ($date) {
+ * return !$date->isDayOff();
+ * });
+ * if ($someDate->isDayOff()) ...
+ * if ($someDate->isNotDayOff()) ...
+ * // Add 5 not-off days
+ * $count = 5;
+ * while ($someDate->isDayOff() || ($count-- > 0)) {
+ * $someDate->addDay();
+ * }
+ * ```
+ * Set weekend days
+ * @method bool shouldOverflowMonths() Get the month overflow global behavior (can be overridden in specific instances).
+ * @method bool shouldOverflowYears() Get the month overflow global behavior (can be overridden in specific instances).
+ * @method string singularUnit(string $unit) Returns standardized singular of a given singular/plural unit name (in English).
+ * @method CarbonImmutable today($tz = null) Create a Carbon instance for today.
+ * @method CarbonImmutable tomorrow($tz = null) Create a Carbon instance for tomorrow.
+ * @method string translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL) Translate a time string from a locale to an other.
+ * @method string translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null) Translate using translation string or callback available.
+ * @method void useMonthsOverflow($monthsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method CarbonImmutable useStrictMode($strictModeEnabled = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @method void useYearsOverflow($yearsOverflow = true) @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @method mixed withTestNow($testNow, $callback) Temporarily sets a static date to be used within the callback.
+ * Using setTestNow to set the date, executing the callback, then
+ * clearing the test instance.
+ * /!\ Use this method for unit tests only.
+ * @method CarbonImmutable yesterday($tz = null) Create a Carbon instance for yesterday.
+ *
+ *
+ */
+class FactoryImmutable extends Factory implements ClockInterface
+{
+ protected $className = CarbonImmutable::class;
+
+ /**
+ * Get a Carbon instance for the current date and time.
+ *
+ * @param DateTimeZone|string|int|null $tz
+ *
+ * @return CarbonImmutable
+ */
+ public function now($tz = null): DateTimeImmutable
+ {
+ $className = $this->className;
+
+ return new $className(null, $tz);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa.php
new file mode 100644
index 0000000..f3431e4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/aa_DJ.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php
new file mode 100644
index 0000000..c6e23c0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_DJ.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'],
+ 'months_short' => ['qun', 'nah', 'cig', 'agd', 'cax', 'qas', 'qad', 'leq', 'way', 'dit', 'xim', 'kax'],
+ 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'],
+ 'weekdays_short' => ['aca', 'etl', 'tal', 'arb', 'kam', 'gum', 'sab'],
+ 'weekdays_min' => ['aca', 'etl', 'tal', 'arb', 'kam', 'gum', 'sab'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['saaku', 'carra'],
+
+ 'year' => ':count gaqambo', // less reliable
+ 'y' => ':count gaqambo', // less reliable
+ 'a_year' => ':count gaqambo', // less reliable
+
+ 'month' => ':count àlsa',
+ 'm' => ':count àlsa',
+ 'a_month' => ':count àlsa',
+
+ 'day' => ':count saaku', // less reliable
+ 'd' => ':count saaku', // less reliable
+ 'a_day' => ':count saaku', // less reliable
+
+ 'hour' => ':count ayti', // less reliable
+ 'h' => ':count ayti', // less reliable
+ 'a_hour' => ':count ayti', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php
new file mode 100644
index 0000000..f8f395b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Qunxa Garablu', 'Naharsi Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Leqeeni', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'],
+ 'months_short' => ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way', 'Dit', 'Xim', 'Kax'],
+ 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'],
+ 'weekdays_short' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
+ 'weekdays_min' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['saaku', 'carra'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php
new file mode 100644
index 0000000..6461225
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ER@saaho.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Qunxa Garablu', 'Naharsi Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Leqeeni', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'],
+ 'months_short' => ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way', 'Dit', 'Xim', 'Kax'],
+ 'weekdays' => ['Naba Sambat', 'Sani', 'Salus', 'Rabuq', 'Camus', 'Jumqata', 'Qunxa Sambat'],
+ 'weekdays_short' => ['Nab', 'San', 'Sal', 'Rab', 'Cam', 'Jum', 'Qun'],
+ 'weekdays_min' => ['Nab', 'San', 'Sal', 'Rab', 'Cam', 'Jum', 'Qun'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['saaku', 'carra'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php
new file mode 100644
index 0000000..e55e591
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/aa_ET.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxisso', 'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli', 'Ximoli', 'Kaxxa Garablu'],
+ 'months_short' => ['Qun', 'Kud', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way', 'Dit', 'Xim', 'Kax'],
+ 'weekdays' => ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata', 'Sabti'],
+ 'weekdays_short' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
+ 'weekdays_min' => ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['saaku', 'carra'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af.php b/vendor/nesbot/carbon/src/Carbon/Lang/af.php
new file mode 100644
index 0000000..27771d7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/af.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - JD Isaacks
+ * - Pierre du Plessis
+ */
+return [
+ 'year' => ':count jaar',
+ 'a_year' => '\'n jaar|:count jaar',
+ 'y' => ':count j.',
+ 'month' => ':count maand|:count maande',
+ 'a_month' => '\'n maand|:count maande',
+ 'm' => ':count maa.',
+ 'week' => ':count week|:count weke',
+ 'a_week' => '\'n week|:count weke',
+ 'w' => ':count w.',
+ 'day' => ':count dag|:count dae',
+ 'a_day' => '\'n dag|:count dae',
+ 'd' => ':count d.',
+ 'hour' => ':count uur',
+ 'a_hour' => '\'n uur|:count uur',
+ 'h' => ':count u.',
+ 'minute' => ':count minuut|:count minute',
+ 'a_minute' => '\'n minuut|:count minute',
+ 'min' => ':count min.',
+ 'second' => ':count sekond|:count sekondes',
+ 'a_second' => '\'n paar sekondes|:count sekondes',
+ 's' => ':count s.',
+ 'ago' => ':time gelede',
+ 'from_now' => 'oor :time',
+ 'after' => ':time na',
+ 'before' => ':time voor',
+ 'diff_now' => 'Nou',
+ 'diff_today' => 'Vandag',
+ 'diff_today_regexp' => 'Vandag(?:\\s+om)?',
+ 'diff_yesterday' => 'Gister',
+ 'diff_yesterday_regexp' => 'Gister(?:\\s+om)?',
+ 'diff_tomorrow' => 'Môre',
+ 'diff_tomorrow_regexp' => 'Môre(?:\\s+om)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Vandag om] LT',
+ 'nextDay' => '[Môre om] LT',
+ 'nextWeek' => 'dddd [om] LT',
+ 'lastDay' => '[Gister om] LT',
+ 'lastWeek' => '[Laas] dddd [om] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de');
+ },
+ 'meridiem' => ['VM', 'NM'],
+ 'months' => ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
+ 'months_short' => ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag', 'Saterdag'],
+ 'weekdays_short' => ['Son', 'Maa', 'Din', 'Woe', 'Don', 'Vry', 'Sat'],
+ 'weekdays_min' => ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' en '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php b/vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php
new file mode 100644
index 0000000..f2fcf05
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/af_NA.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/af.php', [
+ 'meridiem' => ['v', 'n'],
+ 'weekdays' => ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag', 'Saterdag'],
+ 'weekdays_short' => ['So.', 'Ma.', 'Di.', 'Wo.', 'Do.', 'Vr.', 'Sa.'],
+ 'weekdays_min' => ['So.', 'Ma.', 'Di.', 'Wo.', 'Do.', 'Vr.', 'Sa.'],
+ 'months' => ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
+ 'months_short' => ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Des.'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'DD MMM YYYY',
+ 'LLL' => 'DD MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, DD MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php
new file mode 100644
index 0000000..27896bd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/af_ZA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/af.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/agq.php b/vendor/nesbot/carbon/src/Carbon/Lang/agq.php
new file mode 100644
index 0000000..7011464
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/agq.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['a.g', 'a.k'],
+ 'weekdays' => ['tsuʔntsɨ', 'tsuʔukpà', 'tsuʔughɔe', 'tsuʔutɔ̀mlò', 'tsuʔumè', 'tsuʔughɨ̂m', 'tsuʔndzɨkɔʔɔ'],
+ 'weekdays_short' => ['nts', 'kpa', 'ghɔ', 'tɔm', 'ume', 'ghɨ', 'dzk'],
+ 'weekdays_min' => ['nts', 'kpa', 'ghɔ', 'tɔm', 'ume', 'ghɨ', 'dzk'],
+ 'months' => ['ndzɔ̀ŋɔ̀nùm', 'ndzɔ̀ŋɔ̀kƗ̀zùʔ', 'ndzɔ̀ŋɔ̀tƗ̀dʉ̀ghà', 'ndzɔ̀ŋɔ̀tǎafʉ̄ghā', 'ndzɔ̀ŋèsèe', 'ndzɔ̀ŋɔ̀nzùghò', 'ndzɔ̀ŋɔ̀dùmlo', 'ndzɔ̀ŋɔ̀kwîfɔ̀e', 'ndzɔ̀ŋɔ̀tƗ̀fʉ̀ghàdzughù', 'ndzɔ̀ŋɔ̀ghǔuwelɔ̀m', 'ndzɔ̀ŋɔ̀chwaʔàkaa wo', 'ndzɔ̀ŋèfwòo'],
+ 'months_short' => ['nùm', 'kɨz', 'tɨd', 'taa', 'see', 'nzu', 'dum', 'fɔe', 'dzu', 'lɔm', 'kaa', 'fwo'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/agr.php b/vendor/nesbot/carbon/src/Carbon/Lang/agr.php
new file mode 100644
index 0000000..8f036ae
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/agr.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/agr_PE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php
new file mode 100644
index 0000000..54a326a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/agr_PE.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - somosazucar.org libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Petsatin', 'Kupitin', 'Uyaitin', 'Tayutin', 'Kegketin', 'Tegmatin', 'Kuntutin', 'Yagkujutin', 'Daiktatin', 'Ipamtatin', 'Shinutin', 'Sakamtin'],
+ 'months_short' => ['Pet', 'Kup', 'Uya', 'Tay', 'Keg', 'Teg', 'Kun', 'Yag', 'Dait', 'Ipam', 'Shin', 'Sak'],
+ 'weekdays' => ['Tuntuamtin', 'Achutin', 'Kugkuktin', 'Saketin', 'Shimpitin', 'Imaptin', 'Bataetin'],
+ 'weekdays_short' => ['Tun', 'Ach', 'Kug', 'Sak', 'Shim', 'Im', 'Bat'],
+ 'weekdays_min' => ['Tun', 'Ach', 'Kug', 'Sak', 'Shim', 'Im', 'Bat'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 7,
+ 'meridiem' => ['VM', 'NM'],
+
+ 'year' => ':count yaya', // less reliable
+ 'y' => ':count yaya', // less reliable
+ 'a_year' => ':count yaya', // less reliable
+
+ 'month' => ':count nantu', // less reliable
+ 'm' => ':count nantu', // less reliable
+ 'a_month' => ':count nantu', // less reliable
+
+ 'day' => ':count nayaim', // less reliable
+ 'd' => ':count nayaim', // less reliable
+ 'a_day' => ':count nayaim', // less reliable
+
+ 'hour' => ':count kuwiš', // less reliable
+ 'h' => ':count kuwiš', // less reliable
+ 'a_hour' => ':count kuwiš', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ak.php b/vendor/nesbot/carbon/src/Carbon/Lang/ak.php
new file mode 100644
index 0000000..5a64be3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ak.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ak_GH.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php
new file mode 100644
index 0000000..1381946
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ak_GH.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Sugar Labs // OLPC sugarlabs.org libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY/MM/DD',
+ ],
+ 'months' => ['Sanda-Ɔpɛpɔn', 'Kwakwar-Ɔgyefuo', 'Ebɔw-Ɔbenem', 'Ebɔbira-Oforisuo', 'Esusow Aketseaba-Kɔtɔnimba', 'Obirade-Ayɛwohomumu', 'Ayɛwoho-Kitawonsa', 'Difuu-Ɔsandaa', 'Fankwa-Ɛbɔ', 'Ɔbɛsɛ-Ahinime', 'Ɔberɛfɛw-Obubuo', 'Mumu-Ɔpɛnimba'],
+ 'months_short' => ['S-Ɔ', 'K-Ɔ', 'E-Ɔ', 'E-O', 'E-K', 'O-A', 'A-K', 'D-Ɔ', 'F-Ɛ', 'Ɔ-A', 'Ɔ-O', 'M-Ɔ'],
+ 'weekdays' => ['Kwesida', 'Dwowda', 'Benada', 'Wukuda', 'Yawda', 'Fida', 'Memeneda'],
+ 'weekdays_short' => ['Kwe', 'Dwo', 'Ben', 'Wuk', 'Yaw', 'Fia', 'Mem'],
+ 'weekdays_min' => ['Kwe', 'Dwo', 'Ben', 'Wuk', 'Yaw', 'Fia', 'Mem'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['AN', 'EW'],
+
+ 'year' => ':count afe',
+ 'y' => ':count afe',
+ 'a_year' => ':count afe',
+
+ 'month' => ':count bosume',
+ 'm' => ':count bosume',
+ 'a_month' => ':count bosume',
+
+ 'day' => ':count ɛda',
+ 'd' => ':count ɛda',
+ 'a_day' => ':count ɛda',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/am.php b/vendor/nesbot/carbon/src/Carbon/Lang/am.php
new file mode 100644
index 0000000..63bf72d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/am.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/am_ET.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php
new file mode 100644
index 0000000..ece8062
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/am_ET.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕሪል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክቶበር', 'ኖቬምበር', 'ዲሴምበር'],
+ 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'],
+ 'weekdays' => ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
+ 'weekdays_short' => ['እሑድ', 'ሰኞ ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
+ 'weekdays_min' => ['እሑድ', 'ሰኞ ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ጡዋት', 'ከሰዓት'],
+
+ 'year' => ':count አመት',
+ 'y' => ':count አመት',
+ 'a_year' => ':count አመት',
+
+ 'month' => ':count ወር',
+ 'm' => ':count ወር',
+ 'a_month' => ':count ወር',
+
+ 'week' => ':count ሳምንት',
+ 'w' => ':count ሳምንት',
+ 'a_week' => ':count ሳምንት',
+
+ 'day' => ':count ቀን',
+ 'd' => ':count ቀን',
+ 'a_day' => ':count ቀን',
+
+ 'hour' => ':count ሰዓት',
+ 'h' => ':count ሰዓት',
+ 'a_hour' => ':count ሰዓት',
+
+ 'minute' => ':count ደቂቃ',
+ 'min' => ':count ደቂቃ',
+ 'a_minute' => ':count ደቂቃ',
+
+ 'second' => ':count ሴኮንድ',
+ 's' => ':count ሴኮንድ',
+ 'a_second' => ':count ሴኮንድ',
+
+ 'ago' => 'ከ:time በፊት',
+ 'from_now' => 'በ:time ውስጥ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/an.php b/vendor/nesbot/carbon/src/Carbon/Lang/an.php
new file mode 100644
index 0000000..565abf2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/an.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/an_ES.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php
new file mode 100644
index 0000000..faf8ae0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/an_ES.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Softaragones Jordi Mallach Pérez, Juan Pablo Martínez bug-glibc-locales@gnu.org, softaragones@softaragones.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['chinero', 'febrero', 'marzo', 'abril', 'mayo', 'chunyo', 'chuliol', 'agosto', 'setiembre', 'octubre', 'noviembre', 'aviento'],
+ 'months_short' => ['chi', 'feb', 'mar', 'abr', 'may', 'chn', 'chl', 'ago', 'set', 'oct', 'nov', 'avi'],
+ 'weekdays' => ['domingo', 'luns', 'martes', 'mierques', 'chueves', 'viernes', 'sabado'],
+ 'weekdays_short' => ['dom', 'lun', 'mar', 'mie', 'chu', 'vie', 'sab'],
+ 'weekdays_min' => ['dom', 'lun', 'mar', 'mie', 'chu', 'vie', 'sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count año',
+ 'y' => ':count año',
+ 'a_year' => ':count año',
+
+ 'month' => ':count mes',
+ 'm' => ':count mes',
+ 'a_month' => ':count mes',
+
+ 'week' => ':count semana',
+ 'w' => ':count semana',
+ 'a_week' => ':count semana',
+
+ 'day' => ':count día',
+ 'd' => ':count día',
+ 'a_day' => ':count día',
+
+ 'hour' => ':count reloch', // less reliable
+ 'h' => ':count reloch', // less reliable
+ 'a_hour' => ':count reloch', // less reliable
+
+ 'minute' => ':count minuto',
+ 'min' => ':count minuto',
+ 'a_minute' => ':count minuto',
+
+ 'second' => ':count segundo',
+ 's' => ':count segundo',
+ 'a_second' => ':count segundo',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/anp.php b/vendor/nesbot/carbon/src/Carbon/Lang/anp.php
new file mode 100644
index 0000000..b56c67b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/anp.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/anp_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php
new file mode 100644
index 0000000..11069be
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/anp_IN.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bhashaghar@googlegroups.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितंबर', 'अक्टूबर', 'नवंबर', 'दिसंबर"'],
+ 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितंबर', 'अक्टूबर', 'नवंबर', 'दिसंबर'],
+ 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'बृहस्पतिवार', 'शुक्रवार', 'शनिवार'],
+ 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'],
+ 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar.php
new file mode 100644
index 0000000..5f73f63
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Atef Ben Ali (atefBB)
+ * - Ibrahim AshShohail
+ * - MLTDev
+ * - Mohamed Sabil (mohamedsabil83)
+ * - Yazan Alnugnugh (yazan-alnugnugh)
+ */
+$months = [
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'مايو',
+ 'يونيو',
+ 'يوليو',
+ 'أغسطس',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => ':time من الآن',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدًا(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'اث', 'ثل', 'أر', 'خم', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم عند الساعة] LT',
+ 'nextDay' => '[غدًا عند الساعة] LT',
+ 'nextWeek' => 'dddd [عند الساعة] LT',
+ 'lastDay' => '[أمس عند الساعة] LT',
+ 'lastWeek' => 'dddd [عند الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php
new file mode 100644
index 0000000..35a22b1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_AE.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت '],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php
new file mode 100644
index 0000000..3518096
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_BH.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DJ.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php
new file mode 100644
index 0000000..aea4eee
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_DZ.php
@@ -0,0 +1,92 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Authors:
+ * - Josh Soref
+ * - Noureddine LOUAHEDJ
+ * - JD Isaacks
+ * - Atef Ben Ali (atefBB)
+ * - Mohamed Sabil (mohamedsabil83)
+ */
+$months = [
+ 'جانفي',
+ 'فيفري',
+ 'مارس',
+ 'أفريل',
+ 'ماي',
+ 'جوان',
+ 'جويلية',
+ 'أوت',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => 'في :time',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['أح', 'إث', 'ثلا', 'أر', 'خم', 'جم', 'سب'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 4,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم على الساعة] LT',
+ 'nextDay' => '[غدا على الساعة] LT',
+ 'nextWeek' => 'dddd [على الساعة] LT',
+ 'lastDay' => '[أمس على الساعة] LT',
+ 'lastWeek' => 'dddd [على الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php
new file mode 100644
index 0000000..3518096
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EG.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_EH.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_ER.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IL.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php
new file mode 100644
index 0000000..5fecf70
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IN.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php
new file mode 100644
index 0000000..2d42008
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_IQ.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php
new file mode 100644
index 0000000..2d42008
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_JO.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KM.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php
new file mode 100644
index 0000000..b3fb1cf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_KW.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Authors:
+ * - Josh Soref
+ * - Nusret Parlak
+ * - JD Isaacks
+ * - Atef Ben Ali (atefBB)
+ * - Mohamed Sabil (mohamedsabil83)
+ * - Abdullah-Alhariri
+ */
+$months = [
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'ماي',
+ 'يونيو',
+ 'يوليوز',
+ 'غشت',
+ 'شتنبر',
+ 'أكتوبر',
+ 'نونبر',
+ 'دجنبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => 'في :time',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم على الساعة] LT',
+ 'nextDay' => '[غدا على الساعة] LT',
+ 'nextWeek' => 'dddd [على الساعة] LT',
+ 'lastDay' => '[أمس على الساعة] LT',
+ 'lastWeek' => 'dddd [على الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php
new file mode 100644
index 0000000..2792745
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LB.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php
new file mode 100644
index 0000000..1f0af49
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_LY.php
@@ -0,0 +1,92 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Atef Ben Ali (atefBB)
+ * - Ibrahim AshShohail
+ * - MLTDev
+ */
+
+$months = [
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'مايو',
+ 'يونيو',
+ 'يوليو',
+ 'أغسطس',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر',
+];
+
+return [
+ 'year' => implode('|', [':count سنة', 'سنة', 'سنتين', ':count سنوات', ':count سنة']),
+ 'a_year' => implode('|', [':count سنة', 'سنة', 'سنتين', ':count سنوات', ':count سنة']),
+ 'month' => implode('|', [':count شهر', 'شهر', 'شهرين', ':count أشهر', ':count شهر']),
+ 'a_month' => implode('|', [':count شهر', 'شهر', 'شهرين', ':count أشهر', ':count شهر']),
+ 'week' => implode('|', [':count أسبوع', 'أسبوع', 'أسبوعين', ':count أسابيع', ':count أسبوع']),
+ 'a_week' => implode('|', [':count أسبوع', 'أسبوع', 'أسبوعين', ':count أسابيع', ':count أسبوع']),
+ 'day' => implode('|', [':count يوم', 'يوم', 'يومين', ':count أيام', ':count يوم']),
+ 'a_day' => implode('|', [':count يوم', 'يوم', 'يومين', ':count أيام', ':count يوم']),
+ 'hour' => implode('|', [':count ساعة', 'ساعة', 'ساعتين', ':count ساعات', ':count ساعة']),
+ 'a_hour' => implode('|', [':count ساعة', 'ساعة', 'ساعتين', ':count ساعات', ':count ساعة']),
+ 'minute' => implode('|', [':count دقيقة', 'دقيقة', 'دقيقتين', ':count دقائق', ':count دقيقة']),
+ 'a_minute' => implode('|', [':count دقيقة', 'دقيقة', 'دقيقتين', ':count دقائق', ':count دقيقة']),
+ 'second' => implode('|', [':count ثانية', 'ثانية', 'ثانيتين', ':count ثواني', ':count ثانية']),
+ 'a_second' => implode('|', [':count ثانية', 'ثانية', 'ثانيتين', ':count ثواني', ':count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => ':time من الآن',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدًا(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['مرة', 'مرة', ':count مرتين', ':count مرات', ':count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'اث', 'ثل', 'أر', 'خم', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم عند الساعة] LT',
+ 'nextDay' => '[غدًا عند الساعة] LT',
+ 'nextWeek' => 'dddd [عند الساعة] LT',
+ 'lastDay' => '[أمس عند الساعة] LT',
+ 'lastWeek' => 'dddd [عند الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php
new file mode 100644
index 0000000..047ae05
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MA.php
@@ -0,0 +1,92 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ * - Atef Ben Ali (atefBB)
+ * - Mohamed Sabil (mohamedsabil83)
+ */
+$months = [
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'ماي',
+ 'يونيو',
+ 'يوليوز',
+ 'غشت',
+ 'شتنبر',
+ 'أكتوبر',
+ 'نونبر',
+ 'دجنبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => 'في :time',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم على الساعة] LT',
+ 'nextDay' => '[غدا على الساعة] LT',
+ 'nextWeek' => 'dddd [على الساعة] LT',
+ 'lastDay' => '[أمس على الساعة] LT',
+ 'lastWeek' => 'dddd [على الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_MR.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php
new file mode 100644
index 0000000..3518096
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_OM.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php
new file mode 100644
index 0000000..503c60d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_PS.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php
new file mode 100644
index 0000000..3518096
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_QA.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php
new file mode 100644
index 0000000..550b0c7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SA.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ * - Atef Ben Ali (atefBB)
+ * - Mohamed Sabil (mohamedsabil83)
+ * - Abdullah-Alhariri
+ */
+$months = [
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'مايو',
+ 'يونيو',
+ 'يوليو',
+ 'أغسطس',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => 'في :time',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم على الساعة] LT',
+ 'nextDay' => '[غدا على الساعة] LT',
+ 'nextWeek' => 'dddd [على الساعة] LT',
+ 'lastDay' => '[أمس على الساعة] LT',
+ 'lastWeek' => 'dddd [على الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php
new file mode 100644
index 0000000..3518096
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SD.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SO.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php
new file mode 100644
index 0000000..32f3282
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SS.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php
new file mode 100644
index 0000000..2d42008
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_SY.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'months_short' => ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php
new file mode 100644
index 0000000..c2d4b43
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_Shakl.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Abdellah Chadidi
+ * - Atef Ben Ali (atefBB)
+ * - Mohamed Sabil (mohamedsabil83)
+ */
+// Same for long and short
+$months = [
+ // @TODO add shakl to months
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'مايو',
+ 'يونيو',
+ 'يوليو',
+ 'أغسطس',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سَنَة', '{1}سَنَة', '{2}سَنَتَيْن', ']2,11[:count سَنَوَات', ']10,Inf[:count سَنَة']),
+ 'a_year' => implode('|', ['{0}:count سَنَة', '{1}سَنَة', '{2}سَنَتَيْن', ']2,11[:count سَنَوَات', ']10,Inf[:count سَنَة']),
+ 'month' => implode('|', ['{0}:count شَهْرَ', '{1}شَهْرَ', '{2}شَهْرَيْن', ']2,11[:count أَشْهُر', ']10,Inf[:count شَهْرَ']),
+ 'a_month' => implode('|', ['{0}:count شَهْرَ', '{1}شَهْرَ', '{2}شَهْرَيْن', ']2,11[:count أَشْهُر', ']10,Inf[:count شَهْرَ']),
+ 'week' => implode('|', ['{0}:count أُسْبُوع', '{1}أُسْبُوع', '{2}أُسْبُوعَيْن', ']2,11[:count أَسَابِيع', ']10,Inf[:count أُسْبُوع']),
+ 'a_week' => implode('|', ['{0}:count أُسْبُوع', '{1}أُسْبُوع', '{2}أُسْبُوعَيْن', ']2,11[:count أَسَابِيع', ']10,Inf[:count أُسْبُوع']),
+ 'day' => implode('|', ['{0}:count يَوْم', '{1}يَوْم', '{2}يَوْمَيْن', ']2,11[:count أَيَّام', ']10,Inf[:count يَوْم']),
+ 'a_day' => implode('|', ['{0}:count يَوْم', '{1}يَوْم', '{2}يَوْمَيْن', ']2,11[:count أَيَّام', ']10,Inf[:count يَوْم']),
+ 'hour' => implode('|', ['{0}:count سَاعَة', '{1}سَاعَة', '{2}سَاعَتَيْن', ']2,11[:count سَاعَات', ']10,Inf[:count سَاعَة']),
+ 'a_hour' => implode('|', ['{0}:count سَاعَة', '{1}سَاعَة', '{2}سَاعَتَيْن', ']2,11[:count سَاعَات', ']10,Inf[:count سَاعَة']),
+ 'minute' => implode('|', ['{0}:count دَقِيقَة', '{1}دَقِيقَة', '{2}دَقِيقَتَيْن', ']2,11[:count دَقَائِق', ']10,Inf[:count دَقِيقَة']),
+ 'a_minute' => implode('|', ['{0}:count دَقِيقَة', '{1}دَقِيقَة', '{2}دَقِيقَتَيْن', ']2,11[:count دَقَائِق', ']10,Inf[:count دَقِيقَة']),
+ 'second' => implode('|', ['{0}:count ثَانِيَة', '{1}ثَانِيَة', '{2}ثَانِيَتَيْن', ']2,11[:count ثَوَان', ']10,Inf[:count ثَانِيَة']),
+ 'a_second' => implode('|', ['{0}:count ثَانِيَة', '{1}ثَانِيَة', '{2}ثَانِيَتَيْن', ']2,11[:count ثَوَان', ']10,Inf[:count ثَانِيَة']),
+ 'ago' => 'مُنْذُ :time',
+ 'from_now' => 'مِنَ الْآن :time',
+ 'after' => 'بَعْدَ :time',
+ 'before' => 'قَبْلَ :time',
+
+ // @TODO add shakl to translations below
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدًا(?:\\s+عند)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'اث', 'ثل', 'أر', 'خم', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم عند الساعة] LT',
+ 'nextDay' => '[غدًا عند الساعة] LT',
+ 'nextWeek' => 'dddd [عند الساعة] LT',
+ 'lastDay' => '[أمس عند الساعة] LT',
+ 'lastWeek' => 'dddd [عند الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php
new file mode 100644
index 0000000..e790b99
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TD.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ar.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php
new file mode 100644
index 0000000..f096678
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_TN.php
@@ -0,0 +1,91 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Authors:
+ * - JD Isaacks
+ * - Atef Ben Ali (atefBB)
+ * - Mohamed Sabil (mohamedsabil83)
+ */
+$months = [
+ 'جانفي',
+ 'فيفري',
+ 'مارس',
+ 'أفريل',
+ 'ماي',
+ 'جوان',
+ 'جويلية',
+ 'أوت',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'a_year' => implode('|', ['{0}:count سنة', '{1}سنة', '{2}سنتين', ']2,11[:count سنوات', ']10,Inf[:count سنة']),
+ 'month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'a_month' => implode('|', ['{0}:count شهر', '{1}شهر', '{2}شهرين', ']2,11[:count أشهر', ']10,Inf[:count شهر']),
+ 'week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'a_week' => implode('|', ['{0}:count أسبوع', '{1}أسبوع', '{2}أسبوعين', ']2,11[:count أسابيع', ']10,Inf[:count أسبوع']),
+ 'day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'a_day' => implode('|', ['{0}:count يوم', '{1}يوم', '{2}يومين', ']2,11[:count أيام', ']10,Inf[:count يوم']),
+ 'hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'a_hour' => implode('|', ['{0}:count ساعة', '{1}ساعة', '{2}ساعتين', ']2,11[:count ساعات', ']10,Inf[:count ساعة']),
+ 'minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'a_minute' => implode('|', ['{0}:count دقيقة', '{1}دقيقة', '{2}دقيقتين', ']2,11[:count دقائق', ']10,Inf[:count دقيقة']),
+ 'second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'a_second' => implode('|', ['{0}:count ثانية', '{1}ثانية', '{2}ثانيتين', ']2,11[:count ثواني', ']10,Inf[:count ثانية']),
+ 'ago' => 'منذ :time',
+ 'from_now' => 'في :time',
+ 'after' => 'بعد :time',
+ 'before' => 'قبل :time',
+ 'diff_now' => 'الآن',
+ 'diff_today' => 'اليوم',
+ 'diff_today_regexp' => 'اليوم(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_yesterday' => 'أمس',
+ 'diff_yesterday_regexp' => 'أمس(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_tomorrow' => 'غداً',
+ 'diff_tomorrow_regexp' => 'غدا(?:\\s+على)?(?:\\s+الساعة)?',
+ 'diff_before_yesterday' => 'قبل الأمس',
+ 'diff_after_tomorrow' => 'بعد غد',
+ 'period_recurrences' => implode('|', ['{0}مرة', '{1}مرة', '{2}:count مرتين', ']2,11[:count مرات', ']10,Inf[:count مرة']),
+ 'period_interval' => 'كل :interval',
+ 'period_start_date' => 'من :date',
+ 'period_end_date' => 'إلى :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اليوم على الساعة] LT',
+ 'nextDay' => '[غدا على الساعة] LT',
+ 'nextWeek' => 'dddd [على الساعة] LT',
+ 'lastDay' => '[أمس على الساعة] LT',
+ 'lastWeek' => 'dddd [على الساعة] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ص', 'م'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php
new file mode 100644
index 0000000..169fe88
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ar_YE.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ * - Abdullah-Alhariri
+ */
+return array_replace_recursive(require __DIR__.'/ar.php', [
+ 'formats' => [
+ 'L' => 'DD MMM, YYYY',
+ ],
+ 'months' => ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
+ 'months_short' => ['ينا', 'فبر', 'مار', 'أبر', 'ماي', 'يون', 'يول', 'أغس', 'سبت', 'أكت', 'نوف', 'ديس'],
+ 'weekdays' => ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
+ 'weekdays_short' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'weekdays_min' => ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰٤', '۰٥', '۰٦', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱٤', '۱٥', '۱٦', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲٤', '۲٥', '۲٦', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳٤', '۳٥', '۳٦', '۳۷', '۳۸', '۳۹', '٤۰', '٤۱', '٤۲', '٤۳', '٤٤', '٤٥', '٤٦', '٤۷', '٤۸', '٤۹', '٥۰', '٥۱', '٥۲', '٥۳', '٥٤', '٥٥', '٥٦', '٥۷', '٥۸', '٥۹', '٦۰', '٦۱', '٦۲', '٦۳', '٦٤', '٦٥', '٦٦', '٦۷', '٦۸', '٦۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷٤', '۷٥', '۷٦', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸٤', '۸٥', '۸٦', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹٤', '۹٥', '۹٦', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/as.php b/vendor/nesbot/carbon/src/Carbon/Lang/as.php
new file mode 100644
index 0000000..04bc3df
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/as.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/as_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php
new file mode 100644
index 0000000..5fbc3db
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/as_IN.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Amitakhya Phukan, Red Hat bug-glibc@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D-MM-YYYY',
+ ],
+ 'months' => ['জানুৱাৰী', 'ফেব্ৰুৱাৰী', 'মাৰ্চ', 'এপ্ৰিল', 'মে', 'জুন', 'জুলাই', 'আগষ্ট', 'ছেপ্তেম্বৰ', 'অক্টোবৰ', 'নৱেম্বৰ', 'ডিচেম্বৰ'],
+ 'months_short' => ['জানু', 'ফেব্ৰু', 'মাৰ্চ', 'এপ্ৰিল', 'মে', 'জুন', 'জুলাই', 'আগ', 'সেপ্ট', 'অক্টো', 'নভে', 'ডিসে'],
+ 'weekdays' => ['দেওবাৰ', 'সোমবাৰ', 'মঙ্গলবাৰ', 'বুধবাৰ', 'বৃহষ্পতিবাৰ', 'শুক্ৰবাৰ', 'শনিবাৰ'],
+ 'weekdays_short' => ['দেও', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'],
+ 'weekdays_min' => ['দেও', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['পূৰ্ব্বাহ্ন', 'অপৰাহ্ন'],
+
+ 'year' => ':count বছৰ',
+ 'y' => ':count বছৰ',
+ 'a_year' => ':count বছৰ',
+
+ 'month' => ':count মাহ',
+ 'm' => ':count মাহ',
+ 'a_month' => ':count মাহ',
+
+ 'week' => ':count সপ্তাহ',
+ 'w' => ':count সপ্তাহ',
+ 'a_week' => ':count সপ্তাহ',
+
+ 'day' => ':count বাৰ',
+ 'd' => ':count বাৰ',
+ 'a_day' => ':count বাৰ',
+
+ 'hour' => ':count ঘণ্টা',
+ 'h' => ':count ঘণ্টা',
+ 'a_hour' => ':count ঘণ্টা',
+
+ 'minute' => ':count মিনিট',
+ 'min' => ':count মিনিট',
+ 'a_minute' => ':count মিনিট',
+
+ 'second' => ':count দ্বিতীয়',
+ 's' => ':count দ্বিতীয়',
+ 'a_second' => ':count দ্বিতীয়',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/asa.php b/vendor/nesbot/carbon/src/Carbon/Lang/asa.php
new file mode 100644
index 0000000..03bb483
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/asa.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['icheheavo', 'ichamthi'],
+ 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Ijm', 'Jmo'],
+ 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Ijm', 'Jmo'],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Dec'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ast.php b/vendor/nesbot/carbon/src/Carbon/Lang/ast.php
new file mode 100644
index 0000000..d9bdebe
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ast.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Jordi Mallach jordi@gnu.org
+ * - Adolfo Jayme-Barrientos (fitojb)
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['de xineru', 'de febreru', 'de marzu', 'd’abril', 'de mayu', 'de xunu', 'de xunetu', 'd’agostu', 'de setiembre', 'd’ochobre', 'de payares', 'd’avientu'],
+ 'months_short' => ['xin', 'feb', 'mar', 'abr', 'may', 'xun', 'xnt', 'ago', 'set', 'och', 'pay', 'avi'],
+ 'weekdays' => ['domingu', 'llunes', 'martes', 'miércoles', 'xueves', 'vienres', 'sábadu'],
+ 'weekdays_short' => ['dom', 'llu', 'mar', 'mié', 'xue', 'vie', 'sáb'],
+ 'weekdays_min' => ['dom', 'llu', 'mar', 'mié', 'xue', 'vie', 'sáb'],
+
+ 'year' => ':count añu|:count años',
+ 'y' => ':count añu|:count años',
+ 'a_year' => 'un añu|:count años',
+
+ 'month' => ':count mes',
+ 'm' => ':count mes',
+ 'a_month' => 'un mes|:count mes',
+
+ 'week' => ':count selmana|:count selmanes',
+ 'w' => ':count selmana|:count selmanes',
+ 'a_week' => 'una selmana|:count selmanes',
+
+ 'day' => ':count día|:count díes',
+ 'd' => ':count día|:count díes',
+ 'a_day' => 'un día|:count díes',
+
+ 'hour' => ':count hora|:count hores',
+ 'h' => ':count hora|:count hores',
+ 'a_hour' => 'una hora|:count hores',
+
+ 'minute' => ':count minutu|:count minutos',
+ 'min' => ':count minutu|:count minutos',
+ 'a_minute' => 'un minutu|:count minutos',
+
+ 'second' => ':count segundu|:count segundos',
+ 's' => ':count segundu|:count segundos',
+ 'a_second' => 'un segundu|:count segundos',
+
+ 'ago' => 'hai :time',
+ 'from_now' => 'en :time',
+ 'after' => ':time dempués',
+ 'before' => ':time enantes',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php
new file mode 100644
index 0000000..04d7562
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ast_ES.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ast.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ayc.php b/vendor/nesbot/carbon/src/Carbon/Lang/ayc.php
new file mode 100644
index 0000000..d6a6f63
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ayc.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ayc_PE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php
new file mode 100644
index 0000000..ff18504
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ayc_PE.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - runasimipi.org libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['inïru', 'phiwriru', 'marsu', 'awrila', 'mayu', 'junyu', 'julyu', 'awustu', 'sitimri', 'uktuwri', 'nuwimri', 'risimri'],
+ 'months_short' => ['ini', 'phi', 'mar', 'awr', 'may', 'jun', 'jul', 'awu', 'sit', 'ukt', 'nuw', 'ris'],
+ 'weekdays' => ['tuminku', 'lunisa', 'martisa', 'mirkulisa', 'juywisa', 'wirnisa', 'sawäru'],
+ 'weekdays_short' => ['tum', 'lun', 'mar', 'mir', 'juy', 'wir', 'saw'],
+ 'weekdays_min' => ['tum', 'lun', 'mar', 'mir', 'juy', 'wir', 'saw'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['VM', 'NM'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az.php b/vendor/nesbot/carbon/src/Carbon/Lang/az.php
new file mode 100644
index 0000000..1e92106
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/az.php
@@ -0,0 +1,128 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Kunal Marwaha
+ * - François B
+ * - JD Isaacks
+ * - Orxan
+ * - Şəhriyar İmanov
+ * - Baran Şengül
+ */
+return [
+ 'year' => ':count il',
+ 'a_year' => '{1}bir il|]1,Inf[:count il',
+ 'y' => ':count il',
+ 'month' => ':count ay',
+ 'a_month' => '{1}bir ay|]1,Inf[:count ay',
+ 'm' => ':count ay',
+ 'week' => ':count həftə',
+ 'a_week' => '{1}bir həftə|]1,Inf[:count həftə',
+ 'w' => ':count h.',
+ 'day' => ':count gün',
+ 'a_day' => '{1}bir gün|]1,Inf[:count gün',
+ 'd' => ':count g.',
+ 'hour' => ':count saat',
+ 'a_hour' => '{1}bir saat|]1,Inf[:count saat',
+ 'h' => ':count saat',
+ 'minute' => ':count d.',
+ 'a_minute' => '{1}bir dəqiqə|]1,Inf[:count dəqiqə',
+ 'min' => ':count dəqiqə',
+ 'second' => ':count san.',
+ 'a_second' => '{1}birneçə saniyə|]1,Inf[:count saniyə',
+ 's' => ':count saniyə',
+ 'ago' => ':time əvvəl',
+ 'from_now' => ':time sonra',
+ 'after' => ':time sonra',
+ 'before' => ':time əvvəl',
+ 'diff_now' => 'indi',
+ 'diff_today' => 'bugün',
+ 'diff_today_regexp' => 'bugün(?:\\s+saat)?',
+ 'diff_yesterday' => 'dünən',
+ 'diff_tomorrow' => 'sabah',
+ 'diff_tomorrow_regexp' => 'sabah(?:\\s+saat)?',
+ 'diff_before_yesterday' => 'srağagün',
+ 'diff_after_tomorrow' => 'birisi gün',
+ 'period_recurrences' => ':count dəfədən bir',
+ 'period_interval' => 'hər :interval',
+ 'period_start_date' => ':date tarixindən başlayaraq',
+ 'period_end_date' => ':date tarixinədək',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[bugün saat] LT',
+ 'nextDay' => '[sabah saat] LT',
+ 'nextWeek' => '[gələn həftə] dddd [saat] LT',
+ 'lastDay' => '[dünən] LT',
+ 'lastWeek' => '[keçən həftə] dddd [saat] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ if ($number === 0) { // special case for zero
+ return "$number-ıncı";
+ }
+
+ static $suffixes = [
+ 1 => '-inci',
+ 5 => '-inci',
+ 8 => '-inci',
+ 70 => '-inci',
+ 80 => '-inci',
+ 2 => '-nci',
+ 7 => '-nci',
+ 20 => '-nci',
+ 50 => '-nci',
+ 3 => '-üncü',
+ 4 => '-üncü',
+ 100 => '-üncü',
+ 6 => '-ncı',
+ 9 => '-uncu',
+ 10 => '-uncu',
+ 30 => '-uncu',
+ 60 => '-ıncı',
+ 90 => '-ıncı',
+ ];
+
+ $lastDigit = $number % 10;
+
+ return $number.($suffixes[$lastDigit] ?? $suffixes[$number % 100 - $lastDigit] ?? $suffixes[$number >= 100 ? 100 : -1] ?? '');
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'gecə';
+ }
+ if ($hour < 12) {
+ return 'səhər';
+ }
+ if ($hour < 17) {
+ return 'gündüz';
+ }
+
+ return 'axşam';
+ },
+ 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'],
+ 'months_short' => ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl', 'avq', 'sen', 'okt', 'noy', 'dek'],
+ 'months_standalone' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'İyun', 'İyul', 'Avqust', 'Sentyabr', 'Oktyabr', 'Noyabr', 'Dekabr'],
+ 'weekdays' => ['bazar', 'bazar ertəsi', 'çərşənbə axşamı', 'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
+ 'weekdays_short' => ['baz', 'bze', 'çax', 'çər', 'cax', 'cüm', 'şən'],
+ 'weekdays_min' => ['bz', 'be', 'ça', 'çə', 'ca', 'cü', 'şə'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' və '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php
new file mode 100644
index 0000000..2acf881
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_AZ.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/az.php', [
+ 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avq', 'Sen', 'Okt', 'Noy', 'Dek'],
+ 'weekdays' => ['bazar günü', 'bazar ertəsi', 'çərşənbə axşamı', 'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
+ 'weekdays_short' => ['baz', 'ber', 'çax', 'çər', 'cax', 'cüm', 'şnb'],
+ 'weekdays_min' => ['baz', 'ber', 'çax', 'çər', 'cax', 'cüm', 'şnb'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php
new file mode 100644
index 0000000..28fc62f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_Cyrl.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/az.php', [
+ 'weekdays' => ['базар', 'базар ертәси', 'чәршәнбә ахшамы', 'чәршәнбә', 'ҹүмә ахшамы', 'ҹүмә', 'шәнбә'],
+ 'weekdays_short' => ['Б.', 'Б.Е.', 'Ч.А.', 'Ч.', 'Ҹ.А.', 'Ҹ.', 'Ш.'],
+ 'weekdays_min' => ['Б.', 'Б.Е.', 'Ч.А.', 'Ч.', 'Ҹ.А.', 'Ҹ.', 'Ш.'],
+ 'months' => ['јанвар', 'феврал', 'март', 'апрел', 'май', 'ијун', 'ијул', 'август', 'сентјабр', 'октјабр', 'нојабр', 'декабр'],
+ 'months_short' => ['јан', 'фев', 'мар', 'апр', 'май', 'ијн', 'ијл', 'авг', 'сен', 'окт', 'ној', 'дек'],
+ 'months_standalone' => ['Јанвар', 'Феврал', 'Март', 'Апрел', 'Май', 'Ијун', 'Ијул', 'Август', 'Сентјабр', 'Октјабр', 'Нојабр', 'Декабр'],
+ 'meridiem' => ['а', 'п'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php
new file mode 100644
index 0000000..991a0ef
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_IR.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Mousa Moradi mousamk@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'OY/OM/OD',
+ ],
+ 'months' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مئی', 'ژوئن', 'جولای', 'آقۇست', 'سپتامبر', 'اوْکتوْبر', 'نوْوامبر', 'دسامبر'],
+ 'months_short' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مئی', 'ژوئن', 'جولای', 'آقۇست', 'سپتامبر', 'اوْکتوْبر', 'نوْوامبر', 'دسامبر'],
+ 'weekdays' => ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چارشنبه', 'جۆمعه آخشامی', 'جۆمعه', 'شنبه'],
+ 'weekdays_short' => ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چارشنبه', 'جۆمعه آخشامی', 'جۆمعه', 'شنبه'],
+ 'weekdays_min' => ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چارشنبه', 'جۆمعه آخشامی', 'جۆمعه', 'شنبه'],
+ 'first_day_of_week' => 6,
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰۴', '۰۵', '۰۶', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱۴', '۱۵', '۱۶', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲۴', '۲۵', '۲۶', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳۴', '۳۵', '۳۶', '۳۷', '۳۸', '۳۹', '۴۰', '۴۱', '۴۲', '۴۳', '۴۴', '۴۵', '۴۶', '۴۷', '۴۸', '۴۹', '۵۰', '۵۱', '۵۲', '۵۳', '۵۴', '۵۵', '۵۶', '۵۷', '۵۸', '۵۹', '۶۰', '۶۱', '۶۲', '۶۳', '۶۴', '۶۵', '۶۶', '۶۷', '۶۸', '۶۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷۴', '۷۵', '۷۶', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸۴', '۸۵', '۸۶', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹۴', '۹۵', '۹۶', '۹۷', '۹۸', '۹۹'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php
new file mode 100644
index 0000000..0be3391
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/az_Latn.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/az.php', [
+ 'meridiem' => ['a', 'p'],
+ 'weekdays' => ['bazar', 'bazar ertəsi', 'çərşənbə axşamı', 'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
+ 'weekdays_short' => ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'],
+ 'weekdays_min' => ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'],
+ 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'],
+ 'months_short' => ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl', 'avq', 'sen', 'okt', 'noy', 'dek'],
+ 'months_standalone' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'İyun', 'İyul', 'Avqust', 'Sentyabr', 'Oktyabr', 'Noyabr', 'Dekabr'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'D MMMM YYYY, dddd HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bas.php b/vendor/nesbot/carbon/src/Carbon/Lang/bas.php
new file mode 100644
index 0000000..41bfa1d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bas.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['I bikɛ̂glà', 'I ɓugajɔp'],
+ 'weekdays' => ['ŋgwà nɔ̂y', 'ŋgwà njaŋgumba', 'ŋgwà ûm', 'ŋgwà ŋgê', 'ŋgwà mbɔk', 'ŋgwà kɔɔ', 'ŋgwà jôn'],
+ 'weekdays_short' => ['nɔy', 'nja', 'uum', 'ŋge', 'mbɔ', 'kɔɔ', 'jon'],
+ 'weekdays_min' => ['nɔy', 'nja', 'uum', 'ŋge', 'mbɔ', 'kɔɔ', 'jon'],
+ 'months' => ['Kɔndɔŋ', 'Màcɛ̂l', 'Màtùmb', 'Màtop', 'M̀puyɛ', 'Hìlòndɛ̀', 'Njèbà', 'Hìkaŋ', 'Dìpɔ̀s', 'Bìòôm', 'Màyɛsèp', 'Lìbuy li ńyèe'],
+ 'months_short' => ['kɔn', 'mac', 'mat', 'mto', 'mpu', 'hil', 'nje', 'hik', 'dip', 'bio', 'may', 'liɓ'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'second' => ':count móndî', // less reliable
+ 's' => ':count móndî', // less reliable
+ 'a_second' => ':count móndî', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be.php b/vendor/nesbot/carbon/src/Carbon/Lang/be.php
new file mode 100644
index 0000000..ee73636
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/be.php
@@ -0,0 +1,172 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Carbon\CarbonInterface;
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+ }, 'be');
+}
+// @codeCoverageIgnoreEnd
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - SobakaSlava
+ * - François B
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - AbadonnaAbbys
+ * - Siomkin Alexander
+ */
+return [
+ 'year' => ':count год|:count гады|:count гадоў',
+ 'a_year' => '{1}год|:count год|:count гады|:count гадоў',
+ 'y' => ':count год|:count гады|:count гадоў',
+ 'month' => ':count месяц|:count месяцы|:count месяцаў',
+ 'a_month' => '{1}месяц|:count месяц|:count месяцы|:count месяцаў',
+ 'm' => ':count месяц|:count месяцы|:count месяцаў',
+ 'week' => ':count тыдзень|:count тыдні|:count тыдняў',
+ 'a_week' => '{1}тыдзень|:count тыдзень|:count тыдні|:count тыдняў',
+ 'w' => ':count тыдзень|:count тыдні|:count тыдняў',
+ 'day' => ':count дзень|:count дні|:count дзён',
+ 'a_day' => '{1}дзень|:count дзень|:count дні|:count дзён',
+ 'd' => ':count дн',
+ 'hour' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'a_hour' => '{1}гадзіна|:count гадзіна|:count гадзіны|:count гадзін',
+ 'h' => ':count гадзіна|:count гадзіны|:count гадзін',
+ 'minute' => ':count хвіліна|:count хвіліны|:count хвілін',
+ 'a_minute' => '{1}хвіліна|:count хвіліна|:count хвіліны|:count хвілін',
+ 'min' => ':count хв',
+ 'second' => ':count секунда|:count секунды|:count секунд',
+ 'a_second' => '{1}некалькі секунд|:count секунда|:count секунды|:count секунд',
+ 's' => ':count сек',
+
+ 'hour_ago' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'a_hour_ago' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін',
+ 'h_ago' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'minute_ago' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'a_minute_ago' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін',
+ 'min_ago' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'second_ago' => ':count секунду|:count секунды|:count секунд',
+ 'a_second_ago' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд',
+ 's_ago' => ':count секунду|:count секунды|:count секунд',
+
+ 'hour_from_now' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'a_hour_from_now' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін',
+ 'h_from_now' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'minute_from_now' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'a_minute_from_now' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін',
+ 'min_from_now' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'second_from_now' => ':count секунду|:count секунды|:count секунд',
+ 'a_second_from_now' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд',
+ 's_from_now' => ':count секунду|:count секунды|:count секунд',
+
+ 'hour_after' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'a_hour_after' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін',
+ 'h_after' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'minute_after' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'a_minute_after' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін',
+ 'min_after' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'second_after' => ':count секунду|:count секунды|:count секунд',
+ 'a_second_after' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд',
+ 's_after' => ':count секунду|:count секунды|:count секунд',
+
+ 'hour_before' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'a_hour_before' => '{1}гадзіну|:count гадзіну|:count гадзіны|:count гадзін',
+ 'h_before' => ':count гадзіну|:count гадзіны|:count гадзін',
+ 'minute_before' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'a_minute_before' => '{1}хвіліну|:count хвіліну|:count хвіліны|:count хвілін',
+ 'min_before' => ':count хвіліну|:count хвіліны|:count хвілін',
+ 'second_before' => ':count секунду|:count секунды|:count секунд',
+ 'a_second_before' => '{1}некалькі секунд|:count секунду|:count секунды|:count секунд',
+ 's_before' => ':count секунду|:count секунды|:count секунд',
+
+ 'ago' => ':time таму',
+ 'from_now' => 'праз :time',
+ 'after' => ':time пасля',
+ 'before' => ':time да',
+ 'diff_now' => 'цяпер',
+ 'diff_today' => 'Сёння',
+ 'diff_today_regexp' => 'Сёння(?:\\s+ў)?',
+ 'diff_yesterday' => 'учора',
+ 'diff_yesterday_regexp' => 'Учора(?:\\s+ў)?',
+ 'diff_tomorrow' => 'заўтра',
+ 'diff_tomorrow_regexp' => 'Заўтра(?:\\s+ў)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY г.',
+ 'LLL' => 'D MMMM YYYY г., HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY г., HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Сёння ў] LT',
+ 'nextDay' => '[Заўтра ў] LT',
+ 'nextWeek' => '[У] dddd [ў] LT',
+ 'lastDay' => '[Учора ў] LT',
+ 'lastWeek' => function (CarbonInterface $current) {
+ switch ($current->dayOfWeek) {
+ case 1:
+ case 2:
+ case 4:
+ return '[У мінулы] dddd [ў] LT';
+ default:
+ return '[У мінулую] dddd [ў] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'M':
+ case 'd':
+ case 'DDD':
+ case 'w':
+ case 'W':
+ return ($number % 10 === 2 || $number % 10 === 3) && ($number % 100 !== 12 && $number % 100 !== 13) ? $number.'-і' : $number.'-ы';
+ case 'D':
+ return $number.'-га';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'ночы';
+ }
+ if ($hour < 12) {
+ return 'раніцы';
+ }
+ if ($hour < 17) {
+ return 'дня';
+ }
+
+ return 'вечара';
+ },
+ 'months' => ['студзеня', 'лютага', 'сакавіка', 'красавіка', 'траўня', 'чэрвеня', 'ліпеня', 'жніўня', 'верасня', 'кастрычніка', 'лістапада', 'снежня'],
+ 'months_standalone' => ['студзень', 'люты', 'сакавік', 'красавік', 'травень', 'чэрвень', 'ліпень', 'жнівень', 'верасень', 'кастрычнік', 'лістапад', 'снежань'],
+ 'months_short' => ['студ', 'лют', 'сак', 'крас', 'трав', 'чэрв', 'ліп', 'жнів', 'вер', 'каст', 'ліст', 'снеж'],
+ 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['нядзелю', 'панядзелак', 'аўторак', 'сераду', 'чацвер', 'пятніцу', 'суботу'],
+ 'weekdays_standalone' => ['нядзеля', 'панядзелак', 'аўторак', 'серада', 'чацвер', 'пятніца', 'субота'],
+ 'weekdays_short' => ['нд', 'пн', 'ат', 'ср', 'чц', 'пт', 'сб'],
+ 'weekdays_min' => ['нд', 'пн', 'ат', 'ср', 'чц', 'пт', 'сб'],
+ 'weekdays_regexp' => '/\[ ?[Ууў] ?(?:мінулую|наступную)? ?\] ?dddd/',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' і '],
+ 'months_short_standalone' => ['сту', 'лют', 'сак', 'кра', 'май', 'чэр', 'ліп', 'жні', 'вер', 'кас', 'ліс', 'сне'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php
new file mode 100644
index 0000000..26684b4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/be.php', [
+ 'months' => ['студзеня', 'лютага', 'сакавіка', 'красавіка', 'мая', 'чэрвеня', 'ліпеня', 'жніўня', 'верасня', 'кастрычніка', 'лістапада', 'снежня'],
+ 'months_short' => ['сту', 'лют', 'сак', 'кра', 'мая', 'чэр', 'ліп', 'жні', 'вер', 'кас', 'ліс', 'сне'],
+ 'weekdays' => ['Нядзеля', 'Панядзелак', 'Аўторак', 'Серада', 'Чацвер', 'Пятніца', 'Субота'],
+ 'weekdays_short' => ['Няд', 'Пан', 'Аўт', 'Срд', 'Чцв', 'Пят', 'Суб'],
+ 'weekdays_min' => ['Няд', 'Пан', 'Аўт', 'Срд', 'Чцв', 'Пят', 'Суб'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php
new file mode 100644
index 0000000..517ce83
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/be_BY@latin.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['studzienia', 'lutaha', 'sakavika', 'krasavika', 'maja', 'červienia', 'lipienia', 'žniŭnia', 'vieraśnia', 'kastryčnika', 'listapada', 'śniežnia'],
+ 'months_short' => ['Stu', 'Lut', 'Sak', 'Kra', 'Maj', 'Čer', 'Lip', 'Žni', 'Vie', 'Kas', 'Lis', 'Śni'],
+ 'weekdays' => ['Niadziela', 'Paniadziełak', 'Aŭtorak', 'Sierada', 'Čaćvier', 'Piatnica', 'Subota'],
+ 'weekdays_short' => ['Nia', 'Pan', 'Aŭt', 'Sie', 'Čać', 'Pia', 'Sub'],
+ 'weekdays_min' => ['Nia', 'Pan', 'Aŭt', 'Sie', 'Čać', 'Pia', 'Sub'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bem.php b/vendor/nesbot/carbon/src/Carbon/Lang/bem.php
new file mode 100644
index 0000000..1c3ef03
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bem.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/bem_ZM.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php b/vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php
new file mode 100644
index 0000000..620b579
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bem_ZM.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - ANLoc Martin Benjamin locales@africanlocalization.net
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'MM/DD/YYYY',
+ ],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Epreo', 'Mei', 'Juni', 'Julai', 'Ogasti', 'Septemba', 'Oktoba', 'Novemba', 'Disemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Epr', 'Mei', 'Jun', 'Jul', 'Oga', 'Sep', 'Okt', 'Nov', 'Dis'],
+ 'weekdays' => ['Pa Mulungu', 'Palichimo', 'Palichibuli', 'Palichitatu', 'Palichine', 'Palichisano', 'Pachibelushi'],
+ 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ 'weekdays_min' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['uluchelo', 'akasuba'],
+
+ 'year' => 'myaka :count',
+ 'y' => 'myaka :count',
+ 'a_year' => 'myaka :count',
+
+ 'month' => 'myeshi :count',
+ 'm' => 'myeshi :count',
+ 'a_month' => 'myeshi :count',
+
+ 'week' => 'umulungu :count',
+ 'w' => 'umulungu :count',
+ 'a_week' => 'umulungu :count',
+
+ 'day' => 'inshiku :count',
+ 'd' => 'inshiku :count',
+ 'a_day' => 'inshiku :count',
+
+ 'hour' => 'awala :count',
+ 'h' => 'awala :count',
+ 'a_hour' => 'awala :count',
+
+ 'minute' => 'miniti :count',
+ 'min' => 'miniti :count',
+ 'a_minute' => 'miniti :count',
+
+ 'second' => 'sekondi :count',
+ 's' => 'sekondi :count',
+ 'a_second' => 'sekondi :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ber.php b/vendor/nesbot/carbon/src/Carbon/Lang/ber.php
new file mode 100644
index 0000000..685603c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ber.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ber_DZ.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php
new file mode 100644
index 0000000..38de10a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ber_DZ.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'],
+ 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avq', 'Sen', 'Okt', 'Noy', 'Dek'],
+ 'weekdays' => ['bazar günü', 'birinci gün', 'ikinci gün', 'üçüncü gün', 'dördüncü gün', 'beşinci gün', 'altıncı gün'],
+ 'weekdays_short' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'],
+ 'weekdays_min' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php
new file mode 100644
index 0000000..38de10a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ber_MA.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust', 'sentyabr', 'oktyabr', 'noyabr', 'dekabr'],
+ 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avq', 'Sen', 'Okt', 'Noy', 'Dek'],
+ 'weekdays' => ['bazar günü', 'birinci gün', 'ikinci gün', 'üçüncü gün', 'dördüncü gün', 'beşinci gün', 'altıncı gün'],
+ 'weekdays_short' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'],
+ 'weekdays_min' => ['baz', 'bir', 'iki', 'üçü', 'dör', 'beş', 'alt'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bez.php b/vendor/nesbot/carbon/src/Carbon/Lang/bez.php
new file mode 100644
index 0000000..d59c5ef
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bez.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['pamilau', 'pamunyi'],
+ 'weekdays' => ['pa mulungu', 'pa shahuviluha', 'pa hivili', 'pa hidatu', 'pa hitayi', 'pa hihanu', 'pa shahulembela'],
+ 'weekdays_short' => ['Mul', 'Vil', 'Hiv', 'Hid', 'Hit', 'Hih', 'Lem'],
+ 'weekdays_min' => ['Mul', 'Vil', 'Hiv', 'Hid', 'Hit', 'Hih', 'Lem'],
+ 'months' => ['pa mwedzi gwa hutala', 'pa mwedzi gwa wuvili', 'pa mwedzi gwa wudatu', 'pa mwedzi gwa wutai', 'pa mwedzi gwa wuhanu', 'pa mwedzi gwa sita', 'pa mwedzi gwa saba', 'pa mwedzi gwa nane', 'pa mwedzi gwa tisa', 'pa mwedzi gwa kumi', 'pa mwedzi gwa kumi na moja', 'pa mwedzi gwa kumi na mbili'],
+ 'months_short' => ['Hut', 'Vil', 'Dat', 'Tai', 'Han', 'Sit', 'Sab', 'Nan', 'Tis', 'Kum', 'Kmj', 'Kmb'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bg.php b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php
new file mode 100644
index 0000000..f768074
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bg.php
@@ -0,0 +1,114 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - Glavić
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count година|:count години',
+ 'a_year' => 'година|:count години',
+ 'y' => ':count година|:count години',
+ 'month' => ':count месец|:count месеца',
+ 'a_month' => 'месец|:count месеца',
+ 'm' => ':count месец|:count месеца',
+ 'week' => ':count седмица|:count седмици',
+ 'a_week' => 'седмица|:count седмици',
+ 'w' => ':count седмица|:count седмици',
+ 'day' => ':count ден|:count дни',
+ 'a_day' => 'ден|:count дни',
+ 'd' => ':count ден|:count дни',
+ 'hour' => ':count час|:count часа',
+ 'a_hour' => 'час|:count часа',
+ 'h' => ':count час|:count часа',
+ 'minute' => ':count минута|:count минути',
+ 'a_minute' => 'минута|:count минути',
+ 'min' => ':count минута|:count минути',
+ 'second' => ':count секунда|:count секунди',
+ 'a_second' => 'няколко секунди|:count секунди',
+ 's' => ':count секунда|:count секунди',
+ 'ago' => 'преди :time',
+ 'from_now' => 'след :time',
+ 'after' => 'след :time',
+ 'before' => 'преди :time',
+ 'diff_now' => 'сега',
+ 'diff_today' => 'Днес',
+ 'diff_today_regexp' => 'Днес(?:\\s+в)?',
+ 'diff_yesterday' => 'вчера',
+ 'diff_yesterday_regexp' => 'Вчера(?:\\s+в)?',
+ 'diff_tomorrow' => 'утре',
+ 'diff_tomorrow_regexp' => 'Утре(?:\\s+в)?',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'D.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Днес в] LT',
+ 'nextDay' => '[Утре в] LT',
+ 'nextWeek' => 'dddd [в] LT',
+ 'lastDay' => '[Вчера в] LT',
+ 'lastWeek' => function (CarbonInterface $current) {
+ switch ($current->dayOfWeek) {
+ case 0:
+ case 3:
+ case 6:
+ return '[В изминалата] dddd [в] LT';
+ default:
+ return '[В изминалия] dddd [в] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ $lastDigit = $number % 10;
+ $last2Digits = $number % 100;
+ if ($number === 0) {
+ return "$number-ев";
+ }
+ if ($last2Digits === 0) {
+ return "$number-ен";
+ }
+ if ($last2Digits > 10 && $last2Digits < 20) {
+ return "$number-ти";
+ }
+ if ($lastDigit === 1) {
+ return "$number-ви";
+ }
+ if ($lastDigit === 2) {
+ return "$number-ри";
+ }
+ if ($lastDigit === 7 || $lastDigit === 8) {
+ return "$number-ми";
+ }
+
+ return "$number-ти";
+ },
+ 'months' => ['януари', 'февруари', 'март', 'април', 'май', 'юни', 'юли', 'август', 'септември', 'октомври', 'ноември', 'декември'],
+ 'months_short' => ['яну', 'фев', 'мар', 'апр', 'май', 'юни', 'юли', 'авг', 'сеп', 'окт', 'ное', 'дек'],
+ 'weekdays' => ['неделя', 'понеделник', 'вторник', 'сряда', 'четвъртък', 'петък', 'събота'],
+ 'weekdays_short' => ['нед', 'пон', 'вто', 'сря', 'чет', 'пет', 'съб'],
+ 'weekdays_min' => ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' и '],
+ 'meridiem' => ['преди обяд', 'следобед'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php b/vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php
new file mode 100644
index 0000000..b53874d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bg_BG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/bg.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bhb.php b/vendor/nesbot/carbon/src/Carbon/Lang/bhb.php
new file mode 100644
index 0000000..49f0803
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bhb.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/bhb_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php
new file mode 100644
index 0000000..ab557cb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bhb_IN.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Samsung Electronics Co., Ltd. alexey.merzlyakov@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ 'weekdays' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ 'weekdays_min' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bho.php b/vendor/nesbot/carbon/src/Carbon/Lang/bho.php
new file mode 100644
index 0000000..e9ed0b6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bho.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/bho_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php
new file mode 100644
index 0000000..bc54f36
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bho_IN.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bhashaghar@googlegroups.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर"'],
+ 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर"'],
+ 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'गुरुवार', 'शुक्रवार', 'शनिवार'],
+ 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'],
+ 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरु', 'शुक्र', 'शनि'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+
+ 'hour' => ':count मौसम',
+ 'h' => ':count मौसम',
+ 'a_hour' => ':count मौसम',
+
+ 'minute' => ':count कला',
+ 'min' => ':count कला',
+ 'a_minute' => ':count कला',
+
+ 'second' => ':count सोमार',
+ 's' => ':count सोमार',
+ 'a_second' => ':count सोमार',
+
+ 'year' => ':count साल',
+ 'y' => ':count साल',
+ 'a_year' => ':count साल',
+
+ 'month' => ':count महिना',
+ 'm' => ':count महिना',
+ 'a_month' => ':count महिना',
+
+ 'week' => ':count सप्ताह',
+ 'w' => ':count सप्ताह',
+ 'a_week' => ':count सप्ताह',
+
+ 'day' => ':count दिन',
+ 'd' => ':count दिन',
+ 'a_day' => ':count दिन',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bi.php b/vendor/nesbot/carbon/src/Carbon/Lang/bi.php
new file mode 100644
index 0000000..dd08128
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bi.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/bi_VU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php
new file mode 100644
index 0000000..1fe7770
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bi_VU.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com & maninder1.s@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'dddd DD MMM YYYY',
+ ],
+ 'months' => ['jenuware', 'febwari', 'maj', 'epril', 'mei', 'jun', 'julae', 'ogis', 'septemba', 'oktoba', 'novemba', 'disemba'],
+ 'months_short' => ['jen', 'feb', 'maj', 'epr', 'mei', 'jun', 'jul', 'ogi', 'sep', 'okt', 'nov', 'dis'],
+ 'weekdays' => ['sande', 'mande', 'maj', 'wota', 'fraede', 'sarede'],
+ 'weekdays_short' => ['san', 'man', 'maj', 'wot', 'fra', 'sar'],
+ 'weekdays_min' => ['san', 'man', 'maj', 'wot', 'fra', 'sar'],
+
+ 'year' => ':count seven', // less reliable
+ 'y' => ':count seven', // less reliable
+ 'a_year' => ':count seven', // less reliable
+
+ 'month' => ':count mi', // less reliable
+ 'm' => ':count mi', // less reliable
+ 'a_month' => ':count mi', // less reliable
+
+ 'week' => ':count sarede', // less reliable
+ 'w' => ':count sarede', // less reliable
+ 'a_week' => ':count sarede', // less reliable
+
+ 'day' => ':count betde', // less reliable
+ 'd' => ':count betde', // less reliable
+ 'a_day' => ':count betde', // less reliable
+
+ 'hour' => ':count klok', // less reliable
+ 'h' => ':count klok', // less reliable
+ 'a_hour' => ':count klok', // less reliable
+
+ 'minute' => ':count smol', // less reliable
+ 'min' => ':count smol', // less reliable
+ 'a_minute' => ':count smol', // less reliable
+
+ 'second' => ':count tu', // less reliable
+ 's' => ':count tu', // less reliable
+ 'a_second' => ':count tu', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bm.php b/vendor/nesbot/carbon/src/Carbon/Lang/bm.php
new file mode 100644
index 0000000..92822d2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bm.php
@@ -0,0 +1,70 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Estelle Comment
+ */
+return [
+ 'year' => 'san :count',
+ 'a_year' => '{1}san kelen|san :count',
+ 'y' => 'san :count',
+ 'month' => 'kalo :count',
+ 'a_month' => '{1}kalo kelen|kalo :count',
+ 'm' => 'k. :count',
+ 'week' => 'dɔgɔkun :count',
+ 'a_week' => 'dɔgɔkun kelen',
+ 'w' => 'd. :count',
+ 'day' => 'tile :count',
+ 'd' => 't. :count',
+ 'a_day' => '{1}tile kelen|tile :count',
+ 'hour' => 'lɛrɛ :count',
+ 'a_hour' => '{1}lɛrɛ kelen|lɛrɛ :count',
+ 'h' => 'l. :count',
+ 'minute' => 'miniti :count',
+ 'a_minute' => '{1}miniti kelen|miniti :count',
+ 'min' => 'm. :count',
+ 'second' => 'sekondi :count',
+ 'a_second' => '{1}sanga dama dama|sekondi :count',
+ 's' => 'sek. :count',
+ 'ago' => 'a bɛ :time bɔ',
+ 'from_now' => ':time kɔnɔ',
+ 'diff_today' => 'Bi',
+ 'diff_yesterday' => 'Kunu',
+ 'diff_yesterday_regexp' => 'Kunu(?:\\s+lɛrɛ)?',
+ 'diff_tomorrow' => 'Sini',
+ 'diff_tomorrow_regexp' => 'Sini(?:\\s+lɛrɛ)?',
+ 'diff_today_regexp' => 'Bi(?:\\s+lɛrɛ)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'MMMM [tile] D [san] YYYY',
+ 'LLL' => 'MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm',
+ 'LLLL' => 'dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Bi lɛrɛ] LT',
+ 'nextDay' => '[Sini lɛrɛ] LT',
+ 'nextWeek' => 'dddd [don lɛrɛ] LT',
+ 'lastDay' => '[Kunu lɛrɛ] LT',
+ 'lastWeek' => 'dddd [tɛmɛnen lɛrɛ] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['Zanwuyekalo', 'Fewuruyekalo', 'Marisikalo', 'Awirilikalo', 'Mɛkalo', 'Zuwɛnkalo', 'Zuluyekalo', 'Utikalo', 'Sɛtanburukalo', 'ɔkutɔburukalo', 'Nowanburukalo', 'Desanburukalo'],
+ 'months_short' => ['Zan', 'Few', 'Mar', 'Awi', 'Mɛ', 'Zuw', 'Zul', 'Uti', 'Sɛt', 'ɔku', 'Now', 'Des'],
+ 'weekdays' => ['Kari', 'Ntɛnɛn', 'Tarata', 'Araba', 'Alamisa', 'Juma', 'Sibiri'],
+ 'weekdays_short' => ['Kar', 'Ntɛ', 'Tar', 'Ara', 'Ala', 'Jum', 'Sib'],
+ 'weekdays_min' => ['Ka', 'Nt', 'Ta', 'Ar', 'Al', 'Ju', 'Si'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' ni '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php
new file mode 100644
index 0000000..8e14789
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn.php
@@ -0,0 +1,100 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Shakib Hossain
+ * - Raju
+ * - Aniruddha Adhikary
+ * - JD Isaacks
+ * - Saiful Islam
+ * - Faisal Islam
+ */
+return [
+ 'year' => ':count বছর',
+ 'a_year' => 'এক বছর|:count বছর',
+ 'y' => '১ বছর|:count বছর',
+ 'month' => ':count মাস',
+ 'a_month' => 'এক মাস|:count মাস',
+ 'm' => '১ মাস|:count মাস',
+ 'week' => ':count সপ্তাহ',
+ 'a_week' => '১ সপ্তাহ|:count সপ্তাহ',
+ 'w' => '১ সপ্তাহ|:count সপ্তাহ',
+ 'day' => ':count দিন',
+ 'a_day' => 'এক দিন|:count দিন',
+ 'd' => '১ দিন|:count দিন',
+ 'hour' => ':count ঘন্টা',
+ 'a_hour' => 'এক ঘন্টা|:count ঘন্টা',
+ 'h' => '১ ঘন্টা|:count ঘন্টা',
+ 'minute' => ':count মিনিট',
+ 'a_minute' => 'এক মিনিট|:count মিনিট',
+ 'min' => '১ মিনিট|:count মিনিট',
+ 'second' => ':count সেকেন্ড',
+ 'a_second' => 'কয়েক সেকেন্ড|:count সেকেন্ড',
+ 's' => '১ সেকেন্ড|:count সেকেন্ড',
+ 'ago' => ':time আগে',
+ 'from_now' => ':time পরে',
+ 'after' => ':time পরে',
+ 'before' => ':time আগে',
+ 'diff_now' => 'এখন',
+ 'diff_today' => 'আজ',
+ 'diff_yesterday' => 'গতকাল',
+ 'diff_tomorrow' => 'আগামীকাল',
+ 'period_recurrences' => ':count বার|:count বার',
+ 'period_interval' => 'প্রতি :interval',
+ 'period_start_date' => ':date থেকে',
+ 'period_end_date' => ':date পর্যন্ত',
+ 'formats' => [
+ 'LT' => 'A Oh:Om সময়',
+ 'LTS' => 'A Oh:Om:Os সময়',
+ 'L' => 'OD/OM/OY',
+ 'LL' => 'OD MMMM OY',
+ 'LLL' => 'OD MMMM OY, A Oh:Om সময়',
+ 'LLLL' => 'dddd, OD MMMM OY, A Oh:Om সময়',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[আজ] LT',
+ 'nextDay' => '[আগামীকাল] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[গতকাল] LT',
+ 'lastWeek' => '[গত] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'রাত';
+ }
+ if ($hour < 10) {
+ return 'সকাল';
+ }
+ if ($hour < 17) {
+ return 'দুপুর';
+ }
+ if ($hour < 20) {
+ return 'বিকাল';
+ }
+
+ return 'রাত';
+ },
+ 'months' => ['জানুয়ারী', 'ফেব্রুয়ারি', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'],
+ 'months_short' => ['জানু', 'ফেব', 'মার্চ', 'এপ্র', 'মে', 'জুন', 'জুল', 'আগ', 'সেপ্ট', 'অক্টো', 'নভে', 'ডিসে'],
+ 'weekdays' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহস্পতিবার', 'শুক্রবার', 'শনিবার'],
+ 'weekdays_short' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'],
+ 'weekdays_min' => ['রবি', 'সোম', 'মঙ্গ', 'বুধ', 'বৃহঃ', 'শুক্র', 'শনি'],
+ 'list' => [', ', ' এবং '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'weekdays_standalone' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহষ্পতিবার', 'শুক্রবার', 'শনিবার'],
+ 'weekdays_min_standalone' => ['রঃ', 'সোঃ', 'মঃ', 'বুঃ', 'বৃঃ', 'শুঃ', 'শনি'],
+ 'months_short_standalone' => ['জানুয়ারী', 'ফেব্রুয়ারী', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'],
+ 'alt_numbers' => ['০', '১', '২', '৩', '৪', '৫', '৬', '৭', '৮', '৯'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php
new file mode 100644
index 0000000..b5b28dd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn_BD.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ankur Group, Taneem Ahmed, Jamil Ahmed
+ */
+return array_replace_recursive(require __DIR__.'/bn.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['জানুয়ারী', 'ফেব্রুয়ারী', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'],
+ 'months_short' => ['জানু', 'ফেব', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'],
+ 'weekdays' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহস্পতিবার', 'শুক্রবার', 'শনিবার'],
+ 'weekdays_short' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহঃ', 'শুক্র', 'শনি'],
+ 'weekdays_min' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহঃ', 'শুক্র', 'শনি'],
+ 'first_day_of_week' => 5,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php
new file mode 100644
index 0000000..8b3a50e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bn_IN.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/bn.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['জানুয়ারী', 'ফেব্রুয়ারী', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'],
+ 'months_short' => ['জানু', 'ফেব', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগস্ট', 'সেপ্টেম্বর', 'অক্টোবর', 'নভেম্বর', 'ডিসেম্বর'],
+ 'weekdays' => ['রবিবার', 'সোমবার', 'মঙ্গলবার', 'বুধবার', 'বৃহস্পতিবার', 'শুক্রবার', 'শনিবার'],
+ 'weekdays_short' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'],
+ 'weekdays_min' => ['রবি', 'সোম', 'মঙ্গল', 'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo.php
new file mode 100644
index 0000000..99e1bf4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ */
+return [
+ 'year' => '{1}ལོ་གཅིག|]1,Inf[:count ལོ',
+ 'month' => '{1}ཟླ་བ་གཅིག|]1,Inf[:count ཟླ་བ',
+ 'week' => ':count བདུན་ཕྲག',
+ 'day' => '{1}ཉིན་གཅིག|]1,Inf[:count ཉིན་',
+ 'hour' => '{1}ཆུ་ཚོད་གཅིག|]1,Inf[:count ཆུ་ཚོད',
+ 'minute' => '{1}སྐར་མ་གཅིག|]1,Inf[:count སྐར་མ',
+ 'second' => '{1}ལམ་སང|]1,Inf[:count སྐར་ཆ།',
+ 'ago' => ':time སྔན་ལ',
+ 'from_now' => ':time ལ་',
+ 'diff_yesterday' => 'ཁ་སང',
+ 'diff_today' => 'དི་རིང',
+ 'diff_tomorrow' => 'སང་ཉིན',
+ 'formats' => [
+ 'LT' => 'A h:mm',
+ 'LTS' => 'A h:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[དི་རིང] LT',
+ 'nextDay' => '[སང་ཉིན] LT',
+ 'nextWeek' => '[བདུན་ཕྲག་རྗེས་མ], LT',
+ 'lastDay' => '[ཁ་སང] LT',
+ 'lastWeek' => '[བདུན་ཕྲག་མཐའ་མ] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'མཚན་མོ';
+ }
+ if ($hour < 10) {
+ return 'ཞོགས་ཀས';
+ }
+ if ($hour < 17) {
+ return 'ཉིན་གུང';
+ }
+ if ($hour < 20) {
+ return 'དགོང་དག';
+ }
+
+ return 'མཚན་མོ';
+ },
+ 'months' => ['ཟླ་བ་དང་པོ', 'ཟླ་བ་གཉིས་པ', 'ཟླ་བ་གསུམ་པ', 'ཟླ་བ་བཞི་པ', 'ཟླ་བ་ལྔ་པ', 'ཟླ་བ་དྲུག་པ', 'ཟླ་བ་བདུན་པ', 'ཟླ་བ་བརྒྱད་པ', 'ཟླ་བ་དགུ་པ', 'ཟླ་བ་བཅུ་པ', 'ཟླ་བ་བཅུ་གཅིག་པ', 'ཟླ་བ་བཅུ་གཉིས་པ'],
+ 'months_short' => ['ཟླ་བ་དང་པོ', 'ཟླ་བ་གཉིས་པ', 'ཟླ་བ་གསུམ་པ', 'ཟླ་བ་བཞི་པ', 'ཟླ་བ་ལྔ་པ', 'ཟླ་བ་དྲུག་པ', 'ཟླ་བ་བདུན་པ', 'ཟླ་བ་བརྒྱད་པ', 'ཟླ་བ་དགུ་པ', 'ཟླ་བ་བཅུ་པ', 'ཟླ་བ་བཅུ་གཅིག་པ', 'ཟླ་བ་བཅུ་གཉིས་པ'],
+ 'weekdays' => ['གཟའ་ཉི་མ་', 'གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་', 'གཟའ་ཕུར་བུ', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་པ་'],
+ 'weekdays_short' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ', 'པ་སངས་', 'སྤེན་པ་'],
+ 'weekdays_min' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ', 'པ་སངས་', 'སྤེན་པ་'],
+ 'list' => [', ', ' ཨནད་ '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'months_standalone' => ['ཟླ་བ་དང་པོ་', 'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་གསུམ་པ་', 'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་པ་', 'ཟླ་བ་དྲུག་པ་', 'ཟླ་བ་བདུན་པ་', 'ཟླ་བ་བརྒྱད་པ་', 'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་', 'ཟླ་བ་བཅུ་གཅིག་པ་', 'ཟླ་བ་བཅུ་གཉིས་པ་'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php
new file mode 100644
index 0000000..380abb1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo_CN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/bo.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php
new file mode 100644
index 0000000..ca50d04
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bo_IN.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/bo.php', [
+ 'meridiem' => ['སྔ་དྲོ་', 'ཕྱི་དྲོ་'],
+ 'weekdays' => ['གཟའ་ཉི་མ་', 'གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་', 'གཟའ་ཕུར་བུ་', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་པ་'],
+ 'weekdays_short' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ་', 'པ་སངས་', 'སྤེན་པ་'],
+ 'weekdays_min' => ['ཉི་མ་', 'ཟླ་བ་', 'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ་', 'པ་སངས་', 'སྤེན་པ་'],
+ 'months' => ['ཟླ་བ་དང་པོ', 'ཟླ་བ་གཉིས་པ', 'ཟླ་བ་གསུམ་པ', 'ཟླ་བ་བཞི་པ', 'ཟླ་བ་ལྔ་པ', 'ཟླ་བ་དྲུག་པ', 'ཟླ་བ་བདུན་པ', 'ཟླ་བ་བརྒྱད་པ', 'ཟླ་བ་དགུ་པ', 'ཟླ་བ་བཅུ་པ', 'ཟླ་བ་བཅུ་གཅིག་པ', 'ཟླ་བ་བཅུ་གཉིས་པ'],
+ 'months_short' => ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣', 'ཟླ་༤', 'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧', 'ཟླ་༨', 'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡', 'ཟླ་༡༢'],
+ 'months_standalone' => ['ཟླ་བ་དང་པོ་', 'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་གསུམ་པ་', 'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་པ་', 'ཟླ་བ་དྲུག་པ་', 'ཟླ་བ་བདུན་པ་', 'ཟླ་བ་བརྒྱད་པ་', 'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་', 'ཟླ་བ་བཅུ་གཅིག་པ་', 'ཟླ་བ་བཅུ་གཉིས་པ་'],
+ 'weekend' => [0, 0],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'YYYY ལོའི་MMMཚེས་D',
+ 'LLL' => 'སྤྱི་ལོ་YYYY MMMMའི་ཚེས་D h:mm a',
+ 'LLLL' => 'YYYY MMMMའི་ཚེས་D, dddd h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/br.php b/vendor/nesbot/carbon/src/Carbon/Lang/br.php
new file mode 100644
index 0000000..583472f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/br.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Serhan Apaydın
+ * - JD Isaacks
+ */
+return [
+ 'year' => '{1}:count bloaz|{3,4,5,9}:count bloaz|[0,Inf[:count vloaz',
+ 'a_year' => '{1}ur bloaz|{3,4,5,9}:count bloaz|[0,Inf[:count vloaz',
+ 'month' => '{1}:count miz|{2}:count viz|[0,Inf[:count miz',
+ 'a_month' => '{1}ur miz|{2}:count viz|[0,Inf[:count miz',
+ 'week' => ':count sizhun',
+ 'a_week' => '{1}ur sizhun|:count sizhun',
+ 'day' => '{1}:count devezh|{2}:count zevezh|[0,Inf[:count devezh',
+ 'a_day' => '{1}un devezh|{2}:count zevezh|[0,Inf[:count devezh',
+ 'hour' => ':count eur',
+ 'a_hour' => '{1}un eur|:count eur',
+ 'minute' => '{1}:count vunutenn|{2}:count vunutenn|[0,Inf[:count munutenn',
+ 'a_minute' => '{1}ur vunutenn|{2}:count vunutenn|[0,Inf[:count munutenn',
+ 'second' => ':count eilenn',
+ 'a_second' => '{1}un nebeud segondennoù|[0,Inf[:count eilenn',
+ 'ago' => ':time \'zo',
+ 'from_now' => 'a-benn :time',
+ 'diff_now' => 'bremañ',
+ 'diff_today' => 'Hiziv',
+ 'diff_today_regexp' => 'Hiziv(?:\\s+da)?',
+ 'diff_yesterday' => 'decʼh',
+ 'diff_yesterday_regexp' => 'Dec\'h(?:\\s+da)?',
+ 'diff_tomorrow' => 'warcʼhoazh',
+ 'diff_tomorrow_regexp' => 'Warc\'hoazh(?:\\s+da)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D [a viz] MMMM YYYY',
+ 'LLL' => 'D [a viz] MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D [a viz] MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Hiziv da] LT',
+ 'nextDay' => '[Warc\'hoazh da] LT',
+ 'nextWeek' => 'dddd [da] LT',
+ 'lastDay' => '[Dec\'h da] LT',
+ 'lastWeek' => 'dddd [paset da] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ return $number.($number === 1 ? 'añ' : 'vet');
+ },
+ 'months' => ['Genver', 'C\'hwevrer', 'Meurzh', 'Ebrel', 'Mae', 'Mezheven', 'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'],
+ 'months_short' => ['Gen', 'C\'hwe', 'Meu', 'Ebr', 'Mae', 'Eve', 'Gou', 'Eos', 'Gwe', 'Her', 'Du', 'Ker'],
+ 'weekdays' => ['Sul', 'Lun', 'Meurzh', 'Merc\'her', 'Yaou', 'Gwener', 'Sadorn'],
+ 'weekdays_short' => ['Sul', 'Lun', 'Meu', 'Mer', 'Yao', 'Gwe', 'Sad'],
+ 'weekdays_min' => ['Su', 'Lu', 'Me', 'Mer', 'Ya', 'Gw', 'Sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' hag '],
+ 'meridiem' => ['A.M.', 'G.M.'],
+
+ 'y' => ':count bl.',
+ 'd' => ':count d',
+ 'h' => ':count e',
+ 'min' => ':count min',
+ 's' => ':count s',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php
new file mode 100644
index 0000000..7f54185
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/br_FR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/br.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/brx.php b/vendor/nesbot/carbon/src/Carbon/Lang/brx.php
new file mode 100644
index 0000000..a0a7bf9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/brx.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/brx_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php
new file mode 100644
index 0000000..2d80ced
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/brx_IN.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'M/D/YY',
+ ],
+ 'months' => ['जानुवारी', 'फेब्रुवारी', 'मार्स', 'एफ्रिल', 'मे', 'जुन', 'जुलाइ', 'आगस्थ', 'सेबथेज्ब़र', 'अखथबर', 'नबेज्ब़र', 'दिसेज्ब़र'],
+ 'months_short' => ['जानुवारी', 'फेब्रुवारी', 'मार्स', 'एप्रिल', 'मे', 'जुन', 'जुलाइ', 'आगस्थ', 'सेबथेज्ब़र', 'अखथबर', 'नबेज्ब़र', 'दिसेज्ब़र'],
+ 'weekdays' => ['रबिबार', 'सोबार', 'मंगलबार', 'बुदबार', 'बिसथिबार', 'सुखुरबार', 'सुनिबार'],
+ 'weekdays_short' => ['रबि', 'सम', 'मंगल', 'बुद', 'बिसथि', 'सुखुर', 'सुनि'],
+ 'weekdays_min' => ['रबि', 'सम', 'मंगल', 'बुद', 'बिसथि', 'सुखुर', 'सुनि'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['फुं.', 'बेलासे.'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs.php
new file mode 100644
index 0000000..e5d6808
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs.php
@@ -0,0 +1,97 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bokideckonja
+ * - Josh Soref
+ * - François B
+ * - shaishavgandhi05
+ * - Serhan Apaydın
+ * - JD Isaacks
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count godina|:count godine|:count godina',
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'm' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'week' => ':count sedmice|:count sedmicu|:count sedmica',
+ 'w' => ':count sedmice|:count sedmicu|:count sedmica',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count dan|:count dana|:count dana',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minut|:count minuta|:count minuta',
+ 'min' => ':count minut|:count minuta|:count minuta',
+ 'second' => ':count sekund|:count sekunda|:count sekundi',
+ 's' => ':count sekund|:count sekunda|:count sekundi',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => 'nakon :time',
+ 'before' => ':time ranije',
+ 'diff_now' => 'sada',
+ 'diff_today' => 'danas',
+ 'diff_today_regexp' => 'danas(?:\\s+u)?',
+ 'diff_yesterday' => 'jučer',
+ 'diff_yesterday_regexp' => 'jučer(?:\\s+u)?',
+ 'diff_tomorrow' => 'sutra',
+ 'diff_tomorrow_regexp' => 'sutra(?:\\s+u)?',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[danas u] LT',
+ 'nextDay' => '[sutra u] LT',
+ 'nextWeek' => function (CarbonInterface $current) {
+ switch ($current->dayOfWeek) {
+ case 0:
+ return '[u] [nedjelju] [u] LT';
+ case 3:
+ return '[u] [srijedu] [u] LT';
+ case 6:
+ return '[u] [subotu] [u] LT';
+ default:
+ return '[u] dddd [u] LT';
+ }
+ },
+ 'lastDay' => '[jučer u] LT',
+ 'lastWeek' => function (CarbonInterface $current) {
+ switch ($current->dayOfWeek) {
+ case 0:
+ case 3:
+ return '[prošlu] dddd [u] LT';
+ case 6:
+ return '[prošle] [subote] [u] LT';
+ default:
+ return '[prošli] dddd [u] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['januar', 'februar', 'mart', 'april', 'maj', 'juni', 'juli', 'august', 'septembar', 'oktobar', 'novembar', 'decembar'],
+ 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'],
+ 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'],
+ 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sri.', 'čet.', 'pet.', 'sub.'],
+ 'weekdays_min' => ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' i '],
+ 'meridiem' => ['prijepodne', 'popodne'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php
new file mode 100644
index 0000000..0a59117
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_BA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/bs.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php
new file mode 100644
index 0000000..e1a1744
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Cyrl.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/bs.php', [
+ 'meridiem' => ['пре подне', 'поподне'],
+ 'weekdays' => ['недјеља', 'понедјељак', 'уторак', 'сриједа', 'четвртак', 'петак', 'субота'],
+ 'weekdays_short' => ['нед', 'пон', 'уто', 'сри', 'чет', 'пет', 'суб'],
+ 'weekdays_min' => ['нед', 'пон', 'уто', 'сри', 'чет', 'пет', 'суб'],
+ 'months' => ['јануар', 'фебруар', 'март', 'април', 'мај', 'јуни', 'јули', 'аугуст', 'септембар', 'октобар', 'новембар', 'децембар'],
+ 'months_short' => ['јан', 'феб', 'мар', 'апр', 'мај', 'јун', 'јул', 'ауг', 'сеп', 'окт', 'нов', 'дец'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D.M.YYYY.',
+ 'LL' => 'DD.MM.YYYY.',
+ 'LLL' => 'DD. MMMM YYYY. HH:mm',
+ 'LLLL' => 'dddd, DD. MMMM YYYY. HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php
new file mode 100644
index 0000000..b4e363e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/bs_Latn.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/bs.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/byn.php b/vendor/nesbot/carbon/src/Carbon/Lang/byn.php
new file mode 100644
index 0000000..7125f3d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/byn.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/byn_ER.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php
new file mode 100644
index 0000000..ad67533
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/byn_ER.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ልደትሪ', 'ካብኽብቲ', 'ክብላ', 'ፋጅኺሪ', 'ክቢቅሪ', 'ምኪኤል ትጓ̅ኒሪ', 'ኰርኩ', 'ማርያም ትሪ', 'ያኸኒ መሳቅለሪ', 'መተሉ', 'ምኪኤል መሽወሪ', 'ተሕሳስሪ'],
+ 'months_short' => ['ልደት', 'ካብኽ', 'ክብላ', 'ፋጅኺ', 'ክቢቅ', 'ም/ት', 'ኰር', 'ማርያ', 'ያኸኒ', 'መተሉ', 'ም/ም', 'ተሕሳ'],
+ 'weekdays' => ['ሰንበር ቅዳዅ', 'ሰኑ', 'ሰሊጝ', 'ለጓ ወሪ ለብዋ', 'ኣምድ', 'ኣርብ', 'ሰንበር ሽጓዅ'],
+ 'weekdays_short' => ['ሰ/ቅ', 'ሰኑ', 'ሰሊጝ', 'ለጓ', 'ኣምድ', 'ኣርብ', 'ሰ/ሽ'],
+ 'weekdays_min' => ['ሰ/ቅ', 'ሰኑ', 'ሰሊጝ', 'ለጓ', 'ኣምድ', 'ኣርብ', 'ሰ/ሽ'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ፋዱስ ጃብ', 'ፋዱስ ደምቢ'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php
new file mode 100644
index 0000000..b8b1994
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca.php
@@ -0,0 +1,117 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - mestremuten
+ * - François B
+ * - Marc Ordinas i Llopis
+ * - Pere Orga
+ * - JD Isaacks
+ * - Quentí
+ * - Víctor Díaz
+ * - Xavi
+ * - qcardona
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count any|:count anys',
+ 'a_year' => 'un any|:count anys',
+ 'y' => ':count any|:count anys',
+ 'month' => ':count mes|:count mesos',
+ 'a_month' => 'un mes|:count mesos',
+ 'm' => ':count mes|:count mesos',
+ 'week' => ':count setmana|:count setmanes',
+ 'a_week' => 'una setmana|:count setmanes',
+ 'w' => ':count setmana|:count setmanes',
+ 'day' => ':count dia|:count dies',
+ 'a_day' => 'un dia|:count dies',
+ 'd' => ':count d',
+ 'hour' => ':count hora|:count hores',
+ 'a_hour' => 'una hora|:count hores',
+ 'h' => ':count h',
+ 'minute' => ':count minut|:count minuts',
+ 'a_minute' => 'un minut|:count minuts',
+ 'min' => ':count min',
+ 'second' => ':count segon|:count segons',
+ 'a_second' => 'uns segons|:count segons',
+ 's' => ':count s',
+ 'ago' => 'fa :time',
+ 'from_now' => 'd\'aquí a :time',
+ 'after' => ':time després',
+ 'before' => ':time abans',
+ 'diff_now' => 'ara mateix',
+ 'diff_today' => 'avui',
+ 'diff_today_regexp' => 'avui(?:\\s+a)?(?:\\s+les)?',
+ 'diff_yesterday' => 'ahir',
+ 'diff_yesterday_regexp' => 'ahir(?:\\s+a)?(?:\\s+les)?',
+ 'diff_tomorrow' => 'demà',
+ 'diff_tomorrow_regexp' => 'demà(?:\\s+a)?(?:\\s+les)?',
+ 'diff_before_yesterday' => 'abans d\'ahir',
+ 'diff_after_tomorrow' => 'demà passat',
+ 'period_recurrences' => ':count cop|:count cops',
+ 'period_interval' => 'cada :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'fins a :date',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM [de] YYYY',
+ 'LLL' => 'D MMMM [de] YYYY [a les] H:mm',
+ 'LLLL' => 'dddd D MMMM [de] YYYY [a les] H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => function (CarbonInterface $current) {
+ return '[avui a '.($current->hour !== 1 ? 'les' : 'la').'] LT';
+ },
+ 'nextDay' => function (CarbonInterface $current) {
+ return '[demà a '.($current->hour !== 1 ? 'les' : 'la').'] LT';
+ },
+ 'nextWeek' => function (CarbonInterface $current) {
+ return 'dddd [a '.($current->hour !== 1 ? 'les' : 'la').'] LT';
+ },
+ 'lastDay' => function (CarbonInterface $current) {
+ return '[ahir a '.($current->hour !== 1 ? 'les' : 'la').'] LT';
+ },
+ 'lastWeek' => function (CarbonInterface $current) {
+ return '[el] dddd [passat a '.($current->hour !== 1 ? 'les' : 'la').'] LT';
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ return $number.(
+ ($period === 'w' || $period === 'W') ? 'a' : (
+ ($number === 1) ? 'r' : (
+ ($number === 2) ? 'n' : (
+ ($number === 3) ? 'r' : (
+ ($number === 4) ? 't' : 'è'
+ )
+ )
+ )
+ )
+ );
+ },
+ 'months' => ['de gener', 'de febrer', 'de març', 'd\'abril', 'de maig', 'de juny', 'de juliol', 'd\'agost', 'de setembre', 'd\'octubre', 'de novembre', 'de desembre'],
+ 'months_standalone' => ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
+ 'months_short' => ['de gen.', 'de febr.', 'de març', 'd\'abr.', 'de maig', 'de juny', 'de jul.', 'd\'ag.', 'de set.', 'd\'oct.', 'de nov.', 'de des.'],
+ 'months_short_standalone' => ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'],
+ 'months_regexp' => '/(D[oD]?[\s,]+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous', 'divendres', 'dissabte'],
+ 'weekdays_short' => ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
+ 'weekdays_min' => ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' i '],
+ 'meridiem' => ['a. m.', 'p. m.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php
new file mode 100644
index 0000000..861acd2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_AD.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ca.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php
new file mode 100644
index 0000000..5004978
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ca.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php
new file mode 100644
index 0000000..1c16421
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_ES_Valencia.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'ca');
+ }, 'ca_ES_Valencia');
+}
+// @codeCoverageIgnoreEnd
+
+return array_replace_recursive(require __DIR__.'/ca.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php
new file mode 100644
index 0000000..861acd2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_FR.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ca.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php
new file mode 100644
index 0000000..861acd2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ca_IT.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ca.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php b/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php
new file mode 100644
index 0000000..99c1dca
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ccp.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['𑄢𑄧𑄝𑄨𑄝𑄢𑄴', '𑄥𑄧𑄟𑄴𑄝𑄢𑄴', '𑄟𑄧𑄁𑄉𑄧𑄣𑄴𑄝𑄢𑄴', '𑄝𑄪𑄖𑄴𑄝𑄢𑄴', '𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴𑄝𑄢𑄴', '𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴𑄝𑄢𑄴', '𑄥𑄧𑄚𑄨𑄝𑄢𑄴'],
+ 'weekdays_short' => ['𑄢𑄧𑄝𑄨', '𑄥𑄧𑄟𑄴', '𑄟𑄧𑄁𑄉𑄧𑄣𑄴', '𑄝𑄪𑄖𑄴', '𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴', '𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴', '𑄥𑄧𑄚𑄨'],
+ 'weekdays_min' => ['𑄢𑄧𑄝𑄨', '𑄥𑄧𑄟𑄴', '𑄟𑄧𑄁𑄉𑄧𑄣𑄴', '𑄝𑄪𑄖𑄴', '𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴', '𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴', '𑄥𑄧𑄚𑄨'],
+ 'months' => ['𑄎𑄚𑄪𑄠𑄢𑄨', '𑄜𑄬𑄛𑄴𑄝𑄳𑄢𑄪𑄠𑄢𑄨', '𑄟𑄢𑄴𑄌𑄧', '𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴', '𑄟𑄬', '𑄎𑄪𑄚𑄴', '𑄎𑄪𑄣𑄭', '𑄃𑄉𑄧𑄌𑄴𑄑𑄴', '𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄃𑄧𑄇𑄴𑄑𑄬𑄝𑄧𑄢𑄴', '𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄧𑄢𑄴'],
+ 'months_short' => ['𑄎𑄚𑄪', '𑄜𑄬𑄛𑄴', '𑄟𑄢𑄴𑄌𑄧', '𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴', '𑄟𑄬', '𑄎𑄪𑄚𑄴', '𑄎𑄪𑄣𑄭', '𑄃𑄉𑄧𑄌𑄴𑄑𑄴', '𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄃𑄧𑄇𑄴𑄑𑄮𑄝𑄧𑄢𑄴', '𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄢𑄴'],
+ 'months_short_standalone' => ['𑄎𑄚𑄪𑄠𑄢𑄨', '𑄜𑄬𑄛𑄴𑄝𑄳𑄢𑄪𑄠𑄢𑄨', '𑄟𑄢𑄴𑄌𑄧', '𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴', '𑄟𑄬', '𑄎𑄪𑄚𑄴', '𑄎𑄪𑄣𑄭', '𑄃𑄉𑄧𑄌𑄴𑄑𑄴', '𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄃𑄧𑄇𑄴𑄑𑄮𑄝𑄧𑄢𑄴', '𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴', '𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄧𑄢𑄴'],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM, YYYY h:mm a',
+ 'LLLL' => 'dddd, D MMMM, YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php
new file mode 100644
index 0000000..c1fa8af
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ccp_IN.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ccp.php', [
+ 'weekend' => [0, 0],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ce.php b/vendor/nesbot/carbon/src/Carbon/Lang/ce.php
new file mode 100644
index 0000000..f99f6ff
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ce.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ce_RU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php
new file mode 100644
index 0000000..f769856
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ce_RU.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - ANCHR
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY.DD.MM',
+ ],
+ 'months' => ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
+ 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'weekdays' => ['КӀиранан де', 'Оршотан де', 'Шинарин де', 'Кхаарин де', 'Еарин де', 'ПӀераскан де', 'Шот де'],
+ 'weekdays_short' => ['КӀ', 'Ор', 'Ши', 'Кх', 'Еа', 'ПӀ', 'Шо'],
+ 'weekdays_min' => ['КӀ', 'Ор', 'Ши', 'Кх', 'Еа', 'ПӀ', 'Шо'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count шо',
+ 'y' => ':count шо',
+ 'a_year' => ':count шо',
+
+ 'month' => ':count бутт',
+ 'm' => ':count бутт',
+ 'a_month' => ':count бутт',
+
+ 'week' => ':count кӏира',
+ 'w' => ':count кӏира',
+ 'a_week' => ':count кӏира',
+
+ 'day' => ':count де',
+ 'd' => ':count де',
+ 'a_day' => ':count де',
+
+ 'hour' => ':count сахьт',
+ 'h' => ':count сахьт',
+ 'a_hour' => ':count сахьт',
+
+ 'minute' => ':count минот',
+ 'min' => ':count минот',
+ 'a_minute' => ':count минот',
+
+ 'second' => ':count секунд',
+ 's' => ':count секунд',
+ 'a_second' => ':count секунд',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cgg.php b/vendor/nesbot/carbon/src/Carbon/Lang/cgg.php
new file mode 100644
index 0000000..09bcc1c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cgg.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu', 'Orwakana', 'Orwakataano', 'Orwamukaaga'],
+ 'weekdays_short' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
+ 'weekdays_min' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
+ 'months' => ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana', 'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda', 'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'],
+ 'months_short' => ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS', 'KMN', 'KMW', 'KKM', 'KNK', 'KNB'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'day' => ':count ruhanga', // less reliable
+ 'd' => ':count ruhanga', // less reliable
+ 'a_day' => ':count ruhanga', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/chr.php b/vendor/nesbot/carbon/src/Carbon/Lang/chr.php
new file mode 100644
index 0000000..e26190f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/chr.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/chr_US.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php
new file mode 100644
index 0000000..371353e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/chr_US.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Cherokee Nation Joseph Erb josepherb7@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'MM/DD/YYYY',
+ ],
+ 'months' => ['ᎤᏃᎸᏔᏅ', 'ᎧᎦᎵ', 'ᎠᏅᏱ', 'ᎧᏬᏂ', 'ᎠᏂᏍᎬᏘ', 'ᏕᎭᎷᏱ', 'ᎫᏰᏉᏂ', 'ᎦᎶᏂ', 'ᏚᎵᏍᏗ', 'ᏚᏂᏅᏗ', 'ᏅᏓᏕᏆ', 'ᎥᏍᎩᏱ'],
+ 'months_short' => ['ᎤᏃ', 'ᎧᎦ', 'ᎠᏅ', 'ᎧᏬ', 'ᎠᏂ', 'ᏕᎭ', 'ᎫᏰ', 'ᎦᎶ', 'ᏚᎵ', 'ᏚᏂ', 'ᏅᏓ', 'ᎥᏍ'],
+ 'weekdays' => ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ', 'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ', 'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'],
+ 'weekdays_short' => ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
+ 'weekdays_min' => ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ᏌᎾᎴ', 'ᏒᎯᏱᎢᏗᏢ', 'ꮜꮎꮄ', 'ꮢꭿᏹꭲꮧꮲ'],
+
+ 'second' => ':count ᏐᎢ', // less reliable
+ 's' => ':count ᏐᎢ', // less reliable
+ 'a_second' => ':count ᏐᎢ', // less reliable
+
+ 'year' => ':count ᏑᏕᏘᏴᏓ',
+ 'y' => ':count ᏑᏕᏘᏴᏓ',
+ 'a_year' => ':count ᏑᏕᏘᏴᏓ',
+
+ 'month' => ':count ᏏᏅᏙ',
+ 'm' => ':count ᏏᏅᏙ',
+ 'a_month' => ':count ᏏᏅᏙ',
+
+ 'week' => ':count ᏑᎾᏙᏓᏆᏍᏗ',
+ 'w' => ':count ᏑᎾᏙᏓᏆᏍᏗ',
+ 'a_week' => ':count ᏑᎾᏙᏓᏆᏍᏗ',
+
+ 'day' => ':count ᎢᎦ',
+ 'd' => ':count ᎢᎦ',
+ 'a_day' => ':count ᎢᎦ',
+
+ 'hour' => ':count ᏑᏟᎶᏛ',
+ 'h' => ':count ᏑᏟᎶᏛ',
+ 'a_hour' => ':count ᏑᏟᎶᏛ',
+
+ 'minute' => ':count ᎢᏯᏔᏬᏍᏔᏅ',
+ 'min' => ':count ᎢᏯᏔᏬᏍᏔᏅ',
+ 'a_minute' => ':count ᎢᏯᏔᏬᏍᏔᏅ',
+
+ 'ago' => ':time ᏥᎨᏒ',
+ 'from_now' => 'ᎾᎿ :time',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php b/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php
new file mode 100644
index 0000000..acf4dc2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ckb.php
@@ -0,0 +1,89 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Swara Mohammed
+ */
+$months = [
+ 'ڕێبەندان',
+ 'ڕەشەمە',
+ 'نەورۆز',
+ 'گوڵان',
+ 'جۆزەردان',
+ 'پوشپەڕ',
+ 'گەلاوێژ',
+ 'خەرمانان',
+ 'ڕەزبەر',
+ 'گەڵاڕێزان',
+ 'سەرماوەرز',
+ 'بەفرانبار',
+];
+
+return [
+ 'year' => implode('|', ['{0}:count ساڵێک', '{1}ساڵێک', '{2}دوو ساڵ', ']2,11[:count ساڵ', ']10,Inf[:count ساڵ']),
+ 'a_year' => implode('|', ['{0}:count ساڵێک', '{1}ساڵێک', '{2}دوو ساڵ', ']2,11[:count ساڵ', ']10,Inf[:count ساڵ']),
+ 'month' => implode('|', ['{0}:count مانگێک', '{1}مانگێک', '{2}دوو مانگ', ']2,11[:count مانگ', ']10,Inf[:count مانگ']),
+ 'a_month' => implode('|', ['{0}:count مانگێک', '{1}مانگێک', '{2}دوو مانگ', ']2,11[:count مانگ', ']10,Inf[:count مانگ']),
+ 'week' => implode('|', ['{0}:count هەفتەیەک', '{1}هەفتەیەک', '{2}دوو هەفتە', ']2,11[:count هەفتە', ']10,Inf[:count هەفتە']),
+ 'a_week' => implode('|', ['{0}:count هەفتەیەک', '{1}هەفتەیەک', '{2}دوو هەفتە', ']2,11[:count هەفتە', ']10,Inf[:count هەفتە']),
+ 'day' => implode('|', ['{0}:count ڕۆژێک', '{1}ڕۆژێک', '{2}دوو ڕۆژ', ']2,11[:count ڕۆژ', ']10,Inf[:count ڕۆژ']),
+ 'a_day' => implode('|', ['{0}:count ڕۆژێک', '{1}ڕۆژێک', '{2}دوو ڕۆژ', ']2,11[:count ڕۆژ', ']10,Inf[:count ڕۆژ']),
+ 'hour' => implode('|', ['{0}:count کاتژمێرێک', '{1}کاتژمێرێک', '{2}دوو کاتژمێر', ']2,11[:count کاتژمێر', ']10,Inf[:count کاتژمێر']),
+ 'a_hour' => implode('|', ['{0}:count کاتژمێرێک', '{1}کاتژمێرێک', '{2}دوو کاتژمێر', ']2,11[:count کاتژمێر', ']10,Inf[:count کاتژمێر']),
+ 'minute' => implode('|', ['{0}:count خولەکێک', '{1}خولەکێک', '{2}دوو خولەک', ']2,11[:count خولەک', ']10,Inf[:count خولەک']),
+ 'a_minute' => implode('|', ['{0}:count خولەکێک', '{1}خولەکێک', '{2}دوو خولەک', ']2,11[:count خولەک', ']10,Inf[:count خولەک']),
+ 'second' => implode('|', ['{0}:count چرکەیەک', '{1}چرکەیەک', '{2}دوو چرکە', ']2,11[:count چرکە', ']10,Inf[:count چرکە']),
+ 'a_second' => implode('|', ['{0}:count چرکەیەک', '{1}چرکەیەک', '{2}دوو چرکە', ']2,11[:count چرکە', ']10,Inf[:count چرکە']),
+ 'ago' => 'پێش :time',
+ 'from_now' => ':time لە ئێستاوە',
+ 'after' => 'دوای :time',
+ 'before' => 'پێش :time',
+ 'diff_now' => 'ئێستا',
+ 'diff_today' => 'ئەمڕۆ',
+ 'diff_today_regexp' => 'ڕۆژ(?:\\s+لە)?(?:\\s+کاتژمێر)?',
+ 'diff_yesterday' => 'دوێنێ',
+ 'diff_yesterday_regexp' => 'دوێنێ(?:\\s+لە)?(?:\\s+کاتژمێر)?',
+ 'diff_tomorrow' => 'سبەینێ',
+ 'diff_tomorrow_regexp' => 'سبەینێ(?:\\s+لە)?(?:\\s+کاتژمێر)?',
+ 'diff_before_yesterday' => 'پێش دوێنێ',
+ 'diff_after_tomorrow' => 'دوای سبەینێ',
+ 'period_recurrences' => implode('|', ['{0}جار', '{1}جار', '{2}:count دووجار', ']2,11[:count جار', ']10,Inf[:count جار']),
+ 'period_interval' => 'هەموو :interval',
+ 'period_start_date' => 'لە :date',
+ 'period_end_date' => 'بۆ :date',
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => ['یەکشەممە', 'دووشەممە', 'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'هەینی', 'شەممە'],
+ 'weekdays_short' => ['یەکشەممە', 'دووشەممە', 'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'هەینی', 'شەممە'],
+ 'weekdays_min' => ['یەکشەممە', 'دووشەممە', 'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'هەینی', 'شەممە'],
+ 'list' => ['، ', ' و '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ئەمڕۆ لە کاتژمێر] LT',
+ 'nextDay' => '[سبەینێ لە کاتژمێر] LT',
+ 'nextWeek' => 'dddd [لە کاتژمێر] LT',
+ 'lastDay' => '[دوێنێ لە کاتژمێر] LT',
+ 'lastWeek' => 'dddd [لە کاتژمێر] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['پ.ن', 'د.ن'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cmn.php b/vendor/nesbot/carbon/src/Carbon/Lang/cmn.php
new file mode 100644
index 0000000..80b1d69
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cmn.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/cmn_TW.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php
new file mode 100644
index 0000000..7e43f9d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cmn_TW.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY年MM月DD號',
+ ],
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => [' 1月', ' 2月', ' 3月', ' 4月', ' 5月', ' 6月', ' 7月', ' 8月', ' 9月', '10月', '11月', '12月'],
+ 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+ 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'meridiem' => ['上午', '下午'],
+
+ 'year' => ':count 年',
+ 'y' => ':count 年',
+ 'a_year' => ':count 年',
+
+ 'month' => ':count 月',
+ 'm' => ':count 月',
+ 'a_month' => ':count 月',
+
+ 'week' => ':count 周',
+ 'w' => ':count 周',
+ 'a_week' => ':count 周',
+
+ 'day' => ':count 白天',
+ 'd' => ':count 白天',
+ 'a_day' => ':count 白天',
+
+ 'hour' => ':count 小时',
+ 'h' => ':count 小时',
+ 'a_hour' => ':count 小时',
+
+ 'minute' => ':count 分钟',
+ 'min' => ':count 分钟',
+ 'a_minute' => ':count 分钟',
+
+ 'second' => ':count 秒',
+ 's' => ':count 秒',
+ 'a_second' => ':count 秒',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/crh.php b/vendor/nesbot/carbon/src/Carbon/Lang/crh.php
new file mode 100644
index 0000000..a1d7ce6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/crh.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/crh_UA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php b/vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php
new file mode 100644
index 0000000..0513933
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/crh_UA.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Reşat SABIQ tilde.birlik@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'Mayıs', 'İyun', 'İyul', 'Avgust', 'Sentâbr', 'Oktâbr', 'Noyabr', 'Dekabr'],
+ 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'İyn', 'İyl', 'Avg', 'Sen', 'Okt', 'Noy', 'Dek'],
+ 'weekdays' => ['Bazar', 'Bazarertesi', 'Salı', 'Çarşembe', 'Cumaaqşamı', 'Cuma', 'Cumaertesi'],
+ 'weekdays_short' => ['Baz', 'Ber', 'Sal', 'Çar', 'Caq', 'Cum', 'Cer'],
+ 'weekdays_min' => ['Baz', 'Ber', 'Sal', 'Çar', 'Caq', 'Cum', 'Cer'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ÜE', 'ÜS'],
+
+ 'year' => ':count yıl',
+ 'y' => ':count yıl',
+ 'a_year' => ':count yıl',
+
+ 'month' => ':count ay',
+ 'm' => ':count ay',
+ 'a_month' => ':count ay',
+
+ 'week' => ':count afta',
+ 'w' => ':count afta',
+ 'a_week' => ':count afta',
+
+ 'day' => ':count kün',
+ 'd' => ':count kün',
+ 'a_day' => ':count kün',
+
+ 'hour' => ':count saat',
+ 'h' => ':count saat',
+ 'a_hour' => ':count saat',
+
+ 'minute' => ':count daqqa',
+ 'min' => ':count daqqa',
+ 'a_minute' => ':count daqqa',
+
+ 'second' => ':count ekinci',
+ 's' => ':count ekinci',
+ 'a_second' => ':count ekinci',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cs.php b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php
new file mode 100644
index 0000000..c01e3cc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cs.php
@@ -0,0 +1,123 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Jakub Tesinsky
+ * - Martin Suja
+ * - Nikos Timiopulos
+ * - Bohuslav Blín
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Lukas Svoboda
+ * - Max Melentiev
+ * - Juanito Fatas
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Václav Pávek
+ * - CodeSkills
+ * - Tlapi
+ * - newman101
+ * - Petr Kadlec
+ * - tommaskraus
+ * - Karel Sommer (calvera)
+ */
+$za = function ($time) {
+ return 'za '.strtr($time, [
+ 'hodina' => 'hodinu',
+ 'minuta' => 'minutu',
+ 'sekunda' => 'sekundu',
+ ]);
+};
+
+$pred = function ($time) {
+ $time = strtr($time, [
+ 'hodina' => 'hodinou',
+ 'minuta' => 'minutou',
+ 'sekunda' => 'sekundou',
+ ]);
+ $time = preg_replace('/hodiny?(?!\w)/', 'hodinami', $time);
+ $time = preg_replace('/minuty?(?!\w)/', 'minutami', $time);
+ $time = preg_replace('/sekundy?(?!\w)/', 'sekundami', $time);
+
+ return "před $time";
+};
+
+return [
+ 'year' => ':count rok|:count roky|:count let',
+ 'y' => ':count rok|:count roky|:count let',
+ 'a_year' => 'rok|:count roky|:count let',
+ 'month' => ':count měsíc|:count měsíce|:count měsíců',
+ 'm' => ':count měs.',
+ 'a_month' => 'měsíc|:count měsíce|:count měsíců',
+ 'week' => ':count týden|:count týdny|:count týdnů',
+ 'w' => ':count týd.',
+ 'a_week' => 'týden|:count týdny|:count týdnů',
+ 'day' => ':count den|:count dny|:count dní',
+ 'd' => ':count den|:count dny|:count dní',
+ 'a_day' => 'den|:count dny|:count dní',
+ 'hour' => ':count hodina|:count hodiny|:count hodin',
+ 'h' => ':count hod.',
+ 'a_hour' => 'hodina|:count hodiny|:count hodin',
+ 'minute' => ':count minuta|:count minuty|:count minut',
+ 'min' => ':count min.',
+ 'a_minute' => 'minuta|:count minuty|:count minut',
+ 'second' => ':count sekunda|:count sekundy|:count sekund',
+ 's' => ':count sek.',
+ 'a_second' => 'pár sekund|:count sekundy|:count sekund',
+
+ 'month_ago' => ':count měsícem|:count měsíci|:count měsíci',
+ 'a_month_ago' => 'měsícem|:count měsíci|:count měsíci',
+ 'day_ago' => ':count dnem|:count dny|:count dny',
+ 'a_day_ago' => 'dnem|:count dny|:count dny',
+ 'week_ago' => ':count týdnem|:count týdny|:count týdny',
+ 'a_week_ago' => 'týdnem|:count týdny|:count týdny',
+ 'year_ago' => ':count rokem|:count roky|:count lety',
+ 'y_ago' => ':count rok.|:count rok.|:count let.',
+ 'a_year_ago' => 'rokem|:count roky|:count lety',
+
+ 'month_before' => ':count měsícem|:count měsíci|:count měsíci',
+ 'a_month_before' => 'měsícem|:count měsíci|:count měsíci',
+ 'day_before' => ':count dnem|:count dny|:count dny',
+ 'a_day_before' => 'dnem|:count dny|:count dny',
+ 'week_before' => ':count týdnem|:count týdny|:count týdny',
+ 'a_week_before' => 'týdnem|:count týdny|:count týdny',
+ 'year_before' => ':count rokem|:count roky|:count lety',
+ 'y_before' => ':count rok.|:count rok.|:count let.',
+ 'a_year_before' => 'rokem|:count roky|:count lety',
+
+ 'ago' => $pred,
+ 'from_now' => $za,
+ 'before' => $pred,
+ 'after' => $za,
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'months' => ['ledna', 'února', 'března', 'dubna', 'května', 'června', 'července', 'srpna', 'září', 'října', 'listopadu', 'prosince'],
+ 'months_standalone' => ['leden', 'únor', 'březen', 'duben', 'květen', 'červen', 'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'],
+ 'months_short' => ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc', 'srp', 'zář', 'říj', 'lis', 'pro'],
+ 'weekdays' => ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota'],
+ 'weekdays_short' => ['ned', 'pon', 'úte', 'stř', 'čtv', 'pát', 'sob'],
+ 'weekdays_min' => ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
+ 'list' => [', ', ' a '],
+ 'diff_now' => 'nyní',
+ 'diff_yesterday' => 'včera',
+ 'diff_tomorrow' => 'zítra',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD. MM. YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D. MMMM YYYY HH:mm',
+ ],
+ 'meridiem' => ['dopoledne', 'odpoledne'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php
new file mode 100644
index 0000000..ea2517e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cs_CZ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/cs.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/csb.php b/vendor/nesbot/carbon/src/Carbon/Lang/csb.php
new file mode 100644
index 0000000..a35d281
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/csb.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/csb_PL.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php b/vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php
new file mode 100644
index 0000000..25e0ca8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/csb_PL.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - csb_PL locale Michal Ostrowski bug-glibc-locales@gnu.org
+ */
+return [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'MMMM DD, YYYY',
+ 'LLL' => 'DD MMM HH:mm',
+ 'LLLL' => 'MMMM DD, YYYY HH:mm',
+ ],
+ 'months' => ['stëcznika', 'gromicznika', 'strëmiannika', 'łżëkwiata', 'maja', 'czerwińca', 'lëpińca', 'zélnika', 'séwnika', 'rujana', 'lëstopadnika', 'gòdnika'],
+ 'months_short' => ['stë', 'gro', 'str', 'łżë', 'maj', 'cze', 'lëp', 'zél', 'séw', 'ruj', 'lës', 'gòd'],
+ 'weekdays' => ['niedzela', 'pòniedzôłk', 'wtórk', 'strzoda', 'czwiôrtk', 'piątk', 'sobòta'],
+ 'weekdays_short' => ['nie', 'pòn', 'wtó', 'str', 'czw', 'pią', 'sob'],
+ 'weekdays_min' => ['nie', 'pòn', 'wtó', 'str', 'czw', 'pią', 'sob'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' a téż '],
+ 'two_words_connector' => ' a téż ',
+ 'year' => ':count rok',
+ 'month' => ':count miesiąc',
+ 'week' => ':count tidzéń',
+ 'day' => ':count dzéń',
+ 'hour' => ':count gòdzëna',
+ 'minute' => ':count minuta',
+ 'second' => ':count sekunda',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cu.php b/vendor/nesbot/carbon/src/Carbon/Lang/cu.php
new file mode 100644
index 0000000..d6d1312
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cu.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'months' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'],
+ 'months_short' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'YYYY MMMM D, dddd HH:mm',
+ ],
+
+ 'year' => ':count лѣто',
+ 'y' => ':count лѣто',
+ 'a_year' => ':count лѣто',
+
+ 'month' => ':count мѣсѧць',
+ 'm' => ':count мѣсѧць',
+ 'a_month' => ':count мѣсѧць',
+
+ 'week' => ':count сєдмица',
+ 'w' => ':count сєдмица',
+ 'a_week' => ':count сєдмица',
+
+ 'day' => ':count дьнь',
+ 'd' => ':count дьнь',
+ 'a_day' => ':count дьнь',
+
+ 'hour' => ':count година',
+ 'h' => ':count година',
+ 'a_hour' => ':count година',
+
+ 'minute' => ':count малъ', // less reliable
+ 'min' => ':count малъ', // less reliable
+ 'a_minute' => ':count малъ', // less reliable
+
+ 'second' => ':count въторъ',
+ 's' => ':count въторъ',
+ 'a_second' => ':count въторъ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cv.php b/vendor/nesbot/carbon/src/Carbon/Lang/cv.php
new file mode 100644
index 0000000..8aeb73a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cv.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - JD Isaacks
+ */
+return [
+ 'year' => ':count ҫул',
+ 'a_year' => '{1}пӗр ҫул|:count ҫул',
+ 'month' => ':count уйӑх',
+ 'a_month' => '{1}пӗр уйӑх|:count уйӑх',
+ 'week' => ':count эрне',
+ 'a_week' => '{1}пӗр эрне|:count эрне',
+ 'day' => ':count кун',
+ 'a_day' => '{1}пӗр кун|:count кун',
+ 'hour' => ':count сехет',
+ 'a_hour' => '{1}пӗр сехет|:count сехет',
+ 'minute' => ':count минут',
+ 'a_minute' => '{1}пӗр минут|:count минут',
+ 'second' => ':count ҫеккунт',
+ 'a_second' => '{1}пӗр-ик ҫеккунт|:count ҫеккунт',
+ 'ago' => ':time каялла',
+ 'from_now' => function ($time) {
+ return $time.(preg_match('/сехет$/u', $time) ? 'рен' : (preg_match('/ҫул/u', $time) ? 'тан' : 'ран'));
+ },
+ 'diff_yesterday' => 'Ӗнер',
+ 'diff_today' => 'Паян',
+ 'diff_tomorrow' => 'Ыран',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD-MM-YYYY',
+ 'LL' => 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]',
+ 'LLL' => 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm',
+ 'LLLL' => 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Паян] LT [сехетре]',
+ 'nextDay' => '[Ыран] LT [сехетре]',
+ 'nextWeek' => '[Ҫитес] dddd LT [сехетре]',
+ 'lastDay' => '[Ӗнер] LT [сехетре]',
+ 'lastWeek' => '[Иртнӗ] dddd LT [сехетре]',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number-мӗш',
+ 'months' => ['кӑрлач', 'нарӑс', 'пуш', 'ака', 'май', 'ҫӗртме', 'утӑ', 'ҫурла', 'авӑн', 'юпа', 'чӳк', 'раштав'],
+ 'months_short' => ['кӑр', 'нар', 'пуш', 'ака', 'май', 'ҫӗр', 'утӑ', 'ҫур', 'авн', 'юпа', 'чӳк', 'раш'],
+ 'weekdays' => ['вырсарникун', 'тунтикун', 'ытларикун', 'юнкун', 'кӗҫнерникун', 'эрнекун', 'шӑматкун'],
+ 'weekdays_short' => ['выр', 'тун', 'ытл', 'юн', 'кӗҫ', 'эрн', 'шӑм'],
+ 'weekdays_min' => ['вр', 'тн', 'ыт', 'юн', 'кҫ', 'эр', 'шм'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' тата '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php
new file mode 100644
index 0000000..197bd8d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cv_RU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/cv.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cy.php b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php
new file mode 100644
index 0000000..119274f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cy.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - JD Isaacks
+ * - Daniel Monaghan
+ */
+return [
+ 'year' => '{1}blwyddyn|]1,Inf[:count flynedd',
+ 'y' => ':countbl',
+ 'month' => '{1}mis|]1,Inf[:count mis',
+ 'm' => ':countmi',
+ 'week' => ':count wythnos',
+ 'w' => ':countw',
+ 'day' => '{1}diwrnod|]1,Inf[:count diwrnod',
+ 'd' => ':countd',
+ 'hour' => '{1}awr|]1,Inf[:count awr',
+ 'h' => ':counth',
+ 'minute' => '{1}munud|]1,Inf[:count munud',
+ 'min' => ':countm',
+ 'second' => '{1}ychydig eiliadau|]1,Inf[:count eiliad',
+ 's' => ':counts',
+ 'ago' => ':time yn ôl',
+ 'from_now' => 'mewn :time',
+ 'after' => ':time ar ôl',
+ 'before' => ':time o\'r blaen',
+ 'diff_now' => 'nawr',
+ 'diff_today' => 'Heddiw',
+ 'diff_today_regexp' => 'Heddiw(?:\\s+am)?',
+ 'diff_yesterday' => 'ddoe',
+ 'diff_yesterday_regexp' => 'Ddoe(?:\\s+am)?',
+ 'diff_tomorrow' => 'yfory',
+ 'diff_tomorrow_regexp' => 'Yfory(?:\\s+am)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Heddiw am] LT',
+ 'nextDay' => '[Yfory am] LT',
+ 'nextWeek' => 'dddd [am] LT',
+ 'lastDay' => '[Ddoe am] LT',
+ 'lastWeek' => 'dddd [diwethaf am] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ return $number.(
+ $number > 20
+ ? (\in_array((int) $number, [40, 50, 60, 80, 100], true) ? 'fed' : 'ain')
+ : ([
+ '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed
+ 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed', // 11eg to 20fed
+ ])[$number] ?? ''
+ );
+ },
+ 'months' => ['Ionawr', 'Chwefror', 'Mawrth', 'Ebrill', 'Mai', 'Mehefin', 'Gorffennaf', 'Awst', 'Medi', 'Hydref', 'Tachwedd', 'Rhagfyr'],
+ 'months_short' => ['Ion', 'Chwe', 'Maw', 'Ebr', 'Mai', 'Meh', 'Gor', 'Aws', 'Med', 'Hyd', 'Tach', 'Rhag'],
+ 'weekdays' => ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher', 'Dydd Iau', 'Dydd Gwener', 'Dydd Sadwrn'],
+ 'weekdays_short' => ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwe', 'Sad'],
+ 'weekdays_min' => ['Su', 'Ll', 'Ma', 'Me', 'Ia', 'Gw', 'Sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' a '],
+ 'meridiem' => ['yb', 'yh'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php
new file mode 100644
index 0000000..2c8148d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/cy_GB.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/cy.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da.php b/vendor/nesbot/carbon/src/Carbon/Lang/da.php
new file mode 100644
index 0000000..322f91d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/da.php
@@ -0,0 +1,81 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Rune Mønnike
+ * - François B
+ * - codenhagen
+ * - JD Isaacks
+ * - Jens Herlevsen
+ * - Ulrik McArdle (mcardle)
+ * - Frederik Sauer (FrittenKeeZ)
+ * - Janus Bahs Jacquet (kokoshneta)
+ */
+return [
+ 'year' => ':count år|:count år',
+ 'a_year' => 'et år|:count år',
+ 'y' => ':count år|:count år',
+ 'month' => ':count måned|:count måneder',
+ 'a_month' => 'en måned|:count måneder',
+ 'm' => ':count mdr.',
+ 'week' => ':count uge|:count uger',
+ 'a_week' => 'en uge|:count uger',
+ 'w' => ':count u.',
+ 'day' => ':count dag|:count dage',
+ 'a_day' => ':count dag|:count dage',
+ 'd' => ':count d.',
+ 'hour' => ':count time|:count timer',
+ 'a_hour' => 'en time|:count timer',
+ 'h' => ':count t.',
+ 'minute' => ':count minut|:count minutter',
+ 'a_minute' => 'et minut|:count minutter',
+ 'min' => ':count min.',
+ 'second' => ':count sekund|:count sekunder',
+ 'a_second' => 'få sekunder|:count sekunder',
+ 's' => ':count s.',
+ 'ago' => 'for :time siden',
+ 'from_now' => 'om :time',
+ 'after' => ':time efter',
+ 'before' => ':time før',
+ 'diff_now' => 'nu',
+ 'diff_today' => 'i dag',
+ 'diff_today_regexp' => 'i dag(?:\\s+kl.)?',
+ 'diff_yesterday' => 'i går',
+ 'diff_yesterday_regexp' => 'i går(?:\\s+kl.)?',
+ 'diff_tomorrow' => 'i morgen',
+ 'diff_tomorrow_regexp' => 'i morgen(?:\\s+kl.)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd [d.] D. MMMM YYYY [kl.] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[i dag kl.] LT',
+ 'nextDay' => '[i morgen kl.] LT',
+ 'nextWeek' => 'på dddd [kl.] LT',
+ 'lastDay' => '[i går kl.] LT',
+ 'lastWeek' => '[i] dddd[s kl.] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'],
+ 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'],
+ 'weekdays' => ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'],
+ 'weekdays_short' => ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
+ 'weekdays_min' => ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' og '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php
new file mode 100644
index 0000000..392c484
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/da_DK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/da.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php b/vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php
new file mode 100644
index 0000000..ea5698b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/da_GL.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/da.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D. MMM YYYY',
+ 'LLL' => 'D. MMMM YYYY HH.mm',
+ 'LLLL' => 'dddd [den] D. MMMM YYYY HH.mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dav.php b/vendor/nesbot/carbon/src/Carbon/Lang/dav.php
new file mode 100644
index 0000000..e95ec4b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dav.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Luma lwa K', 'luma lwa p'],
+ 'weekdays' => ['Ituku ja jumwa', 'Kuramuka jimweri', 'Kuramuka kawi', 'Kuramuka kadadu', 'Kuramuka kana', 'Kuramuka kasanu', 'Kifula nguwo'],
+ 'weekdays_short' => ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'],
+ 'weekdays_min' => ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'],
+ 'months' => ['Mori ghwa imbiri', 'Mori ghwa kawi', 'Mori ghwa kadadu', 'Mori ghwa kana', 'Mori ghwa kasanu', 'Mori ghwa karandadu', 'Mori ghwa mfungade', 'Mori ghwa wunyanya', 'Mori ghwa ikenda', 'Mori ghwa ikumi', 'Mori ghwa ikumi na imweri', 'Mori ghwa ikumi na iwi'],
+ 'months_short' => ['Imb', 'Kaw', 'Kad', 'Kan', 'Kas', 'Kar', 'Mfu', 'Wun', 'Ike', 'Iku', 'Imw', 'Iwi'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de.php b/vendor/nesbot/carbon/src/Carbon/Lang/de.php
new file mode 100644
index 0000000..3b70750
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de.php
@@ -0,0 +1,117 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Michael Hohl
+ * - sheriffmarley
+ * - dennisoderwald
+ * - Timo
+ * - Karag2006
+ * - Pete Scopes (pdscopes)
+ */
+return [
+ 'year' => ':count Jahr|:count Jahre',
+ 'a_year' => 'ein Jahr|:count Jahre',
+ 'y' => ':count J.',
+ 'month' => ':count Monat|:count Monate',
+ 'a_month' => 'ein Monat|:count Monate',
+ 'm' => ':count Mon.',
+ 'week' => ':count Woche|:count Wochen',
+ 'a_week' => 'eine Woche|:count Wochen',
+ 'w' => ':count Wo.',
+ 'day' => ':count Tag|:count Tage',
+ 'a_day' => 'ein Tag|:count Tage',
+ 'd' => ':count Tg.',
+ 'hour' => ':count Stunde|:count Stunden',
+ 'a_hour' => 'eine Stunde|:count Stunden',
+ 'h' => ':count Std.',
+ 'minute' => ':count Minute|:count Minuten',
+ 'a_minute' => 'eine Minute|:count Minuten',
+ 'min' => ':count Min.',
+ 'second' => ':count Sekunde|:count Sekunden',
+ 'a_second' => 'ein paar Sekunden|:count Sekunden',
+ 's' => ':count Sek.',
+ 'millisecond' => ':count Millisekunde|:count Millisekunden',
+ 'a_millisecond' => 'eine Millisekunde|:count Millisekunden',
+ 'ms' => ':countms',
+ 'microsecond' => ':count Mikrosekunde|:count Mikrosekunden',
+ 'a_microsecond' => 'eine Mikrosekunde|:count Mikrosekunden',
+ 'µs' => ':countµs',
+ 'ago' => 'vor :time',
+ 'from_now' => 'in :time',
+ 'after' => ':time später',
+ 'before' => ':time zuvor',
+
+ 'year_from_now' => ':count Jahr|:count Jahren',
+ 'month_from_now' => ':count Monat|:count Monaten',
+ 'week_from_now' => ':count Woche|:count Wochen',
+ 'day_from_now' => ':count Tag|:count Tagen',
+ 'year_ago' => ':count Jahr|:count Jahren',
+ 'month_ago' => ':count Monat|:count Monaten',
+ 'week_ago' => ':count Woche|:count Wochen',
+ 'day_ago' => ':count Tag|:count Tagen',
+ 'a_year_from_now' => 'ein Jahr|:count Jahren',
+ 'a_month_from_now' => 'ein Monat|:count Monaten',
+ 'a_week_from_now' => 'eine Woche|:count Wochen',
+ 'a_day_from_now' => 'ein Tag|:count Tagen',
+ 'a_year_ago' => 'ein Jahr|:count Jahren',
+ 'a_month_ago' => 'ein Monat|:count Monaten',
+ 'a_week_ago' => 'eine Woche|:count Wochen',
+ 'a_day_ago' => 'ein Tag|:count Tagen',
+
+ 'diff_now' => 'Gerade eben',
+ 'diff_today' => 'heute',
+ 'diff_today_regexp' => 'heute(?:\\s+um)?',
+ 'diff_yesterday' => 'Gestern',
+ 'diff_yesterday_regexp' => 'gestern(?:\\s+um)?',
+ 'diff_tomorrow' => 'Morgen',
+ 'diff_tomorrow_regexp' => 'morgen(?:\\s+um)?',
+ 'diff_before_yesterday' => 'Vorgestern',
+ 'diff_after_tomorrow' => 'Übermorgen',
+
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY HH:mm',
+ ],
+
+ 'calendar' => [
+ 'sameDay' => '[heute um] LT [Uhr]',
+ 'nextDay' => '[morgen um] LT [Uhr]',
+ 'nextWeek' => 'dddd [um] LT [Uhr]',
+ 'lastDay' => '[gestern um] LT [Uhr]',
+ 'lastWeek' => '[letzten] dddd [um] LT [Uhr]',
+ 'sameElse' => 'L',
+ ],
+
+ 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
+ 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+ 'weekdays' => ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
+ 'weekdays_short' => ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
+ 'weekdays_min' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+ 'ordinal' => ':number.',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' und '],
+ 'ordinal_words' => [
+ 'of' => 'im',
+ 'first' => 'erster',
+ 'second' => 'zweiter',
+ 'third' => 'dritter',
+ 'fourth' => 'vierten',
+ 'fifth' => 'fünfter',
+ 'last' => 'letzten',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php
new file mode 100644
index 0000000..a2ea4c0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_AT.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - sheriffmarley
+ * - Timo
+ * - Michael Hohl
+ * - Namoshek
+ * - Bernhard Baumrock (BernhardBaumrock)
+ */
+return array_replace_recursive(require __DIR__.'/de.php', [
+ 'months' => [
+ 0 => 'Jänner',
+ ],
+ 'months_short' => [
+ 0 => 'Jän',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php
new file mode 100644
index 0000000..8ed8dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_BE.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/de.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php
new file mode 100644
index 0000000..a869ab4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_CH.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - sheriffmarley
+ * - Timo
+ * - Michael Hohl
+ */
+return array_replace_recursive(require __DIR__.'/de.php', [
+ 'weekdays_short' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php
new file mode 100644
index 0000000..fb1209d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_DE.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return require __DIR__.'/de.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php
new file mode 100644
index 0000000..604a856
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_IT.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Matthias Dieter Wallno:fer libc-locales@sourceware.org
+ */
+return require __DIR__.'/de.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php
new file mode 100644
index 0000000..03e606a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_LI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/de.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php
new file mode 100644
index 0000000..8ed8dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/de_LU.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/de.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dje.php b/vendor/nesbot/carbon/src/Carbon/Lang/dje.php
new file mode 100644
index 0000000..74b7ac1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dje.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Subbaahi', 'Zaarikay b'],
+ 'weekdays' => ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamisi', 'Alzuma', 'Asibti'],
+ 'weekdays_short' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
+ 'weekdays_min' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
+ 'months' => ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
+ 'months_short' => ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count hari', // less reliable
+ 'y' => ':count hari', // less reliable
+ 'a_year' => ':count hari', // less reliable
+
+ 'week' => ':count alzuma', // less reliable
+ 'w' => ':count alzuma', // less reliable
+ 'a_week' => ':count alzuma', // less reliable
+
+ 'second' => ':count atinni', // less reliable
+ 's' => ':count atinni', // less reliable
+ 'a_second' => ':count atinni', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/doi.php b/vendor/nesbot/carbon/src/Carbon/Lang/doi.php
new file mode 100644
index 0000000..cb679c5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/doi.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/doi_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php
new file mode 100644
index 0000000..d359721
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/doi_IN.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat Pune libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'एप्रैल', 'मेई', 'जून', 'जूलै', 'अगस्त', 'सितंबर', 'अक्तूबर', 'नवंबर', 'दिसंबर'],
+ 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'एप्रैल', 'मेई', 'जून', 'जूलै', 'अगस्त', 'सितंबर', 'अक्तूबर', 'नवंबर', 'दिसंबर'],
+ 'weekdays' => ['ऐतबार', 'सोमबार', 'मंगलबर', 'बुधबार', 'बीरबार', 'शुक्करबार', 'श्नीचरबार'],
+ 'weekdays_short' => ['ऐत', 'सोम', 'मंगल', 'बुध', 'बीर', 'शुक्कर', 'श्नीचर'],
+ 'weekdays_min' => ['ऐत', 'सोम', 'मंगल', 'बुध', 'बीर', 'शुक्कर', 'श्नीचर'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['सञं', 'सबेर'],
+
+ 'second' => ':count सङार', // less reliable
+ 's' => ':count सङार', // less reliable
+ 'a_second' => ':count सङार', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dsb.php b/vendor/nesbot/carbon/src/Carbon/Lang/dsb.php
new file mode 100644
index 0000000..1d214d5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dsb.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/dsb_DE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php
new file mode 100644
index 0000000..1b94187
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dsb_DE.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Information from Michael Wolf bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'DD. MMMM YYYY',
+ 'LLL' => 'DD. MMMM, HH:mm [góź.]',
+ 'LLLL' => 'dddd, DD. MMMM YYYY, HH:mm [góź.]',
+ ],
+ 'months' => ['januara', 'februara', 'měrca', 'apryla', 'maja', 'junija', 'julija', 'awgusta', 'septembra', 'oktobra', 'nowembra', 'decembra'],
+ 'months_short' => ['Jan', 'Feb', 'Měr', 'Apr', 'Maj', 'Jun', 'Jul', 'Awg', 'Sep', 'Okt', 'Now', 'Dec'],
+ 'weekdays' => ['Njeźela', 'Pónjeźele', 'Wałtora', 'Srjoda', 'Stwórtk', 'Pětk', 'Sobota'],
+ 'weekdays_short' => ['Nj', 'Pó', 'Wa', 'Sr', 'St', 'Pě', 'So'],
+ 'weekdays_min' => ['Nj', 'Pó', 'Wa', 'Sr', 'St', 'Pě', 'So'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count lěto',
+ 'y' => ':count lěto',
+ 'a_year' => ':count lěto',
+
+ 'month' => ':count mjasec',
+ 'm' => ':count mjasec',
+ 'a_month' => ':count mjasec',
+
+ 'week' => ':count tyźeń',
+ 'w' => ':count tyźeń',
+ 'a_week' => ':count tyźeń',
+
+ 'day' => ':count źeń',
+ 'd' => ':count źeń',
+ 'a_day' => ':count źeń',
+
+ 'hour' => ':count góźina',
+ 'h' => ':count góźina',
+ 'a_hour' => ':count góźina',
+
+ 'minute' => ':count minuta',
+ 'min' => ':count minuta',
+ 'a_minute' => ':count minuta',
+
+ 'second' => ':count drugi',
+ 's' => ':count drugi',
+ 'a_second' => ':count drugi',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dua.php b/vendor/nesbot/carbon/src/Carbon/Lang/dua.php
new file mode 100644
index 0000000..55e5c7c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dua.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['idiɓa', 'ebyámu'],
+ 'weekdays' => ['éti', 'mɔ́sú', 'kwasú', 'mukɔ́sú', 'ŋgisú', 'ɗónɛsú', 'esaɓasú'],
+ 'weekdays_short' => ['ét', 'mɔ́s', 'kwa', 'muk', 'ŋgi', 'ɗón', 'esa'],
+ 'weekdays_min' => ['ét', 'mɔ́s', 'kwa', 'muk', 'ŋgi', 'ɗón', 'esa'],
+ 'months' => ['dimɔ́di', 'ŋgɔndɛ', 'sɔŋɛ', 'diɓáɓá', 'emiasele', 'esɔpɛsɔpɛ', 'madiɓɛ́díɓɛ́', 'diŋgindi', 'nyɛtɛki', 'mayésɛ́', 'tiníní', 'eláŋgɛ́'],
+ 'months_short' => ['di', 'ŋgɔn', 'sɔŋ', 'diɓ', 'emi', 'esɔ', 'mad', 'diŋ', 'nyɛt', 'may', 'tin', 'elá'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count ma mbu', // less reliable
+ 'y' => ':count ma mbu', // less reliable
+ 'a_year' => ':count ma mbu', // less reliable
+
+ 'month' => ':count myo̱di', // less reliable
+ 'm' => ':count myo̱di', // less reliable
+ 'a_month' => ':count myo̱di', // less reliable
+
+ 'week' => ':count woki', // less reliable
+ 'w' => ':count woki', // less reliable
+ 'a_week' => ':count woki', // less reliable
+
+ 'day' => ':count buńa', // less reliable
+ 'd' => ':count buńa', // less reliable
+ 'a_day' => ':count buńa', // less reliable
+
+ 'hour' => ':count ma awa', // less reliable
+ 'h' => ':count ma awa', // less reliable
+ 'a_hour' => ':count ma awa', // less reliable
+
+ 'minute' => ':count minuti', // less reliable
+ 'min' => ':count minuti', // less reliable
+ 'a_minute' => ':count minuti', // less reliable
+
+ 'second' => ':count maba', // less reliable
+ 's' => ':count maba', // less reliable
+ 'a_second' => ':count maba', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dv.php b/vendor/nesbot/carbon/src/Carbon/Lang/dv.php
new file mode 100644
index 0000000..4b8d7e1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dv.php
@@ -0,0 +1,89 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+$months = [
+ 'ޖެނުއަރީ',
+ 'ފެބްރުއަރީ',
+ 'މާރިޗު',
+ 'އޭޕްރީލު',
+ 'މޭ',
+ 'ޖޫން',
+ 'ޖުލައި',
+ 'އޯގަސްޓު',
+ 'ސެޕްޓެމްބަރު',
+ 'އޮކްޓޯބަރު',
+ 'ނޮވެމްބަރު',
+ 'ޑިސެމްބަރު',
+];
+
+$weekdays = [
+ 'އާދިއްތަ',
+ 'ހޯމަ',
+ 'އަންގާރަ',
+ 'ބުދަ',
+ 'ބުރާސްފަތި',
+ 'ހުކުރު',
+ 'ހޮނިހިރު',
+];
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Jawish Hameed
+ */
+return [
+ 'year' => ':count '.'އަހަރު',
+ 'a_year' => '{1}'.'އަހަރެއް'.'|:count '.'އަހަރު',
+ 'month' => ':count '.'މަސް',
+ 'a_month' => '{1}'.'މަހެއް'.'|:count '.'މަސް',
+ 'week' => ':count '.'ހަފްތާ',
+ 'a_week' => '{1}'.'ސިކުންތުކޮޅެއް'.'|:count '.'ހަފްތާ',
+ 'day' => ':count '.'ދުވަސް',
+ 'a_day' => '{1}'.'ދުވަހެއް'.'|:count '.'ދުވަސް',
+ 'hour' => ':count '.'ގަޑިއިރު',
+ 'a_hour' => '{1}'.'ގަޑިއިރެއް'.'|:count '.'ގަޑިއިރު',
+ 'minute' => ':count '.'މިނިޓު',
+ 'a_minute' => '{1}'.'މިނިޓެއް'.'|:count '.'މިނިޓު',
+ 'second' => ':count '.'ސިކުންތު',
+ 'a_second' => '{1}'.'ސިކުންތުކޮޅެއް'.'|:count '.'ސިކުންތު',
+ 'ago' => 'ކުރިން :time',
+ 'from_now' => 'ތެރޭގައި :time',
+ 'after' => ':time ފަހުން',
+ 'before' => ':time ކުރި',
+ 'diff_yesterday' => 'އިއްޔެ',
+ 'diff_today' => 'މިއަދު',
+ 'diff_tomorrow' => 'މާދަމާ',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[މިއަދު] LT',
+ 'nextDay' => '[މާދަމާ] LT',
+ 'nextWeek' => 'dddd LT',
+ 'lastDay' => '[އިއްޔެ] LT',
+ 'lastWeek' => '[ފާއިތުވި] dddd LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['މކ', 'މފ'],
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => $weekdays,
+ 'weekdays_short' => $weekdays,
+ 'weekdays_min' => ['އާދި', 'ހޯމަ', 'އަން', 'ބުދަ', 'ބުރާ', 'ހުކު', 'ހޮނި'],
+ 'list' => [', ', ' އަދި '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php b/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php
new file mode 100644
index 0000000..2668d5b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dv_MV.php
@@ -0,0 +1,87 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ahmed Ali
+ */
+
+$months = [
+ 'ޖެނުއަރީ',
+ 'ފެބްރުއަރީ',
+ 'މާރިޗު',
+ 'އޭޕްރީލު',
+ 'މޭ',
+ 'ޖޫން',
+ 'ޖުލައި',
+ 'އޯގަސްޓު',
+ 'ސެޕްޓެމްބަރު',
+ 'އޮކްޓޯބަރު',
+ 'ނޮވެމްބަރު',
+ 'ޑިސެމްބަރު',
+];
+
+$weekdays = [
+ 'އާދިއްތަ',
+ 'ހޯމަ',
+ 'އަންގާރަ',
+ 'ބުދަ',
+ 'ބުރާސްފަތި',
+ 'ހުކުރު',
+ 'ހޮނިހިރު',
+];
+
+return [
+ 'year' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު',
+ 'y' => '{0}އަހަރެއް|[1,Inf]:count އަހަރު',
+ 'month' => '{0}މައްސަރެއް|[1,Inf]:count މަސް',
+ 'm' => '{0}މައްސަރެއް|[1,Inf]:count މަސް',
+ 'week' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ',
+ 'w' => '{0}ހަފްތާއެއް|[1,Inf]:count ހަފްތާ',
+ 'day' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް',
+ 'd' => '{0}ދުވަސް|[1,Inf]:count ދުވަސް',
+ 'hour' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި',
+ 'h' => '{0}ގަޑިއިރެއް|[1,Inf]:count ގަޑި',
+ 'minute' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް',
+ 'min' => '{0}މިނެޓެއް|[1,Inf]:count މިނެޓް',
+ 'second' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު',
+ 's' => '{0}ސިކުންތެއް|[1,Inf]:count ސިކުންތު',
+ 'ago' => ':time ކުރިން',
+ 'from_now' => ':time ފަހުން',
+ 'after' => ':time ފަހުން',
+ 'before' => ':time ކުރި',
+ 'diff_yesterday' => 'އިއްޔެ',
+ 'diff_today' => 'މިއަދު',
+ 'diff_tomorrow' => 'މާދަމާ',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[މިއަދު] LT',
+ 'nextDay' => '[މާދަމާ] LT',
+ 'nextWeek' => 'dddd LT',
+ 'lastDay' => '[އިއްޔެ] LT',
+ 'lastWeek' => '[ފާއިތުވި] dddd LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['މކ', 'މފ'],
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => $weekdays,
+ 'weekdays_short' => $weekdays,
+ 'weekdays_min' => ['އާދި', 'ހޯމަ', 'އަން', 'ބުދަ', 'ބުރާ', 'ހުކު', 'ހޮނި'],
+ 'list' => [', ', ' އަދި '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dyo.php b/vendor/nesbot/carbon/src/Carbon/Lang/dyo.php
new file mode 100644
index 0000000..33082e6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dyo.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Dimas', 'Teneŋ', 'Talata', 'Alarbay', 'Aramisay', 'Arjuma', 'Sibiti'],
+ 'weekdays_short' => ['Dim', 'Ten', 'Tal', 'Ala', 'Ara', 'Arj', 'Sib'],
+ 'weekdays_min' => ['Dim', 'Ten', 'Tal', 'Ala', 'Ara', 'Arj', 'Sib'],
+ 'months' => ['Sanvie', 'Fébirie', 'Mars', 'Aburil', 'Mee', 'Sueŋ', 'Súuyee', 'Ut', 'Settembar', 'Oktobar', 'Novembar', 'Disambar'],
+ 'months_short' => ['Sa', 'Fe', 'Ma', 'Ab', 'Me', 'Su', 'Sú', 'Ut', 'Se', 'Ok', 'No', 'De'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dz.php b/vendor/nesbot/carbon/src/Carbon/Lang/dz.php
new file mode 100644
index 0000000..cc17e69
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dz.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/dz_BT.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php b/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php
new file mode 100644
index 0000000..bfbcaf4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/dz_BT.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Sherubtse College bug-glibc@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'པསྱི་ལོYYཟལMMཚེསDD',
+ ],
+ 'months' => ['ཟླ་བ་དང་པ་', 'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་གསུམ་པ་', 'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་ཕ་', 'ཟླ་བ་དྲུག་པ་', 'ཟླ་བ་བདུནཔ་', 'ཟླ་བ་བརྒྱད་པ་', 'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་', 'ཟླ་བ་བཅུ་གཅིག་པ་', 'ཟླ་བ་བཅུ་གཉིས་པ་'],
+ 'months_short' => ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣', 'ཟླ་༤', 'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧', 'ཟླ་༨', 'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡', 'ཟླ་༡༢'],
+ 'weekdays' => ['གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་ཕ་', 'གཟའ་པུར་བུ་', 'གཟའ་པ་སངས་', 'གཟའ་སྤེན་ཕ་', 'གཟའ་ཉི་མ་'],
+ 'weekdays_short' => ['ཟླ་', 'མིར་', 'ལྷག་', 'པུར་', 'སངས་', 'སྤེན་', 'ཉི་'],
+ 'weekdays_min' => ['ཟླ་', 'མིར་', 'ལྷག་', 'པུར་', 'སངས་', 'སྤེན་', 'ཉི་'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ངས་ཆ', 'ཕྱི་ཆ'],
+
+ 'year' => ':count ཆརཔ', // less reliable
+ 'y' => ':count ཆརཔ', // less reliable
+ 'a_year' => ':count ཆརཔ', // less reliable
+
+ 'month' => ':count ཟླ་བ', // less reliable
+ 'm' => ':count ཟླ་བ', // less reliable
+ 'a_month' => ':count ཟླ་བ', // less reliable
+
+ 'day' => ':count ཉི', // less reliable
+ 'd' => ':count ཉི', // less reliable
+ 'a_day' => ':count ཉི', // less reliable
+
+ 'second' => ':count ཆ', // less reliable
+ 's' => ':count ཆ', // less reliable
+ 'a_second' => ':count ཆ', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php b/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php
new file mode 100644
index 0000000..f60bc6f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ebu.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['KI', 'UT'],
+ 'weekdays' => ['Kiumia', 'Njumatatu', 'Njumaine', 'Njumatano', 'Aramithi', 'Njumaa', 'NJumamothii'],
+ 'weekdays_short' => ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'],
+ 'weekdays_min' => ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'],
+ 'months' => ['Mweri wa mbere', 'Mweri wa kaĩri', 'Mweri wa kathatũ', 'Mweri wa kana', 'Mweri wa gatano', 'Mweri wa gatantatũ', 'Mweri wa mũgwanja', 'Mweri wa kanana', 'Mweri wa kenda', 'Mweri wa ikũmi', 'Mweri wa ikũmi na ũmwe', 'Mweri wa ikũmi na Kaĩrĩ'],
+ 'months_short' => ['Mbe', 'Kai', 'Kat', 'Kan', 'Gat', 'Gan', 'Mug', 'Knn', 'Ken', 'Iku', 'Imw', 'Igi'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ee.php b/vendor/nesbot/carbon/src/Carbon/Lang/ee.php
new file mode 100644
index 0000000..f96c5c9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ee.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['ŋ', 'ɣ'],
+ 'weekdays' => ['kɔsiɖa', 'dzoɖa', 'blaɖa', 'kuɖa', 'yawoɖa', 'fiɖa', 'memleɖa'],
+ 'weekdays_short' => ['kɔs', 'dzo', 'bla', 'kuɖ', 'yaw', 'fiɖ', 'mem'],
+ 'weekdays_min' => ['kɔs', 'dzo', 'bla', 'kuɖ', 'yaw', 'fiɖ', 'mem'],
+ 'months' => ['dzove', 'dzodze', 'tedoxe', 'afɔfĩe', 'dama', 'masa', 'siamlɔm', 'deasiamime', 'anyɔnyɔ', 'kele', 'adeɛmekpɔxe', 'dzome'],
+ 'months_short' => ['dzv', 'dzd', 'ted', 'afɔ', 'dam', 'mas', 'sia', 'dea', 'any', 'kel', 'ade', 'dzm'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'a [ga] h:mm',
+ 'LTS' => 'a [ga] h:mm:ss',
+ 'L' => 'M/D/YYYY',
+ 'LL' => 'MMM D [lia], YYYY',
+ 'LLL' => 'a [ga] h:mm MMMM D [lia] YYYY',
+ 'LLLL' => 'a [ga] h:mm dddd, MMMM D [lia] YYYY',
+ ],
+
+ 'year' => 'ƒe :count',
+ 'y' => 'ƒe :count',
+ 'a_year' => 'ƒe :count',
+
+ 'month' => 'ɣleti :count',
+ 'm' => 'ɣleti :count',
+ 'a_month' => 'ɣleti :count',
+
+ 'week' => 'kwasiɖa :count',
+ 'w' => 'kwasiɖa :count',
+ 'a_week' => 'kwasiɖa :count',
+
+ 'day' => 'ŋkeke :count',
+ 'd' => 'ŋkeke :count',
+ 'a_day' => 'ŋkeke :count',
+
+ 'hour' => 'gaƒoƒo :count',
+ 'h' => 'gaƒoƒo :count',
+ 'a_hour' => 'gaƒoƒo :count',
+
+ 'minute' => 'miniti :count', // less reliable
+ 'min' => 'miniti :count', // less reliable
+ 'a_minute' => 'miniti :count', // less reliable
+
+ 'second' => 'sɛkɛnd :count', // less reliable
+ 's' => 'sɛkɛnd :count', // less reliable
+ 'a_second' => 'sɛkɛnd :count', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php
new file mode 100644
index 0000000..7a8b36c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ee_TG.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ee.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'LLL' => 'HH:mm MMMM D [lia] YYYY',
+ 'LLLL' => 'HH:mm dddd, MMMM D [lia] YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el.php b/vendor/nesbot/carbon/src/Carbon/Lang/el.php
new file mode 100644
index 0000000..7c40f9c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/el.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Alessandro Di Felice
+ * - François B
+ * - Tim Fish
+ * - Gabriel Monteagudo
+ * - JD Isaacks
+ * - yiannisdesp
+ * - Ilias Kasmeridis (iliaskasm)
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count χρόνος|:count χρόνια',
+ 'a_year' => 'ένας χρόνος|:count χρόνια',
+ 'y' => ':count χρ.',
+ 'month' => ':count μήνας|:count μήνες',
+ 'a_month' => 'ένας μήνας|:count μήνες',
+ 'm' => ':count μήν.',
+ 'week' => ':count εβδομάδα|:count εβδομάδες',
+ 'a_week' => 'μια εβδομάδα|:count εβδομάδες',
+ 'w' => ':count εβδ.',
+ 'day' => ':count μέρα|:count μέρες',
+ 'a_day' => 'μία μέρα|:count μέρες',
+ 'd' => ':count μέρ.',
+ 'hour' => ':count ώρα|:count ώρες',
+ 'a_hour' => 'μία ώρα|:count ώρες',
+ 'h' => ':count ώρα|:count ώρες',
+ 'minute' => ':count λεπτό|:count λεπτά',
+ 'a_minute' => 'ένα λεπτό|:count λεπτά',
+ 'min' => ':count λεπ.',
+ 'second' => ':count δευτερόλεπτο|:count δευτερόλεπτα',
+ 'a_second' => 'λίγα δευτερόλεπτα|:count δευτερόλεπτα',
+ 's' => ':count δευ.',
+ 'ago' => 'πριν :time',
+ 'from_now' => 'σε :time',
+ 'after' => ':time μετά',
+ 'before' => ':time πριν',
+ 'diff_now' => 'τώρα',
+ 'diff_today' => 'Σήμερα',
+ 'diff_today_regexp' => 'Σήμερα(?:\\s+{})?',
+ 'diff_yesterday' => 'χθες',
+ 'diff_yesterday_regexp' => 'Χθες(?:\\s+{})?',
+ 'diff_tomorrow' => 'αύριο',
+ 'diff_tomorrow_regexp' => 'Αύριο(?:\\s+{})?',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm A',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm A',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Σήμερα {}] LT',
+ 'nextDay' => '[Αύριο {}] LT',
+ 'nextWeek' => 'dddd [{}] LT',
+ 'lastDay' => '[Χθες {}] LT',
+ 'lastWeek' => function (CarbonInterface $current) {
+ switch ($current->dayOfWeek) {
+ case 6:
+ return '[το προηγούμενο] dddd [{}] LT';
+ default:
+ return '[την προηγούμενη] dddd [{}] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberη',
+ 'meridiem' => ['ΠΜ', 'ΜΜ', 'πμ', 'μμ'],
+ 'months' => ['Ιανουαρίου', 'Φεβρουαρίου', 'Μαρτίου', 'Απριλίου', 'Μαΐου', 'Ιουνίου', 'Ιουλίου', 'Αυγούστου', 'Σεπτεμβρίου', 'Οκτωβρίου', 'Νοεμβρίου', 'Δεκεμβρίου'],
+ 'months_standalone' => ['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
+ 'months_regexp' => '/(D[oD]?[\s,]+MMMM|L{2,4}|l{2,4})/',
+ 'months_short' => ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαϊ', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
+ 'weekdays' => ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
+ 'weekdays_short' => ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'],
+ 'weekdays_min' => ['Κυ', 'Δε', 'Τρ', 'Τε', 'Πε', 'Πα', 'Σα'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' και '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php
new file mode 100644
index 0000000..8a693c1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/el_CY.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Greek Debian Translation Team bug-glibc@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/el.php', [
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php b/vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php
new file mode 100644
index 0000000..df196af
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/el_GR.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/el.php', [
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en.php b/vendor/nesbot/carbon/src/Carbon/Lang/en.php
new file mode 100644
index 0000000..f81f617
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en.php
@@ -0,0 +1,87 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Milos Sakovic
+ * - Paul
+ * - Pete Scopes (pdscopes)
+ */
+return [
+ /*
+ * {1}, {0} and ]1,Inf[ are not needed as it's the default for English pluralization.
+ * But as some languages are using en.php as a fallback, it's better to specify it
+ * explicitly so those languages also fallback to English pluralization when a unit
+ * is missing.
+ */
+ 'year' => '{1}:count year|{0}:count years|]1,Inf[:count years',
+ 'a_year' => '{1}a year|{0}:count years|]1,Inf[:count years',
+ 'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs',
+ 'month' => '{1}:count month|{0}:count months|]1,Inf[:count months',
+ 'a_month' => '{1}a month|{0}:count months|]1,Inf[:count months',
+ 'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos',
+ 'week' => '{1}:count week|{0}:count weeks|]1,Inf[:count weeks',
+ 'a_week' => '{1}a week|{0}:count weeks|]1,Inf[:count weeks',
+ 'w' => ':countw',
+ 'day' => '{1}:count day|{0}:count days|]1,Inf[:count days',
+ 'a_day' => '{1}a day|{0}:count days|]1,Inf[:count days',
+ 'd' => ':countd',
+ 'hour' => '{1}:count hour|{0}:count hours|]1,Inf[:count hours',
+ 'a_hour' => '{1}an hour|{0}:count hours|]1,Inf[:count hours',
+ 'h' => ':counth',
+ 'minute' => '{1}:count minute|{0}:count minutes|]1,Inf[:count minutes',
+ 'a_minute' => '{1}a minute|{0}:count minutes|]1,Inf[:count minutes',
+ 'min' => ':countm',
+ 'second' => '{1}:count second|{0}:count seconds|]1,Inf[:count seconds',
+ 'a_second' => '{1}a few seconds|{0}:count seconds|]1,Inf[:count seconds',
+ 's' => ':counts',
+ 'millisecond' => '{1}:count millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds',
+ 'a_millisecond' => '{1}a millisecond|{0}:count milliseconds|]1,Inf[:count milliseconds',
+ 'ms' => ':countms',
+ 'microsecond' => '{1}:count microsecond|{0}:count microseconds|]1,Inf[:count microseconds',
+ 'a_microsecond' => '{1}a microsecond|{0}:count microseconds|]1,Inf[:count microseconds',
+ 'µs' => ':countµs',
+ 'ago' => ':time ago',
+ 'from_now' => ':time from now',
+ 'after' => ':time after',
+ 'before' => ':time before',
+ 'diff_now' => 'just now',
+ 'diff_today' => 'today',
+ 'diff_yesterday' => 'yesterday',
+ 'diff_tomorrow' => 'tomorrow',
+ 'diff_before_yesterday' => 'before yesterday',
+ 'diff_after_tomorrow' => 'after tomorrow',
+ 'period_recurrences' => '{1}once|{0}:count times|]1,Inf[:count times',
+ 'period_interval' => 'every :interval',
+ 'period_start_date' => 'from :date',
+ 'period_end_date' => 'to :date',
+ 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ 'weekdays' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ 'weekdays_short' => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ 'weekdays_min' => ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
+ 'ordinal' => function ($number) {
+ $lastDigit = $number % 10;
+
+ return $number.(
+ ((int) ($number % 100 / 10) === 1) ? 'th' : (
+ ($lastDigit === 1) ? 'st' : (
+ ($lastDigit === 2) ? 'nd' : (
+ ($lastDigit === 3) ? 'rd' : 'th'
+ )
+ )
+ )
+ );
+ },
+ 'list' => [', ', ' and '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_001.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_150.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php
new file mode 100644
index 0000000..2c1c64f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AG.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AI.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AS.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AT.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php
new file mode 100644
index 0000000..f16bd4f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_AU.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - François B
+ * - Mayank Badola
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm A',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm A',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BB.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BE.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BI.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BM.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BS.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_BZ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php
new file mode 100644
index 0000000..e656086
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CA.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Zhan Tong Zhang
+ * - Mayank Badola
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'MMMM D, YYYY',
+ 'LLL' => 'MMMM D, YYYY h:mm A',
+ 'LLLL' => 'dddd, MMMM D, YYYY h:mm A',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php
new file mode 100644
index 0000000..10d9cd8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CH.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CK.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CM.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CX.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php
new file mode 100644
index 0000000..a44c350
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_CY.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - NehaGautam
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD-MM-YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DE.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DG.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php
new file mode 100644
index 0000000..9e8a8c6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DK.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Danish Standards Association bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_DM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ER.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FI.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FJ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FK.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_FM.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php
new file mode 100644
index 0000000..67d9fd6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GB.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Mayank Badola
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GD.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GG.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GH.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GI.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GM.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_GY.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php
new file mode 100644
index 0000000..34aae98
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_HK.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php
new file mode 100644
index 0000000..c8d3c2f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IE.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Martin McWhorter
+ * - François B
+ * - Chris Cartlidge
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD-MM-YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php
new file mode 100644
index 0000000..e607924
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IL.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Yoav Amit
+ * - François B
+ * - Mayank Badola
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IM.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php
new file mode 100644
index 0000000..00414e9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IN.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YY',
+ 'LL' => 'MMMM DD, YYYY',
+ 'LLL' => 'DD MMM HH:mm',
+ 'LLLL' => 'MMMM DD, YYYY HH:mm',
+ ],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_IO.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php
new file mode 100644
index 0000000..11457b0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ISO.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'dddd, YYYY MMMM DD HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_JE.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_JM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KI.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KN.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_KY.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LR.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_LS.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MG.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MH.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MP.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MS.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MT.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MU.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MW.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_MY.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NA.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NF.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php
new file mode 100644
index 0000000..67bceaa
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NG.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NL.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NR.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NU.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php
new file mode 100644
index 0000000..6a206a0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_NZ.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Mayank Badola
+ * - Luke McGregor
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm A',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm A',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PG.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php
new file mode 100644
index 0000000..34aae98
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PH.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PN.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_PW.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_RW.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SB.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php
new file mode 100644
index 0000000..c4e2557
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SD.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 6],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SE.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php
new file mode 100644
index 0000000..5ee9524
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SG.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'from_now' => 'in :time',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SH.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SI.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SL.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SS.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SX.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_SZ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TK.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TO.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TT.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TV.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_TZ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_UG.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_UM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_US.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_US_Posix.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VG.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php
new file mode 100644
index 0000000..e2dd81d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_VU.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_WS.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php
new file mode 100644
index 0000000..48ea947
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZA.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YY',
+ 'LL' => 'MMMM DD, YYYY',
+ 'LLL' => 'DD MMM HH:mm',
+ 'LLLL' => 'MMMM DD, YYYY HH:mm',
+ ],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php
new file mode 100644
index 0000000..d8a8cb5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZM.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - ANLoc Martin Benjamin locales@africanlocalization.net
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php
new file mode 100644
index 0000000..f086dc6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/en_ZW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/en.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eo.php b/vendor/nesbot/carbon/src/Carbon/Lang/eo.php
new file mode 100644
index 0000000..7c2efba
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/eo.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - Mia Nordentoft
+ * - JD Isaacks
+ */
+return [
+ 'year' => ':count jaro|:count jaroj',
+ 'a_year' => 'jaro|:count jaroj',
+ 'y' => ':count j.',
+ 'month' => ':count monato|:count monatoj',
+ 'a_month' => 'monato|:count monatoj',
+ 'm' => ':count mo.',
+ 'week' => ':count semajno|:count semajnoj',
+ 'a_week' => 'semajno|:count semajnoj',
+ 'w' => ':count sem.',
+ 'day' => ':count tago|:count tagoj',
+ 'a_day' => 'tago|:count tagoj',
+ 'd' => ':count t.',
+ 'hour' => ':count horo|:count horoj',
+ 'a_hour' => 'horo|:count horoj',
+ 'h' => ':count h.',
+ 'minute' => ':count minuto|:count minutoj',
+ 'a_minute' => 'minuto|:count minutoj',
+ 'min' => ':count min.',
+ 'second' => ':count sekundo|:count sekundoj',
+ 'a_second' => 'sekundoj|:count sekundoj',
+ 's' => ':count sek.',
+ 'ago' => 'antaŭ :time',
+ 'from_now' => 'post :time',
+ 'after' => ':time poste',
+ 'before' => ':time antaŭe',
+ 'diff_yesterday' => 'Hieraŭ',
+ 'diff_yesterday_regexp' => 'Hieraŭ(?:\\s+je)?',
+ 'diff_today' => 'Hodiaŭ',
+ 'diff_today_regexp' => 'Hodiaŭ(?:\\s+je)?',
+ 'diff_tomorrow' => 'Morgaŭ',
+ 'diff_tomorrow_regexp' => 'Morgaŭ(?:\\s+je)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'D[-a de] MMMM, YYYY',
+ 'LLL' => 'D[-a de] MMMM, YYYY HH:mm',
+ 'LLLL' => 'dddd, [la] D[-a de] MMMM, YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Hodiaŭ je] LT',
+ 'nextDay' => '[Morgaŭ je] LT',
+ 'nextWeek' => 'dddd [je] LT',
+ 'lastDay' => '[Hieraŭ je] LT',
+ 'lastWeek' => '[pasinta] dddd [je] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numbera',
+ 'meridiem' => ['a.t.m.', 'p.t.m.'],
+ 'months' => ['januaro', 'februaro', 'marto', 'aprilo', 'majo', 'junio', 'julio', 'aŭgusto', 'septembro', 'oktobro', 'novembro', 'decembro'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aŭg', 'sep', 'okt', 'nov', 'dec'],
+ 'weekdays' => ['dimanĉo', 'lundo', 'mardo', 'merkredo', 'ĵaŭdo', 'vendredo', 'sabato'],
+ 'weekdays_short' => ['dim', 'lun', 'mard', 'merk', 'ĵaŭ', 'ven', 'sab'],
+ 'weekdays_min' => ['di', 'lu', 'ma', 'me', 'ĵa', 've', 'sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' kaj '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es.php b/vendor/nesbot/carbon/src/Carbon/Lang/es.php
new file mode 100644
index 0000000..1c4fcfd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es.php
@@ -0,0 +1,121 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - kostas
+ * - François B
+ * - Tim Fish
+ * - Claire Coloma
+ * - Steven Heinrich
+ * - JD Isaacks
+ * - Raphael Amorim
+ * - Jorge Y. Castillo
+ * - Víctor Díaz
+ * - Diego
+ * - Sebastian Thierer
+ * - quinterocesar
+ * - Daniel Commesse Liévanos (danielcommesse)
+ * - Pete Scopes (pdscopes)
+ * - gam04
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count año|:count años',
+ 'a_year' => 'un año|:count años',
+ 'y' => ':count año|:count años',
+ 'month' => ':count mes|:count meses',
+ 'a_month' => 'un mes|:count meses',
+ 'm' => ':count mes|:count meses',
+ 'week' => ':count semana|:count semanas',
+ 'a_week' => 'una semana|:count semanas',
+ 'w' => ':countsem',
+ 'day' => ':count día|:count días',
+ 'a_day' => 'un día|:count días',
+ 'd' => ':countd',
+ 'hour' => ':count hora|:count horas',
+ 'a_hour' => 'una hora|:count horas',
+ 'h' => ':counth',
+ 'minute' => ':count minuto|:count minutos',
+ 'a_minute' => 'un minuto|:count minutos',
+ 'min' => ':countm',
+ 'second' => ':count segundo|:count segundos',
+ 'a_second' => 'unos segundos|:count segundos',
+ 's' => ':counts',
+ 'millisecond' => ':count milisegundo|:count milisegundos',
+ 'a_millisecond' => 'un milisegundo|:count milisegundos',
+ 'ms' => ':countms',
+ 'microsecond' => ':count microsegundo|:count microsegundos',
+ 'a_microsecond' => 'un microsegundo|:count microsegundos',
+ 'µs' => ':countµs',
+ 'ago' => 'hace :time',
+ 'from_now' => 'en :time',
+ 'after' => ':time después',
+ 'before' => ':time antes',
+ 'diff_now' => 'ahora mismo',
+ 'diff_today' => 'hoy',
+ 'diff_today_regexp' => 'hoy(?:\\s+a)?(?:\\s+las)?',
+ 'diff_yesterday' => 'ayer',
+ 'diff_yesterday_regexp' => 'ayer(?:\\s+a)?(?:\\s+las)?',
+ 'diff_tomorrow' => 'mañana',
+ 'diff_tomorrow_regexp' => 'mañana(?:\\s+a)?(?:\\s+las)?',
+ 'diff_before_yesterday' => 'anteayer',
+ 'diff_after_tomorrow' => 'pasado mañana',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D [de] MMMM [de] YYYY',
+ 'LLL' => 'D [de] MMMM [de] YYYY H:mm',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => function (CarbonInterface $current) {
+ return '[hoy a la'.($current->hour !== 1 ? 's' : '').'] LT';
+ },
+ 'nextDay' => function (CarbonInterface $current) {
+ return '[mañana a la'.($current->hour !== 1 ? 's' : '').'] LT';
+ },
+ 'nextWeek' => function (CarbonInterface $current) {
+ return 'dddd [a la'.($current->hour !== 1 ? 's' : '').'] LT';
+ },
+ 'lastDay' => function (CarbonInterface $current) {
+ return '[ayer a la'.($current->hour !== 1 ? 's' : '').'] LT';
+ },
+ 'lastWeek' => function (CarbonInterface $current) {
+ return '[el] dddd [pasado a la'.($current->hour !== 1 ? 's' : '').'] LT';
+ },
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
+ 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
+ 'mmm_suffix' => '.',
+ 'ordinal' => ':numberº',
+ 'weekdays' => ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
+ 'weekdays_short' => ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
+ 'weekdays_min' => ['do', 'lu', 'ma', 'mi', 'ju', 'vi', 'sá'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' y '],
+ 'meridiem' => ['a. m.', 'p. m.'],
+ 'ordinal_words' => [
+ 'of' => 'de',
+ 'first' => 'primer',
+ 'second' => 'segundo',
+ 'third' => 'tercer',
+ 'fourth' => 'cuarto',
+ 'fifth' => 'quinto',
+ 'last' => 'último',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_419.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_419.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_419.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_AR.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php
new file mode 100644
index 0000000..c9b8432
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_BO.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php
new file mode 100644
index 0000000..378d054
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_BR.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php
new file mode 100644
index 0000000..378d054
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_BZ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CL.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CO.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php
new file mode 100644
index 0000000..553fc09
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CR.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php
new file mode 100644
index 0000000..f02e1a6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_CU.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php
new file mode 100644
index 0000000..0f855ba
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_DO.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - kostas
+ * - François B
+ * - Tim Fish
+ * - Chiel Robben
+ * - Claire Coloma
+ * - Steven Heinrich
+ * - JD Isaacks
+ * - Raphael Amorim
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'diff_before_yesterday' => 'anteayer',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'LLL' => 'D [de] MMMM [de] YYYY h:mm A',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY h:mm A',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php
new file mode 100644
index 0000000..f02e1a6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_EA.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_EC.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php
new file mode 100644
index 0000000..19217c2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_ES.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return require __DIR__.'/es.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php
new file mode 100644
index 0000000..f02e1a6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_GQ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_GT.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_HN.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php
new file mode 100644
index 0000000..f02e1a6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_IC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php
new file mode 100644
index 0000000..61e14cf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_MX.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'diff_before_yesterday' => 'antier',
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php
new file mode 100644
index 0000000..6b964c1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_NI.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PA.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PE.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php
new file mode 100644
index 0000000..deae06a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PH.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/M/yy',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D [de] MMMM [de] YYYY h:mm a',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php
new file mode 100644
index 0000000..6b964c1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PR.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_PY.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php
new file mode 100644
index 0000000..00db08e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_SV.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
+ 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_US.php
new file mode 100644
index 0000000..f333136
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_US.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - Josh Soref
+ * - Jørn Ølmheim
+ * - Craig Patik
+ * - bustta
+ * - François B
+ * - Tim Fish
+ * - Claire Coloma
+ * - Steven Heinrich
+ * - JD Isaacks
+ * - Raphael Amorim
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'diff_before_yesterday' => 'anteayer',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'MM/DD/YYYY',
+ 'LL' => 'MMMM [de] D [de] YYYY',
+ 'LLL' => 'MMMM [de] D [de] YYYY h:mm A',
+ 'LLLL' => 'dddd, MMMM [de] D [de] YYYY h:mm A',
+ ],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php
new file mode 100644
index 0000000..39baff8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_UY.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
+ 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'set', 'oct', 'nov', 'dic'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php b/vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php
new file mode 100644
index 0000000..a74806e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/es_VE.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/es.php', [
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/et.php b/vendor/nesbot/carbon/src/Carbon/Lang/et.php
new file mode 100644
index 0000000..f49c880
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/et.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Andres Ivanov
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Max Melentiev
+ * - Juanito Fatas
+ * - RM87
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Esko Lehtme
+ * - Mart Karu
+ * - Nicolás Hock Isaza
+ * - Kevin Valdek
+ * - Zahhar Kirillov
+ * - João Magalhães
+ * - Ingmar
+ * - Illimar Tambek
+ * - Mihkel
+ */
+return [
+ 'year' => ':count aasta|:count aastat',
+ 'y' => ':count a',
+ 'month' => ':count kuu|:count kuud',
+ 'm' => ':count k',
+ 'week' => ':count nädal|:count nädalat',
+ 'w' => ':count näd',
+ 'day' => ':count päev|:count päeva',
+ 'd' => ':count p',
+ 'hour' => ':count tund|:count tundi',
+ 'h' => ':count t',
+ 'minute' => ':count minut|:count minutit',
+ 'min' => ':count min',
+ 'second' => ':count sekund|:count sekundit',
+ 's' => ':count s',
+ 'ago' => ':time tagasi',
+ 'from_now' => ':time pärast',
+ 'after' => ':time pärast',
+ 'before' => ':time enne',
+ 'year_from_now' => ':count aasta',
+ 'month_from_now' => ':count kuu',
+ 'week_from_now' => ':count nädala',
+ 'day_from_now' => ':count päeva',
+ 'hour_from_now' => ':count tunni',
+ 'minute_from_now' => ':count minuti',
+ 'second_from_now' => ':count sekundi',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'diff_now' => 'nüüd',
+ 'diff_today' => 'täna',
+ 'diff_yesterday' => 'eile',
+ 'diff_tomorrow' => 'homme',
+ 'diff_before_yesterday' => 'üleeile',
+ 'diff_after_tomorrow' => 'ülehomme',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[täna] LT',
+ 'nextDay' => '[homme] LT',
+ 'lastDay' => '[eile] LT',
+ 'nextWeek' => 'dddd LT',
+ 'lastWeek' => '[eelmine] dddd LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'],
+ 'months_short' => ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni', 'juuli', 'aug', 'sept', 'okt', 'nov', 'dets'],
+ 'weekdays' => ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev', 'neljapäev', 'reede', 'laupäev'],
+ 'weekdays_short' => ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
+ 'weekdays_min' => ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
+ 'list' => [', ', ' ja '],
+ 'meridiem' => ['enne lõunat', 'pärast lõunat'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php b/vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php
new file mode 100644
index 0000000..0f112b3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/et_EE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/et.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eu.php b/vendor/nesbot/carbon/src/Carbon/Lang/eu.php
new file mode 100644
index 0000000..a543f1a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/eu.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - JD Isaacks
+ */
+return [
+ 'year' => 'urte bat|:count urte',
+ 'y' => 'Urte 1|:count urte',
+ 'month' => 'hilabete bat|:count hilabete',
+ 'm' => 'Hile 1|:count hile',
+ 'week' => 'Aste 1|:count aste',
+ 'w' => 'Aste 1|:count aste',
+ 'day' => 'egun bat|:count egun',
+ 'd' => 'Egun 1|:count egun',
+ 'hour' => 'ordu bat|:count ordu',
+ 'h' => 'Ordu 1|:count ordu',
+ 'minute' => 'minutu bat|:count minutu',
+ 'min' => 'Minutu 1|:count minutu',
+ 'second' => 'segundo batzuk|:count segundo',
+ 's' => 'Segundu 1|:count segundu',
+ 'ago' => 'duela :time',
+ 'from_now' => ':time barru',
+ 'after' => ':time geroago',
+ 'before' => ':time lehenago',
+ 'diff_now' => 'orain',
+ 'diff_today' => 'gaur',
+ 'diff_yesterday' => 'atzo',
+ 'diff_tomorrow' => 'bihar',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'YYYY[ko] MMMM[ren] D[a]',
+ 'LLL' => 'YYYY[ko] MMMM[ren] D[a] HH:mm',
+ 'LLLL' => 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[gaur] LT[etan]',
+ 'nextDay' => '[bihar] LT[etan]',
+ 'nextWeek' => 'dddd LT[etan]',
+ 'lastDay' => '[atzo] LT[etan]',
+ 'lastWeek' => '[aurreko] dddd LT[etan]',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['urtarrila', 'otsaila', 'martxoa', 'apirila', 'maiatza', 'ekaina', 'uztaila', 'abuztua', 'iraila', 'urria', 'azaroa', 'abendua'],
+ 'months_short' => ['urt.', 'ots.', 'mar.', 'api.', 'mai.', 'eka.', 'uzt.', 'abu.', 'ira.', 'urr.', 'aza.', 'abe.'],
+ 'weekdays' => ['igandea', 'astelehena', 'asteartea', 'asteazkena', 'osteguna', 'ostirala', 'larunbata'],
+ 'weekdays_short' => ['ig.', 'al.', 'ar.', 'az.', 'og.', 'ol.', 'lr.'],
+ 'weekdays_min' => ['ig', 'al', 'ar', 'az', 'og', 'ol', 'lr'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' eta '],
+ 'meridiem' => ['g', 'a'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php
new file mode 100644
index 0000000..0d1e82a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/eu_ES.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/eu.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ewo.php b/vendor/nesbot/carbon/src/Carbon/Lang/ewo.php
new file mode 100644
index 0000000..7808ab5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ewo.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['kíkíríg', 'ngəgógəle'],
+ 'weekdays' => ['sɔ́ndɔ', 'mɔ́ndi', 'sɔ́ndɔ məlú mə́bɛ̌', 'sɔ́ndɔ məlú mə́lɛ́', 'sɔ́ndɔ məlú mə́nyi', 'fúladé', 'séradé'],
+ 'weekdays_short' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'fúl', 'sér'],
+ 'weekdays_min' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'fúl', 'sér'],
+ 'months' => ['ngɔn osú', 'ngɔn bɛ̌', 'ngɔn lála', 'ngɔn nyina', 'ngɔn tána', 'ngɔn saməna', 'ngɔn zamgbála', 'ngɔn mwom', 'ngɔn ebulú', 'ngɔn awóm', 'ngɔn awóm ai dziá', 'ngɔn awóm ai bɛ̌'],
+ 'months_short' => ['ngo', 'ngb', 'ngl', 'ngn', 'ngt', 'ngs', 'ngz', 'ngm', 'nge', 'nga', 'ngad', 'ngab'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ // Too unreliable
+ /*
+ 'year' => ':count mbu', // less reliable
+ 'y' => ':count mbu', // less reliable
+ 'a_year' => ':count mbu', // less reliable
+
+ 'month' => ':count ngòn', // less reliable
+ 'm' => ':count ngòn', // less reliable
+ 'a_month' => ':count ngòn', // less reliable
+
+ 'week' => ':count mësë', // less reliable
+ 'w' => ':count mësë', // less reliable
+ 'a_week' => ':count mësë', // less reliable
+
+ 'day' => ':count mësë', // less reliable
+ 'd' => ':count mësë', // less reliable
+ 'a_day' => ':count mësë', // less reliable
+
+ 'hour' => ':count awola', // less reliable
+ 'h' => ':count awola', // less reliable
+ 'a_hour' => ':count awola', // less reliable
+
+ 'minute' => ':count awola', // less reliable
+ 'min' => ':count awola', // less reliable
+ 'a_minute' => ':count awola', // less reliable
+ */
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa.php
new file mode 100644
index 0000000..72e0308
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa.php
@@ -0,0 +1,84 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - Nasser Ghiasi
+ * - JD Isaacks
+ * - Hossein Jabbari
+ * - nimamo
+ * - hafezdivandari
+ * - Hassan Pezeshk (hpez)
+ */
+return [
+ 'year' => ':count سال',
+ 'a_year' => 'یک سال'.'|:count '.'سال',
+ 'y' => ':count سال',
+ 'month' => ':count ماه',
+ 'a_month' => 'یک ماه'.'|:count '.'ماه',
+ 'm' => ':count ماه',
+ 'week' => ':count هفته',
+ 'a_week' => 'یک هفته'.'|:count '.'هفته',
+ 'w' => ':count هفته',
+ 'day' => ':count روز',
+ 'a_day' => 'یک روز'.'|:count '.'روز',
+ 'd' => ':count روز',
+ 'hour' => ':count ساعت',
+ 'a_hour' => 'یک ساعت'.'|:count '.'ساعت',
+ 'h' => ':count ساعت',
+ 'minute' => ':count دقیقه',
+ 'a_minute' => 'یک دقیقه'.'|:count '.'دقیقه',
+ 'min' => ':count دقیقه',
+ 'second' => ':count ثانیه',
+ 's' => ':count ثانیه',
+ 'ago' => ':time پیش',
+ 'from_now' => ':time دیگر',
+ 'after' => ':time پس از',
+ 'before' => ':time پیش از',
+ 'diff_now' => 'اکنون',
+ 'diff_today' => 'امروز',
+ 'diff_today_regexp' => 'امروز(?:\\s+ساعت)?',
+ 'diff_yesterday' => 'دیروز',
+ 'diff_yesterday_regexp' => 'دیروز(?:\\s+ساعت)?',
+ 'diff_tomorrow' => 'فردا',
+ 'diff_tomorrow_regexp' => 'فردا(?:\\s+ساعت)?',
+ 'formats' => [
+ 'LT' => 'OH:Om',
+ 'LTS' => 'OH:Om:Os',
+ 'L' => 'OD/OM/OY',
+ 'LL' => 'OD MMMM OY',
+ 'LLL' => 'OD MMMM OY OH:Om',
+ 'LLLL' => 'dddd, OD MMMM OY OH:Om',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[امروز ساعت] LT',
+ 'nextDay' => '[فردا ساعت] LT',
+ 'nextWeek' => 'dddd [ساعت] LT',
+ 'lastDay' => '[دیروز ساعت] LT',
+ 'lastWeek' => 'dddd [پیش] [ساعت] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':timeم',
+ 'meridiem' => ['قبل از ظهر', 'بعد از ظهر'],
+ 'months' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'],
+ 'months_short' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'],
+ 'weekdays' => ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
+ 'weekdays_short' => ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
+ 'weekdays_min' => ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => ['، ', ' و '],
+ 'alt_numbers' => ['۰۰', '۰۱', '۰۲', '۰۳', '۰۴', '۰۵', '۰۶', '۰۷', '۰۸', '۰۹', '۱۰', '۱۱', '۱۲', '۱۳', '۱۴', '۱۵', '۱۶', '۱۷', '۱۸', '۱۹', '۲۰', '۲۱', '۲۲', '۲۳', '۲۴', '۲۵', '۲۶', '۲۷', '۲۸', '۲۹', '۳۰', '۳۱', '۳۲', '۳۳', '۳۴', '۳۵', '۳۶', '۳۷', '۳۸', '۳۹', '۴۰', '۴۱', '۴۲', '۴۳', '۴۴', '۴۵', '۴۶', '۴۷', '۴۸', '۴۹', '۵۰', '۵۱', '۵۲', '۵۳', '۵۴', '۵۵', '۵۶', '۵۷', '۵۸', '۵۹', '۶۰', '۶۱', '۶۲', '۶۳', '۶۴', '۶۵', '۶۶', '۶۷', '۶۸', '۶۹', '۷۰', '۷۱', '۷۲', '۷۳', '۷۴', '۷۵', '۷۶', '۷۷', '۷۸', '۷۹', '۸۰', '۸۱', '۸۲', '۸۳', '۸۴', '۸۵', '۸۶', '۸۷', '۸۸', '۸۹', '۹۰', '۹۱', '۹۲', '۹۳', '۹۴', '۹۵', '۹۶', '۹۷', '۹۸', '۹۹'],
+ 'months_short_standalone' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'],
+ 'weekend' => [5, 5],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php
new file mode 100644
index 0000000..6947100
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa_AF.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fa.php', [
+ 'meridiem' => ['ق', 'ب'],
+ 'weekend' => [4, 5],
+ 'formats' => [
+ 'L' => 'OY/OM/OD',
+ 'LL' => 'OD MMM OY',
+ 'LLL' => 'OD MMMM OY، H:mm',
+ 'LLLL' => 'dddd OD MMMM OY، H:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php b/vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php
new file mode 100644
index 0000000..08d0182
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fa_IR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fa.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff.php
new file mode 100644
index 0000000..9525c95
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'months' => ['siilo', 'colte', 'mbooy', 'seeɗto', 'duujal', 'korse', 'morso', 'juko', 'siilto', 'yarkomaa', 'jolal', 'bowte'],
+ 'months_short' => ['sii', 'col', 'mbo', 'see', 'duu', 'kor', 'mor', 'juk', 'slt', 'yar', 'jol', 'bow'],
+ 'weekdays' => ['dewo', 'aaɓnde', 'mawbaare', 'njeslaare', 'naasaande', 'mawnde', 'hoore-biir'],
+ 'weekdays_short' => ['dew', 'aaɓ', 'maw', 'nje', 'naa', 'mwd', 'hbi'],
+ 'weekdays_min' => ['dew', 'aaɓ', 'maw', 'nje', 'naa', 'mwd', 'hbi'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['subaka', 'kikiiɗe'],
+
+ 'year' => ':count baret', // less reliable
+ 'y' => ':count baret', // less reliable
+ 'a_year' => ':count baret', // less reliable
+
+ 'month' => ':count lewru', // less reliable
+ 'm' => ':count lewru', // less reliable
+ 'a_month' => ':count lewru', // less reliable
+
+ 'week' => ':count naange', // less reliable
+ 'w' => ':count naange', // less reliable
+ 'a_week' => ':count naange', // less reliable
+
+ 'day' => ':count dian', // less reliable
+ 'd' => ':count dian', // less reliable
+ 'a_day' => ':count dian', // less reliable
+
+ 'hour' => ':count montor', // less reliable
+ 'h' => ':count montor', // less reliable
+ 'a_hour' => ':count montor', // less reliable
+
+ 'minute' => ':count tokossuoum', // less reliable
+ 'min' => ':count tokossuoum', // less reliable
+ 'a_minute' => ':count tokossuoum', // less reliable
+
+ 'second' => ':count tenen', // less reliable
+ 's' => ':count tenen', // less reliable
+ 'a_second' => ':count tenen', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php
new file mode 100644
index 0000000..b797ac0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_CM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ff.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php
new file mode 100644
index 0000000..b797ac0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_GN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ff.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php
new file mode 100644
index 0000000..2f4c29f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_MR.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ff.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php
new file mode 100644
index 0000000..1e4c8b6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ff_SN.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Pular-Fulfulde.org Ibrahima Sarr admin@pulaar-fulfulde.org
+ */
+return require __DIR__.'/ff.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fi.php b/vendor/nesbot/carbon/src/Carbon/Lang/fi.php
new file mode 100644
index 0000000..edf2d6d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fi.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Janne Warén
+ * - digitalfrost
+ * - Tsutomu Kuroda
+ * - Roope Salmi
+ * - tjku
+ * - Max Melentiev
+ * - Sami Haahtinen
+ * - Teemu Leisti
+ * - Artem Ignatyev
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Robert Bjarnason
+ * - Aaron Patterson
+ * - Nicolás Hock Isaza
+ * - Tom Hughes
+ * - Sven Fuchs
+ * - Petri Kivikangas
+ * - Nizar Jouini
+ * - Marko Seppae
+ * - Tomi Mynttinen (Pikseli)
+ * - Petteri (powergrip)
+ */
+return [
+ 'year' => ':count vuosi|:count vuotta',
+ 'y' => ':count v',
+ 'month' => ':count kuukausi|:count kuukautta',
+ 'm' => ':count kk',
+ 'week' => ':count viikko|:count viikkoa',
+ 'w' => ':count vk',
+ 'day' => ':count päivä|:count päivää',
+ 'd' => ':count pv',
+ 'hour' => ':count tunti|:count tuntia',
+ 'h' => ':count t',
+ 'minute' => ':count minuutti|:count minuuttia',
+ 'min' => ':count min',
+ 'second' => ':count sekunti|:count sekuntia',
+ 'a_second' => 'muutama sekunti|:count sekuntia',
+ 's' => ':count s',
+ 'ago' => ':time sitten',
+ 'from_now' => ':time päästä',
+ 'year_from_now' => ':count vuoden',
+ 'month_from_now' => ':count kuukauden',
+ 'week_from_now' => ':count viikon',
+ 'day_from_now' => ':count päivän',
+ 'hour_from_now' => ':count tunnin',
+ 'minute_from_now' => ':count minuutin',
+ 'second_from_now' => ':count sekunnin',
+ 'after' => ':time sen jälkeen',
+ 'before' => ':time ennen',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' ja '],
+ 'diff_now' => 'nyt',
+ 'diff_yesterday' => 'eilen',
+ 'diff_tomorrow' => 'huomenna',
+ 'formats' => [
+ 'LT' => 'HH.mm',
+ 'LTS' => 'HH.mm:ss',
+ 'L' => 'D.M.YYYY',
+ 'LL' => 'dddd D. MMMM[ta] YYYY',
+ 'll' => 'ddd D. MMM YYYY',
+ 'LLL' => 'D.MM. HH.mm',
+ 'LLLL' => 'D. MMMM[ta] YYYY HH.mm',
+ 'llll' => 'D. MMM YY HH.mm',
+ ],
+ 'weekdays' => ['sunnuntai', 'maanantai', 'tiistai', 'keskiviikko', 'torstai', 'perjantai', 'lauantai'],
+ 'weekdays_short' => ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
+ 'weekdays_min' => ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
+ 'months' => ['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu', 'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu', 'marraskuu', 'joulukuu'],
+ 'months_short' => ['tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä', 'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'],
+ 'meridiem' => ['aamupäivä', 'iltapäivä'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php
new file mode 100644
index 0000000..920f1ca
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fi_FI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fi.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fil.php b/vendor/nesbot/carbon/src/Carbon/Lang/fil.php
new file mode 100644
index 0000000..61114e3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fil.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/fil_PH.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php
new file mode 100644
index 0000000..bcf1580
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fil_PH.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Rene Torres Rene Torres, Pablo Saratxaga rgtorre@rocketmail.com, pablo@mandrakesoft.com
+ * - Jaycee Mariano (alohajaycee)
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'MM/DD/YY',
+ ],
+ 'months' => ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
+ 'months_short' => ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set', 'Okt', 'Nob', 'Dis'],
+ 'weekdays' => ['Linggo', 'Lunes', 'Martes', 'Miyerkoles', 'Huwebes', 'Biyernes', 'Sabado'],
+ 'weekdays_short' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
+ 'weekdays_min' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['N.U.', 'N.H.'],
+
+ 'before' => ':time bago',
+ 'after' => ':time pagkatapos',
+
+ 'year' => ':count taon',
+ 'y' => ':count taon',
+ 'a_year' => ':count taon',
+
+ 'month' => ':count buwan',
+ 'm' => ':count buwan',
+ 'a_month' => ':count buwan',
+
+ 'week' => ':count linggo',
+ 'w' => ':count linggo',
+ 'a_week' => ':count linggo',
+
+ 'day' => ':count araw',
+ 'd' => ':count araw',
+ 'a_day' => ':count araw',
+
+ 'hour' => ':count oras',
+ 'h' => ':count oras',
+ 'a_hour' => ':count oras',
+
+ 'minute' => ':count minuto',
+ 'min' => ':count minuto',
+ 'a_minute' => ':count minuto',
+
+ 'second' => ':count segundo',
+ 's' => ':count segundo',
+ 'a_second' => ':count segundo',
+
+ 'ago' => ':time ang nakalipas',
+ 'from_now' => 'sa :time',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo.php
new file mode 100644
index 0000000..6a14a6f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo.php
@@ -0,0 +1,69 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kristian Sakarisson
+ * - François B
+ * - JD Isaacks
+ * - Sverri Mohr Olsen
+ */
+return [
+ 'year' => 'eitt ár|:count ár',
+ 'y' => ':count ár|:count ár',
+ 'month' => 'ein mánaði|:count mánaðir',
+ 'm' => ':count mánaður|:count mánaðir',
+ 'week' => ':count vika|:count vikur',
+ 'w' => ':count vika|:count vikur',
+ 'day' => 'ein dagur|:count dagar',
+ 'd' => ':count dag|:count dagar',
+ 'hour' => 'ein tími|:count tímar',
+ 'h' => ':count tími|:count tímar',
+ 'minute' => 'ein minutt|:count minuttir',
+ 'min' => ':count minutt|:count minuttir',
+ 'second' => 'fá sekund|:count sekundir',
+ 's' => ':count sekund|:count sekundir',
+ 'ago' => ':time síðani',
+ 'from_now' => 'um :time',
+ 'after' => ':time aftaná',
+ 'before' => ':time áðrenn',
+ 'diff_today' => 'Í',
+ 'diff_yesterday' => 'Í',
+ 'diff_yesterday_regexp' => 'Í(?:\\s+gjár)?(?:\\s+kl.)?',
+ 'diff_tomorrow' => 'Í',
+ 'diff_tomorrow_regexp' => 'Í(?:\\s+morgin)?(?:\\s+kl.)?',
+ 'diff_today_regexp' => 'Í(?:\\s+dag)?(?:\\s+kl.)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D. MMMM, YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Í dag kl.] LT',
+ 'nextDay' => '[Í morgin kl.] LT',
+ 'nextWeek' => 'dddd [kl.] LT',
+ 'lastDay' => '[Í gjár kl.] LT',
+ 'lastWeek' => '[síðstu] dddd [kl] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['januar', 'februar', 'mars', 'apríl', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ 'weekdays' => ['sunnudagur', 'mánadagur', 'týsdagur', 'mikudagur', 'hósdagur', 'fríggjadagur', 'leygardagur'],
+ 'weekdays_short' => ['sun', 'mán', 'týs', 'mik', 'hós', 'frí', 'ley'],
+ 'weekdays_min' => ['su', 'má', 'tý', 'mi', 'hó', 'fr', 'le'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' og '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php
new file mode 100644
index 0000000..657f2c5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo_DK.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fo.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.yy',
+ 'LL' => 'DD.MM.YYYY',
+ 'LLL' => 'D. MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY, HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php b/vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php
new file mode 100644
index 0000000..6d73616
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fo_FO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fo.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php
new file mode 100644
index 0000000..f4c7247
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr.php
@@ -0,0 +1,123 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Dieter Sting
+ * - François B
+ * - Maxime VALY
+ * - JD Isaacks
+ * - Dieter Sting
+ * - François B
+ * - JD Isaacks
+ * - Sebastian Thierer
+ * - Fastfuel
+ * - Pete Scopes (pdscopes)
+ */
+return [
+ 'year' => ':count an|:count ans',
+ 'a_year' => 'un an|:count ans',
+ 'y' => ':count an|:count ans',
+ 'month' => ':count mois|:count mois',
+ 'a_month' => 'un mois|:count mois',
+ 'm' => ':count mois',
+ 'week' => ':count semaine|:count semaines',
+ 'a_week' => 'une semaine|:count semaines',
+ 'w' => ':count sem.',
+ 'day' => ':count jour|:count jours',
+ 'a_day' => 'un jour|:count jours',
+ 'd' => ':count j',
+ 'hour' => ':count heure|:count heures',
+ 'a_hour' => 'une heure|:count heures',
+ 'h' => ':count h',
+ 'minute' => ':count minute|:count minutes',
+ 'a_minute' => 'une minute|:count minutes',
+ 'min' => ':count min',
+ 'second' => ':count seconde|:count secondes',
+ 'a_second' => 'quelques secondes|:count secondes',
+ 's' => ':count s',
+ 'millisecond' => ':count milliseconde|:count millisecondes',
+ 'a_millisecond' => 'une milliseconde|:count millisecondes',
+ 'ms' => ':countms',
+ 'microsecond' => ':count microseconde|:count microsecondes',
+ 'a_microsecond' => 'une microseconde|:count microsecondes',
+ 'µs' => ':countµs',
+ 'ago' => 'il y a :time',
+ 'from_now' => 'dans :time',
+ 'after' => ':time après',
+ 'before' => ':time avant',
+ 'diff_now' => "à l'instant",
+ 'diff_today' => "aujourd'hui",
+ 'diff_today_regexp' => "aujourd'hui(?:\s+à)?",
+ 'diff_yesterday' => 'hier',
+ 'diff_yesterday_regexp' => 'hier(?:\s+à)?',
+ 'diff_tomorrow' => 'demain',
+ 'diff_tomorrow_regexp' => 'demain(?:\s+à)?',
+ 'diff_before_yesterday' => 'avant-hier',
+ 'diff_after_tomorrow' => 'après-demain',
+ 'period_recurrences' => ':count fois',
+ 'period_interval' => 'tous les :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'à :date',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Aujourd’hui à] LT',
+ 'nextDay' => '[Demain à] LT',
+ 'nextWeek' => 'dddd [à] LT',
+ 'lastDay' => '[Hier à] LT',
+ 'lastWeek' => 'dddd [dernier à] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
+ 'months_short' => ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
+ 'weekdays' => ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
+ 'weekdays_short' => ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
+ 'weekdays_min' => ['di', 'lu', 'ma', 'me', 'je', 've', 'sa'],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ // In French, only the first has to be ordinal, other number remains cardinal
+ // @link https://fr.wikihow.com/%C3%A9crire-la-date-en-fran%C3%A7ais
+ case 'D':
+ return $number.($number === 1 ? 'er' : '');
+
+ default:
+ case 'M':
+ case 'Q':
+ case 'DDD':
+ case 'd':
+ return $number.($number === 1 ? 'er' : 'e');
+
+ // Words with feminine grammatical gender: semaine
+ case 'w':
+ case 'W':
+ return $number.($number === 1 ? 're' : 'e');
+ }
+ },
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' et '],
+ 'ordinal_words' => [
+ 'of' => 'de',
+ 'first' => 'premier',
+ 'second' => 'deuxième',
+ 'third' => 'troisième',
+ 'fourth' => 'quatrième',
+ 'fifth' => 'cinquième',
+ 'last' => 'dernier',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php
new file mode 100644
index 0000000..f6cafe8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BE.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'months_short' => ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BJ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_BL.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php
new file mode 100644
index 0000000..c9f6346
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CA.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Dieter Sting
+ * - François B
+ * - Maxime VALY
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CD.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php
new file mode 100644
index 0000000..8674c27
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CH.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Dieter Sting
+ * - François B
+ * - Gaspard Bucher
+ * - Maxime VALY
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php
new file mode 100644
index 0000000..67d3787
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_CM.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'meridiem' => ['mat.', 'soir'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php
new file mode 100644
index 0000000..2f06086
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DJ.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'first_day_of_week' => 6,
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php
new file mode 100644
index 0000000..ae8db5f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_DZ.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 6],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_FR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GP.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_GQ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_HT.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_KM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php
new file mode 100644
index 0000000..8e37d85
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_LU.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months_short' => ['jan', 'fév', 'mar', 'avr', 'mai', 'jun', 'jui', 'aoû', 'sep', 'oct', 'nov', 'déc'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php
new file mode 100644
index 0000000..1bf034d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MA.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 6],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MC.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_ML.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MQ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php
new file mode 100644
index 0000000..37cf83f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MR.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_MU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NC.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_NE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_PM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_RW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SC.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php
new file mode 100644
index 0000000..ae8db5f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_SY.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 6],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php
new file mode 100644
index 0000000..37cf83f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TD.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php
new file mode 100644
index 0000000..6905e7a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_TN.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'weekend' => [5, 6],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php
new file mode 100644
index 0000000..37cf83f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_VU.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fr.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_WF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php b/vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php
new file mode 100644
index 0000000..ec3ee35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fr_YT.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/fr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fur.php b/vendor/nesbot/carbon/src/Carbon/Lang/fur.php
new file mode 100644
index 0000000..36c2564
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fur.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/fur_IT.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php
new file mode 100644
index 0000000..0147a59
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fur_IT.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD. MM. YY',
+ 'LL' => 'DD di MMMM dal YYYY',
+ 'LLL' => 'DD di MMM HH:mm',
+ 'LLLL' => 'DD di MMMM dal YYYY HH:mm',
+ ],
+ 'months' => ['zenâr', 'fevrâr', 'març', 'avrîl', 'mai', 'jugn', 'lui', 'avost', 'setembar', 'otubar', 'novembar', 'dicembar'],
+ 'months_short' => ['zen', 'fev', 'mar', 'avr', 'mai', 'jug', 'lui', 'avo', 'set', 'otu', 'nov', 'dic'],
+ 'weekdays' => ['domenie', 'lunis', 'martars', 'miercus', 'joibe', 'vinars', 'sabide'],
+ 'weekdays_short' => ['dom', 'lun', 'mar', 'mie', 'joi', 'vin', 'sab'],
+ 'weekdays_min' => ['dom', 'lun', 'mar', 'mie', 'joi', 'vin', 'sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'year' => ':count an',
+ 'month' => ':count mês',
+ 'week' => ':count setemane',
+ 'day' => ':count zornade',
+ 'hour' => ':count ore',
+ 'minute' => ':count minût',
+ 'second' => ':count secont',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy.php
new file mode 100644
index 0000000..c1b5439
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Tim Fish
+ * - JD Isaacks
+ */
+return [
+ 'year' => ':count jier|:count jierren',
+ 'a_year' => 'ien jier|:count jierren',
+ 'y' => ':count j',
+ 'month' => ':count moanne|:count moannen',
+ 'a_month' => 'ien moanne|:count moannen',
+ 'm' => ':count moa.',
+ 'week' => ':count wike|:count wiken',
+ 'a_week' => 'in wike|:count wiken',
+ 'a' => ':count w.',
+ 'day' => ':count dei|:count dagen',
+ 'a_day' => 'ien dei|:count dagen',
+ 'd' => ':count d.',
+ 'hour' => ':count oere|:count oeren',
+ 'a_hour' => 'ien oere|:count oeren',
+ 'h' => ':count o.',
+ 'minute' => ':count minút|:count minuten',
+ 'a_minute' => 'ien minút|:count minuten',
+ 'min' => ':count min.',
+ 'second' => ':count sekonde|:count sekonden',
+ 'a_second' => 'in pear sekonden|:count sekonden',
+ 's' => ':count s.',
+ 'ago' => ':time lyn',
+ 'from_now' => 'oer :time',
+ 'diff_yesterday' => 'juster',
+ 'diff_yesterday_regexp' => 'juster(?:\\s+om)?',
+ 'diff_today' => 'hjoed',
+ 'diff_today_regexp' => 'hjoed(?:\\s+om)?',
+ 'diff_tomorrow' => 'moarn',
+ 'diff_tomorrow_regexp' => 'moarn(?:\\s+om)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD-MM-YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[hjoed om] LT',
+ 'nextDay' => '[moarn om] LT',
+ 'nextWeek' => 'dddd [om] LT',
+ 'lastDay' => '[juster om] LT',
+ 'lastWeek' => '[ôfrûne] dddd [om] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de');
+ },
+ 'months' => ['jannewaris', 'febrewaris', 'maart', 'april', 'maaie', 'juny', 'july', 'augustus', 'septimber', 'oktober', 'novimber', 'desimber'],
+ 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ 'mmm_suffix' => '.',
+ 'weekdays' => ['snein', 'moandei', 'tiisdei', 'woansdei', 'tongersdei', 'freed', 'sneon'],
+ 'weekdays_short' => ['si.', 'mo.', 'ti.', 'wo.', 'to.', 'fr.', 'so.'],
+ 'weekdays_min' => ['Si', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'So'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' en '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php
new file mode 100644
index 0000000..8559d5c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy_DE.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandriva.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Jaunuwoa', 'Februwoa', 'Moaz', 'Aprell', 'Mai', 'Juni', 'Juli', 'August', 'Septamba', 'Oktoba', 'Nowamba', 'Dezamba'],
+ 'months_short' => ['Jan', 'Feb', 'Moz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Now', 'Dez'],
+ 'weekdays' => ['Sinndag', 'Mondag', 'Dingsdag', 'Meddwäakj', 'Donnadag', 'Friedag', 'Sinnowend'],
+ 'weekdays_short' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'],
+ 'weekdays_min' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php
new file mode 100644
index 0000000..01cc96c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/fy_NL.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/fy.php', [
+ 'formats' => [
+ 'L' => 'DD-MM-YY',
+ ],
+ 'months' => ['Jannewaris', 'Febrewaris', 'Maart', 'April', 'Maaie', 'Juny', 'July', 'Augustus', 'Septimber', 'Oktober', 'Novimber', 'Desimber'],
+ 'months_short' => ['Jan', 'Feb', 'Mrt', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Snein', 'Moandei', 'Tiisdei', 'Woansdei', 'Tongersdei', 'Freed', 'Sneon'],
+ 'weekdays_short' => ['Sn', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'Sn'],
+ 'weekdays_min' => ['Sn', 'Mo', 'Ti', 'Wo', 'To', 'Fr', 'Sn'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ga.php b/vendor/nesbot/carbon/src/Carbon/Lang/ga.php
new file mode 100644
index 0000000..9f07a26
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ga.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Thanks to André Silva : https://github.com/askpt
+ */
+
+return [
+ 'year' => ':count bliain',
+ 'a_year' => '{1}bliain|:count bliain',
+ 'y' => ':countb',
+ 'month' => ':count mí',
+ 'a_month' => '{1}mí|:count mí',
+ 'm' => ':countm',
+ 'week' => ':count sheachtain',
+ 'a_week' => '{1}sheachtain|:count sheachtain',
+ 'w' => ':countsh',
+ 'day' => ':count lá',
+ 'a_day' => '{1}lá|:count lá',
+ 'd' => ':countl',
+ 'hour' => ':count uair an chloig',
+ 'a_hour' => '{1}uair an chloig|:count uair an chloig',
+ 'h' => ':countu',
+ 'minute' => ':count nóiméad',
+ 'a_minute' => '{1}nóiméad|:count nóiméad',
+ 'min' => ':countn',
+ 'second' => ':count soicind',
+ 'a_second' => '{1}cúpla soicind|:count soicind',
+ 's' => ':countso',
+ 'ago' => ':time ó shin',
+ 'from_now' => 'i :time',
+ 'after' => ':time tar éis',
+ 'before' => ':time roimh',
+ 'diff_now' => 'anois',
+ 'diff_today' => 'Inniu',
+ 'diff_today_regexp' => 'Inniu(?:\\s+ag)?',
+ 'diff_yesterday' => 'inné',
+ 'diff_yesterday_regexp' => 'Inné(?:\\s+aig)?',
+ 'diff_tomorrow' => 'amárach',
+ 'diff_tomorrow_regexp' => 'Amárach(?:\\s+ag)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Inniu ag] LT',
+ 'nextDay' => '[Amárach ag] LT',
+ 'nextWeek' => 'dddd [ag] LT',
+ 'lastDay' => '[Inné aig] LT',
+ 'lastWeek' => 'dddd [seo caite] [ag] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine', 'Méitheamh', 'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deaireadh Fómhair', 'Samhain', 'Nollaig'],
+ 'months_short' => ['Eaná', 'Feab', 'Márt', 'Aibr', 'Beal', 'Méit', 'Iúil', 'Lúna', 'Meán', 'Deai', 'Samh', 'Noll'],
+ 'weekdays' => ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Satharn'],
+ 'weekdays_short' => ['Dom', 'Lua', 'Mái', 'Céa', 'Déa', 'hAo', 'Sat'],
+ 'weekdays_min' => ['Do', 'Lu', 'Má', 'Ce', 'Dé', 'hA', 'Sa'],
+ 'ordinal' => function ($number) {
+ return $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh'));
+ },
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' agus '],
+ 'meridiem' => ['r.n.', 'i.n.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php
new file mode 100644
index 0000000..57b0c4f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ga_IE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ga.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gd.php b/vendor/nesbot/carbon/src/Carbon/Lang/gd.php
new file mode 100644
index 0000000..63d064d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gd.php
@@ -0,0 +1,75 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Jon Ashdown
+ */
+return [
+ 'year' => ':count bliadhna',
+ 'a_year' => '{1}bliadhna|:count bliadhna',
+ 'y' => ':count b.',
+ 'month' => ':count mìosan',
+ 'a_month' => '{1}mìos|:count mìosan',
+ 'm' => ':count ms.',
+ 'week' => ':count seachdainean',
+ 'a_week' => '{1}seachdain|:count seachdainean',
+ 'w' => ':count s.',
+ 'day' => ':count latha',
+ 'a_day' => '{1}latha|:count latha',
+ 'd' => ':count l.',
+ 'hour' => ':count uairean',
+ 'a_hour' => '{1}uair|:count uairean',
+ 'h' => ':count u.',
+ 'minute' => ':count mionaidean',
+ 'a_minute' => '{1}mionaid|:count mionaidean',
+ 'min' => ':count md.',
+ 'second' => ':count diogan',
+ 'a_second' => '{1}beagan diogan|:count diogan',
+ 's' => ':count d.',
+ 'ago' => 'bho chionn :time',
+ 'from_now' => 'ann an :time',
+ 'diff_yesterday' => 'An-dè',
+ 'diff_yesterday_regexp' => 'An-dè(?:\\s+aig)?',
+ 'diff_today' => 'An-diugh',
+ 'diff_today_regexp' => 'An-diugh(?:\\s+aig)?',
+ 'diff_tomorrow' => 'A-màireach',
+ 'diff_tomorrow_regexp' => 'A-màireach(?:\\s+aig)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[An-diugh aig] LT',
+ 'nextDay' => '[A-màireach aig] LT',
+ 'nextWeek' => 'dddd [aig] LT',
+ 'lastDay' => '[An-dè aig] LT',
+ 'lastWeek' => 'dddd [seo chaidh] [aig] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ return $number.($number === 1 ? 'd' : ($number % 10 === 2 ? 'na' : 'mh'));
+ },
+ 'months' => ['Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd'],
+ 'months_short' => ['Faoi', 'Gear', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch', 'Lùn', 'Sult', 'Dàmh', 'Samh', 'Dùbh'],
+ 'weekdays' => ['Didòmhnaich', 'Diluain', 'Dimàirt', 'Diciadain', 'Diardaoin', 'Dihaoine', 'Disathairne'],
+ 'weekdays_short' => ['Did', 'Dil', 'Dim', 'Dic', 'Dia', 'Dih', 'Dis'],
+ 'weekdays_min' => ['Dò', 'Lu', 'Mà', 'Ci', 'Ar', 'Ha', 'Sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' agus '],
+ 'meridiem' => ['m', 'f'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php
new file mode 100644
index 0000000..4fc26b3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gd_GB.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/gd.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez.php
new file mode 100644
index 0000000..b8a2f0e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/gez_ER.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php
new file mode 100644
index 0000000..f19d1df
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ER.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጠሐረ', 'ከተተ', 'መገበ', 'አኀዘ', 'ግንባት', 'ሠንየ', 'ሐመለ', 'ነሐሰ', 'ከረመ', 'ጠቀመ', 'ኀደረ', 'ኀሠሠ'],
+ 'months_short' => ['ጠሐረ', 'ከተተ', 'መገበ', 'አኀዘ', 'ግንባ', 'ሠንየ', 'ሐመለ', 'ነሐሰ', 'ከረመ', 'ጠቀመ', 'ኀደረ', 'ኀሠሠ'],
+ 'weekdays' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚት'],
+ 'weekdays_short' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'],
+ 'weekdays_min' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ጽባሕ', 'ምሴት'],
+
+ 'month' => ':count ወርሕ', // less reliable
+ 'm' => ':count ወርሕ', // less reliable
+ 'a_month' => ':count ወርሕ', // less reliable
+
+ 'week' => ':count ሰብዑ', // less reliable
+ 'w' => ':count ሰብዑ', // less reliable
+ 'a_week' => ':count ሰብዑ', // less reliable
+
+ 'hour' => ':count አንትሙ', // less reliable
+ 'h' => ':count አንትሙ', // less reliable
+ 'a_hour' => ':count አንትሙ', // less reliable
+
+ 'minute' => ':count ንኡስ', // less reliable
+ 'min' => ':count ንኡስ', // less reliable
+ 'a_minute' => ':count ንኡስ', // less reliable
+
+ 'year' => ':count ዓመት',
+ 'y' => ':count ዓመት',
+ 'a_year' => ':count ዓመት',
+
+ 'day' => ':count ዕለት',
+ 'd' => ':count ዕለት',
+ 'a_day' => ':count ዕለት',
+
+ 'second' => ':count ካልእ',
+ 's' => ':count ካልእ',
+ 'a_second' => ':count ካልእ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php
new file mode 100644
index 0000000..3933009
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gez_ET.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'],
+ 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'],
+ 'weekdays' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚት'],
+ 'weekdays_short' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'],
+ 'weekdays_min' => ['እኁድ', 'ሰኑይ', 'ሠሉስ', 'ራብዕ', 'ሐሙስ', 'ዓርበ', 'ቀዳሚ'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ጽባሕ', 'ምሴት'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gl.php b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php
new file mode 100644
index 0000000..088b0f2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gl.php
@@ -0,0 +1,98 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Fidel Pita
+ * - JD Isaacks
+ * - Diego Vilariño
+ * - Sebastian Thierer
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count ano|:count anos',
+ 'a_year' => 'un ano|:count anos',
+ 'y' => ':count a.',
+ 'month' => ':count mes|:count meses',
+ 'a_month' => 'un mes|:count meses',
+ 'm' => ':count mes.',
+ 'week' => ':count semana|:count semanas',
+ 'a_week' => 'unha semana|:count semanas',
+ 'w' => ':count sem.',
+ 'day' => ':count día|:count días',
+ 'a_day' => 'un día|:count días',
+ 'd' => ':count d.',
+ 'hour' => ':count hora|:count horas',
+ 'a_hour' => 'unha hora|:count horas',
+ 'h' => ':count h.',
+ 'minute' => ':count minuto|:count minutos',
+ 'a_minute' => 'un minuto|:count minutos',
+ 'min' => ':count min.',
+ 'second' => ':count segundo|:count segundos',
+ 'a_second' => 'uns segundos|:count segundos',
+ 's' => ':count seg.',
+ 'ago' => 'hai :time',
+ 'from_now' => function ($time) {
+ if (str_starts_with($time, 'un')) {
+ return "n$time";
+ }
+
+ return "en $time";
+ },
+ 'diff_now' => 'agora',
+ 'diff_today' => 'hoxe',
+ 'diff_today_regexp' => 'hoxe(?:\\s+ás)?',
+ 'diff_yesterday' => 'onte',
+ 'diff_yesterday_regexp' => 'onte(?:\\s+á)?',
+ 'diff_tomorrow' => 'mañá',
+ 'diff_tomorrow_regexp' => 'mañá(?:\\s+ás)?',
+ 'after' => ':time despois',
+ 'before' => ':time antes',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D [de] MMMM [de] YYYY',
+ 'LLL' => 'D [de] MMMM [de] YYYY H:mm',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => function (CarbonInterface $current) {
+ return '[hoxe '.($current->hour !== 1 ? 'ás' : 'á').'] LT';
+ },
+ 'nextDay' => function (CarbonInterface $current) {
+ return '[mañá '.($current->hour !== 1 ? 'ás' : 'á').'] LT';
+ },
+ 'nextWeek' => function (CarbonInterface $current) {
+ return 'dddd ['.($current->hour !== 1 ? 'ás' : 'á').'] LT';
+ },
+ 'lastDay' => function (CarbonInterface $current) {
+ return '[onte '.($current->hour !== 1 ? 'á' : 'a').'] LT';
+ },
+ 'lastWeek' => function (CarbonInterface $current) {
+ return '[o] dddd [pasado '.($current->hour !== 1 ? 'ás' : 'á').'] LT';
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberº',
+ 'months' => ['xaneiro', 'febreiro', 'marzo', 'abril', 'maio', 'xuño', 'xullo', 'agosto', 'setembro', 'outubro', 'novembro', 'decembro'],
+ 'months_short' => ['xan.', 'feb.', 'mar.', 'abr.', 'mai.', 'xuñ.', 'xul.', 'ago.', 'set.', 'out.', 'nov.', 'dec.'],
+ 'weekdays' => ['domingo', 'luns', 'martes', 'mércores', 'xoves', 'venres', 'sábado'],
+ 'weekdays_short' => ['dom.', 'lun.', 'mar.', 'mér.', 'xov.', 'ven.', 'sáb.'],
+ 'weekdays_min' => ['do', 'lu', 'ma', 'mé', 'xo', 've', 'sá'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' e '],
+ 'meridiem' => ['a.m.', 'p.m.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php b/vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php
new file mode 100644
index 0000000..9d6c1d9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gl_ES.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/gl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gom.php b/vendor/nesbot/carbon/src/Carbon/Lang/gom.php
new file mode 100644
index 0000000..2a0584f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gom.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/gom_Latn.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php
new file mode 100644
index 0000000..612bb88
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gom_Latn.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+ 'year' => ':count voros|:count vorsam',
+ 'y' => ':countv',
+ 'month' => ':count mhoino|:count mhoine',
+ 'm' => ':countmh',
+ 'week' => ':count satolleacho|:count satolleache',
+ 'w' => ':countsa|:countsa',
+ 'day' => ':count dis',
+ 'd' => ':countd',
+ 'hour' => ':count hor|:count horam',
+ 'h' => ':counth',
+ 'minute' => ':count minute|:count mintam',
+ 'min' => ':countm',
+ 'second' => ':count second',
+ 's' => ':counts',
+
+ 'diff_today' => 'Aiz',
+ 'diff_yesterday' => 'Kal',
+ 'diff_tomorrow' => 'Faleam',
+ 'formats' => [
+ 'LT' => 'A h:mm [vazta]',
+ 'LTS' => 'A h:mm:ss [vazta]',
+ 'L' => 'DD-MM-YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY A h:mm [vazta]',
+ 'LLLL' => 'dddd, MMMM[achea] Do, YYYY, A h:mm [vazta]',
+ 'llll' => 'ddd, D MMM YYYY, A h:mm [vazta]',
+ ],
+
+ 'calendar' => [
+ 'sameDay' => '[Aiz] LT',
+ 'nextDay' => '[Faleam] LT',
+ 'nextWeek' => '[Ieta to] dddd[,] LT',
+ 'lastDay' => '[Kal] LT',
+ 'lastWeek' => '[Fatlo] dddd[,] LT',
+ 'sameElse' => 'L',
+ ],
+
+ 'months' => ['Janer', 'Febrer', 'Mars', 'Abril', 'Mai', 'Jun', 'Julai', 'Agost', 'Setembr', 'Otubr', 'Novembr', 'Dezembr'],
+ 'months_short' => ['Jan.', 'Feb.', 'Mars', 'Abr.', 'Mai', 'Jun', 'Jul.', 'Ago.', 'Set.', 'Otu.', 'Nov.', 'Dez.'],
+ 'weekdays' => ['Aitar', 'Somar', 'Mongllar', 'Budvar', 'Brestar', 'Sukrar', 'Son\'var'],
+ 'weekdays_short' => ['Ait.', 'Som.', 'Mon.', 'Bud.', 'Bre.', 'Suk.', 'Son.'],
+ 'weekdays_min' => ['Ai', 'Sm', 'Mo', 'Bu', 'Br', 'Su', 'Sn'],
+
+ 'ordinal' => function ($number, $period) {
+ return $number.($period === 'D' ? 'er' : '');
+ },
+
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'rati';
+ }
+ if ($hour < 12) {
+ return 'sokalli';
+ }
+ if ($hour < 16) {
+ return 'donparam';
+ }
+ if ($hour < 20) {
+ return 'sanje';
+ }
+
+ return 'rati';
+ },
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' ani '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw.php
new file mode 100644
index 0000000..c5c850e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Christopher Dell
+ * - Akira Matsuda
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Henning Kiel
+ * - Aaron Patterson
+ * - Florian Hanke
+ */
+return [
+ 'year' => ':count Johr',
+ 'month' => ':count Monet',
+ 'week' => ':count Woche',
+ 'day' => ':count Tag',
+ 'hour' => ':count Schtund',
+ 'minute' => ':count Minute',
+ 'second' => ':count Sekunde',
+ 'weekdays' => ['Sunntig', 'Mäntig', 'Ziischtig', 'Mittwuch', 'Dunschtig', 'Friitig', 'Samschtig'],
+ 'weekdays_short' => ['Su', 'Mä', 'Zi', 'Mi', 'Du', 'Fr', 'Sa'],
+ 'weekdays_min' => ['Su', 'Mä', 'Zi', 'Mi', 'Du', 'Fr', 'Sa'],
+ 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'Auguscht', 'September', 'Oktober', 'November', 'Dezember'],
+ 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+ 'meridiem' => ['am Vormittag', 'am Namittag'],
+ 'ordinal' => ':number.',
+ 'list' => [', ', ' und '],
+ 'diff_now' => 'now',
+ 'diff_yesterday' => 'geschter',
+ 'diff_tomorrow' => 'moorn',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'Do MMMM YYYY',
+ 'LLL' => 'Do MMMM, HH:mm [Uhr]',
+ 'LLLL' => 'dddd, Do MMMM YYYY, HH:mm [Uhr]',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php
new file mode 100644
index 0000000..594eb25
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_CH.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/gsw.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php
new file mode 100644
index 0000000..3581dcf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_FR.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/gsw.php', [
+ 'meridiem' => ['vorm.', 'nam.'],
+ 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LLL' => 'Do MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, Do MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php
new file mode 100644
index 0000000..3581dcf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gsw_LI.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/gsw.php', [
+ 'meridiem' => ['vorm.', 'nam.'],
+ 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LLL' => 'Do MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, Do MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gu.php b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php
new file mode 100644
index 0000000..8bc4311
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gu.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Kaushik Thanki
+ * - Josh Soref
+ */
+return [
+ 'year' => 'એક વર્ષ|:count વર્ષ',
+ 'y' => ':countવર્ષ|:countવર્ષો',
+ 'month' => 'એક મહિનો|:count મહિના',
+ 'm' => ':countમહિનો|:countમહિના',
+ 'week' => ':count અઠવાડિયું|:count અઠવાડિયા',
+ 'w' => ':countઅઠ.|:countઅઠ.',
+ 'day' => 'એક દિવસ|:count દિવસ',
+ 'd' => ':countદિ.|:countદિ.',
+ 'hour' => 'એક કલાક|:count કલાક',
+ 'h' => ':countક.|:countક.',
+ 'minute' => 'એક મિનિટ|:count મિનિટ',
+ 'min' => ':countમિ.|:countમિ.',
+ 'second' => 'અમુક પળો|:count સેકંડ',
+ 's' => ':countસે.|:countસે.',
+ 'ago' => ':time પેહલા',
+ 'from_now' => ':time મા',
+ 'after' => ':time પછી',
+ 'before' => ':time પહેલા',
+ 'diff_now' => 'હમણાં',
+ 'diff_today' => 'આજ',
+ 'diff_yesterday' => 'ગઇકાલે',
+ 'diff_tomorrow' => 'કાલે',
+ 'formats' => [
+ 'LT' => 'A h:mm વાગ્યે',
+ 'LTS' => 'A h:mm:ss વાગ્યે',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm વાગ્યે',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm વાગ્યે',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[આજ] LT',
+ 'nextDay' => '[કાલે] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[ગઇકાલે] LT',
+ 'lastWeek' => '[પાછલા] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'રાત';
+ }
+ if ($hour < 10) {
+ return 'સવાર';
+ }
+ if ($hour < 17) {
+ return 'બપોર';
+ }
+ if ($hour < 20) {
+ return 'સાંજ';
+ }
+
+ return 'રાત';
+ },
+ 'months' => ['જાન્યુઆરી', 'ફેબ્રુઆરી', 'માર્ચ', 'એપ્રિલ', 'મે', 'જૂન', 'જુલાઈ', 'ઑગસ્ટ', 'સપ્ટેમ્બર', 'ઑક્ટ્બર', 'નવેમ્બર', 'ડિસેમ્બર'],
+ 'months_short' => ['જાન્યુ.', 'ફેબ્રુ.', 'માર્ચ', 'એપ્રિ.', 'મે', 'જૂન', 'જુલા.', 'ઑગ.', 'સપ્ટે.', 'ઑક્ટ્.', 'નવે.', 'ડિસે.'],
+ 'weekdays' => ['રવિવાર', 'સોમવાર', 'મંગળવાર', 'બુધ્વાર', 'ગુરુવાર', 'શુક્રવાર', 'શનિવાર'],
+ 'weekdays_short' => ['રવિ', 'સોમ', 'મંગળ', 'બુધ્', 'ગુરુ', 'શુક્ર', 'શનિ'],
+ 'weekdays_min' => ['ર', 'સો', 'મં', 'બુ', 'ગુ', 'શુ', 'શ'],
+ 'list' => [', ', ' અને '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php
new file mode 100644
index 0000000..02654b1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gu_IN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/gu.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/guz.php b/vendor/nesbot/carbon/src/Carbon/Lang/guz.php
new file mode 100644
index 0000000..6230165
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/guz.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Ma', 'Mo'],
+ 'weekdays' => ['Chumapiri', 'Chumatato', 'Chumaine', 'Chumatano', 'Aramisi', 'Ichuma', 'Esabato'],
+ 'weekdays_short' => ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'],
+ 'weekdays_min' => ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'],
+ 'months' => ['Chanuari', 'Feburari', 'Machi', 'Apiriri', 'Mei', 'Juni', 'Chulai', 'Agosti', 'Septemba', 'Okitoba', 'Nobemba', 'Disemba'],
+ 'months_short' => ['Can', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Cul', 'Agt', 'Sep', 'Okt', 'Nob', 'Dis'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'month' => ':count omotunyi', // less reliable
+ 'm' => ':count omotunyi', // less reliable
+ 'a_month' => ':count omotunyi', // less reliable
+
+ 'week' => ':count isano naibere', // less reliable
+ 'w' => ':count isano naibere', // less reliable
+ 'a_week' => ':count isano naibere', // less reliable
+
+ 'second' => ':count ibere', // less reliable
+ 's' => ':count ibere', // less reliable
+ 'a_second' => ':count ibere', // less reliable
+
+ 'year' => ':count omwaka',
+ 'y' => ':count omwaka',
+ 'a_year' => ':count omwaka',
+
+ 'day' => ':count rituko',
+ 'd' => ':count rituko',
+ 'a_day' => ':count rituko',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gv.php b/vendor/nesbot/carbon/src/Carbon/Lang/gv.php
new file mode 100644
index 0000000..7c52b94
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gv.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/gv_GB.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php
new file mode 100644
index 0000000..6b1168f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/gv_GB.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Alastair McKinstry bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Jerrey-geuree', 'Toshiaght-arree', 'Mayrnt', 'Averil', 'Boaldyn', 'Mean-souree', 'Jerrey-souree', 'Luanistyn', 'Mean-fouyir', 'Jerrey-fouyir', 'Mee Houney', 'Mee ny Nollick'],
+ 'months_short' => ['J-guer', 'T-arree', 'Mayrnt', 'Avrril', 'Boaldyn', 'M-souree', 'J-souree', 'Luanistyn', 'M-fouyir', 'J-fouyir', 'M.Houney', 'M.Nollick'],
+ 'weekdays' => ['Jedoonee', 'Jelhein', 'Jemayrt', 'Jercean', 'Jerdein', 'Jeheiney', 'Jesarn'],
+ 'weekdays_short' => ['Jed', 'Jel', 'Jem', 'Jerc', 'Jerd', 'Jeh', 'Jes'],
+ 'weekdays_min' => ['Jed', 'Jel', 'Jem', 'Jerc', 'Jerd', 'Jeh', 'Jes'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count blein',
+ 'y' => ':count blein',
+ 'a_year' => ':count blein',
+
+ 'month' => ':count mee',
+ 'm' => ':count mee',
+ 'a_month' => ':count mee',
+
+ 'week' => ':count shiaghtin',
+ 'w' => ':count shiaghtin',
+ 'a_week' => ':count shiaghtin',
+
+ 'day' => ':count laa',
+ 'd' => ':count laa',
+ 'a_day' => ':count laa',
+
+ 'hour' => ':count oor',
+ 'h' => ':count oor',
+ 'a_hour' => ':count oor',
+
+ 'minute' => ':count feer veg',
+ 'min' => ':count feer veg',
+ 'a_minute' => ':count feer veg',
+
+ 'second' => ':count derrey',
+ 's' => ':count derrey',
+ 'a_second' => ':count derrey',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha.php
new file mode 100644
index 0000000..cd8e34d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - pablo@mandriva.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM, YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM, YYYY HH:mm',
+ ],
+ 'months' => ['Janairu', 'Faburairu', 'Maris', 'Afirilu', 'Mayu', 'Yuni', 'Yuli', 'Agusta', 'Satumba', 'Oktoba', 'Nuwamba', 'Disamba'],
+ 'months_short' => ['Jan', 'Fab', 'Mar', 'Afi', 'May', 'Yun', 'Yul', 'Agu', 'Sat', 'Okt', 'Nuw', 'Dis'],
+ 'weekdays' => ['Lahadi', 'Litini', 'Talata', 'Laraba', 'Alhamis', 'Jumaʼa', 'Asabar'],
+ 'weekdays_short' => ['Lah', 'Lit', 'Tal', 'Lar', 'Alh', 'Jum', 'Asa'],
+ 'weekdays_min' => ['Lh', 'Li', 'Ta', 'Lr', 'Al', 'Ju', 'As'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => 'shekara :count',
+ 'y' => 'shekara :count',
+ 'a_year' => 'shekara :count',
+
+ 'month' => ':count wátàa',
+ 'm' => ':count wátàa',
+ 'a_month' => ':count wátàa',
+
+ 'week' => ':count mako',
+ 'w' => ':count mako',
+ 'a_week' => ':count mako',
+
+ 'day' => ':count rana',
+ 'd' => ':count rana',
+ 'a_day' => ':count rana',
+
+ 'hour' => ':count áwàa',
+ 'h' => ':count áwàa',
+ 'a_hour' => ':count áwàa',
+
+ 'minute' => 'minti :count',
+ 'min' => 'minti :count',
+ 'a_minute' => 'minti :count',
+
+ 'second' => ':count ná bíyú',
+ 's' => ':count ná bíyú',
+ 'a_second' => ':count ná bíyú',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php
new file mode 100644
index 0000000..f9f99a7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha_GH.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ha.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php
new file mode 100644
index 0000000..f9f99a7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ha.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php
new file mode 100644
index 0000000..f9f99a7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ha_NG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ha.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hak.php b/vendor/nesbot/carbon/src/Carbon/Lang/hak.php
new file mode 100644
index 0000000..6c3260e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hak.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/hak_TW.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php
new file mode 100644
index 0000000..fe23986
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hak_TW.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY年MM月DD日',
+ ],
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => [' 1月', ' 2月', ' 3月', ' 4月', ' 5月', ' 6月', ' 7月', ' 8月', ' 9月', '10月', '11月', '12月'],
+ 'weekdays' => ['禮拜日', '禮拜一', '禮拜二', '禮拜三', '禮拜四', '禮拜五', '禮拜六'],
+ 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['上晝', '下晝'],
+
+ 'year' => ':count ngien11',
+ 'y' => ':count ngien11',
+ 'a_year' => ':count ngien11',
+
+ 'month' => ':count ngie̍t',
+ 'm' => ':count ngie̍t',
+ 'a_month' => ':count ngie̍t',
+
+ 'week' => ':count lî-pai',
+ 'w' => ':count lî-pai',
+ 'a_week' => ':count lî-pai',
+
+ 'day' => ':count ngit',
+ 'd' => ':count ngit',
+ 'a_day' => ':count ngit',
+
+ 'hour' => ':count sṳ̀',
+ 'h' => ':count sṳ̀',
+ 'a_hour' => ':count sṳ̀',
+
+ 'minute' => ':count fûn',
+ 'min' => ':count fûn',
+ 'a_minute' => ':count fûn',
+
+ 'second' => ':count miéu',
+ 's' => ':count miéu',
+ 'a_second' => ':count miéu',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/haw.php b/vendor/nesbot/carbon/src/Carbon/Lang/haw.php
new file mode 100644
index 0000000..cdd3686
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/haw.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'months' => ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei', 'Iune', 'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa', 'Kekemapa'],
+ 'months_short' => ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.', 'Iul.', 'ʻAu.', 'Kep.', 'ʻOk.', 'Now.', 'Kek.'],
+ 'weekdays' => ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu', 'Poʻahā', 'Poʻalima', 'Poʻaono'],
+ 'weekdays_short' => ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
+ 'weekdays_min' => ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm a',
+ ],
+
+ 'year' => ':count makahiki',
+ 'y' => ':count makahiki',
+ 'a_year' => ':count makahiki',
+
+ 'month' => ':count mahina',
+ 'm' => ':count mahina',
+ 'a_month' => ':count mahina',
+
+ 'week' => ':count pule',
+ 'w' => ':count pule',
+ 'a_week' => ':count pule',
+
+ 'day' => ':count lā',
+ 'd' => ':count lā',
+ 'a_day' => ':count lā',
+
+ 'hour' => ':count hola',
+ 'h' => ':count hola',
+ 'a_hour' => ':count hola',
+
+ 'minute' => ':count minuke',
+ 'min' => ':count minuke',
+ 'a_minute' => ':count minuke',
+
+ 'second' => ':count lua',
+ 's' => ':count lua',
+ 'a_second' => ':count lua',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/he.php b/vendor/nesbot/carbon/src/Carbon/Lang/he.php
new file mode 100644
index 0000000..c3fb3e9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/he.php
@@ -0,0 +1,86 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Daniel Cohen Gindi
+ * - JD Isaacks
+ * - Itai Nathaniel
+ * - GabMic
+ * - Yaakov Dahan (yakidahan)
+ */
+return [
+ 'year' => 'שנה|{2}שנתיים|:count שנים',
+ 'y' => 'שנה|:count שנ׳',
+ 'month' => 'חודש|{2}חודשיים|:count חודשים',
+ 'm' => 'חודש|:count חו׳',
+ 'week' => 'שבוע|{2}שבועיים|:count שבועות',
+ 'w' => 'שבוע|:count שב׳',
+ 'day' => 'יום|{2}יומיים|:count ימים',
+ 'd' => 'יום|:count ימ׳',
+ 'hour' => 'שעה|{2}שעתיים|:count שעות',
+ 'h' => 'שעה|:count שע׳',
+ 'minute' => 'דקה|{2}שתי דקות|:count דקות',
+ 'min' => 'דקה|:count דק׳',
+ 'second' => 'שנייה|:count שניות',
+ 'a_second' => 'כמה שניות|:count שניות',
+ 's' => 'שניה|:count שנ׳',
+ 'ago' => 'לפני :time',
+ 'from_now' => 'בעוד :time מעכשיו',
+ 'after' => 'אחרי :time',
+ 'before' => 'לפני :time',
+ 'diff_now' => 'עכשיו',
+ 'diff_today' => 'היום',
+ 'diff_today_regexp' => 'היום(?:\\s+ב־)?',
+ 'diff_yesterday' => 'אתמול',
+ 'diff_yesterday_regexp' => 'אתמול(?:\\s+ב־)?',
+ 'diff_tomorrow' => 'מחר',
+ 'diff_tomorrow_regexp' => 'מחר(?:\\s+ב־)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D [ב]MMMM YYYY',
+ 'LLL' => 'D [ב]MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D [ב]MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[היום ב־]LT',
+ 'nextDay' => '[מחר ב־]LT',
+ 'nextWeek' => 'dddd [בשעה] LT',
+ 'lastDay' => '[אתמול ב־]LT',
+ 'lastWeek' => '[ביום] dddd [האחרון בשעה] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour, $minute, $isLower) {
+ if ($hour < 5) {
+ return 'לפנות בוקר';
+ }
+ if ($hour < 10) {
+ return 'בבוקר';
+ }
+ if ($hour < 12) {
+ return $isLower ? 'לפנה"צ' : 'לפני הצהריים';
+ }
+ if ($hour < 18) {
+ return $isLower ? 'אחה"צ' : 'אחרי הצהריים';
+ }
+
+ return 'בערב';
+ },
+ 'months' => ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'],
+ 'months_short' => ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳', 'דצמ׳'],
+ 'weekdays' => ['ראשון', 'שני', 'שלישי', 'רביעי', 'חמישי', 'שישי', 'שבת'],
+ 'weekdays_short' => ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
+ 'weekdays_min' => ['א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ש'],
+ 'list' => [', ', ' ו -'],
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php b/vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php
new file mode 100644
index 0000000..14fab3e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/he_IL.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/he.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hi.php b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php
new file mode 100644
index 0000000..70c57a2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hi.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - abhimanyu003
+ * - Josh Soref
+ * - JD Isaacks
+ */
+return [
+ 'year' => 'एक वर्ष|:count वर्ष',
+ 'y' => '1 वर्ष|:count वर्षों',
+ 'month' => 'एक महीने|:count महीने',
+ 'm' => '1 माह|:count महीने',
+ 'week' => '1 सप्ताह|:count सप्ताह',
+ 'w' => '1 सप्ताह|:count सप्ताह',
+ 'day' => 'एक दिन|:count दिन',
+ 'd' => '1 दिन|:count दिनों',
+ 'hour' => 'एक घंटा|:count घंटे',
+ 'h' => '1 घंटा|:count घंटे',
+ 'minute' => 'एक मिनट|:count मिनट',
+ 'min' => '1 मिनट|:count मिनटों',
+ 'second' => 'कुछ ही क्षण|:count सेकंड',
+ 's' => '1 सेकंड|:count सेकंड',
+ 'ago' => ':time पहले',
+ 'from_now' => ':time में',
+ 'after' => ':time के बाद',
+ 'before' => ':time के पहले',
+ 'diff_now' => 'अब',
+ 'diff_today' => 'आज',
+ 'diff_yesterday' => 'कल',
+ 'diff_tomorrow' => 'कल',
+ 'formats' => [
+ 'LT' => 'A h:mm बजे',
+ 'LTS' => 'A h:mm:ss बजे',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm बजे',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm बजे',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[आज] LT',
+ 'nextDay' => '[कल] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[कल] LT',
+ 'lastWeek' => '[पिछले] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'रात';
+ }
+ if ($hour < 10) {
+ return 'सुबह';
+ }
+ if ($hour < 17) {
+ return 'दोपहर';
+ }
+ if ($hour < 20) {
+ return 'शाम';
+ }
+
+ return 'रात';
+ },
+ 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'months_short' => ['जन.', 'फ़र.', 'मार्च', 'अप्रै.', 'मई', 'जून', 'जुल.', 'अग.', 'सित.', 'अक्टू.', 'नव.', 'दिस.'],
+ 'weekdays' => ['रविवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'गुरूवार', 'शुक्रवार', 'शनिवार'],
+ 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'गुरू', 'शुक्र', 'शनि'],
+ 'weekdays_min' => ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'],
+ 'list' => [', ', ' और '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php
new file mode 100644
index 0000000..749dd97
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hi_IN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/hi.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hif.php b/vendor/nesbot/carbon/src/Carbon/Lang/hif.php
new file mode 100644
index 0000000..65791dd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hif.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/hif_FJ.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php
new file mode 100644
index 0000000..30ad5e7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hif_FJ.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'dddd DD MMM YYYY',
+ ],
+ 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ 'weekdays' => ['Ravivar', 'Somvar', 'Mangalvar', 'Budhvar', 'Guruvar', 'Shukravar', 'Shanivar'],
+ 'weekdays_short' => ['Ravi', 'Som', 'Mangal', 'Budh', 'Guru', 'Shukra', 'Shani'],
+ 'weekdays_min' => ['Ravi', 'Som', 'Mangal', 'Budh', 'Guru', 'Shukra', 'Shani'],
+ 'meridiem' => ['Purvahan', 'Aparaahna'],
+
+ 'hour' => ':count minit', // less reliable
+ 'h' => ':count minit', // less reliable
+ 'a_hour' => ':count minit', // less reliable
+
+ 'year' => ':count saal',
+ 'y' => ':count saal',
+ 'a_year' => ':count saal',
+
+ 'month' => ':count Mahina',
+ 'm' => ':count Mahina',
+ 'a_month' => ':count Mahina',
+
+ 'week' => ':count Hafta',
+ 'w' => ':count Hafta',
+ 'a_week' => ':count Hafta',
+
+ 'day' => ':count Din',
+ 'd' => ':count Din',
+ 'a_day' => ':count Din',
+
+ 'minute' => ':count Minit',
+ 'min' => ':count Minit',
+ 'a_minute' => ':count Minit',
+
+ 'second' => ':count Second',
+ 's' => ':count Second',
+ 'a_second' => ':count Second',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hne.php b/vendor/nesbot/carbon/src/Carbon/Lang/hne.php
new file mode 100644
index 0000000..4bcb05c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hne.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/hne_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php
new file mode 100644
index 0000000..a5ca758
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hne_IN.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat, Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अपरेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितमबर', 'अकटूबर', 'नवमबर', 'दिसमबर'],
+ 'months_short' => ['जन', 'फर', 'मार्च', 'अप', 'मई', 'जून', 'जुला', 'अग', 'सित', 'अकटू', 'नव', 'दिस'],
+ 'weekdays' => ['इतवार', 'सोमवार', 'मंगलवार', 'बुधवार', 'बिरसपत', 'सुकरवार', 'सनिवार'],
+ 'weekdays_short' => ['इत', 'सोम', 'मंग', 'बुध', 'बिर', 'सुक', 'सनि'],
+ 'weekdays_min' => ['इत', 'सोम', 'मंग', 'बुध', 'बिर', 'सुक', 'सनि'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['बिहिनियाँ', 'मंझनियाँ'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php
new file mode 100644
index 0000000..cfd85fd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr.php
@@ -0,0 +1,111 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - Tim Fish
+ * - shaishavgandhi05
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - tomhorvat
+ * - Josh Soref
+ * - François B
+ * - shaishavgandhi05
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - tomhorvat
+ * - Stjepan Majdak
+ * - Vanja Retkovac (vr00)
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count godinu|:count godine|:count godina',
+ 'y' => ':count god.|:count god.|:count god.',
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'm' => ':count mj.|:count mj.|:count mj.',
+ 'week' => ':count tjedan|:count tjedna|:count tjedana',
+ 'w' => ':count tj.|:count tj.|:count tj.',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count d.|:count d.|:count d.',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count sat|:count sata|:count sati',
+ 'minute' => ':count minutu|:count minute|:count minuta',
+ 'min' => ':count min.|:count min.|:count min.',
+ 'second' => ':count sekundu|:count sekunde|:count sekundi',
+ 'a_second' => 'nekoliko sekundi|:count sekunde|:count sekundi',
+ 's' => ':count sek.|:count sek.|:count sek.',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => ':time poslije',
+ 'before' => ':time prije',
+ 'diff_now' => 'sad',
+ 'diff_today' => 'danas',
+ 'diff_today_regexp' => 'danas(?:\\s+u)?',
+ 'diff_yesterday' => 'jučer',
+ 'diff_yesterday_regexp' => 'jučer(?:\\s+u)?',
+ 'diff_tomorrow' => 'sutra',
+ 'diff_tomorrow_regexp' => 'sutra(?:\\s+u)?',
+ 'diff_before_yesterday' => 'prekjučer',
+ 'diff_after_tomorrow' => 'prekosutra',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'D. M. YYYY.',
+ 'LL' => 'D. MMMM YYYY.',
+ 'LLL' => 'D. MMMM YYYY. H:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY. H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[danas u] LT',
+ 'nextDay' => '[sutra u] LT',
+ 'nextWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[u] [nedjelju] [u] LT';
+ case 3:
+ return '[u] [srijedu] [u] LT';
+ case 6:
+ return '[u] [subotu] [u] LT';
+ default:
+ return '[u] dddd [u] LT';
+ }
+ },
+ 'lastDay' => '[jučer u] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ case 3:
+ return '[prošlu] dddd [u] LT';
+ case 6:
+ return '[prošle] [subote] [u] LT';
+ default:
+ return '[prošli] dddd [u] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja', 'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'],
+ 'months_standalone' => ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj', 'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'],
+ 'months_short' => ['sij.', 'velj.', 'ožu.', 'tra.', 'svi.', 'lip.', 'srp.', 'kol.', 'ruj.', 'lis.', 'stu.', 'pro.'],
+ 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['nedjelju', 'ponedjeljak', 'utorak', 'srijedu', 'četvrtak', 'petak', 'subotu'],
+ 'weekdays_standalone' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'],
+ 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sri.', 'čet.', 'pet.', 'sub.'],
+ 'weekdays_min' => ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' i '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php
new file mode 100644
index 0000000..7763a45
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr_BA.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - DarkoDevelop
+ */
+return array_replace_recursive(require __DIR__.'/hr.php', [
+ 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'],
+ 'weekdays_short' => ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
+ 'weekdays_min' => ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
+ 'months' => ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja', 'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'],
+ 'months_short' => ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj', 'lis', 'stu', 'pro'],
+ 'months_standalone' => ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj', 'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D. M. yy.',
+ 'LL' => 'D. MMM YYYY.',
+ 'LLL' => 'D. MMMM YYYY. HH:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY. HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php b/vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php
new file mode 100644
index 0000000..db74d8c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hr_HR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/hr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hsb.php b/vendor/nesbot/carbon/src/Carbon/Lang/hsb.php
new file mode 100644
index 0000000..3537b8b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hsb.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/hsb_DE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php
new file mode 100644
index 0000000..6ba2271
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hsb_DE.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Information from Michael Wolf Andrzej Krzysztofowicz ankry@mif.pg.gda.pl
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'DD. MMMM YYYY',
+ 'LLL' => 'DD. MMMM, HH:mm [hodź.]',
+ 'LLLL' => 'dddd, DD. MMMM YYYY, HH:mm [hodź.]',
+ ],
+ 'months' => ['januara', 'februara', 'měrca', 'apryla', 'meje', 'junija', 'julija', 'awgusta', 'septembra', 'oktobra', 'nowembra', 'decembra'],
+ 'months_short' => ['Jan', 'Feb', 'Měr', 'Apr', 'Mej', 'Jun', 'Jul', 'Awg', 'Sep', 'Okt', 'Now', 'Dec'],
+ 'weekdays' => ['Njedźela', 'Póndźela', 'Wutora', 'Srjeda', 'Štvórtk', 'Pjatk', 'Sobota'],
+ 'weekdays_short' => ['Nj', 'Pó', 'Wu', 'Sr', 'Št', 'Pj', 'So'],
+ 'weekdays_min' => ['Nj', 'Pó', 'Wu', 'Sr', 'Št', 'Pj', 'So'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count lěto',
+ 'y' => ':count lěto',
+ 'a_year' => ':count lěto',
+
+ 'month' => ':count měsac',
+ 'm' => ':count měsac',
+ 'a_month' => ':count měsac',
+
+ 'week' => ':count tydźeń',
+ 'w' => ':count tydźeń',
+ 'a_week' => ':count tydźeń',
+
+ 'day' => ':count dźeń',
+ 'd' => ':count dźeń',
+ 'a_day' => ':count dźeń',
+
+ 'hour' => ':count hodźina',
+ 'h' => ':count hodźina',
+ 'a_hour' => ':count hodźina',
+
+ 'minute' => ':count chwila',
+ 'min' => ':count chwila',
+ 'a_minute' => ':count chwila',
+
+ 'second' => ':count druhi',
+ 's' => ':count druhi',
+ 'a_second' => ':count druhi',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ht.php b/vendor/nesbot/carbon/src/Carbon/Lang/ht.php
new file mode 100644
index 0000000..ebd12ad
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ht.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ht_HT.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php b/vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php
new file mode 100644
index 0000000..139b813
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ht_HT.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Sugar Labs // OLPC sugarlabs.org libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['janvye', 'fevriye', 'mas', 'avril', 'me', 'jen', 'jiyè', 'out', 'septanm', 'oktòb', 'novanm', 'desanm'],
+ 'months_short' => ['jan', 'fev', 'mas', 'avr', 'me', 'jen', 'jiy', 'out', 'sep', 'okt', 'nov', 'des'],
+ 'weekdays' => ['dimanch', 'lendi', 'madi', 'mèkredi', 'jedi', 'vandredi', 'samdi'],
+ 'weekdays_short' => ['dim', 'len', 'mad', 'mèk', 'jed', 'van', 'sam'],
+ 'weekdays_min' => ['dim', 'len', 'mad', 'mèk', 'jed', 'van', 'sam'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count lane',
+ 'y' => ':count lane',
+ 'a_year' => ':count lane',
+
+ 'month' => 'mwa :count',
+ 'm' => 'mwa :count',
+ 'a_month' => 'mwa :count',
+
+ 'week' => 'semèn :count',
+ 'w' => 'semèn :count',
+ 'a_week' => 'semèn :count',
+
+ 'day' => ':count jou',
+ 'd' => ':count jou',
+ 'a_day' => ':count jou',
+
+ 'hour' => ':count lè',
+ 'h' => ':count lè',
+ 'a_hour' => ':count lè',
+
+ 'minute' => ':count minit',
+ 'min' => ':count minit',
+ 'a_minute' => ':count minit',
+
+ 'second' => ':count segonn',
+ 's' => ':count segonn',
+ 'a_second' => ':count segonn',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hu.php b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php
new file mode 100644
index 0000000..b2d2ac1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hu.php
@@ -0,0 +1,118 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Adam Brunner
+ * - Brett Johnson
+ * - balping
+ */
+
+use Carbon\CarbonInterface;
+
+$huWeekEndings = ['vasárnap', 'hétfőn', 'kedden', 'szerdán', 'csütörtökön', 'pénteken', 'szombaton'];
+
+return [
+ 'year' => ':count év',
+ 'y' => ':count év',
+ 'month' => ':count hónap',
+ 'm' => ':count hónap',
+ 'week' => ':count hét',
+ 'w' => ':count hét',
+ 'day' => ':count nap',
+ 'd' => ':count nap',
+ 'hour' => ':count óra',
+ 'h' => ':count óra',
+ 'minute' => ':count perc',
+ 'min' => ':count perc',
+ 'second' => ':count másodperc',
+ 's' => ':count másodperc',
+ 'ago' => ':time',
+ 'from_now' => ':time múlva',
+ 'after' => ':time később',
+ 'before' => ':time korábban',
+ 'year_ago' => ':count éve',
+ 'y_ago' => ':count éve',
+ 'month_ago' => ':count hónapja',
+ 'm_ago' => ':count hónapja',
+ 'week_ago' => ':count hete',
+ 'w_ago' => ':count hete',
+ 'day_ago' => ':count napja',
+ 'd_ago' => ':count napja',
+ 'hour_ago' => ':count órája',
+ 'h_ago' => ':count órája',
+ 'minute_ago' => ':count perce',
+ 'min_ago' => ':count perce',
+ 'second_ago' => ':count másodperce',
+ 's_ago' => ':count másodperce',
+ 'year_after' => ':count évvel',
+ 'y_after' => ':count évvel',
+ 'month_after' => ':count hónappal',
+ 'm_after' => ':count hónappal',
+ 'week_after' => ':count héttel',
+ 'w_after' => ':count héttel',
+ 'day_after' => ':count nappal',
+ 'd_after' => ':count nappal',
+ 'hour_after' => ':count órával',
+ 'h_after' => ':count órával',
+ 'minute_after' => ':count perccel',
+ 'min_after' => ':count perccel',
+ 'second_after' => ':count másodperccel',
+ 's_after' => ':count másodperccel',
+ 'year_before' => ':count évvel',
+ 'y_before' => ':count évvel',
+ 'month_before' => ':count hónappal',
+ 'm_before' => ':count hónappal',
+ 'week_before' => ':count héttel',
+ 'w_before' => ':count héttel',
+ 'day_before' => ':count nappal',
+ 'd_before' => ':count nappal',
+ 'hour_before' => ':count órával',
+ 'h_before' => ':count órával',
+ 'minute_before' => ':count perccel',
+ 'min_before' => ':count perccel',
+ 'second_before' => ':count másodperccel',
+ 's_before' => ':count másodperccel',
+ 'months' => ['január', 'február', 'március', 'április', 'május', 'június', 'július', 'augusztus', 'szeptember', 'október', 'november', 'december'],
+ 'months_short' => ['jan.', 'feb.', 'márc.', 'ápr.', 'máj.', 'jún.', 'júl.', 'aug.', 'szept.', 'okt.', 'nov.', 'dec.'],
+ 'weekdays' => ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat'],
+ 'weekdays_short' => ['vas', 'hét', 'kedd', 'sze', 'csüt', 'pén', 'szo'],
+ 'weekdays_min' => ['v', 'h', 'k', 'sze', 'cs', 'p', 'sz'],
+ 'ordinal' => ':number.',
+ 'diff_now' => 'most',
+ 'diff_today' => 'ma',
+ 'diff_yesterday' => 'tegnap',
+ 'diff_tomorrow' => 'holnap',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'YYYY.MM.DD.',
+ 'LL' => 'YYYY. MMMM D.',
+ 'LLL' => 'YYYY. MMMM D. H:mm',
+ 'LLLL' => 'YYYY. MMMM D., dddd H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ma] LT[-kor]',
+ 'nextDay' => '[holnap] LT[-kor]',
+ 'nextWeek' => function (CarbonInterface $date) use ($huWeekEndings) {
+ return '['.$huWeekEndings[$date->dayOfWeek].'] LT[-kor]';
+ },
+ 'lastDay' => '[tegnap] LT[-kor]',
+ 'lastWeek' => function (CarbonInterface $date) use ($huWeekEndings) {
+ return '[múlt '.$huWeekEndings[$date->dayOfWeek].'] LT[-kor]';
+ },
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['DE', 'DU'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' és '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php b/vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php
new file mode 100644
index 0000000..b1c4854
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hu_HU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/hu.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hy.php b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php
new file mode 100644
index 0000000..8b12994
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hy.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - mhamlet
+ */
+return [
+ 'year' => ':count տարի',
+ 'a_year' => 'տարի|:count տարի',
+ 'y' => ':countտ',
+ 'month' => ':count ամիս',
+ 'a_month' => 'ամիս|:count ամիս',
+ 'm' => ':countամ',
+ 'week' => ':count շաբաթ',
+ 'a_week' => 'շաբաթ|:count շաբաթ',
+ 'w' => ':countշ',
+ 'day' => ':count օր',
+ 'a_day' => 'օր|:count օր',
+ 'd' => ':countօր',
+ 'hour' => ':count ժամ',
+ 'a_hour' => 'ժամ|:count ժամ',
+ 'h' => ':countժ',
+ 'minute' => ':count րոպե',
+ 'a_minute' => 'րոպե|:count րոպե',
+ 'min' => ':countր',
+ 'second' => ':count վայրկյան',
+ 'a_second' => 'մի քանի վայրկյան|:count վայրկյան',
+ 's' => ':countվրկ',
+ 'ago' => ':time առաջ',
+ 'from_now' => ':timeից',
+ 'after' => ':time հետո',
+ 'before' => ':time առաջ',
+ 'diff_now' => 'հիմա',
+ 'diff_today' => 'այսօր',
+ 'diff_yesterday' => 'երեկ',
+ 'diff_tomorrow' => 'վաղը',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY թ.',
+ 'LLL' => 'D MMMM YYYY թ., HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY թ., HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[այսօր] LT',
+ 'nextDay' => '[վաղը] LT',
+ 'nextWeek' => 'dddd [օրը ժամը] LT',
+ 'lastDay' => '[երեկ] LT',
+ 'lastWeek' => '[անցած] dddd [օրը ժամը] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'DDD':
+ case 'w':
+ case 'W':
+ case 'DDDo':
+ return $number.($number === 1 ? '-ին' : '-րդ');
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'գիշերվա';
+ }
+ if ($hour < 12) {
+ return 'առավոտվա';
+ }
+ if ($hour < 17) {
+ return 'ցերեկվա';
+ }
+
+ return 'երեկոյան';
+ },
+ 'months' => ['հունվարի', 'փետրվարի', 'մարտի', 'ապրիլի', 'մայիսի', 'հունիսի', 'հուլիսի', 'օգոստոսի', 'սեպտեմբերի', 'հոկտեմբերի', 'նոյեմբերի', 'դեկտեմբերի'],
+ 'months_standalone' => ['հունվար', 'փետրվար', 'մարտ', 'ապրիլ', 'մայիս', 'հունիս', 'հուլիս', 'օգոստոս', 'սեպտեմբեր', 'հոկտեմբեր', 'նոյեմբեր', 'դեկտեմբեր'],
+ 'months_short' => ['հնվ', 'փտր', 'մրտ', 'ապր', 'մյս', 'հնս', 'հլս', 'օգս', 'սպտ', 'հկտ', 'նմբ', 'դկտ'],
+ 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['կիրակի', 'երկուշաբթի', 'երեքշաբթի', 'չորեքշաբթի', 'հինգշաբթի', 'ուրբաթ', 'շաբաթ'],
+ 'weekdays_short' => ['կրկ', 'երկ', 'երք', 'չրք', 'հնգ', 'ուրբ', 'շբթ'],
+ 'weekdays_min' => ['կրկ', 'երկ', 'երք', 'չրք', 'հնգ', 'ուրբ', 'շբթ'],
+ 'list' => [', ', ' եւ '],
+ 'first_day_of_week' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php b/vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php
new file mode 100644
index 0000000..4587df5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/hy_AM.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - Tim Fish
+ * - Serhan Apaydın
+ * - JD Isaacks
+ */
+return array_replace_recursive(require __DIR__.'/hy.php', [
+ 'from_now' => ':time հետո',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/i18n.php b/vendor/nesbot/carbon/src/Carbon/Lang/i18n.php
new file mode 100644
index 0000000..e65449b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/i18n.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+ 'months' => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+ 'months_short' => ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+ 'weekdays' => ['1', '2', '3', '4', '5', '6', '7'],
+ 'weekdays_short' => ['1', '2', '3', '4', '5', '6', '7'],
+ 'weekdays_min' => ['1', '2', '3', '4', '5', '6', '7'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ia.php b/vendor/nesbot/carbon/src/Carbon/Lang/ia.php
new file mode 100644
index 0000000..0a0d5e6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ia.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ia_FR.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php
new file mode 100644
index 0000000..de4b2fa
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ia_FR.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Fedora Project Nik Kalach nikka@fedoraproject.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['januario', 'februario', 'martio', 'april', 'maio', 'junio', 'julio', 'augusto', 'septembre', 'octobre', 'novembre', 'decembre'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'],
+ 'weekdays' => ['dominica', 'lunedi', 'martedi', 'mercuridi', 'jovedi', 'venerdi', 'sabbato'],
+ 'weekdays_short' => ['dom', 'lun', 'mar', 'mer', 'jov', 'ven', 'sab'],
+ 'weekdays_min' => ['dom', 'lun', 'mar', 'mer', 'jov', 'ven', 'sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => 'anno :count',
+ 'y' => 'anno :count',
+ 'a_year' => 'anno :count',
+
+ 'month' => ':count mense',
+ 'm' => ':count mense',
+ 'a_month' => ':count mense',
+
+ 'week' => ':count septimana',
+ 'w' => ':count septimana',
+ 'a_week' => ':count septimana',
+
+ 'day' => ':count die',
+ 'd' => ':count die',
+ 'a_day' => ':count die',
+
+ 'hour' => ':count hora',
+ 'h' => ':count hora',
+ 'a_hour' => ':count hora',
+
+ 'minute' => ':count minuscule',
+ 'min' => ':count minuscule',
+ 'a_minute' => ':count minuscule',
+
+ 'second' => ':count secunda',
+ 's' => ':count secunda',
+ 'a_second' => ':count secunda',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/id.php b/vendor/nesbot/carbon/src/Carbon/Lang/id.php
new file mode 100644
index 0000000..afaf78f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/id.php
@@ -0,0 +1,92 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - du
+ * - JD Isaacks
+ * - Nafies Luthfi
+ * - Raymundus Jati Primanda (mundusjp)
+ * - diankur313
+ * - a-wip0
+ */
+return [
+ 'year' => ':count tahun',
+ 'a_year' => '{1}setahun|]1,Inf[:count tahun',
+ 'y' => ':countthn',
+ 'month' => ':count bulan',
+ 'a_month' => '{1}sebulan|]1,Inf[:count bulan',
+ 'm' => ':countbln',
+ 'week' => ':count minggu',
+ 'a_week' => '{1}seminggu|]1,Inf[:count minggu',
+ 'w' => ':countmgg',
+ 'day' => ':count hari',
+ 'a_day' => '{1}sehari|]1,Inf[:count hari',
+ 'd' => ':counthr',
+ 'hour' => ':count jam',
+ 'a_hour' => '{1}sejam|]1,Inf[:count jam',
+ 'h' => ':countj',
+ 'minute' => ':count menit',
+ 'a_minute' => '{1}semenit|]1,Inf[:count menit',
+ 'min' => ':countmnt',
+ 'second' => ':count detik',
+ 'a_second' => '{1}beberapa detik|]1,Inf[:count detik',
+ 's' => ':countdt',
+ 'ago' => ':time yang lalu',
+ 'from_now' => ':time dari sekarang',
+ 'after' => ':time setelahnya',
+ 'before' => ':time sebelumnya',
+ 'diff_now' => 'sekarang',
+ 'diff_today' => 'Hari',
+ 'diff_today_regexp' => 'Hari(?:\\s+ini)?(?:\\s+pukul)?',
+ 'diff_yesterday' => 'kemarin',
+ 'diff_yesterday_regexp' => 'Kemarin(?:\\s+pukul)?',
+ 'diff_tomorrow' => 'besok',
+ 'diff_tomorrow_regexp' => 'Besok(?:\\s+pukul)?',
+ 'formats' => [
+ 'LT' => 'HH.mm',
+ 'LTS' => 'HH.mm.ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY [pukul] HH.mm',
+ 'LLLL' => 'dddd, D MMMM YYYY [pukul] HH.mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Hari ini pukul] LT',
+ 'nextDay' => '[Besok pukul] LT',
+ 'nextWeek' => 'dddd [pukul] LT',
+ 'lastDay' => '[Kemarin pukul] LT',
+ 'lastWeek' => 'dddd [lalu pukul] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 11) {
+ return 'pagi';
+ }
+ if ($hour < 15) {
+ return 'siang';
+ }
+ if ($hour < 19) {
+ return 'sore';
+ }
+
+ return 'malam';
+ },
+ 'months' => ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'],
+ 'weekdays_short' => ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
+ 'weekdays_min' => ['Mg', 'Sn', 'Sl', 'Rb', 'Km', 'Jm', 'Sb'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' dan '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php b/vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php
new file mode 100644
index 0000000..d5953a1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/id_ID.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/id.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ig.php b/vendor/nesbot/carbon/src/Carbon/Lang/ig.php
new file mode 100644
index 0000000..de51e9c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ig.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ig_NG.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php
new file mode 100644
index 0000000..0034e35
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ig_NG.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - pablo@mandriva.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Jenụwarị', 'Febrụwarị', 'Maachị', 'Eprel', 'Mee', 'Juun', 'Julaị', 'Ọgọọst', 'Septemba', 'Ọktoba', 'Novemba', 'Disemba'],
+ 'months_short' => ['Jen', 'Feb', 'Maa', 'Epr', 'Mee', 'Juu', 'Jul', 'Ọgọ', 'Sep', 'Ọkt', 'Nov', 'Dis'],
+ 'weekdays' => ['sọnde', 'mọnde', 'tuzde', 'wenzde', 'tọsde', 'fraịde', 'satọde'],
+ 'weekdays_short' => ['sọn', 'mọn', 'tuz', 'wen', 'tọs', 'fra', 'sat'],
+ 'weekdays_min' => ['sọn', 'mọn', 'tuz', 'wen', 'tọs', 'fra', 'sat'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => 'afo :count',
+ 'y' => 'afo :count',
+ 'a_year' => 'afo :count',
+
+ 'month' => 'önwa :count',
+ 'm' => 'önwa :count',
+ 'a_month' => 'önwa :count',
+
+ 'week' => 'izu :count',
+ 'w' => 'izu :count',
+ 'a_week' => 'izu :count',
+
+ 'day' => 'ụbọchị :count',
+ 'd' => 'ụbọchị :count',
+ 'a_day' => 'ụbọchị :count',
+
+ 'hour' => 'awa :count',
+ 'h' => 'awa :count',
+ 'a_hour' => 'awa :count',
+
+ 'minute' => 'minit :count',
+ 'min' => 'minit :count',
+ 'a_minute' => 'minit :count',
+
+ 'second' => 'sekọnd :count',
+ 's' => 'sekọnd :count',
+ 'a_second' => 'sekọnd :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ii.php b/vendor/nesbot/carbon/src/Carbon/Lang/ii.php
new file mode 100644
index 0000000..a4246c2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ii.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['ꎸꄑ', 'ꁯꋒ'],
+ 'weekdays' => ['ꑭꆏꑍ', 'ꆏꊂꋍ', 'ꆏꊂꑍ', 'ꆏꊂꌕ', 'ꆏꊂꇖ', 'ꆏꊂꉬ', 'ꆏꊂꃘ'],
+ 'weekdays_short' => ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ', 'ꆏꉬ', 'ꆏꃘ'],
+ 'weekdays_min' => ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ', 'ꆏꉬ', 'ꆏꃘ'],
+ 'months' => null,
+ 'months_short' => ['ꋍꆪ', 'ꑍꆪ', 'ꌕꆪ', 'ꇖꆪ', 'ꉬꆪ', 'ꃘꆪ', 'ꏃꆪ', 'ꉆꆪ', 'ꈬꆪ', 'ꊰꆪ', 'ꊰꊪꆪ', 'ꊰꑋꆪ'],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D h:mm a',
+ 'LLLL' => 'YYYY MMMM D, dddd h:mm a',
+ ],
+
+ 'year' => ':count ꒉ', // less reliable
+ 'y' => ':count ꒉ', // less reliable
+ 'a_year' => ':count ꒉ', // less reliable
+
+ 'month' => ':count ꆪ',
+ 'm' => ':count ꆪ',
+ 'a_month' => ':count ꆪ',
+
+ 'week' => ':count ꏃ', // less reliable
+ 'w' => ':count ꏃ', // less reliable
+ 'a_week' => ':count ꏃ', // less reliable
+
+ 'day' => ':count ꏜ', // less reliable
+ 'd' => ':count ꏜ', // less reliable
+ 'a_day' => ':count ꏜ', // less reliable
+
+ 'hour' => ':count ꄮꈉ',
+ 'h' => ':count ꄮꈉ',
+ 'a_hour' => ':count ꄮꈉ',
+
+ 'minute' => ':count ꀄꊭ', // less reliable
+ 'min' => ':count ꀄꊭ', // less reliable
+ 'a_minute' => ':count ꀄꊭ', // less reliable
+
+ 'second' => ':count ꇅ', // less reliable
+ 's' => ':count ꇅ', // less reliable
+ 'a_second' => ':count ꇅ', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ik.php b/vendor/nesbot/carbon/src/Carbon/Lang/ik.php
new file mode 100644
index 0000000..7a13aa2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ik.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ik_CA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php
new file mode 100644
index 0000000..bb2a109
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ik_CA.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - pablo@mandriva.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Siqiññaatchiaq', 'Siqiññaasrugruk', 'Paniqsiqsiivik', 'Qilġich Tatqiat', 'Suppivik', 'Iġñivik', 'Itchavik', 'Tiññivik', 'Amiġaiqsivik', 'Sikkuvik', 'Nippivik', 'Siqiñġiḷaq'],
+ 'months_short' => ['Sñt', 'Sñs', 'Pan', 'Qil', 'Sup', 'Iġñ', 'Itc', 'Tiñ', 'Ami', 'Sik', 'Nip', 'Siq'],
+ 'weekdays' => ['Minġuiqsioiq', 'Savałłiq', 'Ilaqtchiioiq', 'Qitchiioiq', 'Sisamiioiq', 'Tallimmiioiq', 'Maqinġuoiq'],
+ 'weekdays_short' => ['Min', 'Sav', 'Ila', 'Qit', 'Sis', 'Tal', 'Maq'],
+ 'weekdays_min' => ['Min', 'Sav', 'Ila', 'Qit', 'Sis', 'Tal', 'Maq'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count ukiuq',
+ 'y' => ':count ukiuq',
+ 'a_year' => ':count ukiuq',
+
+ 'month' => ':count Tatqiat',
+ 'm' => ':count Tatqiat',
+ 'a_month' => ':count Tatqiat',
+
+ 'week' => ':count tatqiat', // less reliable
+ 'w' => ':count tatqiat', // less reliable
+ 'a_week' => ':count tatqiat', // less reliable
+
+ 'day' => ':count siqiñiq', // less reliable
+ 'd' => ':count siqiñiq', // less reliable
+ 'a_day' => ':count siqiñiq', // less reliable
+
+ 'hour' => ':count Siḷa', // less reliable
+ 'h' => ':count Siḷa', // less reliable
+ 'a_hour' => ':count Siḷa', // less reliable
+
+ 'second' => ':count iġñiq', // less reliable
+ 's' => ':count iġñiq', // less reliable
+ 'a_second' => ':count iġñiq', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/in.php b/vendor/nesbot/carbon/src/Carbon/Lang/in.php
new file mode 100644
index 0000000..d5953a1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/in.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/id.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/is.php b/vendor/nesbot/carbon/src/Carbon/Lang/is.php
new file mode 100644
index 0000000..9990168
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/is.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kristján Ingi Geirsson
+ */
+return [
+ 'year' => '1 ár|:count ár',
+ 'y' => '1 ár|:count ár',
+ 'month' => '1 mánuður|:count mánuðir',
+ 'm' => '1 mánuður|:count mánuðir',
+ 'week' => '1 vika|:count vikur',
+ 'w' => '1 vika|:count vikur',
+ 'day' => '1 dagur|:count dagar',
+ 'd' => '1 dagur|:count dagar',
+ 'hour' => '1 klukkutími|:count klukkutímar',
+ 'h' => '1 klukkutími|:count klukkutímar',
+ 'minute' => '1 mínúta|:count mínútur',
+ 'min' => '1 mínúta|:count mínútur',
+ 'second' => '1 sekúnda|:count sekúndur',
+ 's' => '1 sekúnda|:count sekúndur',
+ 'ago' => ':time síðan',
+ 'from_now' => ':time síðan',
+ 'after' => ':time eftir',
+ 'before' => ':time fyrir',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' og '],
+ 'meridiem' => ['fh', 'eh'],
+ 'diff_now' => 'núna',
+ 'diff_yesterday' => 'í gær',
+ 'diff_tomorrow' => 'á morgun',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM [kl.] HH:mm',
+ 'LLLL' => 'dddd D. MMMM YYYY [kl.] HH:mm',
+ ],
+ 'weekdays' => ['sunnudaginn', 'mánudaginn', 'þriðjudaginn', 'miðvikudaginn', 'fimmtudaginn', 'föstudaginn', 'laugardaginn'],
+ 'weekdays_short' => ['sun', 'mán', 'þri', 'mið', 'fim', 'fös', 'lau'],
+ 'weekdays_min' => ['sun', 'mán', 'þri', 'mið', 'fim', 'fös', 'lau'],
+ 'months' => ['janúar', 'febrúar', 'mars', 'apríl', 'maí', 'júní', 'júlí', 'ágúst', 'september', 'október', 'nóvember', 'desember'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maí', 'jún', 'júl', 'ágú', 'sep', 'okt', 'nóv', 'des'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php b/vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php
new file mode 100644
index 0000000..4d35c44
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/is_IS.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/is.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it.php b/vendor/nesbot/carbon/src/Carbon/Lang/it.php
new file mode 100644
index 0000000..49875d7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/it.php
@@ -0,0 +1,115 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ash
+ * - François B
+ * - Marco Perrando
+ * - Massimiliano Caniparoli
+ * - JD Isaacks
+ * - Andrea Martini
+ * - Francesco Marasco
+ * - Tizianoz93
+ * - Davide Casiraghi (davide-casiraghi)
+ * - Pete Scopes (pdscopes)
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count anno|:count anni',
+ 'a_year' => 'un anno|:count anni',
+ 'y' => ':count anno|:count anni',
+ 'month' => ':count mese|:count mesi',
+ 'a_month' => 'un mese|:count mesi',
+ 'm' => ':count mese|:count mesi',
+ 'week' => ':count settimana|:count settimane',
+ 'a_week' => 'una settimana|:count settimane',
+ 'w' => ':count set.',
+ 'day' => ':count giorno|:count giorni',
+ 'a_day' => 'un giorno|:count giorni',
+ 'd' => ':count g|:count gg',
+ 'hour' => ':count ora|:count ore',
+ 'a_hour' => 'un\'ora|:count ore',
+ 'h' => ':count h',
+ 'minute' => ':count minuto|:count minuti',
+ 'a_minute' => 'un minuto|:count minuti',
+ 'min' => ':count min.',
+ 'second' => ':count secondo|:count secondi',
+ 'a_second' => 'alcuni secondi|:count secondi',
+ 's' => ':count sec.',
+ 'millisecond' => ':count millisecondo|:count millisecondi',
+ 'a_millisecond' => 'un millisecondo|:count millisecondi',
+ 'ms' => ':countms',
+ 'microsecond' => ':count microsecondo|:count microsecondi',
+ 'a_microsecond' => 'un microsecondo|:count microsecondi',
+ 'µs' => ':countµs',
+ 'ago' => ':time fa',
+ 'from_now' => function ($time) {
+ return (preg_match('/^\d.+$/', $time) ? 'tra' : 'in')." $time";
+ },
+ 'after' => ':time dopo',
+ 'before' => ':time prima',
+ 'diff_now' => 'proprio ora',
+ 'diff_today' => 'Oggi',
+ 'diff_today_regexp' => 'Oggi(?:\\s+alle)?',
+ 'diff_yesterday' => 'ieri',
+ 'diff_yesterday_regexp' => 'Ieri(?:\\s+alle)?',
+ 'diff_tomorrow' => 'domani',
+ 'diff_tomorrow_regexp' => 'Domani(?:\\s+alle)?',
+ 'diff_before_yesterday' => 'l\'altro ieri',
+ 'diff_after_tomorrow' => 'dopodomani',
+ 'period_interval' => 'ogni :interval',
+ 'period_start_date' => 'dal :date',
+ 'period_end_date' => 'al :date',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Oggi alle] LT',
+ 'nextDay' => '[Domani alle] LT',
+ 'nextWeek' => 'dddd [alle] LT',
+ 'lastDay' => '[Ieri alle] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[la scorsa] dddd [alle] LT';
+ default:
+ return '[lo scorso] dddd [alle] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberº',
+ 'months' => ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
+ 'months_short' => ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'],
+ 'weekdays' => ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato'],
+ 'weekdays_short' => ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
+ 'weekdays_min' => ['do', 'lu', 'ma', 'me', 'gi', 've', 'sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' e '],
+ 'ordinal_words' => [
+ 'of' => 'di',
+ 'first' => 'primo',
+ 'second' => 'secondo',
+ 'third' => 'terzo',
+ 'fourth' => 'quarto',
+ 'fifth' => 'quinto',
+ 'last' => 'ultimo',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php
new file mode 100644
index 0000000..c23cc50
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_CH.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Propaganistas
+ */
+return array_replace_recursive(require __DIR__.'/it.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php
new file mode 100644
index 0000000..a5d1981
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_IT.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return require __DIR__.'/it.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php
new file mode 100644
index 0000000..5e8fc92
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_SM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/it.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php b/vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php
new file mode 100644
index 0000000..5e8fc92
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/it_VA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/it.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iu.php b/vendor/nesbot/carbon/src/Carbon/Lang/iu.php
new file mode 100644
index 0000000..4fa9742
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/iu.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/iu_CA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php
new file mode 100644
index 0000000..6ab7e14
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/iu_CA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Pablo Saratxaga pablo@mandriva.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'MM/DD/YY',
+ ],
+ 'months' => ['ᔮᓄᐊᓕ', 'ᕕᕗᐊᓕ', 'ᒪᔅᓯ', 'ᐃᐳᓗ', 'ᒪᐃ', 'ᔪᓂ', 'ᔪᓚᐃ', 'ᐊᒋᓯ', 'ᓯᑎᕙ', 'ᐊᑦᑐᕙ', 'ᓄᕕᕙ', 'ᑎᓯᕝᕙ'],
+ 'months_short' => ['ᔮᓄ', 'ᕕᕗ', 'ᒪᔅ', 'ᐃᐳ', 'ᒪᐃ', 'ᔪᓂ', 'ᔪᓚ', 'ᐊᒋ', 'ᓯᑎ', 'ᐊᑦ', 'ᓄᕕ', 'ᑎᓯ'],
+ 'weekdays' => ['ᓈᑦᑎᖑᔭᕐᕕᒃ', 'ᓇᒡᒐᔾᔭᐅ', 'ᓇᒡᒐᔾᔭᐅᓕᖅᑭᑦ', 'ᐱᖓᓲᓕᖅᓯᐅᑦ', 'ᕿᑎᖅᑰᑦ', 'ᐅᓪᓗᕈᓘᑐᐃᓇᖅ', 'ᓯᕙᑖᕕᒃ'],
+ 'weekdays_short' => ['ᓈ', 'ᓇ', 'ᓕ', 'ᐱ', 'ᕿ', 'ᐅ', 'ᓯ'],
+ 'weekdays_min' => ['ᓈ', 'ᓇ', 'ᓕ', 'ᐱ', 'ᕿ', 'ᐅ', 'ᓯ'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count ᐅᑭᐅᖅ',
+ 'y' => ':count ᐅᑭᐅᖅ',
+ 'a_year' => ':count ᐅᑭᐅᖅ',
+
+ 'month' => ':count qaammat',
+ 'm' => ':count qaammat',
+ 'a_month' => ':count qaammat',
+
+ 'week' => ':count sapaatip akunnera',
+ 'w' => ':count sapaatip akunnera',
+ 'a_week' => ':count sapaatip akunnera',
+
+ 'day' => ':count ulloq',
+ 'd' => ':count ulloq',
+ 'a_day' => ':count ulloq',
+
+ 'hour' => ':count ikarraq',
+ 'h' => ':count ikarraq',
+ 'a_hour' => ':count ikarraq',
+
+ 'minute' => ':count titiqqaralaaq', // less reliable
+ 'min' => ':count titiqqaralaaq', // less reliable
+ 'a_minute' => ':count titiqqaralaaq', // less reliable
+
+ 'second' => ':count marluk', // less reliable
+ 's' => ':count marluk', // less reliable
+ 'a_second' => ':count marluk', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/iw.php b/vendor/nesbot/carbon/src/Carbon/Lang/iw.php
new file mode 100644
index 0000000..a26e350
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/iw.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'months' => ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'],
+ 'months_short' => ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳', 'דצמ׳'],
+ 'weekdays' => ['יום ראשון', 'יום שני', 'יום שלישי', 'יום רביעי', 'יום חמישי', 'יום שישי', 'יום שבת'],
+ 'weekdays_short' => ['יום א׳', 'יום ב׳', 'יום ג׳', 'יום ד׳', 'יום ה׳', 'יום ו׳', 'שבת'],
+ 'weekdays_min' => ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
+ 'meridiem' => ['לפנה״צ', 'אחה״צ'],
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'D.M.YYYY',
+ 'LL' => 'D בMMM YYYY',
+ 'LLL' => 'D בMMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D בMMMM YYYY H:mm',
+ ],
+
+ 'year' => ':count שנה',
+ 'y' => ':count שנה',
+ 'a_year' => ':count שנה',
+
+ 'month' => ':count חודש',
+ 'm' => ':count חודש',
+ 'a_month' => ':count חודש',
+
+ 'week' => ':count שבוע',
+ 'w' => ':count שבוע',
+ 'a_week' => ':count שבוע',
+
+ 'day' => ':count יום',
+ 'd' => ':count יום',
+ 'a_day' => ':count יום',
+
+ 'hour' => ':count שעה',
+ 'h' => ':count שעה',
+ 'a_hour' => ':count שעה',
+
+ 'minute' => ':count דקה',
+ 'min' => ':count דקה',
+ 'a_minute' => ':count דקה',
+
+ 'second' => ':count שניה',
+ 's' => ':count שניה',
+ 'a_second' => ':count שניה',
+
+ 'ago' => 'לפני :time',
+ 'from_now' => 'בעוד :time',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ja.php b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php
new file mode 100644
index 0000000..1ca6751
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ja.php
@@ -0,0 +1,102 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Takuya Sawada
+ * - Atsushi Tanaka
+ * - François B
+ * - Jason Katz-Brown
+ * - Serhan Apaydın
+ * - XueWei
+ * - JD Isaacks
+ * - toyama satoshi
+ * - atakigawa
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count年',
+ 'y' => ':count年',
+ 'month' => ':countヶ月',
+ 'm' => ':countヶ月',
+ 'week' => ':count週間',
+ 'w' => ':count週間',
+ 'day' => ':count日',
+ 'd' => ':count日',
+ 'hour' => ':count時間',
+ 'h' => ':count時間',
+ 'minute' => ':count分',
+ 'min' => ':count分',
+ 'second' => ':count秒',
+ 'a_second' => '{1}数秒|]1,Inf[:count秒',
+ 's' => ':count秒',
+ 'ago' => ':time前',
+ 'from_now' => ':time後',
+ 'after' => ':time後',
+ 'before' => ':time前',
+ 'diff_now' => '今',
+ 'diff_today' => '今日',
+ 'diff_yesterday' => '昨日',
+ 'diff_tomorrow' => '明日',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY/MM/DD',
+ 'LL' => 'YYYY年M月D日',
+ 'LLL' => 'YYYY年M月D日 HH:mm',
+ 'LLLL' => 'YYYY年M月D日 dddd HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[今日] LT',
+ 'nextDay' => '[明日] LT',
+ 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) {
+ if ($other->week !== $current->week) {
+ return '[来週]dddd LT';
+ }
+
+ return 'dddd LT';
+ },
+ 'lastDay' => '[昨日] LT',
+ 'lastWeek' => function (CarbonInterface $current, CarbonInterface $other) {
+ if ($other->week !== $current->week) {
+ return '[先週]dddd LT';
+ }
+
+ return 'dddd LT';
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'd':
+ case 'D':
+ case 'DDD':
+ return $number.'日';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => ['午前', '午後'],
+ 'months' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+ 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+ 'weekdays' => ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
+ 'weekdays_short' => ['日', '月', '火', '水', '木', '金', '土'],
+ 'weekdays_min' => ['日', '月', '火', '水', '木', '金', '土'],
+ 'list' => '、',
+ 'alt_numbers' => ['〇', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '二十一', '二十二', '二十三', '二十四', '二十五', '二十六', '二十七', '二十八', '二十九', '三十', '三十一', '三十二', '三十三', '三十四', '三十五', '三十六', '三十七', '三十八', '三十九', '四十', '四十一', '四十二', '四十三', '四十四', '四十五', '四十六', '四十七', '四十八', '四十九', '五十', '五十一', '五十二', '五十三', '五十四', '五十五', '五十六', '五十七', '五十八', '五十九', '六十', '六十一', '六十二', '六十三', '六十四', '六十五', '六十六', '六十七', '六十八', '六十九', '七十', '七十一', '七十二', '七十三', '七十四', '七十五', '七十六', '七十七', '七十八', '七十九', '八十', '八十一', '八十二', '八十三', '八十四', '八十五', '八十六', '八十七', '八十八', '八十九', '九十', '九十一', '九十二', '九十三', '九十四', '九十五', '九十六', '九十七', '九十八', '九十九'],
+ 'alt_numbers_pow' => [
+ 10000 => '万',
+ 1000 => '千',
+ 100 => '百',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php b/vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php
new file mode 100644
index 0000000..c283625
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ja_JP.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ja.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php b/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php
new file mode 100644
index 0000000..6a1e77a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/jgo.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jmc.php b/vendor/nesbot/carbon/src/Carbon/Lang/jmc.php
new file mode 100644
index 0000000..ed92e8e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/jmc.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['utuko', 'kyiukonyi'],
+ 'weekdays' => ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/jv.php b/vendor/nesbot/carbon/src/Carbon/Lang/jv.php
new file mode 100644
index 0000000..bcbe044
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/jv.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - tgfjt
+ * - JD Isaacks
+ */
+return [
+ 'year' => '{1}setaun|]1,Inf[:count taun',
+ 'month' => '{1}sewulan|]1,Inf[:count wulan',
+ 'week' => '{1}sakminggu|]1,Inf[:count minggu',
+ 'day' => '{1}sedinten|]1,Inf[:count dinten',
+ 'hour' => '{1}setunggal jam|]1,Inf[:count jam',
+ 'minute' => '{1}setunggal menit|]1,Inf[:count menit',
+ 'second' => '{1}sawetawis detik|]1,Inf[:count detik',
+ 'ago' => ':time ingkang kepengker',
+ 'from_now' => 'wonten ing :time',
+ 'diff_today' => 'Dinten',
+ 'diff_yesterday' => 'Kala',
+ 'diff_yesterday_regexp' => 'Kala(?:\\s+wingi)?(?:\\s+pukul)?',
+ 'diff_tomorrow' => 'Mbenjang',
+ 'diff_tomorrow_regexp' => 'Mbenjang(?:\\s+pukul)?',
+ 'diff_today_regexp' => 'Dinten(?:\\s+puniko)?(?:\\s+pukul)?',
+ 'formats' => [
+ 'LT' => 'HH.mm',
+ 'LTS' => 'HH.mm.ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY [pukul] HH.mm',
+ 'LLLL' => 'dddd, D MMMM YYYY [pukul] HH.mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Dinten puniko pukul] LT',
+ 'nextDay' => '[Mbenjang pukul] LT',
+ 'nextWeek' => 'dddd [pukul] LT',
+ 'lastDay' => '[Kala wingi pukul] LT',
+ 'lastWeek' => 'dddd [kepengker pukul] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 11) {
+ return 'enjing';
+ }
+ if ($hour < 15) {
+ return 'siyang';
+ }
+ if ($hour < 19) {
+ return 'sonten';
+ }
+
+ return 'ndalu';
+ },
+ 'months' => ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'Nopember', 'Desember'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ags', 'Sep', 'Okt', 'Nop', 'Des'],
+ 'weekdays' => ['Minggu', 'Senen', 'Seloso', 'Rebu', 'Kemis', 'Jemuwah', 'Septu'],
+ 'weekdays_short' => ['Min', 'Sen', 'Sel', 'Reb', 'Kem', 'Jem', 'Sep'],
+ 'weekdays_min' => ['Mg', 'Sn', 'Sl', 'Rb', 'Km', 'Jm', 'Sp'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' lan '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ka.php b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php
new file mode 100644
index 0000000..a5d563d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ka.php
@@ -0,0 +1,204 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Tornike Razmadze
+ * - François B
+ * - Lasha Dolidze
+ * - Tim Fish
+ * - JD Isaacks
+ * - Tornike Razmadze
+ * - François B
+ * - Lasha Dolidze
+ * - JD Isaacks
+ * - LONGMAN
+ * - Avtandil Kikabidze (akalongman)
+ * - Levan Velijanashvili (Stichoza)
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count წელი',
+ 'y' => ':count წელი',
+ 'a_year' => '{1}წელი|]1,Inf[:count წელი',
+ 'month' => ':count თვე',
+ 'm' => ':count თვე',
+ 'a_month' => '{1}თვე|]1,Inf[:count თვე',
+ 'week' => ':count კვირა',
+ 'w' => ':count კვირა',
+ 'a_week' => '{1}კვირა|]1,Inf[:count კვირა',
+ 'day' => ':count დღე',
+ 'd' => ':count დღე',
+ 'a_day' => '{1}დღე|]1,Inf[:count დღე',
+ 'hour' => ':count საათი',
+ 'h' => ':count საათი',
+ 'a_hour' => '{1}საათი|]1,Inf[:count საათი',
+ 'minute' => ':count წუთი',
+ 'min' => ':count წუთი',
+ 'a_minute' => '{1}წუთი|]1,Inf[:count წუთი',
+ 'second' => ':count წამი',
+ 's' => ':count წამი',
+ 'a_second' => '{1}რამდენიმე წამი|]1,Inf[:count წამი',
+ 'ago' => function ($time) {
+ $replacements = [
+ // year
+ 'წელი' => 'წლის',
+ // month
+ 'თვე' => 'თვის',
+ // week
+ 'კვირა' => 'კვირის',
+ // day
+ 'დღე' => 'დღის',
+ // hour
+ 'საათი' => 'საათის',
+ // minute
+ 'წუთი' => 'წუთის',
+ // second
+ 'წამი' => 'წამის',
+ ];
+ $time = strtr($time, array_flip($replacements));
+ $time = strtr($time, $replacements);
+
+ return "$time წინ";
+ },
+ 'from_now' => function ($time) {
+ $replacements = [
+ // year
+ 'წელი' => 'წელიწადში',
+ // week
+ 'კვირა' => 'კვირაში',
+ // day
+ 'დღე' => 'დღეში',
+ // month
+ 'თვე' => 'თვეში',
+ // hour
+ 'საათი' => 'საათში',
+ // minute
+ 'წუთი' => 'წუთში',
+ // second
+ 'წამი' => 'წამში',
+ ];
+ $time = strtr($time, array_flip($replacements));
+ $time = strtr($time, $replacements);
+
+ return $time;
+ },
+ 'after' => function ($time) {
+ $replacements = [
+ // year
+ 'წელი' => 'წლის',
+ // month
+ 'თვე' => 'თვის',
+ // week
+ 'კვირა' => 'კვირის',
+ // day
+ 'დღე' => 'დღის',
+ // hour
+ 'საათი' => 'საათის',
+ // minute
+ 'წუთი' => 'წუთის',
+ // second
+ 'წამი' => 'წამის',
+ ];
+ $time = strtr($time, array_flip($replacements));
+ $time = strtr($time, $replacements);
+
+ return "$time შემდეგ";
+ },
+ 'before' => function ($time) {
+ $replacements = [
+ // year
+ 'წელი' => 'წლით',
+ // month
+ 'თვე' => 'თვით',
+ // week
+ 'კვირა' => 'კვირით',
+ // day
+ 'დღე' => 'დღით',
+ // hour
+ 'საათი' => 'საათით',
+ // minute
+ 'წუთი' => 'წუთით',
+ // second
+ 'წამი' => 'წამით',
+ ];
+ $time = strtr($time, array_flip($replacements));
+ $time = strtr($time, $replacements);
+
+ return "$time ადრე";
+ },
+ 'diff_now' => 'ახლა',
+ 'diff_today' => 'დღეს',
+ 'diff_yesterday' => 'გუშინ',
+ 'diff_tomorrow' => 'ხვალ',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[დღეს], LT[-ზე]',
+ 'nextDay' => '[ხვალ], LT[-ზე]',
+ 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) {
+ return ($current->isSameWeek($other) ? '' : '[შემდეგ] ').'dddd, LT[-ზე]';
+ },
+ 'lastDay' => '[გუშინ], LT[-ზე]',
+ 'lastWeek' => '[წინა] dddd, LT-ზე',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ if ($number === 0) {
+ return $number;
+ }
+ if ($number === 1) {
+ return $number.'-ლი';
+ }
+ if (($number < 20) || ($number <= 100 && ($number % 20 === 0)) || ($number % 100 === 0)) {
+ return 'მე-'.$number;
+ }
+
+ return $number.'-ე';
+ },
+ 'months' => ['იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი'],
+ 'months_standalone' => ['იანვარს', 'თებერვალს', 'მარტს', 'აპრილს', 'მაისს', 'ივნისს', 'ივლისს', 'აგვისტოს', 'სექტემბერს', 'ოქტომბერს', 'ნოემბერს', 'დეკემბერს'],
+ 'months_short' => ['იან', 'თებ', 'მარ', 'აპრ', 'მაი', 'ივნ', 'ივლ', 'აგვ', 'სექ', 'ოქტ', 'ნოე', 'დეკ'],
+ 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['კვირას', 'ორშაბათს', 'სამშაბათს', 'ოთხშაბათს', 'ხუთშაბათს', 'პარასკევს', 'შაბათს'],
+ 'weekdays_standalone' => ['კვირა', 'ორშაბათი', 'სამშაბათი', 'ოთხშაბათი', 'ხუთშაბათი', 'პარასკევი', 'შაბათი'],
+ 'weekdays_short' => ['კვი', 'ორშ', 'სამ', 'ოთხ', 'ხუთ', 'პარ', 'შაბ'],
+ 'weekdays_min' => ['კვ', 'ორ', 'სა', 'ოთ', 'ხუ', 'პა', 'შა'],
+ 'weekdays_regexp' => '/^([^d].*|.*[^d])$/',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' და '],
+ 'meridiem' => function ($hour) {
+ if ($hour >= 4) {
+ if ($hour < 11) {
+ return 'დილის';
+ }
+
+ if ($hour < 16) {
+ return 'შუადღის';
+ }
+
+ if ($hour < 22) {
+ return 'საღამოს';
+ }
+ }
+
+ return 'ღამის';
+ },
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php b/vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php
new file mode 100644
index 0000000..a26d930
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ka_GE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ka.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kab.php b/vendor/nesbot/carbon/src/Carbon/Lang/kab.php
new file mode 100644
index 0000000..94d6473
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kab.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/kab_DZ.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php
new file mode 100644
index 0000000..796660b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kab_DZ.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - belkacem77@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Yennayer', 'Fuṛar', 'Meɣres', 'Yebrir', 'Mayyu', 'Yunyu', 'Yulyu', 'ɣuct', 'Ctembeṛ', 'Tubeṛ', 'Wambeṛ', 'Dujembeṛ'],
+ 'months_short' => ['Yen', 'Fur', 'Meɣ', 'Yeb', 'May', 'Yun', 'Yul', 'ɣuc', 'Cte', 'Tub', 'Wam', 'Duj'],
+ 'weekdays' => ['Acer', 'Arim', 'Aram', 'Ahad', 'Amhad', 'Sem', 'Sed'],
+ 'weekdays_short' => ['Ace', 'Ari', 'Ara', 'Aha', 'Amh', 'Sem', 'Sed'],
+ 'weekdays_min' => ['Ace', 'Ari', 'Ara', 'Aha', 'Amh', 'Sem', 'Sed'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['FT', 'MD'],
+
+ 'year' => ':count n yiseggasen',
+ 'y' => ':count n yiseggasen',
+ 'a_year' => ':count n yiseggasen',
+
+ 'month' => ':count n wayyuren',
+ 'm' => ':count n wayyuren',
+ 'a_month' => ':count n wayyuren',
+
+ 'week' => ':count n ledwaṛ', // less reliable
+ 'w' => ':count n ledwaṛ', // less reliable
+ 'a_week' => ':count n ledwaṛ', // less reliable
+
+ 'day' => ':count n wussan',
+ 'd' => ':count n wussan',
+ 'a_day' => ':count n wussan',
+
+ 'hour' => ':count n tsaɛtin',
+ 'h' => ':count n tsaɛtin',
+ 'a_hour' => ':count n tsaɛtin',
+
+ 'minute' => ':count n tedqiqin',
+ 'min' => ':count n tedqiqin',
+ 'a_minute' => ':count n tedqiqin',
+
+ 'second' => ':count tasdidt', // less reliable
+ 's' => ':count tasdidt', // less reliable
+ 'a_second' => ':count tasdidt', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kam.php b/vendor/nesbot/carbon/src/Carbon/Lang/kam.php
new file mode 100644
index 0000000..0fc70d7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kam.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Ĩyakwakya', 'Ĩyawĩoo'],
+ 'weekdays' => ['Wa kyumwa', 'Wa kwambĩlĩlya', 'Wa kelĩ', 'Wa katatũ', 'Wa kana', 'Wa katano', 'Wa thanthatũ'],
+ 'weekdays_short' => ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'],
+ 'weekdays_min' => ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'],
+ 'months' => ['Mwai wa mbee', 'Mwai wa kelĩ', 'Mwai wa katatũ', 'Mwai wa kana', 'Mwai wa katano', 'Mwai wa thanthatũ', 'Mwai wa muonza', 'Mwai wa nyaanya', 'Mwai wa kenda', 'Mwai wa ĩkumi', 'Mwai wa ĩkumi na ĩmwe', 'Mwai wa ĩkumi na ilĩ'],
+ 'months_short' => ['Mbe', 'Kel', 'Ktũ', 'Kan', 'Ktn', 'Tha', 'Moo', 'Nya', 'Knd', 'Ĩku', 'Ĩkm', 'Ĩkl'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ // Too unreliable
+ /*
+ 'year' => ':count mbua', // less reliable
+ 'y' => ':count mbua', // less reliable
+ 'a_year' => ':count mbua', // less reliable
+
+ 'month' => ':count ndakitali', // less reliable
+ 'm' => ':count ndakitali', // less reliable
+ 'a_month' => ':count ndakitali', // less reliable
+
+ 'day' => ':count wia', // less reliable
+ 'd' => ':count wia', // less reliable
+ 'a_day' => ':count wia', // less reliable
+
+ 'hour' => ':count orasan', // less reliable
+ 'h' => ':count orasan', // less reliable
+ 'a_hour' => ':count orasan', // less reliable
+
+ 'minute' => ':count orasan', // less reliable
+ 'min' => ':count orasan', // less reliable
+ 'a_minute' => ':count orasan', // less reliable
+ */
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kde.php b/vendor/nesbot/carbon/src/Carbon/Lang/kde.php
new file mode 100644
index 0000000..fbcc9f3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kde.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Muhi', 'Chilo'],
+ 'weekdays' => ['Liduva lyapili', 'Liduva lyatatu', 'Liduva lyanchechi', 'Liduva lyannyano', 'Liduva lyannyano na linji', 'Liduva lyannyano na mavili', 'Liduva litandi'],
+ 'weekdays_short' => ['Ll2', 'Ll3', 'Ll4', 'Ll5', 'Ll6', 'Ll7', 'Ll1'],
+ 'weekdays_min' => ['Ll2', 'Ll3', 'Ll4', 'Ll5', 'Ll6', 'Ll7', 'Ll1'],
+ 'months' => ['Mwedi Ntandi', 'Mwedi wa Pili', 'Mwedi wa Tatu', 'Mwedi wa Nchechi', 'Mwedi wa Nnyano', 'Mwedi wa Nnyano na Umo', 'Mwedi wa Nnyano na Mivili', 'Mwedi wa Nnyano na Mitatu', 'Mwedi wa Nnyano na Nchechi', 'Mwedi wa Nnyano na Nnyano', 'Mwedi wa Nnyano na Nnyano na U', 'Mwedi wa Nnyano na Nnyano na M'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kea.php b/vendor/nesbot/carbon/src/Carbon/Lang/kea.php
new file mode 100644
index 0000000..8b6c21b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kea.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['a', 'p'],
+ 'weekdays' => ['dumingu', 'sigunda-fera', 'tersa-fera', 'kuarta-fera', 'kinta-fera', 'sesta-fera', 'sabadu'],
+ 'weekdays_short' => ['dum', 'sig', 'ter', 'kua', 'kin', 'ses', 'sab'],
+ 'weekdays_min' => ['du', 'si', 'te', 'ku', 'ki', 'se', 'sa'],
+ 'weekdays_standalone' => ['dumingu', 'sigunda-fera', 'tersa-fera', 'kuarta-fera', 'kinta-fera', 'sesta-fera', 'sábadu'],
+ 'months' => ['Janeru', 'Febreru', 'Marsu', 'Abril', 'Maiu', 'Junhu', 'Julhu', 'Agostu', 'Setenbru', 'Otubru', 'Nuvenbru', 'Dizenbru'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Otu', 'Nuv', 'Diz'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D [di] MMMM [di] YYYY HH:mm',
+ 'LLLL' => 'dddd, D [di] MMMM [di] YYYY HH:mm',
+ ],
+
+ 'year' => ':count otunu', // less reliable
+ 'y' => ':count otunu', // less reliable
+ 'a_year' => ':count otunu', // less reliable
+
+ 'week' => ':count día dumingu', // less reliable
+ 'w' => ':count día dumingu', // less reliable
+ 'a_week' => ':count día dumingu', // less reliable
+
+ 'day' => ':count diâ', // less reliable
+ 'd' => ':count diâ', // less reliable
+ 'a_day' => ':count diâ', // less reliable
+
+ 'minute' => ':count sugundu', // less reliable
+ 'min' => ':count sugundu', // less reliable
+ 'a_minute' => ':count sugundu', // less reliable
+
+ 'second' => ':count dós', // less reliable
+ 's' => ':count dós', // less reliable
+ 'a_second' => ':count dós', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/khq.php b/vendor/nesbot/carbon/src/Carbon/Lang/khq.php
new file mode 100644
index 0000000..7a834cf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/khq.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Adduha', 'Aluula'],
+ 'weekdays' => ['Alhadi', 'Atini', 'Atalata', 'Alarba', 'Alhamiisa', 'Aljuma', 'Assabdu'],
+ 'weekdays_short' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alj', 'Ass'],
+ 'weekdays_min' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alj', 'Ass'],
+ 'months' => ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
+ 'months_short' => ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ki.php b/vendor/nesbot/carbon/src/Carbon/Lang/ki.php
new file mode 100644
index 0000000..d86afc5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ki.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Kiroko', 'Hwaĩ-inĩ'],
+ 'weekdays' => ['Kiumia', 'Njumatatũ', 'Njumaine', 'Njumatana', 'Aramithi', 'Njumaa', 'Njumamothi'],
+ 'weekdays_short' => ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'],
+ 'weekdays_min' => ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'],
+ 'months' => ['Njenuarĩ', 'Mwere wa kerĩ', 'Mwere wa gatatũ', 'Mwere wa kana', 'Mwere wa gatano', 'Mwere wa gatandatũ', 'Mwere wa mũgwanja', 'Mwere wa kanana', 'Mwere wa kenda', 'Mwere wa ikũmi', 'Mwere wa ikũmi na ũmwe', 'Ndithemba'],
+ 'months_short' => ['JEN', 'WKR', 'WGT', 'WKN', 'WTN', 'WTD', 'WMJ', 'WNN', 'WKD', 'WIK', 'WMW', 'DIT'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count mĩaka', // less reliable
+ 'y' => ':count mĩaka', // less reliable
+ 'a_year' => ':count mĩaka', // less reliable
+
+ 'month' => ':count mweri', // less reliable
+ 'm' => ':count mweri', // less reliable
+ 'a_month' => ':count mweri', // less reliable
+
+ 'week' => ':count kiumia', // less reliable
+ 'w' => ':count kiumia', // less reliable
+ 'a_week' => ':count kiumia', // less reliable
+
+ 'day' => ':count mũthenya', // less reliable
+ 'd' => ':count mũthenya', // less reliable
+ 'a_day' => ':count mũthenya', // less reliable
+
+ 'hour' => ':count thaa', // less reliable
+ 'h' => ':count thaa', // less reliable
+ 'a_hour' => ':count thaa', // less reliable
+
+ 'minute' => ':count mundu', // less reliable
+ 'min' => ':count mundu', // less reliable
+ 'a_minute' => ':count mundu', // less reliable
+
+ 'second' => ':count igego', // less reliable
+ 's' => ':count igego', // less reliable
+ 'a_second' => ':count igego', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kk.php b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php
new file mode 100644
index 0000000..59fa9af
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kk.php
@@ -0,0 +1,103 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - Talat Uspanov
+ * - Нурлан Рахимжанов
+ * - Toleugazy Kali
+ */
+return [
+ 'year' => ':count жыл',
+ 'a_year' => '{1}бір жыл|:count жыл',
+ 'y' => ':count ж.',
+ 'month' => ':count ай',
+ 'a_month' => '{1}бір ай|:count ай',
+ 'm' => ':count ай',
+ 'week' => ':count апта',
+ 'a_week' => '{1}бір апта',
+ 'w' => ':count ап.',
+ 'day' => ':count күн',
+ 'a_day' => '{1}бір күн|:count күн',
+ 'd' => ':count к.',
+ 'hour' => ':count сағат',
+ 'a_hour' => '{1}бір сағат|:count сағат',
+ 'h' => ':count са.',
+ 'minute' => ':count минут',
+ 'a_minute' => '{1}бір минут|:count минут',
+ 'min' => ':count м.',
+ 'second' => ':count секунд',
+ 'a_second' => '{1}бірнеше секунд|:count секунд',
+ 's' => ':count се.',
+ 'ago' => ':time бұрын',
+ 'from_now' => ':time ішінде',
+ 'after' => ':time кейін',
+ 'before' => ':time бұрын',
+ 'diff_now' => 'қазір',
+ 'diff_today' => 'Бүгін',
+ 'diff_today_regexp' => 'Бүгін(?:\\s+сағат)?',
+ 'diff_yesterday' => 'кеше',
+ 'diff_yesterday_regexp' => 'Кеше(?:\\s+сағат)?',
+ 'diff_tomorrow' => 'ертең',
+ 'diff_tomorrow_regexp' => 'Ертең(?:\\s+сағат)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Бүгін сағат] LT',
+ 'nextDay' => '[Ертең сағат] LT',
+ 'nextWeek' => 'dddd [сағат] LT',
+ 'lastDay' => '[Кеше сағат] LT',
+ 'lastWeek' => '[Өткен аптаның] dddd [сағат] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ static $suffixes = [
+ 0 => '-ші',
+ 1 => '-ші',
+ 2 => '-ші',
+ 3 => '-ші',
+ 4 => '-ші',
+ 5 => '-ші',
+ 6 => '-шы',
+ 7 => '-ші',
+ 8 => '-ші',
+ 9 => '-шы',
+ 10 => '-шы',
+ 20 => '-шы',
+ 30 => '-шы',
+ 40 => '-шы',
+ 50 => '-ші',
+ 60 => '-шы',
+ 70 => '-ші',
+ 80 => '-ші',
+ 90 => '-шы',
+ 100 => '-ші',
+ ];
+
+ return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? '');
+ },
+ 'months' => ['қаңтар', 'ақпан', 'наурыз', 'сәуір', 'мамыр', 'маусым', 'шілде', 'тамыз', 'қыркүйек', 'қазан', 'қараша', 'желтоқсан'],
+ 'months_short' => ['қаң', 'ақп', 'нау', 'сәу', 'мам', 'мау', 'шіл', 'там', 'қыр', 'қаз', 'қар', 'жел'],
+ 'weekdays' => ['жексенбі', 'дүйсенбі', 'сейсенбі', 'сәрсенбі', 'бейсенбі', 'жұма', 'сенбі'],
+ 'weekdays_short' => ['жек', 'дүй', 'сей', 'сәр', 'бей', 'жұм', 'сен'],
+ 'weekdays_min' => ['жк', 'дй', 'сй', 'ср', 'бй', 'жм', 'сн'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' және '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php
new file mode 100644
index 0000000..7dc5ebc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kk_KZ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/kk.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php b/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php
new file mode 100644
index 0000000..6a1e77a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kkj.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kl.php b/vendor/nesbot/carbon/src/Carbon/Lang/kl.php
new file mode 100644
index 0000000..7329a07
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kl.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/kl_GL.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php b/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php
new file mode 100644
index 0000000..4fed720
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kl_GL.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Danish Standards Association bug-glibc-locales@gnu.org
+ * - John Eyðstein Johannesen (mashema)
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd [d.] D. MMMM YYYY [kl.] HH:mm',
+ ],
+ 'months' => ['januaarip', 'februaarip', 'marsip', 'apriilip', 'maajip', 'juunip', 'juulip', 'aggustip', 'septembarip', 'oktobarip', 'novembarip', 'decembarip'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
+ 'weekdays' => ['sapaat', 'ataasinngorneq', 'marlunngorneq', 'pingasunngorneq', 'sisamanngorneq', 'tallimanngorneq', 'arfininngorneq'],
+ 'weekdays_short' => ['sap', 'ata', 'mar', 'pin', 'sis', 'tal', 'arf'],
+ 'weekdays_min' => ['sap', 'ata', 'mar', 'pin', 'sis', 'tal', 'arf'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => '{1}ukioq :count|{0}:count ukiut|]1,Inf[ukiut :count',
+ 'a_year' => '{1}ukioq|{0}:count ukiut|]1,Inf[ukiut :count',
+ 'y' => '{1}:countyr|{0}:countyrs|]1,Inf[:countyrs',
+
+ 'month' => '{1}qaammat :count|{0}:count qaammatit|]1,Inf[qaammatit :count',
+ 'a_month' => '{1}qaammat|{0}:count qaammatit|]1,Inf[qaammatit :count',
+ 'm' => '{1}:countmo|{0}:countmos|]1,Inf[:countmos',
+
+ 'week' => '{1}:count sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.',
+ 'a_week' => '{1}a sap. ak.|{0}:count sap. ak.|]1,Inf[:count sap. ak.',
+ 'w' => ':countw',
+
+ 'day' => '{1}:count ulloq|{0}:count ullut|]1,Inf[:count ullut',
+ 'a_day' => '{1}a ulloq|{0}:count ullut|]1,Inf[:count ullut',
+ 'd' => ':countd',
+
+ 'hour' => '{1}:count tiimi|{0}:count tiimit|]1,Inf[:count tiimit',
+ 'a_hour' => '{1}tiimi|{0}:count tiimit|]1,Inf[:count tiimit',
+ 'h' => ':counth',
+
+ 'minute' => '{1}:count minutsi|{0}:count minutsit|]1,Inf[:count minutsit',
+ 'a_minute' => '{1}a minutsi|{0}:count minutsit|]1,Inf[:count minutsit',
+ 'min' => ':countm',
+
+ 'second' => '{1}:count sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit',
+ 'a_second' => '{1}sikunti|{0}:count sikuntit|]1,Inf[:count sikuntit',
+ 's' => ':counts',
+
+ 'ago' => ':time matuma siorna',
+
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kln.php b/vendor/nesbot/carbon/src/Carbon/Lang/kln.php
new file mode 100644
index 0000000..b9c3996
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kln.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['krn', 'koosk'],
+ 'weekdays' => ['Kotisap', 'Kotaai', 'Koaeng’', 'Kosomok', 'Koang’wan', 'Komuut', 'Kolo'],
+ 'weekdays_short' => ['Kts', 'Kot', 'Koo', 'Kos', 'Koa', 'Kom', 'Kol'],
+ 'weekdays_min' => ['Kts', 'Kot', 'Koo', 'Kos', 'Koa', 'Kom', 'Kol'],
+ 'months' => ['Mulgul', 'Ng’atyaato', 'Kiptaamo', 'Iwootkuut', 'Mamuut', 'Paagi', 'Ng’eiyeet', 'Rooptui', 'Bureet', 'Epeeso', 'Kipsuunde ne taai', 'Kipsuunde nebo aeng’'],
+ 'months_short' => ['Mul', 'Ngat', 'Taa', 'Iwo', 'Mam', 'Paa', 'Nge', 'Roo', 'Bur', 'Epe', 'Kpt', 'Kpa'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count maghatiat', // less reliable
+ 'y' => ':count maghatiat', // less reliable
+ 'a_year' => ':count maghatiat', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/km.php b/vendor/nesbot/carbon/src/Carbon/Lang/km.php
new file mode 100644
index 0000000..da790ac
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/km.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kruy Vanna
+ * - Sereysethy Touch
+ * - JD Isaacks
+ * - Sovichet Tep
+ */
+return [
+ 'year' => '{1}មួយឆ្នាំ|]1,Inf[:count ឆ្នាំ',
+ 'y' => ':count ឆ្នាំ',
+ 'month' => '{1}មួយខែ|]1,Inf[:count ខែ',
+ 'm' => ':count ខែ',
+ 'week' => ':count សប្ដាហ៍',
+ 'w' => ':count សប្ដាហ៍',
+ 'day' => '{1}មួយថ្ងៃ|]1,Inf[:count ថ្ងៃ',
+ 'd' => ':count ថ្ងៃ',
+ 'hour' => '{1}មួយម៉ោង|]1,Inf[:count ម៉ោង',
+ 'h' => ':count ម៉ោង',
+ 'minute' => '{1}មួយនាទី|]1,Inf[:count នាទី',
+ 'min' => ':count នាទី',
+ 'second' => '{1}ប៉ុន្មានវិនាទី|]1,Inf[:count វិនាទី',
+ 's' => ':count វិនាទី',
+ 'ago' => ':timeមុន',
+ 'from_now' => ':timeទៀត',
+ 'after' => 'នៅក្រោយ :time',
+ 'before' => 'នៅមុន :time',
+ 'diff_now' => 'ឥឡូវ',
+ 'diff_today' => 'ថ្ងៃនេះ',
+ 'diff_today_regexp' => 'ថ្ងៃនេះ(?:\\s+ម៉ោង)?',
+ 'diff_yesterday' => 'ម្សិលមិញ',
+ 'diff_yesterday_regexp' => 'ម្សិលមិញ(?:\\s+ម៉ោង)?',
+ 'diff_tomorrow' => 'ថ្ងៃស្អែក',
+ 'diff_tomorrow_regexp' => 'ស្អែក(?:\\s+ម៉ោង)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ថ្ងៃនេះ ម៉ោង] LT',
+ 'nextDay' => '[ស្អែក ម៉ោង] LT',
+ 'nextWeek' => 'dddd [ម៉ោង] LT',
+ 'lastDay' => '[ម្សិលមិញ ម៉ោង] LT',
+ 'lastWeek' => 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => 'ទី:number',
+ 'meridiem' => ['ព្រឹក', 'ល្ងាច'],
+ 'months' => ['មករា', 'កុម្ភៈ', 'មីនា', 'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា', 'សីហា', 'កញ្ញា', 'តុលា', 'វិច្ឆិកា', 'ធ្នូ'],
+ 'months_short' => ['មករា', 'កុម្ភៈ', 'មីនា', 'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា', 'សីហា', 'កញ្ញា', 'តុលា', 'វិច្ឆិកា', 'ធ្នូ'],
+ 'weekdays' => ['អាទិត្យ', 'ច័ន្ទ', 'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ', 'សៅរ៍'],
+ 'weekdays_short' => ['អា', 'ច', 'អ', 'ព', 'ព្រ', 'សុ', 'ស'],
+ 'weekdays_min' => ['អា', 'ច', 'អ', 'ព', 'ព្រ', 'សុ', 'ស'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', 'និង '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php b/vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php
new file mode 100644
index 0000000..92e5fdb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/km_KH.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/km.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kn.php b/vendor/nesbot/carbon/src/Carbon/Lang/kn.php
new file mode 100644
index 0000000..0d2ad08
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kn.php
@@ -0,0 +1,75 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - MOHAN M U
+ * - François B
+ * - rajeevnaikte
+ */
+return [
+ 'year' => '{1}ಒಂದು ವರ್ಷ|]1,Inf[:count ವರ್ಷ',
+ 'month' => '{1}ಒಂದು ತಿಂಗಳು|]1,Inf[:count ತಿಂಗಳು',
+ 'week' => '{1}ಒಂದು ವಾರ|]1,Inf[:count ವಾರಗಳು',
+ 'day' => '{1}ಒಂದು ದಿನ|]1,Inf[:count ದಿನ',
+ 'hour' => '{1}ಒಂದು ಗಂಟೆ|]1,Inf[:count ಗಂಟೆ',
+ 'minute' => '{1}ಒಂದು ನಿಮಿಷ|]1,Inf[:count ನಿಮಿಷ',
+ 'second' => '{1}ಕೆಲವು ಕ್ಷಣಗಳು|]1,Inf[:count ಸೆಕೆಂಡುಗಳು',
+ 'ago' => ':time ಹಿಂದೆ',
+ 'from_now' => ':time ನಂತರ',
+ 'diff_now' => 'ಈಗ',
+ 'diff_today' => 'ಇಂದು',
+ 'diff_yesterday' => 'ನಿನ್ನೆ',
+ 'diff_tomorrow' => 'ನಾಳೆ',
+ 'formats' => [
+ 'LT' => 'A h:mm',
+ 'LTS' => 'A h:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ಇಂದು] LT',
+ 'nextDay' => '[ನಾಳೆ] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[ನಿನ್ನೆ] LT',
+ 'lastWeek' => '[ಕೊನೆಯ] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberನೇ',
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'ರಾತ್ರಿ';
+ }
+ if ($hour < 10) {
+ return 'ಬೆಳಿಗ್ಗೆ';
+ }
+ if ($hour < 17) {
+ return 'ಮಧ್ಯಾಹ್ನ';
+ }
+ if ($hour < 20) {
+ return 'ಸಂಜೆ';
+ }
+
+ return 'ರಾತ್ರಿ';
+ },
+ 'months' => ['ಜನವರಿ', 'ಫೆಬ್ರವರಿ', 'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್', 'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್', 'ಡಿಸೆಂಬರ್'],
+ 'months_short' => ['ಜನ', 'ಫೆಬ್ರ', 'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂ', 'ಅಕ್ಟೋ', 'ನವೆಂ', 'ಡಿಸೆಂ'],
+ 'weekdays' => ['ಭಾನುವಾರ', 'ಸೋಮವಾರ', 'ಮಂಗಳವಾರ', 'ಬುಧವಾರ', 'ಗುರುವಾರ', 'ಶುಕ್ರವಾರ', 'ಶನಿವಾರ'],
+ 'weekdays_short' => ['ಭಾನು', 'ಸೋಮ', 'ಮಂಗಳ', 'ಬುಧ', 'ಗುರು', 'ಶುಕ್ರ', 'ಶನಿ'],
+ 'weekdays_min' => ['ಭಾ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'],
+ 'list' => ', ',
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php
new file mode 100644
index 0000000..30e3d88
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kn_IN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/kn.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php
new file mode 100644
index 0000000..4fa6237
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko.php
@@ -0,0 +1,91 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - FourwingsY
+ * - François B
+ * - Jason Katz-Brown
+ * - Seokjun Kim
+ * - Junho Kim
+ * - JD Isaacks
+ * - Juwon Kim
+ */
+return [
+ 'year' => ':count년',
+ 'a_year' => '{1}일년|]1,Inf[:count년',
+ 'y' => ':count년',
+ 'month' => ':count개월',
+ 'a_month' => '{1}한달|]1,Inf[:count개월',
+ 'm' => ':count개월',
+ 'week' => ':count주',
+ 'a_week' => '{1}일주일|]1,Inf[:count 주',
+ 'w' => ':count주일',
+ 'day' => ':count일',
+ 'a_day' => '{1}하루|]1,Inf[:count일',
+ 'd' => ':count일',
+ 'hour' => ':count시간',
+ 'a_hour' => '{1}한시간|]1,Inf[:count시간',
+ 'h' => ':count시간',
+ 'minute' => ':count분',
+ 'a_minute' => '{1}일분|]1,Inf[:count분',
+ 'min' => ':count분',
+ 'second' => ':count초',
+ 'a_second' => '{1}몇초|]1,Inf[:count초',
+ 's' => ':count초',
+ 'ago' => ':time 전',
+ 'from_now' => ':time 후',
+ 'after' => ':time 후',
+ 'before' => ':time 전',
+ 'diff_now' => '지금',
+ 'diff_today' => '오늘',
+ 'diff_yesterday' => '어제',
+ 'diff_tomorrow' => '내일',
+ 'formats' => [
+ 'LT' => 'A h:mm',
+ 'LTS' => 'A h:mm:ss',
+ 'L' => 'YYYY.MM.DD.',
+ 'LL' => 'YYYY년 MMMM D일',
+ 'LLL' => 'YYYY년 MMMM D일 A h:mm',
+ 'LLLL' => 'YYYY년 MMMM D일 dddd A h:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '오늘 LT',
+ 'nextDay' => '내일 LT',
+ 'nextWeek' => 'dddd LT',
+ 'lastDay' => '어제 LT',
+ 'lastWeek' => '지난주 dddd LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'd':
+ case 'D':
+ case 'DDD':
+ return $number.'일';
+ case 'M':
+ return $number.'월';
+ case 'w':
+ case 'W':
+ return $number.'주';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => ['오전', '오후'],
+ 'months' => ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
+ 'months_short' => ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
+ 'weekdays' => ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'],
+ 'weekdays_short' => ['일', '월', '화', '수', '목', '금', '토'],
+ 'weekdays_min' => ['일', '월', '화', '수', '목', '금', '토'],
+ 'list' => ' ',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php
new file mode 100644
index 0000000..4ba802b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KP.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ko.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php
new file mode 100644
index 0000000..9d873a2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ko_KR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ko.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kok.php b/vendor/nesbot/carbon/src/Carbon/Lang/kok.php
new file mode 100644
index 0000000..4adcddc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kok.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/kok_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php
new file mode 100644
index 0000000..92ba844
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kok_IN.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat, Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D-M-YY',
+ ],
+ 'months' => ['जानेवारी', 'फेब्रुवारी', 'मार्च', 'एप्रिल', 'मे', 'जून', 'जुलै', 'ओगस्ट', 'सेप्टेंबर', 'ओक्टोबर', 'नोव्हेंबर', 'डिसेंबर'],
+ 'months_short' => ['जानेवारी', 'फेब्रुवारी', 'मार्च', 'एप्रिल', 'मे', 'जून', 'जुलै', 'ओगस्ट', 'सेप्टेंबर', 'ओक्टोबर', 'नोव्हेंबर', 'डिसेंबर'],
+ 'weekdays' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'],
+ 'weekdays_short' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'],
+ 'weekdays_min' => ['आयतार', 'सोमार', 'मंगळवार', 'बुधवार', 'बेरेसतार', 'शुकरार', 'शेनवार'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['म.पू.', 'म.नं.'],
+
+ 'year' => ':count वैशाकु', // less reliable
+ 'y' => ':count वैशाकु', // less reliable
+ 'a_year' => ':count वैशाकु', // less reliable
+
+ 'week' => ':count आदित्यवार', // less reliable
+ 'w' => ':count आदित्यवार', // less reliable
+ 'a_week' => ':count आदित्यवार', // less reliable
+
+ 'minute' => ':count नोंद', // less reliable
+ 'min' => ':count नोंद', // less reliable
+ 'a_minute' => ':count नोंद', // less reliable
+
+ 'second' => ':count तेंको', // less reliable
+ 's' => ':count तेंको', // less reliable
+ 'a_second' => ':count तेंको', // less reliable
+
+ 'month' => ':count मैनो',
+ 'm' => ':count मैनो',
+ 'a_month' => ':count मैनो',
+
+ 'day' => ':count दिवसु',
+ 'd' => ':count दिवसु',
+ 'a_day' => ':count दिवसु',
+
+ 'hour' => ':count घंते',
+ 'h' => ':count घंते',
+ 'a_hour' => ':count घंते',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks.php
new file mode 100644
index 0000000..9876079
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ks_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php
new file mode 100644
index 0000000..ce9d5d4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat, Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'M/D/YY',
+ ],
+ 'months' => ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل', 'میٔ', 'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر', 'اکتوٗبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل', 'میٔ', 'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر', 'اکتوٗبر', 'نومبر', 'دسمبر'],
+ 'weekdays' => ['آتهوار', 'ژءندروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'],
+ 'weekdays_short' => ['آتهوار', 'ژءنتروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'],
+ 'weekdays_min' => ['آتهوار', 'ژءنتروار', 'بوءںوار', 'بودهوار', 'برىسوار', 'جمع', 'بٹوار'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['دوپھربرونھ', 'دوپھرپتھ'],
+
+ 'year' => ':count آب', // less reliable
+ 'y' => ':count آب', // less reliable
+ 'a_year' => ':count آب', // less reliable
+
+ 'month' => ':count रान्', // less reliable
+ 'm' => ':count रान्', // less reliable
+ 'a_month' => ':count रान्', // less reliable
+
+ 'week' => ':count آتھٕوار', // less reliable
+ 'w' => ':count آتھٕوار', // less reliable
+ 'a_week' => ':count آتھٕوار', // less reliable
+
+ 'hour' => ':count سۄن', // less reliable
+ 'h' => ':count سۄن', // less reliable
+ 'a_hour' => ':count سۄن', // less reliable
+
+ 'minute' => ':count فَن', // less reliable
+ 'min' => ':count فَن', // less reliable
+ 'a_minute' => ':count فَن', // less reliable
+
+ 'second' => ':count दोʼयुम', // less reliable
+ 's' => ':count दोʼयुम', // less reliable
+ 'a_second' => ':count दोʼयुम', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php
new file mode 100644
index 0000000..a2ae8b6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ks_IN@devanagari.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - ks-gnome-trans-commits@lists.code.indlinux.net
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'M/D/YY',
+ ],
+ 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'weekdays' => ['आथवार', 'चॅ़दुरवार', 'बोमवार', 'ब्वदवार', 'ब्रसवार', 'शोकुरवार', 'बटुवार'],
+ 'weekdays_short' => ['आथ ', 'चॅ़दुर', 'बोम', 'ब्वद', 'ब्रस', 'शोकुर', 'बटु'],
+ 'weekdays_min' => ['आथ ', 'चॅ़दुर', 'बोम', 'ब्वद', 'ब्रस', 'शोकुर', 'बटु'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ksb.php b/vendor/nesbot/carbon/src/Carbon/Lang/ksb.php
new file mode 100644
index 0000000..aaa0061
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ksb.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['makeo', 'nyiaghuo'],
+ 'weekdays' => ['Jumaapii', 'Jumaatatu', 'Jumaane', 'Jumaatano', 'Alhamisi', 'Ijumaa', 'Jumaamosi'],
+ 'weekdays_short' => ['Jpi', 'Jtt', 'Jmn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Jpi', 'Jtt', 'Jmn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'months' => ['Januali', 'Febluali', 'Machi', 'Aplili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ksf.php b/vendor/nesbot/carbon/src/Carbon/Lang/ksf.php
new file mode 100644
index 0000000..84a5967
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ksf.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['sárúwá', 'cɛɛ́nko'],
+ 'weekdays' => ['sɔ́ndǝ', 'lǝndí', 'maadí', 'mɛkrɛdí', 'jǝǝdí', 'júmbá', 'samdí'],
+ 'weekdays_short' => ['sɔ́n', 'lǝn', 'maa', 'mɛk', 'jǝǝ', 'júm', 'sam'],
+ 'weekdays_min' => ['sɔ́n', 'lǝn', 'maa', 'mɛk', 'jǝǝ', 'júm', 'sam'],
+ 'months' => ['ŋwíí a ntɔ́ntɔ', 'ŋwíí akǝ bɛ́ɛ', 'ŋwíí akǝ ráá', 'ŋwíí akǝ nin', 'ŋwíí akǝ táan', 'ŋwíí akǝ táafɔk', 'ŋwíí akǝ táabɛɛ', 'ŋwíí akǝ táaraa', 'ŋwíí akǝ táanin', 'ŋwíí akǝ ntɛk', 'ŋwíí akǝ ntɛk di bɔ́k', 'ŋwíí akǝ ntɛk di bɛ́ɛ'],
+ 'months_short' => ['ŋ1', 'ŋ2', 'ŋ3', 'ŋ4', 'ŋ5', 'ŋ6', 'ŋ7', 'ŋ8', 'ŋ9', 'ŋ10', 'ŋ11', 'ŋ12'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ksh.php b/vendor/nesbot/carbon/src/Carbon/Lang/ksh.php
new file mode 100644
index 0000000..95457e2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ksh.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['v.M.', 'n.M.'],
+ 'weekdays' => ['Sunndaach', 'Mohndaach', 'Dinnsdaach', 'Metwoch', 'Dunnersdaach', 'Friidaach', 'Samsdaach'],
+ 'weekdays_short' => ['Su.', 'Mo.', 'Di.', 'Me.', 'Du.', 'Fr.', 'Sa.'],
+ 'weekdays_min' => ['Su', 'Mo', 'Di', 'Me', 'Du', 'Fr', 'Sa'],
+ 'months' => ['Jannewa', 'Fäbrowa', 'Määz', 'Aprell', 'Mai', 'Juuni', 'Juuli', 'Oujoß', 'Septämber', 'Oktohber', 'Novämber', 'Dezämber'],
+ 'months_short' => ['Jan', 'Fäb', 'Mäz', 'Apr', 'Mai', 'Jun', 'Jul', 'Ouj', 'Säp', 'Okt', 'Nov', 'Dez'],
+ 'months_short_standalone' => ['Jan.', 'Fäb.', 'Mäz.', 'Apr.', 'Mai', 'Jun.', 'Jul.', 'Ouj.', 'Säp.', 'Okt.', 'Nov.', 'Dez.'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D. M. YYYY',
+ 'LL' => 'D. MMM. YYYY',
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, [dä] D. MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count Johr',
+ 'y' => ':count Johr',
+ 'a_year' => ':count Johr',
+
+ 'month' => ':count Moohnd',
+ 'm' => ':count Moohnd',
+ 'a_month' => ':count Moohnd',
+
+ 'week' => ':count woch',
+ 'w' => ':count woch',
+ 'a_week' => ':count woch',
+
+ 'day' => ':count Daach',
+ 'd' => ':count Daach',
+ 'a_day' => ':count Daach',
+
+ 'hour' => ':count Uhr',
+ 'h' => ':count Uhr',
+ 'a_hour' => ':count Uhr',
+
+ 'minute' => ':count Menutt',
+ 'min' => ':count Menutt',
+ 'a_minute' => ':count Menutt',
+
+ 'second' => ':count Sekůndt',
+ 's' => ':count Sekůndt',
+ 'a_second' => ':count Sekůndt',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ku.php b/vendor/nesbot/carbon/src/Carbon/Lang/ku.php
new file mode 100644
index 0000000..189960c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ku.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Unicode, Inc.
+ */
+
+return [
+ 'ago' => 'berî :time',
+ 'from_now' => 'di :time de',
+ 'after' => ':time piştî',
+ 'before' => ':time berê',
+ 'year' => ':count sal',
+ 'year_ago' => ':count salê|:count salan',
+ 'year_from_now' => 'salekê|:count salan',
+ 'month' => ':count meh',
+ 'week' => ':count hefte',
+ 'day' => ':count roj',
+ 'hour' => ':count saet',
+ 'minute' => ':count deqîqe',
+ 'second' => ':count saniye',
+ 'months' => ['rêbendanê', 'reşemiyê', 'adarê', 'avrêlê', 'gulanê', 'pûşperê', 'tîrmehê', 'gelawêjê', 'rezberê', 'kewçêrê', 'sermawezê', 'berfanbarê'],
+ 'months_standalone' => ['rêbendan', 'reşemî', 'adar', 'avrêl', 'gulan', 'pûşper', 'tîrmeh', 'gelawêj', 'rezber', 'kewçêr', 'sermawez', 'berfanbar'],
+ 'months_short' => ['rêb', 'reş', 'ada', 'avr', 'gul', 'pûş', 'tîr', 'gel', 'rez', 'kew', 'ser', 'ber'],
+ 'weekdays' => ['yekşem', 'duşem', 'sêşem', 'çarşem', 'pêncşem', 'în', 'şemî'],
+ 'weekdays_short' => ['yş', 'dş', 'sş', 'çş', 'pş', 'în', 'ş'],
+ 'weekdays_min' => ['Y', 'D', 'S', 'Ç', 'P', 'Î', 'Ş'],
+ 'list' => [', ', ' û '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php b/vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php
new file mode 100644
index 0000000..4243a82
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ku_TR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ku.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kw.php b/vendor/nesbot/carbon/src/Carbon/Lang/kw.php
new file mode 100644
index 0000000..26e242e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kw.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/kw_GB.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php b/vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php
new file mode 100644
index 0000000..00bf52b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/kw_GB.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Alastair McKinstry bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['mis Genver', 'mis Hwevrer', 'mis Meurth', 'mis Ebrel', 'mis Me', 'mis Metheven', 'mis Gortheren', 'mis Est', 'mis Gwynngala', 'mis Hedra', 'mis Du', 'mis Kevardhu'],
+ 'months_short' => ['Gen', 'Hwe', 'Meu', 'Ebr', 'Me', 'Met', 'Gor', 'Est', 'Gwn', 'Hed', 'Du', 'Kev'],
+ 'weekdays' => ['De Sul', 'De Lun', 'De Merth', 'De Merher', 'De Yow', 'De Gwener', 'De Sadorn'],
+ 'weekdays_short' => ['Sul', 'Lun', 'Mth', 'Mhr', 'Yow', 'Gwe', 'Sad'],
+ 'weekdays_min' => ['Sul', 'Lun', 'Mth', 'Mhr', 'Yow', 'Gwe', 'Sad'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count bledhen',
+ 'y' => ':count bledhen',
+ 'a_year' => ':count bledhen',
+
+ 'month' => ':count mis',
+ 'm' => ':count mis',
+ 'a_month' => ':count mis',
+
+ 'week' => ':count seythen',
+ 'w' => ':count seythen',
+ 'a_week' => ':count seythen',
+
+ 'day' => ':count dydh',
+ 'd' => ':count dydh',
+ 'a_day' => ':count dydh',
+
+ 'hour' => ':count eur',
+ 'h' => ':count eur',
+ 'a_hour' => ':count eur',
+
+ 'minute' => ':count mynysen',
+ 'min' => ':count mynysen',
+ 'a_minute' => ':count mynysen',
+
+ 'second' => ':count pryjwyth',
+ 's' => ':count pryjwyth',
+ 'a_second' => ':count pryjwyth',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ky.php b/vendor/nesbot/carbon/src/Carbon/Lang/ky.php
new file mode 100644
index 0000000..e0d1af1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ky.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - acutexyz
+ * - Josh Soref
+ * - François B
+ * - Chyngyz Arystan uulu
+ * - Chyngyz
+ * - acutexyz
+ * - Josh Soref
+ * - François B
+ * - Chyngyz Arystan uulu
+ */
+return [
+ 'year' => ':count жыл',
+ 'a_year' => '{1}бир жыл|:count жыл',
+ 'y' => ':count жыл',
+ 'month' => ':count ай',
+ 'a_month' => '{1}бир ай|:count ай',
+ 'm' => ':count ай',
+ 'week' => ':count апта',
+ 'a_week' => '{1}бир апта|:count апта',
+ 'w' => ':count апт.',
+ 'day' => ':count күн',
+ 'a_day' => '{1}бир күн|:count күн',
+ 'd' => ':count күн',
+ 'hour' => ':count саат',
+ 'a_hour' => '{1}бир саат|:count саат',
+ 'h' => ':count саат.',
+ 'minute' => ':count мүнөт',
+ 'a_minute' => '{1}бир мүнөт|:count мүнөт',
+ 'min' => ':count мүн.',
+ 'second' => ':count секунд',
+ 'a_second' => '{1}бирнече секунд|:count секунд',
+ 's' => ':count сек.',
+ 'ago' => ':time мурун',
+ 'from_now' => ':time ичинде',
+ 'diff_now' => 'азыр',
+ 'diff_today' => 'Бүгүн',
+ 'diff_today_regexp' => 'Бүгүн(?:\\s+саат)?',
+ 'diff_yesterday' => 'кечээ',
+ 'diff_yesterday_regexp' => 'Кече(?:\\s+саат)?',
+ 'diff_tomorrow' => 'эртең',
+ 'diff_tomorrow_regexp' => 'Эртең(?:\\s+саат)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Бүгүн саат] LT',
+ 'nextDay' => '[Эртең саат] LT',
+ 'nextWeek' => 'dddd [саат] LT',
+ 'lastDay' => '[Кече саат] LT',
+ 'lastWeek' => '[Өткен аптанын] dddd [күнү] [саат] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ static $suffixes = [
+ 0 => '-чү',
+ 1 => '-чи',
+ 2 => '-чи',
+ 3 => '-чү',
+ 4 => '-чү',
+ 5 => '-чи',
+ 6 => '-чы',
+ 7 => '-чи',
+ 8 => '-чи',
+ 9 => '-чу',
+ 10 => '-чу',
+ 20 => '-чы',
+ 30 => '-чу',
+ 40 => '-чы',
+ 50 => '-чү',
+ 60 => '-чы',
+ 70 => '-чи',
+ 80 => '-чи',
+ 90 => '-чу',
+ 100 => '-чү',
+ ];
+
+ return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? '');
+ },
+ 'months' => ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
+ 'months_short' => ['янв', 'фев', 'март', 'апр', 'май', 'июнь', 'июль', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'weekdays' => ['Жекшемби', 'Дүйшөмбү', 'Шейшемби', 'Шаршемби', 'Бейшемби', 'Жума', 'Ишемби'],
+ 'weekdays_short' => ['Жек', 'Дүй', 'Шей', 'Шар', 'Бей', 'Жум', 'Ише'],
+ 'weekdays_min' => ['Жк', 'Дй', 'Шй', 'Шр', 'Бй', 'Жм', 'Иш'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => ' ',
+ 'meridiem' => ['таңкы', 'түштөн кийинки'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php
new file mode 100644
index 0000000..9923a31
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ky_KG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ky.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lag.php b/vendor/nesbot/carbon/src/Carbon/Lang/lag.php
new file mode 100644
index 0000000..f3f57f6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lag.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['TOO', 'MUU'],
+ 'weekdays' => ['Jumapíiri', 'Jumatátu', 'Jumaíne', 'Jumatáano', 'Alamíisi', 'Ijumáa', 'Jumamóosi'],
+ 'weekdays_short' => ['Píili', 'Táatu', 'Íne', 'Táano', 'Alh', 'Ijm', 'Móosi'],
+ 'weekdays_min' => ['Píili', 'Táatu', 'Íne', 'Táano', 'Alh', 'Ijm', 'Móosi'],
+ 'months' => ['Kʉfúngatɨ', 'Kʉnaanɨ', 'Kʉkeenda', 'Kwiikumi', 'Kwiinyambála', 'Kwiidwaata', 'Kʉmʉʉnchɨ', 'Kʉvɨɨrɨ', 'Kʉsaatʉ', 'Kwiinyi', 'Kʉsaano', 'Kʉsasatʉ'],
+ 'months_short' => ['Fúngatɨ', 'Naanɨ', 'Keenda', 'Ikúmi', 'Inyambala', 'Idwaata', 'Mʉʉnchɨ', 'Vɨɨrɨ', 'Saatʉ', 'Inyi', 'Saano', 'Sasatʉ'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lb.php b/vendor/nesbot/carbon/src/Carbon/Lang/lb.php
new file mode 100644
index 0000000..7636655
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lb.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - dan-nl
+ * - Simon Lelorrain (slelorrain)
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count Joer',
+ 'y' => ':countJ',
+ 'month' => ':count Mount|:count Méint',
+ 'm' => ':countMo',
+ 'week' => ':count Woch|:count Wochen',
+ 'w' => ':countWo|:countWo',
+ 'day' => ':count Dag|:count Deeg',
+ 'd' => ':countD',
+ 'hour' => ':count Stonn|:count Stonnen',
+ 'h' => ':countSto',
+ 'minute' => ':count Minutt|:count Minutten',
+ 'min' => ':countM',
+ 'second' => ':count Sekonn|:count Sekonnen',
+ 's' => ':countSek',
+
+ 'ago' => 'virun :time',
+ 'from_now' => 'an :time',
+ 'before' => ':time virdrun',
+ 'after' => ':time duerno',
+
+ 'diff_today' => 'Haut',
+ 'diff_yesterday' => 'Gëschter',
+ 'diff_yesterday_regexp' => 'Gëschter(?:\\s+um)?',
+ 'diff_tomorrow' => 'Muer',
+ 'diff_tomorrow_regexp' => 'Muer(?:\\s+um)?',
+ 'diff_today_regexp' => 'Haut(?:\\s+um)?',
+ 'formats' => [
+ 'LT' => 'H:mm [Auer]',
+ 'LTS' => 'H:mm:ss [Auer]',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY H:mm [Auer]',
+ 'LLLL' => 'dddd, D. MMMM YYYY H:mm [Auer]',
+ ],
+
+ 'calendar' => [
+ 'sameDay' => '[Haut um] LT',
+ 'nextDay' => '[Muer um] LT',
+ 'nextWeek' => 'dddd [um] LT',
+ 'lastDay' => '[Gëschter um] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ // Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule
+ switch ($date->dayOfWeek) {
+ case 2:
+ case 4:
+ return '[Leschten] dddd [um] LT';
+ default:
+ return '[Leschte] dddd [um] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+
+ 'months' => ['Januar', 'Februar', 'Mäerz', 'Abrëll', 'Mee', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
+ 'months_short' => ['Jan.', 'Febr.', 'Mrz.', 'Abr.', 'Mee', 'Jun.', 'Jul.', 'Aug.', 'Sept.', 'Okt.', 'Nov.', 'Dez.'],
+ 'weekdays' => ['Sonndeg', 'Méindeg', 'Dënschdeg', 'Mëttwoch', 'Donneschdeg', 'Freideg', 'Samschdeg'],
+ 'weekdays_short' => ['So.', 'Mé.', 'Dë.', 'Më.', 'Do.', 'Fr.', 'Sa.'],
+ 'weekdays_min' => ['So', 'Mé', 'Dë', 'Më', 'Do', 'Fr', 'Sa'],
+ 'ordinal' => ':number.',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' an '],
+ 'meridiem' => ['moies', 'mëttes'],
+ 'weekdays_short_standalone' => ['Son', 'Méi', 'Dën', 'Mët', 'Don', 'Fre', 'Sam'],
+ 'months_short_standalone' => ['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php
new file mode 100644
index 0000000..414bd4d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lb_LU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/lb.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lg.php b/vendor/nesbot/carbon/src/Carbon/Lang/lg.php
new file mode 100644
index 0000000..48bc68b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lg.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/lg_UG.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php
new file mode 100644
index 0000000..aa02214
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lg_UG.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Akademe ya Luganda Kizito Birabwa kompyuta@kizito.uklinux.net
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi', 'Juuni', 'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul', 'Agu', 'Seb', 'Oki', 'Nov', 'Des'],
+ 'weekdays' => ['Sabiiti', 'Balaza', 'Lwakubiri', 'Lwakusatu', 'Lwakuna', 'Lwakutaano', 'Lwamukaaga'],
+ 'weekdays_short' => ['Sab', 'Bal', 'Lw2', 'Lw3', 'Lw4', 'Lw5', 'Lw6'],
+ 'weekdays_min' => ['Sab', 'Bal', 'Lw2', 'Lw3', 'Lw4', 'Lw5', 'Lw6'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'month' => ':count njuba', // less reliable
+ 'm' => ':count njuba', // less reliable
+ 'a_month' => ':count njuba', // less reliable
+
+ 'year' => ':count mwaaka',
+ 'y' => ':count mwaaka',
+ 'a_year' => ':count mwaaka',
+
+ 'week' => ':count sabbiiti',
+ 'w' => ':count sabbiiti',
+ 'a_week' => ':count sabbiiti',
+
+ 'day' => ':count lunaku',
+ 'd' => ':count lunaku',
+ 'a_day' => ':count lunaku',
+
+ 'hour' => 'saawa :count',
+ 'h' => 'saawa :count',
+ 'a_hour' => 'saawa :count',
+
+ 'minute' => 'ddakiika :count',
+ 'min' => 'ddakiika :count',
+ 'a_minute' => 'ddakiika :count',
+
+ 'second' => ':count kyʼokubiri',
+ 's' => ':count kyʼokubiri',
+ 'a_second' => ':count kyʼokubiri',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/li.php b/vendor/nesbot/carbon/src/Carbon/Lang/li.php
new file mode 100644
index 0000000..86c3009
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/li.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/li_NL.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php
new file mode 100644
index 0000000..6c5feb7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/li_NL.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandriva.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['jannewarie', 'fibberwarie', 'miert', 'eprèl', 'meij', 'junie', 'julie', 'augustus', 'september', 'oktober', 'november', 'desember'],
+ 'months_short' => ['jan', 'fib', 'mie', 'epr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ 'weekdays' => ['zóndig', 'maondig', 'daensdig', 'goonsdig', 'dónderdig', 'vriedig', 'zaoterdig'],
+ 'weekdays_short' => ['zón', 'mao', 'dae', 'goo', 'dón', 'vri', 'zao'],
+ 'weekdays_min' => ['zón', 'mao', 'dae', 'goo', 'dón', 'vri', 'zao'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'minute' => ':count momênt', // less reliable
+ 'min' => ':count momênt', // less reliable
+ 'a_minute' => ':count momênt', // less reliable
+
+ 'year' => ':count jaor',
+ 'y' => ':count jaor',
+ 'a_year' => ':count jaor',
+
+ 'month' => ':count maond',
+ 'm' => ':count maond',
+ 'a_month' => ':count maond',
+
+ 'week' => ':count waek',
+ 'w' => ':count waek',
+ 'a_week' => ':count waek',
+
+ 'day' => ':count daag',
+ 'd' => ':count daag',
+ 'a_day' => ':count daag',
+
+ 'hour' => ':count oer',
+ 'h' => ':count oer',
+ 'a_hour' => ':count oer',
+
+ 'second' => ':count Secónd',
+ 's' => ':count Secónd',
+ 'a_second' => ':count Secónd',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lij.php b/vendor/nesbot/carbon/src/Carbon/Lang/lij.php
new file mode 100644
index 0000000..45732b5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lij.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/lij_IT.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php
new file mode 100644
index 0000000..f8726fd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lij_IT.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Gastaldi alessio.gastaldi@libero.it
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['zenâ', 'fevrâ', 'marzo', 'avrî', 'mazzo', 'zûgno', 'lûggio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dixembre'],
+ 'months_short' => ['zen', 'fev', 'mar', 'arv', 'maz', 'zûg', 'lûg', 'ago', 'set', 'ött', 'nov', 'dix'],
+ 'weekdays' => ['domenega', 'lûnedì', 'martedì', 'mercUrdì', 'zêggia', 'venardì', 'sabbo'],
+ 'weekdays_short' => ['dom', 'lûn', 'mar', 'mer', 'zêu', 'ven', 'sab'],
+ 'weekdays_min' => ['dom', 'lûn', 'mar', 'mer', 'zêu', 'ven', 'sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count etæ', // less reliable
+ 'y' => ':count etæ', // less reliable
+ 'a_year' => ':count etæ', // less reliable
+
+ 'month' => ':count meize',
+ 'm' => ':count meize',
+ 'a_month' => ':count meize',
+
+ 'week' => ':count settemannha',
+ 'w' => ':count settemannha',
+ 'a_week' => ':count settemannha',
+
+ 'day' => ':count giorno',
+ 'd' => ':count giorno',
+ 'a_day' => ':count giorno',
+
+ 'hour' => ':count reléuio', // less reliable
+ 'h' => ':count reléuio', // less reliable
+ 'a_hour' => ':count reléuio', // less reliable
+
+ 'minute' => ':count menûo',
+ 'min' => ':count menûo',
+ 'a_minute' => ':count menûo',
+
+ 'second' => ':count segondo',
+ 's' => ':count segondo',
+ 'a_second' => ':count segondo',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php
new file mode 100644
index 0000000..ae73a97
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lkt.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+
+ 'month' => ':count haŋwí', // less reliable
+ 'm' => ':count haŋwí', // less reliable
+ 'a_month' => ':count haŋwí', // less reliable
+
+ 'week' => ':count šakówiŋ', // less reliable
+ 'w' => ':count šakówiŋ', // less reliable
+ 'a_week' => ':count šakówiŋ', // less reliable
+
+ 'hour' => ':count maza škaŋškaŋ', // less reliable
+ 'h' => ':count maza škaŋškaŋ', // less reliable
+ 'a_hour' => ':count maza škaŋškaŋ', // less reliable
+
+ 'minute' => ':count číkʼala', // less reliable
+ 'min' => ':count číkʼala', // less reliable
+ 'a_minute' => ':count číkʼala', // less reliable
+
+ 'year' => ':count waníyetu',
+ 'y' => ':count waníyetu',
+ 'a_year' => ':count waníyetu',
+
+ 'day' => ':count aŋpétu',
+ 'd' => ':count aŋpétu',
+ 'a_day' => ':count aŋpétu',
+
+ 'second' => ':count icinuŋpa',
+ 's' => ':count icinuŋpa',
+ 'a_second' => ':count icinuŋpa',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln.php
new file mode 100644
index 0000000..9d5c35d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ubuntu René Manassé GALEKWA renemanasse@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'months' => ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́', 'sánzá ya zómi na míbalé'],
+ 'months_short' => ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb', 'ɔtb', 'nvb', 'dsb'],
+ 'weekdays' => ['Lomíngo', 'Mosálá mɔ̌kɔ́', 'Misálá míbalé', 'Misálá mísáto', 'Misálá mínei', 'Misálá mítáno', 'Mpɔ́sɔ'],
+ 'weekdays_short' => ['m1.', 'm2.', 'm3.', 'm4.', 'm5.', 'm6.', 'm7.'],
+ 'weekdays_min' => ['m1.', 'm2.', 'm3.', 'm4.', 'm5.', 'm6.', 'm7.'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => 'mbula :count',
+ 'y' => 'mbula :count',
+ 'a_year' => 'mbula :count',
+
+ 'month' => 'sánzá :count',
+ 'm' => 'sánzá :count',
+ 'a_month' => 'sánzá :count',
+
+ 'week' => 'mpɔ́sɔ :count',
+ 'w' => 'mpɔ́sɔ :count',
+ 'a_week' => 'mpɔ́sɔ :count',
+
+ 'day' => 'mokɔlɔ :count',
+ 'd' => 'mokɔlɔ :count',
+ 'a_day' => 'mokɔlɔ :count',
+
+ 'hour' => 'ngonga :count',
+ 'h' => 'ngonga :count',
+ 'a_hour' => 'ngonga :count',
+
+ 'minute' => 'miniti :count',
+ 'min' => 'miniti :count',
+ 'a_minute' => 'miniti :count',
+
+ 'second' => 'segɔnde :count',
+ 's' => 'segɔnde :count',
+ 'a_second' => 'segɔnde :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php
new file mode 100644
index 0000000..7fdb7f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_AO.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ln.php', [
+ 'weekdays' => ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé', 'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno', 'mpɔ́sɔ'],
+ 'weekdays_short' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+ 'weekdays_min' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+ 'meridiem' => ['ntɔ́ngɔ́', 'mpókwa'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php
new file mode 100644
index 0000000..13635fc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CD.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ubuntu René Manassé GALEKWA renemanasse@gmail.com
+ */
+return require __DIR__.'/ln.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php
new file mode 100644
index 0000000..7fdb7f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CF.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ln.php', [
+ 'weekdays' => ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé', 'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno', 'mpɔ́sɔ'],
+ 'weekdays_short' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+ 'weekdays_min' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+ 'meridiem' => ['ntɔ́ngɔ́', 'mpókwa'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php
new file mode 100644
index 0000000..7fdb7f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ln_CG.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ln.php', [
+ 'weekdays' => ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé', 'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno', 'mpɔ́sɔ'],
+ 'weekdays_short' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+ 'weekdays_min' => ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+ 'meridiem' => ['ntɔ́ngɔ́', 'mpókwa'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lo.php b/vendor/nesbot/carbon/src/Carbon/Lang/lo.php
new file mode 100644
index 0000000..48715f5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lo.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - ryanhart2
+ */
+return [
+ 'year' => ':count ປີ',
+ 'y' => ':count ປີ',
+ 'month' => ':count ເດືອນ',
+ 'm' => ':count ດ. ',
+ 'week' => ':count ອາທິດ',
+ 'w' => ':count ອທ. ',
+ 'day' => ':count ມື້',
+ 'd' => ':count ມື້',
+ 'hour' => ':count ຊົ່ວໂມງ',
+ 'h' => ':count ຊມ. ',
+ 'minute' => ':count ນາທີ',
+ 'min' => ':count ນທ. ',
+ 'second' => '{1}ບໍ່ເທົ່າໃດວິນາທີ|]1,Inf[:count ວິນາທີ',
+ 's' => ':count ວິ. ',
+ 'ago' => ':timeຜ່ານມາ',
+ 'from_now' => 'ອີກ :time',
+ 'diff_now' => 'ຕອນນີ້',
+ 'diff_today' => 'ມື້ນີ້ເວລາ',
+ 'diff_yesterday' => 'ມື້ວານນີ້ເວລາ',
+ 'diff_tomorrow' => 'ມື້ອື່ນເວລາ',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'ວັນdddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ມື້ນີ້ເວລາ] LT',
+ 'nextDay' => '[ມື້ອື່ນເວລາ] LT',
+ 'nextWeek' => '[ວັນ]dddd[ໜ້າເວລາ] LT',
+ 'lastDay' => '[ມື້ວານນີ້ເວລາ] LT',
+ 'lastWeek' => '[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => 'ທີ່:number',
+ 'meridiem' => ['ຕອນເຊົ້າ', 'ຕອນແລງ'],
+ 'months' => ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ', 'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ', 'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ', 'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'],
+ 'months_short' => ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ', 'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ', 'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ', 'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'],
+ 'weekdays' => ['ອາທິດ', 'ຈັນ', 'ອັງຄານ', 'ພຸດ', 'ພະຫັດ', 'ສຸກ', 'ເສົາ'],
+ 'weekdays_short' => ['ທິດ', 'ຈັນ', 'ອັງຄານ', 'ພຸດ', 'ພະຫັດ', 'ສຸກ', 'ເສົາ'],
+ 'weekdays_min' => ['ທ', 'ຈ', 'ອຄ', 'ພ', 'ພຫ', 'ສກ', 'ສ'],
+ 'list' => [', ', 'ແລະ '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php b/vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php
new file mode 100644
index 0000000..9b7fd9b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lo_LA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/lo.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php b/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php
new file mode 100644
index 0000000..546e679
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lrc.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+
+ 'minute' => ':count هنر', // less reliable
+ 'min' => ':count هنر', // less reliable
+ 'a_minute' => ':count هنر', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php
new file mode 100644
index 0000000..d42f5e9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lrc_IQ.php
@@ -0,0 +1,13 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/lrc.php', [
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lt.php b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php
new file mode 100644
index 0000000..7d1b6f7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lt.php
@@ -0,0 +1,135 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - tjku
+ * - valdas406
+ * - Justas Palumickas
+ * - Max Melentiev
+ * - Andrius Janauskas
+ * - Juanito Fatas
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Nicolás Hock Isaza
+ * - Laurynas Butkus
+ * - Sven Fuchs
+ * - Dominykas Tijūnaitis
+ * - Justinas Bolys
+ * - Ričardas
+ * - Kirill Chalkin
+ * - Rolandas
+ * - Justinas (Gamesh)
+ */
+return [
+ 'year' => ':count metai|:count metai|:count metų',
+ 'y' => ':count m.',
+ 'month' => ':count mėnuo|:count mėnesiai|:count mėnesį',
+ 'm' => ':count mėn.',
+ 'week' => ':count savaitė|:count savaitės|:count savaitę',
+ 'w' => ':count sav.',
+ 'day' => ':count diena|:count dienos|:count dienų',
+ 'd' => ':count d.',
+ 'hour' => ':count valanda|:count valandos|:count valandų',
+ 'h' => ':count val.',
+ 'minute' => ':count minutė|:count minutės|:count minutę',
+ 'min' => ':count min.',
+ 'second' => ':count sekundė|:count sekundės|:count sekundžių',
+ 's' => ':count sek.',
+
+ 'year_ago' => ':count metus|:count metus|:count metų',
+ 'month_ago' => ':count mėnesį|:count mėnesius|:count mėnesių',
+ 'week_ago' => ':count savaitę|:count savaites|:count savaičių',
+ 'day_ago' => ':count dieną|:count dienas|:count dienų',
+ 'hour_ago' => ':count valandą|:count valandas|:count valandų',
+ 'minute_ago' => ':count minutę|:count minutes|:count minučių',
+ 'second_ago' => ':count sekundę|:count sekundes|:count sekundžių',
+
+ 'year_from_now' => ':count metų',
+ 'month_from_now' => ':count mėnesio|:count mėnesių|:count mėnesių',
+ 'week_from_now' => ':count savaitės|:count savaičių|:count savaičių',
+ 'day_from_now' => ':count dienos|:count dienų|:count dienų',
+ 'hour_from_now' => ':count valandos|:count valandų|:count valandų',
+ 'minute_from_now' => ':count minutės|:count minučių|:count minučių',
+ 'second_from_now' => ':count sekundės|:count sekundžių|:count sekundžių',
+
+ 'year_after' => ':count metų',
+ 'month_after' => ':count mėnesio|:count mėnesių|:count mėnesių',
+ 'week_after' => ':count savaitės|:count savaičių|:count savaičių',
+ 'day_after' => ':count dienos|:count dienų|:count dienų',
+ 'hour_after' => ':count valandos|:count valandų|:count valandų',
+ 'minute_after' => ':count minutės|:count minučių|:count minučių',
+ 'second_after' => ':count sekundės|:count sekundžių|:count sekundžių',
+
+ 'ago' => 'prieš :time',
+ 'from_now' => ':time nuo dabar',
+ 'after' => 'po :time',
+ 'before' => 'už :time',
+
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'diff_now' => 'ką tik',
+ 'diff_today' => 'Šiandien',
+ 'diff_yesterday' => 'vakar',
+ 'diff_yesterday_regexp' => 'Vakar',
+ 'diff_tomorrow' => 'rytoj',
+ 'diff_tomorrow_regexp' => 'Rytoj',
+ 'diff_before_yesterday' => 'užvakar',
+ 'diff_after_tomorrow' => 'poryt',
+
+ 'period_recurrences' => 'kartą|:count kartų',
+ 'period_interval' => 'kiekvieną :interval',
+ 'period_start_date' => 'nuo :date',
+ 'period_end_date' => 'iki :date',
+
+ 'months' => ['sausio', 'vasario', 'kovo', 'balandžio', 'gegužės', 'birželio', 'liepos', 'rugpjūčio', 'rugsėjo', 'spalio', 'lapkričio', 'gruodžio'],
+ 'months_standalone' => ['sausis', 'vasaris', 'kovas', 'balandis', 'gegužė', 'birželis', 'liepa', 'rugpjūtis', 'rugsėjis', 'spalis', 'lapkritis', 'gruodis'],
+ 'months_regexp' => '/(L{2,4}|D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?)/',
+ 'months_short' => ['sau', 'vas', 'kov', 'bal', 'geg', 'bir', 'lie', 'rgp', 'rgs', 'spa', 'lap', 'gru'],
+ 'weekdays' => ['sekmadienį', 'pirmadienį', 'antradienį', 'trečiadienį', 'ketvirtadienį', 'penktadienį', 'šeštadienį'],
+ 'weekdays_standalone' => ['sekmadienis', 'pirmadienis', 'antradienis', 'trečiadienis', 'ketvirtadienis', 'penktadienis', 'šeštadienis'],
+ 'weekdays_short' => ['sek', 'pir', 'ant', 'tre', 'ket', 'pen', 'šeš'],
+ 'weekdays_min' => ['se', 'pi', 'an', 'tr', 'ke', 'pe', 'še'],
+ 'list' => [', ', ' ir '],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'MMMM DD, YYYY',
+ 'LLL' => 'DD MMM HH:mm',
+ 'LLLL' => 'MMMM DD, YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Šiandien] LT',
+ 'nextDay' => '[Rytoj] LT',
+ 'nextWeek' => 'dddd LT',
+ 'lastDay' => '[Vakar] LT',
+ 'lastWeek' => '[Paskutinį] dddd LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ switch ($number) {
+ case 0:
+ return '0-is';
+ case 3:
+ return '3-ias';
+ default:
+ return "$number-as";
+ }
+ },
+ 'meridiem' => ['priešpiet', 'popiet'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php b/vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php
new file mode 100644
index 0000000..f772d38
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lt_LT.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/lt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lu.php b/vendor/nesbot/carbon/src/Carbon/Lang/lu.php
new file mode 100644
index 0000000..c8cd83a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lu.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Dinda', 'Dilolo'],
+ 'weekdays' => ['Lumingu', 'Nkodya', 'Ndàayà', 'Ndangù', 'Njòwa', 'Ngòvya', 'Lubingu'],
+ 'weekdays_short' => ['Lum', 'Nko', 'Ndy', 'Ndg', 'Njw', 'Ngv', 'Lub'],
+ 'weekdays_min' => ['Lum', 'Nko', 'Ndy', 'Ndg', 'Njw', 'Ngv', 'Lub'],
+ 'months' => ['Ciongo', 'Lùishi', 'Lusòlo', 'Mùuyà', 'Lumùngùlù', 'Lufuimi', 'Kabàlàshìpù', 'Lùshìkà', 'Lutongolo', 'Lungùdi', 'Kaswèkèsè', 'Ciswà'],
+ 'months_short' => ['Cio', 'Lui', 'Lus', 'Muu', 'Lum', 'Luf', 'Kab', 'Lush', 'Lut', 'Lun', 'Kas', 'Cis'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/luo.php b/vendor/nesbot/carbon/src/Carbon/Lang/luo.php
new file mode 100644
index 0000000..b55af73
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/luo.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['OD', 'OT'],
+ 'weekdays' => ['Jumapil', 'Wuok Tich', 'Tich Ariyo', 'Tich Adek', 'Tich Ang’wen', 'Tich Abich', 'Ngeso'],
+ 'weekdays_short' => ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'],
+ 'weekdays_min' => ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'],
+ 'months' => ['Dwe mar Achiel', 'Dwe mar Ariyo', 'Dwe mar Adek', 'Dwe mar Ang’wen', 'Dwe mar Abich', 'Dwe mar Auchiel', 'Dwe mar Abiriyo', 'Dwe mar Aboro', 'Dwe mar Ochiko', 'Dwe mar Apar', 'Dwe mar gi achiel', 'Dwe mar Apar gi ariyo'],
+ 'months_short' => ['DAC', 'DAR', 'DAD', 'DAN', 'DAH', 'DAU', 'DAO', 'DAB', 'DOC', 'DAP', 'DGI', 'DAG'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => 'higni :count',
+ 'y' => 'higni :count',
+ 'a_year' => ':higni :count',
+
+ 'month' => 'dweche :count',
+ 'm' => 'dweche :count',
+ 'a_month' => 'dweche :count',
+
+ 'week' => 'jumbe :count',
+ 'w' => 'jumbe :count',
+ 'a_week' => 'jumbe :count',
+
+ 'day' => 'ndalo :count',
+ 'd' => 'ndalo :count',
+ 'a_day' => 'ndalo :count',
+
+ 'hour' => 'seche :count',
+ 'h' => 'seche :count',
+ 'a_hour' => 'seche :count',
+
+ 'minute' => 'dakika :count',
+ 'min' => 'dakika :count',
+ 'a_minute' => 'dakika :count',
+
+ 'second' => 'nus dakika :count',
+ 's' => 'nus dakika :count',
+ 'a_second' => 'nus dakika :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/luy.php b/vendor/nesbot/carbon/src/Carbon/Lang/luy.php
new file mode 100644
index 0000000..2b37e3e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/luy.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Jumapiri', 'Jumatatu', 'Jumanne', 'Jumatano', 'Murwa wa Kanne', 'Murwa wa Katano', 'Jumamosi'],
+ 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'],
+ 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ // Too unreliable
+ /*
+ 'year' => ':count liliino', // less reliable
+ 'y' => ':count liliino', // less reliable
+ 'a_year' => ':count liliino', // less reliable
+
+ 'month' => ':count kumwesi', // less reliable
+ 'm' => ':count kumwesi', // less reliable
+ 'a_month' => ':count kumwesi', // less reliable
+
+ 'week' => ':count olutambi', // less reliable
+ 'w' => ':count olutambi', // less reliable
+ 'a_week' => ':count olutambi', // less reliable
+
+ 'day' => ':count luno', // less reliable
+ 'd' => ':count luno', // less reliable
+ 'a_day' => ':count luno', // less reliable
+
+ 'hour' => ':count ekengele', // less reliable
+ 'h' => ':count ekengele', // less reliable
+ 'a_hour' => ':count ekengele', // less reliable
+
+ 'minute' => ':count omundu', // less reliable
+ 'min' => ':count omundu', // less reliable
+ 'a_minute' => ':count omundu', // less reliable
+
+ 'second' => ':count liliino', // less reliable
+ 's' => ':count liliino', // less reliable
+ 'a_second' => ':count liliino', // less reliable
+ */
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lv.php b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php
new file mode 100644
index 0000000..d5cba7c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lv.php
@@ -0,0 +1,183 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Carbon\CarbonInterface;
+
+/**
+ * This file is part of the Carbon package.
+ *
+ * (c) Brian Nesbitt
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - pirminis
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Andris Zāģeris
+ * - Max Melentiev
+ * - Edgars Beigarts
+ * - Juanito Fatas
+ * - Vitauts Stočka
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Kaspars Bankovskis
+ * - Nicolás Hock Isaza
+ * - Viesturs Kavacs (Kavacky)
+ * - zakse
+ * - Janis Eglitis (janiseglitis)
+ * - Guntars
+ * - Juris Sudmalis
+ */
+$daysOfWeek = ['svētdiena', 'pirmdiena', 'otrdiena', 'trešdiena', 'ceturtdiena', 'piektdiena', 'sestdiena'];
+$daysOfWeekLocativum = ['svētdien', 'pirmdien', 'otrdien', 'trešdien', 'ceturtdien', 'piektdien', 'sestdien'];
+
+$transformDiff = function ($input) {
+ return strtr($input, [
+ // Nominative => "pirms/pēc" Dative
+ 'gads' => 'gada',
+ 'gadi' => 'gadiem',
+ 'gadu' => 'gadiem',
+ 'mēnesis' => 'mēneša',
+ 'mēneši' => 'mēnešiem',
+ 'mēnešu' => 'mēnešiem',
+ 'nedēļa' => 'nedēļas',
+ 'nedēļas' => 'nedēļām',
+ 'nedēļu' => 'nedēļām',
+ 'diena' => 'dienas',
+ 'dienas' => 'dienām',
+ 'dienu' => 'dienām',
+ 'stunda' => 'stundas',
+ 'stundas' => 'stundām',
+ 'stundu' => 'stundām',
+ 'minūte' => 'minūtes',
+ 'minūtes' => 'minūtēm',
+ 'minūšu' => 'minūtēm',
+ 'sekunde' => 'sekundes',
+ 'sekundes' => 'sekundēm',
+ 'sekunžu' => 'sekundēm',
+ ]);
+};
+
+return [
+ 'ago' => function ($time) use ($transformDiff) {
+ return 'pirms '.$transformDiff($time);
+ },
+ 'from_now' => function ($time) use ($transformDiff) {
+ return 'pēc '.$transformDiff($time);
+ },
+
+ 'year' => '0 gadu|:count gads|:count gadi',
+ 'y' => ':count g.',
+ 'a_year' => '{1}gads|0 gadu|:count gads|:count gadi',
+ 'month' => '0 mēnešu|:count mēnesis|:count mēneši',
+ 'm' => ':count mēn.',
+ 'a_month' => '{1}mēnesis|0 mēnešu|:count mēnesis|:count mēneši',
+ 'week' => '0 nedēļu|:count nedēļa|:count nedēļas',
+ 'w' => ':count ned.',
+ 'a_week' => '{1}nedēļa|0 nedēļu|:count nedēļa|:count nedēļas',
+ 'day' => '0 dienu|:count diena|:count dienas',
+ 'd' => ':count d.',
+ 'a_day' => '{1}diena|0 dienu|:count diena|:count dienas',
+ 'hour' => '0 stundu|:count stunda|:count stundas',
+ 'h' => ':count st.',
+ 'a_hour' => '{1}stunda|0 stundu|:count stunda|:count stundas',
+ 'minute' => '0 minūšu|:count minūte|:count minūtes',
+ 'min' => ':count min.',
+ 'a_minute' => '{1}minūte|0 minūšu|:count minūte|:count minūtes',
+ 'second' => '0 sekunžu|:count sekunde|:count sekundes',
+ 's' => ':count sek.',
+ 'a_second' => '{1}sekunde|0 sekunžu|:count sekunde|:count sekundes',
+
+ 'after' => ':time vēlāk',
+ 'year_after' => '0 gadus|:count gadu|:count gadus',
+ 'a_year_after' => '{1}gadu|0 gadus|:count gadu|:count gadus',
+ 'month_after' => '0 mēnešus|:count mēnesi|:count mēnešus',
+ 'a_month_after' => '{1}mēnesi|0 mēnešus|:count mēnesi|:count mēnešus',
+ 'week_after' => '0 nedēļas|:count nedēļu|:count nedēļas',
+ 'a_week_after' => '{1}nedēļu|0 nedēļas|:count nedēļu|:count nedēļas',
+ 'day_after' => '0 dienas|:count dienu|:count dienas',
+ 'a_day_after' => '{1}dienu|0 dienas|:count dienu|:count dienas',
+ 'hour_after' => '0 stundas|:count stundu|:count stundas',
+ 'a_hour_after' => '{1}stundu|0 stundas|:count stundu|:count stundas',
+ 'minute_after' => '0 minūtes|:count minūti|:count minūtes',
+ 'a_minute_after' => '{1}minūti|0 minūtes|:count minūti|:count minūtes',
+ 'second_after' => '0 sekundes|:count sekundi|:count sekundes',
+ 'a_second_after' => '{1}sekundi|0 sekundes|:count sekundi|:count sekundes',
+
+ 'before' => ':time agrāk',
+ 'year_before' => '0 gadus|:count gadu|:count gadus',
+ 'a_year_before' => '{1}gadu|0 gadus|:count gadu|:count gadus',
+ 'month_before' => '0 mēnešus|:count mēnesi|:count mēnešus',
+ 'a_month_before' => '{1}mēnesi|0 mēnešus|:count mēnesi|:count mēnešus',
+ 'week_before' => '0 nedēļas|:count nedēļu|:count nedēļas',
+ 'a_week_before' => '{1}nedēļu|0 nedēļas|:count nedēļu|:count nedēļas',
+ 'day_before' => '0 dienas|:count dienu|:count dienas',
+ 'a_day_before' => '{1}dienu|0 dienas|:count dienu|:count dienas',
+ 'hour_before' => '0 stundas|:count stundu|:count stundas',
+ 'a_hour_before' => '{1}stundu|0 stundas|:count stundu|:count stundas',
+ 'minute_before' => '0 minūtes|:count minūti|:count minūtes',
+ 'a_minute_before' => '{1}minūti|0 minūtes|:count minūti|:count minūtes',
+ 'second_before' => '0 sekundes|:count sekundi|:count sekundes',
+ 'a_second_before' => '{1}sekundi|0 sekundes|:count sekundi|:count sekundes',
+
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' un '],
+
+ 'diff_now' => 'tagad',
+ 'diff_today' => 'šodien',
+ 'diff_yesterday' => 'vakar',
+ 'diff_before_yesterday' => 'aizvakar',
+ 'diff_tomorrow' => 'rīt',
+ 'diff_after_tomorrow' => 'parīt',
+
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY.',
+ 'LL' => 'YYYY. [gada] D. MMMM',
+ 'LLL' => 'DD.MM.YYYY., HH:mm',
+ 'LLLL' => 'YYYY. [gada] D. MMMM, HH:mm',
+ ],
+
+ 'calendar' => [
+ 'sameDay' => '[šodien] [plkst.] LT',
+ 'nextDay' => '[rīt] [plkst.] LT',
+ 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) use ($daysOfWeekLocativum) {
+ if ($current->week !== $other->week) {
+ return '[nākošo] ['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT';
+ }
+
+ return '['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT';
+ },
+ 'lastDay' => '[vakar] [plkst.] LT',
+ 'lastWeek' => function (CarbonInterface $current) use ($daysOfWeekLocativum) {
+ return '[pagājušo] ['.$daysOfWeekLocativum[$current->dayOfWeek].'] [plkst.] LT';
+ },
+ 'sameElse' => 'L',
+ ],
+
+ 'weekdays' => $daysOfWeek,
+ 'weekdays_short' => ['Sv.', 'P.', 'O.', 'T.', 'C.', 'Pk.', 'S.'],
+ 'weekdays_min' => ['Sv.', 'P.', 'O.', 'T.', 'C.', 'Pk.', 'S.'],
+ 'months' => ['janvāris', 'februāris', 'marts', 'aprīlis', 'maijs', 'jūnijs', 'jūlijs', 'augusts', 'septembris', 'oktobris', 'novembris', 'decembris'],
+ 'months_standalone' => ['janvārī', 'februārī', 'martā', 'aprīlī', 'maijā', 'jūnijā', 'jūlijā', 'augustā', 'septembrī', 'oktobrī', 'novembrī', 'decembrī'],
+ 'months_short' => ['janv.', 'febr.', 'martā', 'apr.', 'maijā', 'jūn.', 'jūl.', 'aug.', 'sept.', 'okt.', 'nov.', 'dec.'],
+ 'meridiem' => ['priekšpusdiena', 'pēcpusdiena'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php b/vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php
new file mode 100644
index 0000000..ee91c36
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lv_LV.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/lv.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lzh.php b/vendor/nesbot/carbon/src/Carbon/Lang/lzh.php
new file mode 100644
index 0000000..1180c6b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lzh.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/lzh_TW.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php
new file mode 100644
index 0000000..3b1493e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/lzh_TW.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'OY[年]MMMMOD[日]',
+ ],
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => [' 一 ', ' 二 ', ' 三 ', ' 四 ', ' 五 ', ' 六 ', ' 七 ', ' 八 ', ' 九 ', ' 十 ', '十一', '十二'],
+ 'weekdays' => ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
+ 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['〇', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '廿', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '卅', '卅一'],
+ 'meridiem' => ['朝', '暮'],
+
+ 'year' => ':count 夏', // less reliable
+ 'y' => ':count 夏', // less reliable
+ 'a_year' => ':count 夏', // less reliable
+
+ 'month' => ':count 月', // less reliable
+ 'm' => ':count 月', // less reliable
+ 'a_month' => ':count 月', // less reliable
+
+ 'hour' => ':count 氧', // less reliable
+ 'h' => ':count 氧', // less reliable
+ 'a_hour' => ':count 氧', // less reliable
+
+ 'minute' => ':count 點', // less reliable
+ 'min' => ':count 點', // less reliable
+ 'a_minute' => ':count 點', // less reliable
+
+ 'second' => ':count 楚', // less reliable
+ 's' => ':count 楚', // less reliable
+ 'a_second' => ':count 楚', // less reliable
+
+ 'week' => ':count 星期',
+ 'w' => ':count 星期',
+ 'a_week' => ':count 星期',
+
+ 'day' => ':count 日(曆法)',
+ 'd' => ':count 日(曆法)',
+ 'a_day' => ':count 日(曆法)',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mag.php b/vendor/nesbot/carbon/src/Carbon/Lang/mag.php
new file mode 100644
index 0000000..7532436
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mag.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mag_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php
new file mode 100644
index 0000000..193f67a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mag_IN.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bhashaghar@googlegroups.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'weekdays' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'],
+ 'weekdays_short' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'],
+ 'weekdays_min' => ['एतवार', 'सोमार', 'मंगर', 'बुध', 'बिफे', 'सूक', 'सनिचर'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mai.php b/vendor/nesbot/carbon/src/Carbon/Lang/mai.php
new file mode 100644
index 0000000..792b973
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mai.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mai_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php
new file mode 100644
index 0000000..03049d4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mai_IN.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Maithili Computing Research Center, Pune, India rajeshkajha@yahoo.com,akhilesh.k@samusng.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['बैसाख', 'जेठ', 'अषाढ़', 'सावोन', 'भादो', 'आसिन', 'कातिक', 'अगहन', 'पूस', 'माघ', 'फागुन', 'चैति'],
+ 'months_short' => ['बैसाख', 'जेठ', 'अषाढ़', 'सावोन', 'भादो', 'आसिन', 'कातिक', 'अगहन', 'पूस', 'माघ', 'फागुन', 'चैति'],
+ 'weekdays' => ['रविदिन', 'सोमदिन', 'मंगलदिन', 'बुधदिन', 'बृहस्पतीदिन', 'शुक्रदिन', 'शनीदिन'],
+ 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पती', 'शुक्र', 'शनी'],
+ 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पती', 'शुक्र', 'शनी'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+
+ 'year' => ':count ऋतु', // less reliable
+ 'y' => ':count ऋतु', // less reliable
+ 'a_year' => ':count ऋतु', // less reliable
+
+ 'month' => ':count महिना',
+ 'm' => ':count महिना',
+ 'a_month' => ':count महिना',
+
+ 'week' => ':count श्रेणी:क्यालेन्डर', // less reliable
+ 'w' => ':count श्रेणी:क्यालेन्डर', // less reliable
+ 'a_week' => ':count श्रेणी:क्यालेन्डर', // less reliable
+
+ 'day' => ':count दिन',
+ 'd' => ':count दिन',
+ 'a_day' => ':count दिन',
+
+ 'hour' => ':count घण्टा',
+ 'h' => ':count घण्टा',
+ 'a_hour' => ':count घण्टा',
+
+ 'minute' => ':count समय', // less reliable
+ 'min' => ':count समय', // less reliable
+ 'a_minute' => ':count समय', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mas.php b/vendor/nesbot/carbon/src/Carbon/Lang/mas.php
new file mode 100644
index 0000000..cbd610c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mas.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Ɛnkakɛnyá', 'Ɛndámâ'],
+ 'weekdays' => ['Jumapílí', 'Jumatátu', 'Jumane', 'Jumatánɔ', 'Alaámisi', 'Jumáa', 'Jumamósi'],
+ 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'months' => ['Oladalʉ́', 'Arát', 'Ɔɛnɨ́ɔɨŋɔk', 'Olodoyíóríê inkókúâ', 'Oloilépūnyīē inkókúâ', 'Kújúɔrɔk', 'Mórusásin', 'Ɔlɔ́ɨ́bɔ́rárɛ', 'Kúshîn', 'Olgísan', 'Pʉshʉ́ka', 'Ntʉ́ŋʉ́s'],
+ 'months_short' => ['Dal', 'Ará', 'Ɔɛn', 'Doy', 'Lép', 'Rok', 'Sás', 'Bɔ́r', 'Kús', 'Gís', 'Shʉ́', 'Ntʉ́'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count olameyu', // less reliable
+ 'y' => ':count olameyu', // less reliable
+ 'a_year' => ':count olameyu', // less reliable
+
+ 'week' => ':count engolongeare orwiki', // less reliable
+ 'w' => ':count engolongeare orwiki', // less reliable
+ 'a_week' => ':count engolongeare orwiki', // less reliable
+
+ 'hour' => ':count esahabu', // less reliable
+ 'h' => ':count esahabu', // less reliable
+ 'a_hour' => ':count esahabu', // less reliable
+
+ 'second' => ':count are', // less reliable
+ 's' => ':count are', // less reliable
+ 'a_second' => ':count are', // less reliable
+
+ 'month' => ':count olapa',
+ 'm' => ':count olapa',
+ 'a_month' => ':count olapa',
+
+ 'day' => ':count enkolongʼ',
+ 'd' => ':count enkolongʼ',
+ 'a_day' => ':count enkolongʼ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php
new file mode 100644
index 0000000..56e2905
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mas_TZ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/mas.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mer.php b/vendor/nesbot/carbon/src/Carbon/Lang/mer.php
new file mode 100644
index 0000000..2e14597
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mer.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['RŨ', 'ŨG'],
+ 'weekdays' => ['Kiumia', 'Muramuko', 'Wairi', 'Wethatu', 'Wena', 'Wetano', 'Jumamosi'],
+ 'weekdays_short' => ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'],
+ 'weekdays_min' => ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'],
+ 'months' => ['Januarĩ', 'Feburuarĩ', 'Machi', 'Ĩpurũ', 'Mĩĩ', 'Njuni', 'Njuraĩ', 'Agasti', 'Septemba', 'Oktũba', 'Novemba', 'Dicemba'],
+ 'months_short' => ['JAN', 'FEB', 'MAC', 'ĨPU', 'MĨĨ', 'NJU', 'NJR', 'AGA', 'SPT', 'OKT', 'NOV', 'DEC'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count murume', // less reliable
+ 'y' => ':count murume', // less reliable
+ 'a_year' => ':count murume', // less reliable
+
+ 'month' => ':count muchaara', // less reliable
+ 'm' => ':count muchaara', // less reliable
+ 'a_month' => ':count muchaara', // less reliable
+
+ 'minute' => ':count monto', // less reliable
+ 'min' => ':count monto', // less reliable
+ 'a_minute' => ':count monto', // less reliable
+
+ 'second' => ':count gikeno', // less reliable
+ 's' => ':count gikeno', // less reliable
+ 'a_second' => ':count gikeno', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mfe.php b/vendor/nesbot/carbon/src/Carbon/Lang/mfe.php
new file mode 100644
index 0000000..4d6e6b6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mfe.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mfe_MU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php b/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php
new file mode 100644
index 0000000..2d27b45
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mfe_MU.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['zanvie', 'fevriye', 'mars', 'avril', 'me', 'zin', 'zilye', 'out', 'septam', 'oktob', 'novam', 'desam'],
+ 'months_short' => ['zan', 'fev', 'mar', 'avr', 'me', 'zin', 'zil', 'out', 'sep', 'okt', 'nov', 'des'],
+ 'weekdays' => ['dimans', 'lindi', 'mardi', 'merkredi', 'zedi', 'vandredi', 'samdi'],
+ 'weekdays_short' => ['dim', 'lin', 'mar', 'mer', 'ze', 'van', 'sam'],
+ 'weekdays_min' => ['dim', 'lin', 'mar', 'mer', 'ze', 'van', 'sam'],
+
+ 'year' => ':count banané',
+ 'y' => ':count banané',
+ 'a_year' => ':count banané',
+
+ 'month' => ':count mwa',
+ 'm' => ':count mwa',
+ 'a_month' => ':count mwa',
+
+ 'week' => ':count sémenn',
+ 'w' => ':count sémenn',
+ 'a_week' => ':count sémenn',
+
+ 'day' => ':count zour',
+ 'd' => ':count zour',
+ 'a_day' => ':count zour',
+
+ 'hour' => ':count -er-tan',
+ 'h' => ':count -er-tan',
+ 'a_hour' => ':count -er-tan',
+
+ 'minute' => ':count minitt',
+ 'min' => ':count minitt',
+ 'a_minute' => ':count minitt',
+
+ 'second' => ':count déziém',
+ 's' => ':count déziém',
+ 'a_second' => ':count déziém',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mg.php b/vendor/nesbot/carbon/src/Carbon/Lang/mg.php
new file mode 100644
index 0000000..40bc2a8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mg.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mg_MG.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php b/vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php
new file mode 100644
index 0000000..6a14535
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mg_MG.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - The Debian Project modified by GNU//Linux Malagasy Rado Ramarotafika,Do-Risika RAFIEFERANTSIARONJY rado@linuxmg.org,dourix@free.fr
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Janoary', 'Febroary', 'Martsa', 'Aprily', 'Mey', 'Jona', 'Jolay', 'Aogositra', 'Septambra', 'Oktobra', 'Novambra', 'Desambra'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mey', 'Jon', 'Jol', 'Aog', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['alahady', 'alatsinainy', 'talata', 'alarobia', 'alakamisy', 'zoma', 'sabotsy'],
+ 'weekdays_short' => ['lhd', 'lts', 'tlt', 'lrb', 'lkm', 'zom', 'sab'],
+ 'weekdays_min' => ['lhd', 'lts', 'tlt', 'lrb', 'lkm', 'zom', 'sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'minute' => ':count minitra', // less reliable
+ 'min' => ':count minitra', // less reliable
+ 'a_minute' => ':count minitra', // less reliable
+
+ 'year' => ':count taona',
+ 'y' => ':count taona',
+ 'a_year' => ':count taona',
+
+ 'month' => ':count volana',
+ 'm' => ':count volana',
+ 'a_month' => ':count volana',
+
+ 'week' => ':count herinandro',
+ 'w' => ':count herinandro',
+ 'a_week' => ':count herinandro',
+
+ 'day' => ':count andro',
+ 'd' => ':count andro',
+ 'a_day' => ':count andro',
+
+ 'hour' => ':count ora',
+ 'h' => ':count ora',
+ 'a_hour' => ':count ora',
+
+ 'second' => ':count segondra',
+ 's' => ':count segondra',
+ 'a_second' => ':count segondra',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php b/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php
new file mode 100644
index 0000000..2a80960
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mgh.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['wichishu', 'mchochil’l'],
+ 'weekdays' => ['Sabato', 'Jumatatu', 'Jumanne', 'Jumatano', 'Arahamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'],
+ 'months' => ['Mweri wo kwanza', 'Mweri wo unayeli', 'Mweri wo uneraru', 'Mweri wo unecheshe', 'Mweri wo unethanu', 'Mweri wo thanu na mocha', 'Mweri wo saba', 'Mweri wo nane', 'Mweri wo tisa', 'Mweri wo kumi', 'Mweri wo kumi na moja', 'Mweri wo kumi na yel’li'],
+ 'months_short' => ['Kwa', 'Una', 'Rar', 'Che', 'Tha', 'Moc', 'Sab', 'Nan', 'Tis', 'Kum', 'Moj', 'Yel'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mgo.php b/vendor/nesbot/carbon/src/Carbon/Lang/mgo.php
new file mode 100644
index 0000000..a126c9f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mgo.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5', 'Aneg 6', 'Aneg 7'],
+ 'weekdays_short' => ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5', 'Aneg 6', 'Aneg 7'],
+ 'weekdays_min' => ['1', '2', '3', '4', '5', '6', '7'],
+ 'months' => ['iməg mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi', 'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ', 'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò', 'iməg krizmed'],
+ 'months_short' => ['mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi', 'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ', 'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò', 'iməg krizmed'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'dddd, YYYY MMMM DD HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mhr.php b/vendor/nesbot/carbon/src/Carbon/Lang/mhr.php
new file mode 100644
index 0000000..6bbc9f6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mhr.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mhr_RU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php
new file mode 100644
index 0000000..309ead9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mhr_RU.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - PeshSajSoft Ltd. Vyacheslav Kileev slavakileev@yandex.ru
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY.MM.DD',
+ ],
+ 'months' => ['Шорыкйол', 'Пургыж', 'Ӱярня', 'Вӱдшор', 'Ага', 'Пеледыш', 'Сӱрем', 'Сорла', 'Идым', 'Шыжа', 'Кылме', 'Теле'],
+ 'months_short' => ['Шрк', 'Пгж', 'Ӱрн', 'Вшр', 'Ага', 'Пдш', 'Срм', 'Срл', 'Идм', 'Шыж', 'Клм', 'Тел'],
+ 'weekdays' => ['Рушарня', 'Шочмо', 'Кушкыжмо', 'Вӱргече', 'Изарня', 'Кугарня', 'Шуматкече'],
+ 'weekdays_short' => ['Ршр', 'Шчм', 'Кжм', 'Вгч', 'Изр', 'Кгр', 'Шмт'],
+ 'weekdays_min' => ['Ршр', 'Шчм', 'Кжм', 'Вгч', 'Изр', 'Кгр', 'Шмт'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count идалык',
+ 'y' => ':count идалык',
+ 'a_year' => ':count идалык',
+
+ 'month' => ':count Тылзе',
+ 'm' => ':count Тылзе',
+ 'a_month' => ':count Тылзе',
+
+ 'week' => ':count арня',
+ 'w' => ':count арня',
+ 'a_week' => ':count арня',
+
+ 'day' => ':count кече',
+ 'd' => ':count кече',
+ 'a_day' => ':count кече',
+
+ 'hour' => ':count час',
+ 'h' => ':count час',
+ 'a_hour' => ':count час',
+
+ 'minute' => ':count минут',
+ 'min' => ':count минут',
+ 'a_minute' => ':count минут',
+
+ 'second' => ':count кокымшан',
+ 's' => ':count кокымшан',
+ 'a_second' => ':count кокымшан',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mi.php b/vendor/nesbot/carbon/src/Carbon/Lang/mi.php
new file mode 100644
index 0000000..b7f51ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mi.php
@@ -0,0 +1,66 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - John Corrigan
+ * - François B
+ */
+return [
+ 'year' => ':count tau',
+ 'a_year' => '{1}he tau|:count tau',
+ 'month' => ':count marama',
+ 'a_month' => '{1}he marama|:count marama',
+ 'week' => ':count wiki',
+ 'a_week' => '{1}he wiki|:count wiki',
+ 'day' => ':count ra',
+ 'a_day' => '{1}he ra|:count ra',
+ 'hour' => ':count haora',
+ 'a_hour' => '{1}te haora|:count haora',
+ 'minute' => ':count meneti',
+ 'a_minute' => '{1}he meneti|:count meneti',
+ 'second' => ':count hēkona',
+ 'a_second' => '{1}te hēkona ruarua|:count hēkona',
+ 'ago' => ':time i mua',
+ 'from_now' => 'i roto i :time',
+ 'diff_yesterday' => 'inanahi',
+ 'diff_yesterday_regexp' => 'inanahi(?:\\s+i)?',
+ 'diff_today' => 'i teie',
+ 'diff_today_regexp' => 'i teie(?:\\s+mahana,)?(?:\\s+i)?',
+ 'diff_tomorrow' => 'apopo',
+ 'diff_tomorrow_regexp' => 'apopo(?:\\s+i)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY [i] HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY [i] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[i teie mahana, i] LT',
+ 'nextDay' => '[apopo i] LT',
+ 'nextWeek' => 'dddd [i] LT',
+ 'lastDay' => '[inanahi i] LT',
+ 'lastWeek' => 'dddd [whakamutunga i] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberº',
+ 'months' => ['Kohi-tāte', 'Hui-tanguru', 'Poutū-te-rangi', 'Paenga-whāwhā', 'Haratua', 'Pipiri', 'Hōngoingoi', 'Here-turi-kōkā', 'Mahuru', 'Whiringa-ā-nuku', 'Whiringa-ā-rangi', 'Hakihea'],
+ 'months_short' => ['Kohi', 'Hui', 'Pou', 'Pae', 'Hara', 'Pipi', 'Hōngoi', 'Here', 'Mahu', 'Whi-nu', 'Whi-ra', 'Haki'],
+ 'weekdays' => ['Rātapu', 'Mane', 'Tūrei', 'Wenerei', 'Tāite', 'Paraire', 'Hātarei'],
+ 'weekdays_short' => ['Ta', 'Ma', 'Tū', 'We', 'Tāi', 'Pa', 'Hā'],
+ 'weekdays_min' => ['Ta', 'Ma', 'Tū', 'We', 'Tāi', 'Pa', 'Hā'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' me te '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php
new file mode 100644
index 0000000..6b964e3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mi_NZ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/mi.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/miq.php b/vendor/nesbot/carbon/src/Carbon/Lang/miq.php
new file mode 100644
index 0000000..51e5a98
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/miq.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/miq_NI.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php b/vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php
new file mode 100644
index 0000000..57faa31
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/miq_NI.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['siakwa kati', 'kuswa kati', 'kakamuk kati', 'lî wainhka kati', 'lih mairin kati', 'lî kati', 'pastara kati', 'sikla kati', 'wîs kati', 'waupasa kati', 'yahbra kati', 'trisu kati'],
+ 'months_short' => ['siakwa kati', 'kuswa kati', 'kakamuk kati', 'lî wainhka kati', 'lih mairin kati', 'lî kati', 'pastara kati', 'sikla kati', 'wîs kati', 'waupasa kati', 'yahbra kati', 'trisu kati'],
+ 'weekdays' => ['sandi', 'mundi', 'tiusdi', 'wensde', 'tausde', 'praidi', 'satadi'],
+ 'weekdays_short' => ['san', 'mun', 'tius', 'wens', 'taus', 'prai', 'sat'],
+ 'weekdays_min' => ['san', 'mun', 'tius', 'wens', 'taus', 'prai', 'sat'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 7,
+ 'meridiem' => ['VM', 'NM'],
+
+ 'month' => ':count kati', // less reliable
+ 'm' => ':count kati', // less reliable
+ 'a_month' => ':count kati', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mjw.php b/vendor/nesbot/carbon/src/Carbon/Lang/mjw.php
new file mode 100644
index 0000000..617154c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mjw.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mjw_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php
new file mode 100644
index 0000000..58ed0d1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mjw_IN.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Jor Teron bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['Arkoi', 'Thangthang', 'There', 'Jangmi', 'Aru', 'Vosik', 'Jakhong', 'Paipai', 'Chiti', 'Phere', 'Phaikuni', 'Matijong'],
+ 'months_short' => ['Ark', 'Thang', 'The', 'Jang', 'Aru', 'Vos', 'Jak', 'Pai', 'Chi', 'Phe', 'Phai', 'Mati'],
+ 'weekdays' => ['Bhomkuru', 'Urmi', 'Durmi', 'Thelang', 'Theman', 'Bhomta', 'Bhomti'],
+ 'weekdays_short' => ['Bhom', 'Ur', 'Dur', 'Tkel', 'Tkem', 'Bhta', 'Bhti'],
+ 'weekdays_min' => ['Bhom', 'Ur', 'Dur', 'Tkel', 'Tkem', 'Bhta', 'Bhti'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mk.php b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php
new file mode 100644
index 0000000..d822de0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mk.php
@@ -0,0 +1,116 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Sashko Todorov
+ * - Josh Soref
+ * - François B
+ * - Serhan Apaydın
+ * - Borislav Mickov
+ * - JD Isaacks
+ * - Tomi Atanasoski
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count година|:count години',
+ 'a_year' => 'година|:count години',
+ 'y' => ':count год.',
+ 'month' => ':count месец|:count месеци',
+ 'a_month' => 'месец|:count месеци',
+ 'm' => ':count месец|:count месеци',
+ 'week' => ':count седмица|:count седмици',
+ 'a_week' => 'седмица|:count седмици',
+ 'w' => ':count седмица|:count седмици',
+ 'day' => ':count ден|:count дена',
+ 'a_day' => 'ден|:count дена',
+ 'd' => ':count ден|:count дена',
+ 'hour' => ':count час|:count часа',
+ 'a_hour' => 'час|:count часа',
+ 'h' => ':count час|:count часа',
+ 'minute' => ':count минута|:count минути',
+ 'a_minute' => 'минута|:count минути',
+ 'min' => ':count мин.',
+ 'second' => ':count секунда|:count секунди',
+ 'a_second' => 'неколку секунди|:count секунди',
+ 's' => ':count сек.',
+ 'ago' => 'пред :time',
+ 'from_now' => 'после :time',
+ 'after' => 'по :time',
+ 'before' => 'пред :time',
+ 'diff_now' => 'сега',
+ 'diff_today' => 'Денес',
+ 'diff_today_regexp' => 'Денес(?:\\s+во)?',
+ 'diff_yesterday' => 'вчера',
+ 'diff_yesterday_regexp' => 'Вчера(?:\\s+во)?',
+ 'diff_tomorrow' => 'утре',
+ 'diff_tomorrow_regexp' => 'Утре(?:\\s+во)?',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'D.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Денес во] LT',
+ 'nextDay' => '[Утре во] LT',
+ 'nextWeek' => '[Во] dddd [во] LT',
+ 'lastDay' => '[Вчера во] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ case 3:
+ case 6:
+ return '[Изминатата] dddd [во] LT';
+ default:
+ return '[Изминатиот] dddd [во] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ $lastDigit = $number % 10;
+ $last2Digits = $number % 100;
+ if ($number === 0) {
+ return $number.'-ев';
+ }
+ if ($last2Digits === 0) {
+ return $number.'-ен';
+ }
+ if ($last2Digits > 10 && $last2Digits < 20) {
+ return $number.'-ти';
+ }
+ if ($lastDigit === 1) {
+ return $number.'-ви';
+ }
+ if ($lastDigit === 2) {
+ return $number.'-ри';
+ }
+ if ($lastDigit === 7 || $lastDigit === 8) {
+ return $number.'-ми';
+ }
+
+ return $number.'-ти';
+ },
+ 'months' => ['јануари', 'февруари', 'март', 'април', 'мај', 'јуни', 'јули', 'август', 'септември', 'октомври', 'ноември', 'декември'],
+ 'months_short' => ['јан', 'фев', 'мар', 'апр', 'мај', 'јун', 'јул', 'авг', 'сеп', 'окт', 'ное', 'дек'],
+ 'weekdays' => ['недела', 'понеделник', 'вторник', 'среда', 'четврток', 'петок', 'сабота'],
+ 'weekdays_short' => ['нед', 'пон', 'вто', 'сре', 'чет', 'пет', 'саб'],
+ 'weekdays_min' => ['нe', 'пo', 'вт', 'ср', 'че', 'пе', 'сa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' и '],
+ 'meridiem' => ['АМ', 'ПМ'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php b/vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php
new file mode 100644
index 0000000..95e2ff9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mk_MK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/mk.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ml.php b/vendor/nesbot/carbon/src/Carbon/Lang/ml.php
new file mode 100644
index 0000000..1abd6c4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ml.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - JD Isaacks
+ */
+return [
+ 'year' => ':count വർഷം',
+ 'a_year' => 'ഒരു വർഷം|:count വർഷം',
+ 'month' => ':count മാസം',
+ 'a_month' => 'ഒരു മാസം|:count മാസം',
+ 'week' => ':count ആഴ്ച',
+ 'a_week' => 'ഒരാഴ്ച|:count ആഴ്ച',
+ 'day' => ':count ദിവസം',
+ 'a_day' => 'ഒരു ദിവസം|:count ദിവസം',
+ 'hour' => ':count മണിക്കൂർ',
+ 'a_hour' => 'ഒരു മണിക്കൂർ|:count മണിക്കൂർ',
+ 'minute' => ':count മിനിറ്റ്',
+ 'a_minute' => 'ഒരു മിനിറ്റ്|:count മിനിറ്റ്',
+ 'second' => ':count സെക്കൻഡ്',
+ 'a_second' => 'അൽപ നിമിഷങ്ങൾ|:count സെക്കൻഡ്',
+ 'ago' => ':time മുൻപ്',
+ 'from_now' => ':time കഴിഞ്ഞ്',
+ 'diff_now' => 'ഇപ്പോൾ',
+ 'diff_today' => 'ഇന്ന്',
+ 'diff_yesterday' => 'ഇന്നലെ',
+ 'diff_tomorrow' => 'നാളെ',
+ 'formats' => [
+ 'LT' => 'A h:mm -നു',
+ 'LTS' => 'A h:mm:ss -നു',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm -നു',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm -നു',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ഇന്ന്] LT',
+ 'nextDay' => '[നാളെ] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[ഇന്നലെ] LT',
+ 'lastWeek' => '[കഴിഞ്ഞ] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'രാത്രി';
+ }
+ if ($hour < 12) {
+ return 'രാവിലെ';
+ }
+ if ($hour < 17) {
+ return 'ഉച്ച കഴിഞ്ഞ്';
+ }
+ if ($hour < 20) {
+ return 'വൈകുന്നേരം';
+ }
+
+ return 'രാത്രി';
+ },
+ 'months' => ['ജനുവരി', 'ഫെബ്രുവരി', 'മാർച്ച്', 'ഏപ്രിൽ', 'മേയ്', 'ജൂൺ', 'ജൂലൈ', 'ഓഗസ്റ്റ്', 'സെപ്റ്റംബർ', 'ഒക്ടോബർ', 'നവംബർ', 'ഡിസംബർ'],
+ 'months_short' => ['ജനു.', 'ഫെബ്രു.', 'മാർ.', 'ഏപ്രി.', 'മേയ്', 'ജൂൺ', 'ജൂലൈ.', 'ഓഗ.', 'സെപ്റ്റ.', 'ഒക്ടോ.', 'നവം.', 'ഡിസം.'],
+ 'weekdays' => ['ഞായറാഴ്ച', 'തിങ്കളാഴ്ച', 'ചൊവ്വാഴ്ച', 'ബുധനാഴ്ച', 'വ്യാഴാഴ്ച', 'വെള്ളിയാഴ്ച', 'ശനിയാഴ്ച'],
+ 'weekdays_short' => ['ഞായർ', 'തിങ്കൾ', 'ചൊവ്വ', 'ബുധൻ', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
+ 'weekdays_min' => ['ഞാ', 'തി', 'ചൊ', 'ബു', 'വ്യാ', 'വെ', 'ശ'],
+ 'list' => ', ',
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php
new file mode 100644
index 0000000..000e795
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ml_IN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ml.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mn.php b/vendor/nesbot/carbon/src/Carbon/Lang/mn.php
new file mode 100644
index 0000000..38c6434
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mn.php
@@ -0,0 +1,116 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Max Melentiev
+ * - Zolzaya Erdenebaatar
+ * - Tom Hughes
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Michael Kessler
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Nicolás Hock Isaza
+ * - Ochirkhuyag
+ * - Batmandakh
+ * - lucifer-crybaby
+ */
+return [
+ 'year' => ':count жил',
+ 'y' => ':count жил',
+ 'month' => ':count сар',
+ 'm' => ':count сар',
+ 'week' => ':count долоо хоног',
+ 'w' => ':count долоо хоног',
+ 'day' => ':count өдөр',
+ 'd' => ':count өдөр',
+ 'hour' => ':count цаг',
+ 'h' => ':countц',
+ 'minute' => ':count минут',
+ 'min' => ':countм',
+ 'second' => ':count секунд',
+ 's' => ':countс',
+
+ 'ago_mode' => 'last',
+ 'ago' => ':time өмнө',
+ 'year_ago' => ':count жилийн',
+ 'y_ago' => ':count жилийн',
+ 'month_ago' => ':count сарын',
+ 'm_ago' => ':count сарын',
+ 'day_ago' => ':count хоногийн',
+ 'd_ago' => ':count хоногийн',
+ 'week_ago' => ':count долоо хоногийн',
+ 'w_ago' => ':count долоо хоногийн',
+ 'hour_ago' => ':count цагийн',
+ 'minute_ago' => ':count минутын',
+ 'second_ago' => ':count секундын',
+
+ 'from_now_mode' => 'last',
+ 'from_now' => 'одоогоос :time',
+ 'year_from_now' => ':count жилийн дараа',
+ 'y_from_now' => ':count жилийн дараа',
+ 'month_from_now' => ':count сарын дараа',
+ 'm_from_now' => ':count сарын дараа',
+ 'day_from_now' => ':count хоногийн дараа',
+ 'd_from_now' => ':count хоногийн дараа',
+ 'hour_from_now' => ':count цагийн дараа',
+ 'minute_from_now' => ':count минутын дараа',
+ 'second_from_now' => ':count секундын дараа',
+
+ 'after_mode' => 'last',
+ 'after' => ':time дараа',
+ 'year_after' => ':count жилийн',
+ 'y_after' => ':count жилийн',
+ 'month_after' => ':count сарын',
+ 'm_after' => ':count сарын',
+ 'day_after' => ':count хоногийн',
+ 'd_after' => ':count хоногийн',
+ 'hour_after' => ':count цагийн',
+ 'minute_after' => ':count минутын',
+ 'second_after' => ':count секундын',
+
+ 'before_mode' => 'last',
+ 'before' => ':time өмнө',
+ 'year_before' => ':count жилийн',
+ 'y_before' => ':count жилийн',
+ 'month_before' => ':count сарын',
+ 'm_before' => ':count сарын',
+ 'day_before' => ':count хоногийн',
+ 'd_before' => ':count хоногийн',
+ 'hour_before' => ':count цагийн',
+ 'minute_before' => ':count минутын',
+ 'second_before' => ':count секундын',
+
+ 'list' => ', ',
+ 'diff_now' => 'одоо',
+ 'diff_yesterday' => 'өчигдөр',
+ 'diff_tomorrow' => 'маргааш',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'YYYY MMMM DD',
+ 'LLL' => 'YY-MM-DD, HH:mm',
+ 'LLLL' => 'YYYY MMMM DD, HH:mm',
+ ],
+ 'weekdays' => ['Ням', 'Даваа', 'Мягмар', 'Лхагва', 'Пүрэв', 'Баасан', 'Бямба'],
+ 'weekdays_short' => ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'],
+ 'weekdays_min' => ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'],
+ 'months' => ['1 сар', '2 сар', '3 сар', '4 сар', '5 сар', '6 сар', '7 сар', '8 сар', '9 сар', '10 сар', '11 сар', '12 сар'],
+ 'months_short' => ['1 сар', '2 сар', '3 сар', '4 сар', '5 сар', '6 сар', '7 сар', '8 сар', '9 сар', '10 сар', '11 сар', '12 сар'],
+ 'meridiem' => ['өглөө', 'орой'],
+ 'first_day_of_week' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php
new file mode 100644
index 0000000..e5ce426
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mn_MN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/mn.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mni.php b/vendor/nesbot/carbon/src/Carbon/Lang/mni.php
new file mode 100644
index 0000000..cafa2f8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mni.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/mni_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php
new file mode 100644
index 0000000..45d430e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mni_IN.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat Pune libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['জানুৱারি', 'ফেব্রুৱারি', 'মার্চ', 'এপ্রিল', 'মে', 'জুন', 'জুলাই', 'আগষ্ট', 'সেপ্তেম্বর', 'ওক্তোবর', 'নবেম্বর', 'ডিসেম্বর'],
+ 'months_short' => ['জান', 'ফেব', 'মার', 'এপ্রি', 'মে', 'জুন', 'জুল', 'আগ', 'সেপ', 'ওক্ত', 'নবে', 'ডিস'],
+ 'weekdays' => ['নোংমাইজিং', 'নিংথৌকাবা', 'লৈবাকপোকপা', 'য়ুমশকৈশা', 'শগোলশেন', 'ইরাই', 'থাংজ'],
+ 'weekdays_short' => ['নোং', 'নিং', 'লৈবাক', 'য়ুম', 'শগোল', 'ইরা', 'থাং'],
+ 'weekdays_min' => ['নোং', 'নিং', 'লৈবাক', 'য়ুম', 'শগোল', 'ইরা', 'থাং'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['এ.ম.', 'প.ম.'],
+
+ 'year' => ':count ইসিং', // less reliable
+ 'y' => ':count ইসিং', // less reliable
+ 'a_year' => ':count ইসিং', // less reliable
+
+ 'second' => ':count ꯅꯤꯡꯊꯧꯀꯥꯕ', // less reliable
+ 's' => ':count ꯅꯤꯡꯊꯧꯀꯥꯕ', // less reliable
+ 'a_second' => ':count ꯅꯤꯡꯊꯧꯀꯥꯕ', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mo.php b/vendor/nesbot/carbon/src/Carbon/Lang/mo.php
new file mode 100644
index 0000000..102afcd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mo.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ro.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mr.php b/vendor/nesbot/carbon/src/Carbon/Lang/mr.php
new file mode 100644
index 0000000..4aaeafd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mr.php
@@ -0,0 +1,86 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Vikram-enyota
+ */
+return [
+ 'year' => ':count वर्ष',
+ 'y' => ':count वर्ष',
+ 'month' => ':count महिना|:count महिने',
+ 'm' => ':count महिना|:count महिने',
+ 'week' => ':count आठवडा|:count आठवडे',
+ 'w' => ':count आठवडा|:count आठवडे',
+ 'day' => ':count दिवस',
+ 'd' => ':count दिवस',
+ 'hour' => ':count तास',
+ 'h' => ':count तास',
+ 'minute' => ':count मिनिटे',
+ 'min' => ':count मिनिटे',
+ 'second' => ':count सेकंद',
+ 's' => ':count सेकंद',
+
+ 'ago' => ':timeपूर्वी',
+ 'from_now' => ':timeमध्ये',
+ 'before' => ':timeपूर्वी',
+ 'after' => ':timeनंतर',
+
+ 'diff_now' => 'आत्ता',
+ 'diff_today' => 'आज',
+ 'diff_yesterday' => 'काल',
+ 'diff_tomorrow' => 'उद्या',
+
+ 'formats' => [
+ 'LT' => 'A h:mm वाजता',
+ 'LTS' => 'A h:mm:ss वाजता',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm वाजता',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm वाजता',
+ ],
+
+ 'calendar' => [
+ 'sameDay' => '[आज] LT',
+ 'nextDay' => '[उद्या] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[काल] LT',
+ 'lastWeek' => '[मागील] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'रात्री';
+ }
+ if ($hour < 10) {
+ return 'सकाळी';
+ }
+ if ($hour < 17) {
+ return 'दुपारी';
+ }
+ if ($hour < 20) {
+ return 'सायंकाळी';
+ }
+
+ return 'रात्री';
+ },
+
+ 'months' => ['जानेवारी', 'फेब्रुवारी', 'मार्च', 'एप्रिल', 'मे', 'जून', 'जुलै', 'ऑगस्ट', 'सप्टेंबर', 'ऑक्टोबर', 'नोव्हेंबर', 'डिसेंबर'],
+ 'months_short' => ['जाने.', 'फेब्रु.', 'मार्च.', 'एप्रि.', 'मे.', 'जून.', 'जुलै.', 'ऑग.', 'सप्टें.', 'ऑक्टो.', 'नोव्हें.', 'डिसें.'],
+ 'weekdays' => ['रविवार', 'सोमवार', 'मंगळवार', 'बुधवार', 'गुरूवार', 'शुक्रवार', 'शनिवार'],
+ 'weekdays_short' => ['रवि', 'सोम', 'मंगळ', 'बुध', 'गुरू', 'शुक्र', 'शनि'],
+ 'weekdays_min' => ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'],
+ 'list' => [', ', ' आणि '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php
new file mode 100644
index 0000000..7bca919
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mr_IN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/mr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php
new file mode 100644
index 0000000..c9e8085
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms.php
@@ -0,0 +1,104 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Azri Jamil
+ * - JD Isaacks
+ * - Josh Soref
+ * - Azri Jamil
+ * - Hariadi Hinta
+ * - Ashraf Kamarudin
+ */
+return [
+ 'year' => ':count tahun',
+ 'a_year' => '{1}setahun|]1,Inf[:count tahun',
+ 'y' => ':count tahun',
+ 'month' => ':count bulan',
+ 'a_month' => '{1}sebulan|]1,Inf[:count bulan',
+ 'm' => ':count bulan',
+ 'week' => ':count minggu',
+ 'a_week' => '{1}seminggu|]1,Inf[:count minggu',
+ 'w' => ':count minggu',
+ 'day' => ':count hari',
+ 'a_day' => '{1}sehari|]1,Inf[:count hari',
+ 'd' => ':count hari',
+ 'hour' => ':count jam',
+ 'a_hour' => '{1}sejam|]1,Inf[:count jam',
+ 'h' => ':count jam',
+ 'minute' => ':count minit',
+ 'a_minute' => '{1}seminit|]1,Inf[:count minit',
+ 'min' => ':count minit',
+ 'second' => ':count saat',
+ 'a_second' => '{1}beberapa saat|]1,Inf[:count saat',
+ 'millisecond' => ':count milisaat',
+ 'a_millisecond' => '{1}semilisaat|]1,Inf[:count milliseconds',
+ 'microsecond' => ':count mikrodetik',
+ 'a_microsecond' => '{1}semikrodetik|]1,Inf[:count mikrodetik',
+ 's' => ':count saat',
+ 'ago' => ':time yang lepas',
+ 'from_now' => ':time dari sekarang',
+ 'after' => ':time kemudian',
+ 'before' => ':time sebelum',
+ 'diff_now' => 'sekarang',
+ 'diff_today' => 'Hari',
+ 'diff_today_regexp' => 'Hari(?:\\s+ini)?(?:\\s+pukul)?',
+ 'diff_yesterday' => 'semalam',
+ 'diff_yesterday_regexp' => 'Semalam(?:\\s+pukul)?',
+ 'diff_tomorrow' => 'esok',
+ 'diff_tomorrow_regexp' => 'Esok(?:\\s+pukul)?',
+ 'diff_before_yesterday' => 'kelmarin',
+ 'diff_after_tomorrow' => 'lusa',
+ 'formats' => [
+ 'LT' => 'HH.mm',
+ 'LTS' => 'HH.mm.ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY [pukul] HH.mm',
+ 'LLLL' => 'dddd, D MMMM YYYY [pukul] HH.mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Hari ini pukul] LT',
+ 'nextDay' => '[Esok pukul] LT',
+ 'nextWeek' => 'dddd [pukul] LT',
+ 'lastDay' => '[Kelmarin pukul] LT',
+ 'lastWeek' => 'dddd [lepas pukul] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 1) {
+ return 'tengah malam';
+ }
+
+ if ($hour < 12) {
+ return 'pagi';
+ }
+
+ if ($hour < 13) {
+ return 'tengah hari';
+ }
+
+ if ($hour < 19) {
+ return 'petang';
+ }
+
+ return 'malam';
+ },
+ 'months' => ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun', 'Julai', 'Ogos', 'September', 'Oktober', 'November', 'Disember'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ogs', 'Sep', 'Okt', 'Nov', 'Dis'],
+ 'weekdays' => ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat', 'Sabtu'],
+ 'weekdays_short' => ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
+ 'weekdays_min' => ['Ah', 'Is', 'Sl', 'Rb', 'Km', 'Jm', 'Sb'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' dan '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php
new file mode 100644
index 0000000..ef837a2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms_BN.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ms.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/MM/yy',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY, h:mm a',
+ 'LLLL' => 'dd MMMM YYYY, h:mm a',
+ ],
+ 'meridiem' => ['a', 'p'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php
new file mode 100644
index 0000000..970d604
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms_MY.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Azri Jamil
+ * - JD Isaacks
+ */
+return require __DIR__.'/ms.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php
new file mode 100644
index 0000000..77cb83d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ms_SG.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ms.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/MM/yy',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY, h:mm a',
+ 'LLLL' => 'dddd, D MMMM YYYY, h:mm a',
+ ],
+ 'meridiem' => ['a', 'p'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mt.php b/vendor/nesbot/carbon/src/Carbon/Lang/mt.php
new file mode 100644
index 0000000..e8aadcc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mt.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Alessandro Maruccia
+ */
+return [
+ 'year' => 'sena|:count sni|:count sni|:count sni',
+ 'y' => 'sa sena|:count snin|:count snin|:count snin',
+ 'month' => 'xahar|:count xhur|:count xhur|:count xhur',
+ 'm' => ':count xahar|:count xhur|:count xhur|:count xhur',
+ 'week' => 'gimgħa|:count ġimgħat|:count ġimgħat|:count ġimgħat',
+ 'w' => 'ġimgħa|:count ġimgħat|:count ġimgħat|:count ġimgħat',
+ 'day' => 'ġurnata|:count ġranet|:count ġranet|:count ġranet',
+ 'd' => 'ġurnata|:count ġranet|:count ġranet|:count ġranet',
+ 'hour' => 'siegħa|:count siegħat|:count siegħat|:count siegħat',
+ 'h' => 'siegħa|:count sigħat|:count sigħat|:count sigħat',
+ 'minute' => 'minuta|:count minuti|:count minuti|:count minuti',
+ 'min' => 'min.|:count min.|:count min.|:count min.',
+ 'second' => 'ftit sekondi|:count sekondi|:count sekondi|:count sekondi',
+ 's' => 'sek.|:count sek.|:count sek.|:count sek.',
+ 'ago' => ':time ilu',
+ 'from_now' => 'f’ :time',
+ 'diff_now' => 'issa',
+ 'diff_today' => 'Illum',
+ 'diff_today_regexp' => 'Illum(?:\\s+fil-)?',
+ 'diff_yesterday' => 'lbieraħ',
+ 'diff_yesterday_regexp' => 'Il-bieraħ(?:\\s+fil-)?',
+ 'diff_tomorrow' => 'għada',
+ 'diff_tomorrow_regexp' => 'Għada(?:\\s+fil-)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Illum fil-]LT',
+ 'nextDay' => '[Għada fil-]LT',
+ 'nextWeek' => 'dddd [fil-]LT',
+ 'lastDay' => '[Il-bieraħ fil-]LT',
+ 'lastWeek' => 'dddd [li għadda] [fil-]LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberº',
+ 'months' => ['Jannar', 'Frar', 'Marzu', 'April', 'Mejju', 'Ġunju', 'Lulju', 'Awwissu', 'Settembru', 'Ottubru', 'Novembru', 'Diċembru'],
+ 'months_short' => ['Jan', 'Fra', 'Mar', 'Apr', 'Mej', 'Ġun', 'Lul', 'Aww', 'Set', 'Ott', 'Nov', 'Diċ'],
+ 'weekdays' => ['Il-Ħadd', 'It-Tnejn', 'It-Tlieta', 'L-Erbgħa', 'Il-Ħamis', 'Il-Ġimgħa', 'Is-Sibt'],
+ 'weekdays_short' => ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'],
+ 'weekdays_min' => ['Ħa', 'Tn', 'Tl', 'Er', 'Ħa', 'Ġi', 'Si'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' u '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php b/vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php
new file mode 100644
index 0000000..9534f68
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mt_MT.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/mt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mua.php b/vendor/nesbot/carbon/src/Carbon/Lang/mua.php
new file mode 100644
index 0000000..a3a3c6f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mua.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['comme', 'lilli'],
+ 'weekdays' => ['Com’yakke', 'Comlaaɗii', 'Comzyiiɗii', 'Comkolle', 'Comkaldǝɓlii', 'Comgaisuu', 'Comzyeɓsuu'],
+ 'weekdays_short' => ['Cya', 'Cla', 'Czi', 'Cko', 'Cka', 'Cga', 'Cze'],
+ 'weekdays_min' => ['Cya', 'Cla', 'Czi', 'Cko', 'Cka', 'Cga', 'Cze'],
+ 'months' => ['Fĩi Loo', 'Cokcwaklaŋne', 'Cokcwaklii', 'Fĩi Marfoo', 'Madǝǝuutǝbijaŋ', 'Mamǝŋgwãafahbii', 'Mamǝŋgwãalii', 'Madǝmbii', 'Fĩi Dǝɓlii', 'Fĩi Mundaŋ', 'Fĩi Gwahlle', 'Fĩi Yuru'],
+ 'months_short' => ['FLO', 'CLA', 'CKI', 'FMF', 'MAD', 'MBI', 'MLI', 'MAM', 'FDE', 'FMU', 'FGW', 'FYU'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/my.php b/vendor/nesbot/carbon/src/Carbon/Lang/my.php
new file mode 100644
index 0000000..bbdfba4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/my.php
@@ -0,0 +1,70 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ * - Nay Lin Aung
+ */
+return [
+ 'year' => '{1}တစ်နှစ်|]1,Inf[:count နှစ်',
+ 'y' => ':count နှစ်',
+ 'month' => '{1}တစ်လ|]1,Inf[:count လ',
+ 'm' => ':count လ',
+ 'week' => ':count ပတ်',
+ 'w' => ':count ပတ်',
+ 'day' => '{1}တစ်ရက်|]1,Inf[:count ရက်',
+ 'd' => ':count ရက်',
+ 'hour' => '{1}တစ်နာရီ|]1,Inf[:count နာရီ',
+ 'h' => ':count နာရီ',
+ 'minute' => '{1}တစ်မိနစ်|]1,Inf[:count မိနစ်',
+ 'min' => ':count မိနစ်',
+ 'second' => '{1}စက္ကန်.အနည်းငယ်|]1,Inf[:count စက္ကန့်',
+ 's' => ':count စက္ကန့်',
+ 'ago' => 'လွန်ခဲ့သော :time က',
+ 'from_now' => 'လာမည့် :time မှာ',
+ 'after' => ':time ကြာပြီးနောက်',
+ 'before' => ':time မတိုင်ခင်',
+ 'diff_now' => 'အခုလေးတင်',
+ 'diff_today' => 'ယနေ.',
+ 'diff_yesterday' => 'မနေ့က',
+ 'diff_yesterday_regexp' => 'မနေ.က',
+ 'diff_tomorrow' => 'မနက်ဖြန်',
+ 'diff_before_yesterday' => 'တမြန်နေ့က',
+ 'diff_after_tomorrow' => 'တဘက်ခါ',
+ 'period_recurrences' => ':count ကြိမ်',
+ 'formats' => [
+ 'LT' => 'Oh:Om A',
+ 'LTS' => 'Oh:Om:Os A',
+ 'L' => 'OD/OM/OY',
+ 'LL' => 'OD MMMM OY',
+ 'LLL' => 'OD MMMM OY Oh:Om A',
+ 'LLLL' => 'dddd OD MMMM OY Oh:Om A',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ယနေ.] LT [မှာ]',
+ 'nextDay' => '[မနက်ဖြန်] LT [မှာ]',
+ 'nextWeek' => 'dddd LT [မှာ]',
+ 'lastDay' => '[မနေ.က] LT [မှာ]',
+ 'lastWeek' => '[ပြီးခဲ့သော] dddd LT [မှာ]',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['ဇန်နဝါရီ', 'ဖေဖော်ဝါရီ', 'မတ်', 'ဧပြီ', 'မေ', 'ဇွန်', 'ဇူလိုင်', 'သြဂုတ်', 'စက်တင်ဘာ', 'အောက်တိုဘာ', 'နိုဝင်ဘာ', 'ဒီဇင်ဘာ'],
+ 'months_short' => ['ဇန်', 'ဖေ', 'မတ်', 'ပြီ', 'မေ', 'ဇွန်', 'လိုင်', 'သြ', 'စက်', 'အောက်', 'နို', 'ဒီ'],
+ 'weekdays' => ['တနင်္ဂနွေ', 'တနင်္လာ', 'အင်္ဂါ', 'ဗုဒ္ဓဟူး', 'ကြာသပတေး', 'သောကြာ', 'စနေ'],
+ 'weekdays_short' => ['နွေ', 'လာ', 'ဂါ', 'ဟူး', 'ကြာ', 'သော', 'နေ'],
+ 'weekdays_min' => ['နွေ', 'လာ', 'ဂါ', 'ဟူး', 'ကြာ', 'သော', 'နေ'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'alt_numbers' => ['၀၀', '၀၁', '၀၂', '၀၃', '၀၄', '၀၅', '၀၆', '၀၇', '၀၈', '၀၉', '၁၀', '၁၁', '၁၂', '၁၃', '၁၄', '၁၅', '၁၆', '၁၇', '၁၈', '၁၉', '၂၀', '၂၁', '၂၂', '၂၃', '၂၄', '၂၅', '၂၆', '၂၇', '၂၈', '၂၉', '၃၀', '၃၁', '၃၂', '၃၃', '၃၄', '၃၅', '၃၆', '၃၇', '၃၈', '၃၉', '၄၀', '၄၁', '၄၂', '၄၃', '၄၄', '၄၅', '၄၆', '၄၇', '၄၈', '၄၉', '၅၀', '၅၁', '၅၂', '၅၃', '၅၄', '၅၅', '၅၆', '၅၇', '၅၈', '၅၉', '၆၀', '၆၁', '၆၂', '၆၃', '၆၄', '၆၅', '၆၆', '၆၇', '၆၈', '၆၉', '၇၀', '၇၁', '၇၂', '၇၃', '၇၄', '၇၅', '၇၆', '၇၇', '၇၈', '၇၉', '၈၀', '၈၁', '၈၂', '၈၃', '၈၄', '၈၅', '၈၆', '၈၇', '၈၈', '၈၉', '၉၀', '၉၁', '၉၂', '၉၃', '၉၄', '၉၅', '၉၆', '၉၇', '၉၈', '၉၉'],
+ 'meridiem' => ['နံနက်', 'ညနေ'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php b/vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php
new file mode 100644
index 0000000..a0108dd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/my_MM.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/my.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/mzn.php b/vendor/nesbot/carbon/src/Carbon/Lang/mzn.php
new file mode 100644
index 0000000..70f5f23
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/mzn.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fa.php', [
+ 'months' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'],
+ 'months_short' => ['ژانویه', 'فوریه', 'مارس', 'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'],
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 5],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'YYYY MMMM D, dddd HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan.php
new file mode 100644
index 0000000..0affece
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/nan_TW.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php
new file mode 100644
index 0000000..5c50aa4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY年MM月DD日',
+ ],
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => [' 1月', ' 2月', ' 3月', ' 4月', ' 5月', ' 6月', ' 7月', ' 8月', ' 9月', '10月', '11月', '12月'],
+ 'weekdays' => ['禮拜日', '禮拜一', '禮拜二', '禮拜三', '禮拜四', '禮拜五', '禮拜六'],
+ 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['頂晡', '下晡'],
+
+ 'year' => ':count 年',
+ 'y' => ':count 年',
+ 'a_year' => ':count 年',
+
+ 'month' => ':count goe̍h',
+ 'm' => ':count goe̍h',
+ 'a_month' => ':count goe̍h',
+
+ 'week' => ':count lé-pài',
+ 'w' => ':count lé-pài',
+ 'a_week' => ':count lé-pài',
+
+ 'day' => ':count 日',
+ 'd' => ':count 日',
+ 'a_day' => ':count 日',
+
+ 'hour' => ':count tiám-cheng',
+ 'h' => ':count tiám-cheng',
+ 'a_hour' => ':count tiám-cheng',
+
+ 'minute' => ':count Hun-cheng',
+ 'min' => ':count Hun-cheng',
+ 'a_minute' => ':count Hun-cheng',
+
+ 'second' => ':count Bió',
+ 's' => ':count Bió',
+ 'a_second' => ':count Bió',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php
new file mode 100644
index 0000000..99ca2a4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nan_TW@latin.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Arne Goetje arne@canonical.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+ 'months' => ['1goe̍h', '2goe̍h', '3goe̍h', '4goe̍h', '5goe̍h', '6goe̍h', '7goe̍h', '8goe̍h', '9goe̍h', '10goe̍h', '11goe̍h', '12goe̍h'],
+ 'months_short' => ['1g', '2g', '3g', '4g', '5g', '6g', '7g', '8g', '9g', '10g', '11g', '12g'],
+ 'weekdays' => ['lé-pài-ji̍t', 'pài-it', 'pài-jī', 'pài-saⁿ', 'pài-sì', 'pài-gō͘', 'pài-la̍k'],
+ 'weekdays_short' => ['lp', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'],
+ 'weekdays_min' => ['lp', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['téng-po͘', 'ē-po͘'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/naq.php b/vendor/nesbot/carbon/src/Carbon/Lang/naq.php
new file mode 100644
index 0000000..fbd9be9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/naq.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['ǁgoagas', 'ǃuias'],
+ 'weekdays' => ['Sontaxtsees', 'Mantaxtsees', 'Denstaxtsees', 'Wunstaxtsees', 'Dondertaxtsees', 'Fraitaxtsees', 'Satertaxtsees'],
+ 'weekdays_short' => ['Son', 'Ma', 'De', 'Wu', 'Do', 'Fr', 'Sat'],
+ 'weekdays_min' => ['Son', 'Ma', 'De', 'Wu', 'Do', 'Fr', 'Sat'],
+ 'months' => ['ǃKhanni', 'ǃKhanǀgôab', 'ǀKhuuǁkhâb', 'ǃHôaǂkhaib', 'ǃKhaitsâb', 'Gamaǀaeb', 'ǂKhoesaob', 'Aoǁkhuumûǁkhâb', 'Taraǀkhuumûǁkhâb', 'ǂNûǁnâiseb', 'ǀHooǂgaeb', 'Hôasoreǁkhâb'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm a',
+ ],
+
+ 'year' => ':count kurigu',
+ 'y' => ':count kurigu',
+ 'a_year' => ':count kurigu',
+
+ 'month' => ':count ǁaub', // less reliable
+ 'm' => ':count ǁaub', // less reliable
+ 'a_month' => ':count ǁaub', // less reliable
+
+ 'week' => ':count hû', // less reliable
+ 'w' => ':count hû', // less reliable
+ 'a_week' => ':count hû', // less reliable
+
+ 'day' => ':count ǀhobas', // less reliable
+ 'd' => ':count ǀhobas', // less reliable
+ 'a_day' => ':count ǀhobas', // less reliable
+
+ 'hour' => ':count ǂgaes', // less reliable
+ 'h' => ':count ǂgaes', // less reliable
+ 'a_hour' => ':count ǂgaes', // less reliable
+
+ 'minute' => ':count minutga', // less reliable
+ 'min' => ':count minutga', // less reliable
+ 'a_minute' => ':count minutga', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nb.php b/vendor/nesbot/carbon/src/Carbon/Lang/nb.php
new file mode 100644
index 0000000..371ee84
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nb.php
@@ -0,0 +1,84 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Alexander Tømmerås
+ * - Sigurd Gartmann
+ * - JD Isaacks
+ */
+return [
+ 'year' => ':count år|:count år',
+ 'a_year' => 'ett år|:count år',
+ 'y' => ':count år|:count år',
+ 'month' => ':count måned|:count måneder',
+ 'a_month' => 'en måned|:count måneder',
+ 'm' => ':count md.',
+ 'week' => ':count uke|:count uker',
+ 'a_week' => 'en uke|:count uker',
+ 'w' => ':count u.',
+ 'day' => ':count dag|:count dager',
+ 'a_day' => 'en dag|:count dager',
+ 'd' => ':count d.',
+ 'hour' => ':count time|:count timer',
+ 'a_hour' => 'en time|:count timer',
+ 'h' => ':count t',
+ 'minute' => ':count minutt|:count minutter',
+ 'a_minute' => 'ett minutt|:count minutter',
+ 'min' => ':count min',
+ 'second' => ':count sekund|:count sekunder',
+ 'a_second' => 'noen sekunder|:count sekunder',
+ 's' => ':count sek',
+ 'ago' => ':time siden',
+ 'from_now' => 'om :time',
+ 'after' => ':time etter',
+ 'before' => ':time før',
+ 'diff_now' => 'akkurat nå',
+ 'diff_today' => 'i dag',
+ 'diff_today_regexp' => 'i dag(?:\\s+kl.)?',
+ 'diff_yesterday' => 'i går',
+ 'diff_yesterday_regexp' => 'i går(?:\\s+kl.)?',
+ 'diff_tomorrow' => 'i morgen',
+ 'diff_tomorrow_regexp' => 'i morgen(?:\\s+kl.)?',
+ 'diff_before_yesterday' => 'i forgårs',
+ 'diff_after_tomorrow' => 'i overmorgen',
+ 'period_recurrences' => 'en gang|:count ganger',
+ 'period_interval' => 'hver :interval',
+ 'period_start_date' => 'fra :date',
+ 'period_end_date' => 'til :date',
+ 'months' => ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ 'weekdays' => ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'],
+ 'weekdays_short' => ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],
+ 'weekdays_min' => ['sø', 'ma', 'ti', 'on', 'to', 'fr', 'lø'],
+ 'ordinal' => ':number.',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY [kl.] HH:mm',
+ 'LLLL' => 'dddd D. MMMM YYYY [kl.] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[i dag kl.] LT',
+ 'nextDay' => '[i morgen kl.] LT',
+ 'nextWeek' => 'dddd [kl.] LT',
+ 'lastDay' => '[i går kl.] LT',
+ 'lastWeek' => '[forrige] dddd [kl.] LT',
+ 'sameElse' => 'L',
+ ],
+ 'list' => [', ', ' og '],
+ 'meridiem' => ['a.m.', 'p.m.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php b/vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php
new file mode 100644
index 0000000..31678c5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nb_NO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/nb.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php
new file mode 100644
index 0000000..ce0210b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nb_SJ.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/nb.php', [
+ 'formats' => [
+ 'LL' => 'D. MMM YYYY',
+ 'LLL' => 'D. MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd D. MMMM YYYY, HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nd.php b/vendor/nesbot/carbon/src/Carbon/Lang/nd.php
new file mode 100644
index 0000000..f75d9a7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nd.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Sonto', 'Mvulo', 'Sibili', 'Sithathu', 'Sine', 'Sihlanu', 'Mgqibelo'],
+ 'weekdays_short' => ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'],
+ 'weekdays_min' => ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'],
+ 'months' => ['Zibandlela', 'Nhlolanja', 'Mbimbitho', 'Mabasa', 'Nkwenkwezi', 'Nhlangula', 'Ntulikazi', 'Ncwabakazi', 'Mpandula', 'Mfumfu', 'Lwezi', 'Mpalakazi'],
+ 'months_short' => ['Zib', 'Nhlo', 'Mbi', 'Mab', 'Nkw', 'Nhla', 'Ntu', 'Ncw', 'Mpan', 'Mfu', 'Lwe', 'Mpal'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => 'okweminyaka engu-:count', // less reliable
+ 'y' => 'okweminyaka engu-:count', // less reliable
+ 'a_year' => 'okweminyaka engu-:count', // less reliable
+
+ 'month' => 'inyanga ezingu-:count',
+ 'm' => 'inyanga ezingu-:count',
+ 'a_month' => 'inyanga ezingu-:count',
+
+ 'week' => 'amaviki angu-:count',
+ 'w' => 'amaviki angu-:count',
+ 'a_week' => 'amaviki angu-:count',
+
+ 'day' => 'kwamalanga angu-:count',
+ 'd' => 'kwamalanga angu-:count',
+ 'a_day' => 'kwamalanga angu-:count',
+
+ 'hour' => 'amahola angu-:count',
+ 'h' => 'amahola angu-:count',
+ 'a_hour' => 'amahola angu-:count',
+
+ 'minute' => 'imizuzu engu-:count',
+ 'min' => 'imizuzu engu-:count',
+ 'a_minute' => 'imizuzu engu-:count',
+
+ 'second' => 'imizuzwana engu-:count',
+ 's' => 'imizuzwana engu-:count',
+ 'a_second' => 'imizuzwana engu-:count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nds.php b/vendor/nesbot/carbon/src/Carbon/Lang/nds.php
new file mode 100644
index 0000000..c0b3775
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nds.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/nds_DE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php b/vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php
new file mode 100644
index 0000000..a6c57a9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nds_DE.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Jannuaar', 'Feberwaar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
+ 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
+ 'weekdays' => ['Sünndag', 'Maandag', 'Dingsdag', 'Middeweek', 'Dunnersdag', 'Freedag', 'Sünnavend'],
+ 'weekdays_short' => ['Sdag', 'Maan', 'Ding', 'Midd', 'Dunn', 'Free', 'Svd.'],
+ 'weekdays_min' => ['Sd', 'Ma', 'Di', 'Mi', 'Du', 'Fr', 'Sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count Johr',
+ 'y' => ':countJ',
+ 'a_year' => '{1}een Johr|:count Johr',
+
+ 'month' => ':count Maand',
+ 'm' => ':countM',
+ 'a_month' => '{1}een Maand|:count Maand',
+
+ 'week' => ':count Week|:count Weken',
+ 'w' => ':countW',
+ 'a_week' => '{1}een Week|:count Week|:count Weken',
+
+ 'day' => ':count Dag|:count Daag',
+ 'd' => ':countD',
+ 'a_day' => '{1}een Dag|:count Dag|:count Daag',
+
+ 'hour' => ':count Stünn|:count Stünnen',
+ 'h' => ':countSt',
+ 'a_hour' => '{1}een Stünn|:count Stünn|:count Stünnen',
+
+ 'minute' => ':count Minuut|:count Minuten',
+ 'min' => ':countm',
+ 'a_minute' => '{1}een Minuut|:count Minuut|:count Minuten',
+
+ 'second' => ':count Sekunn|:count Sekunnen',
+ 's' => ':counts',
+ 'a_second' => 'en poor Sekunnen|:count Sekunn|:count Sekunnen',
+
+ 'ago' => 'vör :time',
+ 'from_now' => 'in :time',
+ 'before' => ':time vörher',
+ 'after' => ':time later',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php
new file mode 100644
index 0000000..de2c57b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nds_NL.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - information from Kenneth Christiansen Kenneth Christiansen, Pablo Saratxaga kenneth@gnu.org, pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Jaunuwoa', 'Februwoa', 'Moaz', 'Aprell', 'Mai', 'Juni', 'Juli', 'August', 'Septamba', 'Oktoba', 'Nowamba', 'Dezamba'],
+ 'months_short' => ['Jan', 'Feb', 'Moz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Now', 'Dez'],
+ 'weekdays' => ['Sinndag', 'Mondag', 'Dingsdag', 'Meddwäakj', 'Donnadag', 'Friedag', 'Sinnowend'],
+ 'weekdays_short' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'],
+ 'weekdays_min' => ['Sdg', 'Mdg', 'Dsg', 'Mwk', 'Ddg', 'Fdg', 'Swd'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php
new file mode 100644
index 0000000..d4caf0e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - nootanghimire
+ * - Josh Soref
+ * - Nj Subedi
+ * - JD Isaacks
+ */
+return [
+ 'year' => 'एक बर्ष|:count बर्ष',
+ 'y' => ':count वर्ष',
+ 'month' => 'एक महिना|:count महिना',
+ 'm' => ':count महिना',
+ 'week' => ':count हप्ता',
+ 'w' => ':count हप्ता',
+ 'day' => 'एक दिन|:count दिन',
+ 'd' => ':count दिन',
+ 'hour' => 'एक घण्टा|:count घण्टा',
+ 'h' => ':count घण्टा',
+ 'minute' => 'एक मिनेट|:count मिनेट',
+ 'min' => ':count मिनेट',
+ 'second' => 'केही क्षण|:count सेकेण्ड',
+ 's' => ':count सेकेण्ड',
+ 'ago' => ':time अगाडि',
+ 'from_now' => ':timeमा',
+ 'after' => ':time पछि',
+ 'before' => ':time अघि',
+ 'diff_now' => 'अहिले',
+ 'diff_today' => 'आज',
+ 'diff_yesterday' => 'हिजो',
+ 'diff_tomorrow' => 'भोलि',
+ 'formats' => [
+ 'LT' => 'Aको h:mm बजे',
+ 'LTS' => 'Aको h:mm:ss बजे',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, Aको h:mm बजे',
+ 'LLLL' => 'dddd, D MMMM YYYY, Aको h:mm बजे',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[आज] LT',
+ 'nextDay' => '[भोलि] LT',
+ 'nextWeek' => '[आउँदो] dddd[,] LT',
+ 'lastDay' => '[हिजो] LT',
+ 'lastWeek' => '[गएको] dddd[,] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 3) {
+ return 'राति';
+ }
+ if ($hour < 12) {
+ return 'बिहान';
+ }
+ if ($hour < 16) {
+ return 'दिउँसो';
+ }
+ if ($hour < 20) {
+ return 'साँझ';
+ }
+
+ return 'राति';
+ },
+ 'months' => ['जनवरी', 'फेब्रुवरी', 'मार्च', 'अप्रिल', 'मई', 'जुन', 'जुलाई', 'अगष्ट', 'सेप्टेम्बर', 'अक्टोबर', 'नोभेम्बर', 'डिसेम्बर'],
+ 'months_short' => ['जन.', 'फेब्रु.', 'मार्च', 'अप्रि.', 'मई', 'जुन', 'जुलाई.', 'अग.', 'सेप्ट.', 'अक्टो.', 'नोभे.', 'डिसे.'],
+ 'weekdays' => ['आइतबार', 'सोमबार', 'मङ्गलबार', 'बुधबार', 'बिहिबार', 'शुक्रबार', 'शनिबार'],
+ 'weekdays_short' => ['आइत.', 'सोम.', 'मङ्गल.', 'बुध.', 'बिहि.', 'शुक्र.', 'शनि.'],
+ 'weekdays_min' => ['आ.', 'सो.', 'मं.', 'बु.', 'बि.', 'शु.', 'श.'],
+ 'list' => [', ', ' र '],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php
new file mode 100644
index 0000000..f68d00e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne_IN.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ne.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'yy/M/d',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D, h:mm a',
+ 'LLLL' => 'YYYY MMMM D, dddd, h:mm a',
+ ],
+ 'months' => ['जनवरी', 'फेब्रुअरी', 'मार्च', 'अप्रिल', 'मे', 'जुन', 'जुलाई', 'अगस्ट', 'सेप्टेम्बर', 'अक्टोबर', 'नोभेम्बर', 'डिसेम्बर'],
+ 'months_short' => ['जनवरी', 'फेब्रुअरी', 'मार्च', 'अप्रिल', 'मे', 'जुन', 'जुलाई', 'अगस्ट', 'सेप्टेम्बर', 'अक्टोबर', 'नोभेम्बर', 'डिसेम्बर'],
+ 'weekend' => [0, 0],
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php b/vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php
new file mode 100644
index 0000000..27840c0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ne_NP.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ne.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nhn.php b/vendor/nesbot/carbon/src/Carbon/Lang/nhn.php
new file mode 100644
index 0000000..5a85831
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nhn.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/nhn_MX.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php b/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php
new file mode 100644
index 0000000..9db88a1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nhn_MX.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
+ 'months_short' => ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
+ 'weekdays' => ['teoilhuitl', 'ceilhuitl', 'omeilhuitl', 'yeilhuitl', 'nahuilhuitl', 'macuililhuitl', 'chicuaceilhuitl'],
+ 'weekdays_short' => ['teo', 'cei', 'ome', 'yei', 'nau', 'mac', 'chi'],
+ 'weekdays_min' => ['teo', 'cei', 'ome', 'yei', 'nau', 'mac', 'chi'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'month' => ':count metztli', // less reliable
+ 'm' => ':count metztli', // less reliable
+ 'a_month' => ':count metztli', // less reliable
+
+ 'week' => ':count tonalli', // less reliable
+ 'w' => ':count tonalli', // less reliable
+ 'a_week' => ':count tonalli', // less reliable
+
+ 'day' => ':count tonatih', // less reliable
+ 'd' => ':count tonatih', // less reliable
+ 'a_day' => ':count tonatih', // less reliable
+
+ 'minute' => ':count toltecayotl', // less reliable
+ 'min' => ':count toltecayotl', // less reliable
+ 'a_minute' => ':count toltecayotl', // less reliable
+
+ 'second' => ':count ome', // less reliable
+ 's' => ':count ome', // less reliable
+ 'a_second' => ':count ome', // less reliable
+
+ 'year' => ':count xihuitl',
+ 'y' => ':count xihuitl',
+ 'a_year' => ':count xihuitl',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/niu.php b/vendor/nesbot/carbon/src/Carbon/Lang/niu.php
new file mode 100644
index 0000000..bd9be8a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/niu.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/niu_NU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php b/vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php
new file mode 100644
index 0000000..6e7a697
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/niu_NU.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RockET Systems Emani Fakaotimanava-Lui emani@niue.nu
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Ianuali', 'Fepuali', 'Masi', 'Apelila', 'Me', 'Iuni', 'Iulai', 'Aokuso', 'Sepetema', 'Oketopa', 'Novema', 'Tesemo'],
+ 'months_short' => ['Ian', 'Fep', 'Mas', 'Ape', 'Me', 'Iun', 'Iul', 'Aok', 'Sep', 'Oke', 'Nov', 'Tes'],
+ 'weekdays' => ['Aho Tapu', 'Aho Gofua', 'Aho Ua', 'Aho Lotu', 'Aho Tuloto', 'Aho Falaile', 'Aho Faiumu'],
+ 'weekdays_short' => ['Tapu', 'Gofua', 'Ua', 'Lotu', 'Tuloto', 'Falaile', 'Faiumu'],
+ 'weekdays_min' => ['Tapu', 'Gofua', 'Ua', 'Lotu', 'Tuloto', 'Falaile', 'Faiumu'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count tau',
+ 'y' => ':count tau',
+ 'a_year' => ':count tau',
+
+ 'month' => ':count mahina',
+ 'm' => ':count mahina',
+ 'a_month' => ':count mahina',
+
+ 'week' => ':count faahi tapu',
+ 'w' => ':count faahi tapu',
+ 'a_week' => ':count faahi tapu',
+
+ 'day' => ':count aho',
+ 'd' => ':count aho',
+ 'a_day' => ':count aho',
+
+ 'hour' => ':count e tulā',
+ 'h' => ':count e tulā',
+ 'a_hour' => ':count e tulā',
+
+ 'minute' => ':count minuti',
+ 'min' => ':count minuti',
+ 'a_minute' => ':count minuti',
+
+ 'second' => ':count sekone',
+ 's' => ':count sekone',
+ 'a_second' => ':count sekone',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php
new file mode 100644
index 0000000..2d73770
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl.php
@@ -0,0 +1,113 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Roy
+ * - Stephan
+ * - François B
+ * - Tim Fish
+ * - Kevin Huang
+ * - Jacob Middag
+ * - JD Isaacks
+ * - Roy
+ * - Stephan
+ * - François B
+ * - Tim Fish
+ * - Jacob Middag
+ * - JD Isaacks
+ * - Propaganistas
+ * - MegaXLR
+ * - adriaanzon
+ * - MonkeyPhysics
+ * - JeroenG
+ * - RikSomers
+ * - proclame
+ * - Rik de Groot (hwdegroot)
+ */
+return [
+ 'year' => ':count jaar|:count jaar',
+ 'a_year' => 'een jaar|:count jaar',
+ 'y' => ':countj',
+ 'month' => ':count maand|:count maanden',
+ 'a_month' => 'een maand|:count maanden',
+ 'm' => ':countmnd',
+ 'week' => ':count week|:count weken',
+ 'a_week' => 'een week|:count weken',
+ 'w' => ':countw',
+ 'day' => ':count dag|:count dagen',
+ 'a_day' => 'een dag|:count dagen',
+ 'd' => ':countd',
+ 'hour' => ':count uur|:count uur',
+ 'a_hour' => 'een uur|:count uur',
+ 'h' => ':countu',
+ 'minute' => ':count minuut|:count minuten',
+ 'a_minute' => 'een minuut|:count minuten',
+ 'min' => ':countmin',
+ 'second' => ':count seconde|:count seconden',
+ 'a_second' => 'een paar seconden|:count seconden',
+ 's' => ':counts',
+ 'ago' => ':time geleden',
+ 'from_now' => 'over :time',
+ 'after' => ':time later',
+ 'before' => ':time eerder',
+ 'diff_now' => 'nu',
+ 'diff_today' => 'vandaag',
+ 'diff_today_regexp' => 'vandaag(?:\\s+om)?',
+ 'diff_yesterday' => 'gisteren',
+ 'diff_yesterday_regexp' => 'gisteren(?:\\s+om)?',
+ 'diff_tomorrow' => 'morgen',
+ 'diff_tomorrow_regexp' => 'morgen(?:\\s+om)?',
+ 'diff_after_tomorrow' => 'overmorgen',
+ 'diff_before_yesterday' => 'eergisteren',
+ 'period_recurrences' => ':count keer',
+ 'period_interval' => function (string $interval = '') {
+ /** @var string $output */
+ $output = preg_replace('/^(een|één|1)\s+/u', '', $interval);
+
+ if (preg_match('/^(een|één|1)( jaar|j| uur|u)/u', $interval)) {
+ return "elk $output";
+ }
+
+ return "elke $output";
+ },
+ 'period_start_date' => 'van :date',
+ 'period_end_date' => 'tot :date',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD-MM-YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[vandaag om] LT',
+ 'nextDay' => '[morgen om] LT',
+ 'nextWeek' => 'dddd [om] LT',
+ 'lastDay' => '[gisteren om] LT',
+ 'lastWeek' => '[afgelopen] dddd [om] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ return $number.(($number === 1 || $number === 8 || $number >= 20) ? 'ste' : 'de');
+ },
+ 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
+ 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
+ 'mmm_suffix' => '.',
+ 'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
+ 'weekdays_short' => ['zo.', 'ma.', 'di.', 'wo.', 'do.', 'vr.', 'za.'],
+ 'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' en '],
+ 'meridiem' => ['\'s ochtends', '\'s middags'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php
new file mode 100644
index 0000000..5ec136d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_AW.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Free Software Foundation, Inc. bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/nl.php', [
+ 'formats' => [
+ 'L' => 'DD-MM-YY',
+ ],
+ 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
+ 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
+ 'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
+ 'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+ 'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php
new file mode 100644
index 0000000..037f5b4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BE.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Roy
+ * - Stephan
+ * - François B
+ * - Tim Fish
+ * - Kevin Huang
+ * - Jacob Middag
+ * - JD Isaacks
+ * - Propaganistas
+ */
+return array_replace_recursive(require __DIR__.'/nl.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php
new file mode 100644
index 0000000..c269197
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_BQ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/nl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php
new file mode 100644
index 0000000..c269197
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_CW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/nl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php
new file mode 100644
index 0000000..14e4853
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_NL.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/nl.php', [
+ 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
+ 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
+ 'weekdays' => ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
+ 'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+ 'weekdays_min' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php
new file mode 100644
index 0000000..c269197
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/nl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php
new file mode 100644
index 0000000..c269197
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nl_SX.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/nl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nmg.php b/vendor/nesbot/carbon/src/Carbon/Lang/nmg.php
new file mode 100644
index 0000000..4d1df6e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nmg.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['maná', 'kugú'],
+ 'weekdays' => ['sɔ́ndɔ', 'mɔ́ndɔ', 'sɔ́ndɔ mafú mába', 'sɔ́ndɔ mafú málal', 'sɔ́ndɔ mafú mána', 'mabágá má sukul', 'sásadi'],
+ 'weekdays_short' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'mbs', 'sas'],
+ 'weekdays_min' => ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'mbs', 'sas'],
+ 'months' => ['ngwɛn matáhra', 'ngwɛn ńmba', 'ngwɛn ńlal', 'ngwɛn ńna', 'ngwɛn ńtan', 'ngwɛn ńtuó', 'ngwɛn hɛmbuɛrí', 'ngwɛn lɔmbi', 'ngwɛn rɛbvuâ', 'ngwɛn wum', 'ngwɛn wum navǔr', 'krísimin'],
+ 'months_short' => ['ng1', 'ng2', 'ng3', 'ng4', 'ng5', 'ng6', 'ng7', 'ng8', 'ng9', 'ng10', 'ng11', 'kris'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nn.php b/vendor/nesbot/carbon/src/Carbon/Lang/nn.php
new file mode 100644
index 0000000..041f7b2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nn.php
@@ -0,0 +1,78 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Alexander Tømmerås
+ * - Øystein
+ * - JD Isaacks
+ * - Gaute Hvoslef Kvalnes (gaute)
+ */
+return [
+ 'year' => ':count år',
+ 'a_year' => 'eit år|:count år',
+ 'y' => ':count år',
+ 'month' => ':count månad|:count månader',
+ 'a_month' => 'ein månad|:count månader',
+ 'm' => ':count md',
+ 'week' => ':count veke|:count veker',
+ 'a_week' => 'ei veke|:count veker',
+ 'w' => ':countv',
+ 'day' => ':count dag|:count dagar',
+ 'a_day' => 'ein dag|:count dagar',
+ 'd' => ':countd',
+ 'hour' => ':count time|:count timar',
+ 'a_hour' => 'ein time|:count timar',
+ 'h' => ':countt',
+ 'minute' => ':count minutt',
+ 'a_minute' => 'eit minutt|:count minutt',
+ 'min' => ':countm',
+ 'second' => ':count sekund',
+ 'a_second' => 'nokre sekund|:count sekund',
+ 's' => ':counts',
+ 'ago' => ':time sidan',
+ 'from_now' => 'om :time',
+ 'after' => ':time etter',
+ 'before' => ':time før',
+ 'diff_today' => 'I dag',
+ 'diff_yesterday' => 'I går',
+ 'diff_yesterday_regexp' => 'I går(?:\\s+klokka)?',
+ 'diff_tomorrow' => 'I morgon',
+ 'diff_tomorrow_regexp' => 'I morgon(?:\\s+klokka)?',
+ 'diff_today_regexp' => 'I dag(?:\\s+klokka)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY [kl.] H:mm',
+ 'LLLL' => 'dddd D. MMMM YYYY [kl.] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[I dag klokka] LT',
+ 'nextDay' => '[I morgon klokka] LT',
+ 'nextWeek' => 'dddd [klokka] LT',
+ 'lastDay' => '[I går klokka] LT',
+ 'lastWeek' => '[Føregåande] dddd [klokka] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'],
+ 'weekdays' => ['sundag', 'måndag', 'tysdag', 'onsdag', 'torsdag', 'fredag', 'laurdag'],
+ 'weekdays_short' => ['sun', 'mån', 'tys', 'ons', 'tor', 'fre', 'lau'],
+ 'weekdays_min' => ['su', 'må', 'ty', 'on', 'to', 'fr', 'la'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' og '],
+ 'meridiem' => ['f.m.', 'e.m.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php b/vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php
new file mode 100644
index 0000000..8e16871
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nn_NO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/nn.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nnh.php b/vendor/nesbot/carbon/src/Carbon/Lang/nnh.php
new file mode 100644
index 0000000..007d239
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nnh.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['mbaʼámbaʼ', 'ncwònzém'],
+ 'weekdays' => null,
+ 'weekdays_short' => ['lyɛʼɛ́ sẅíŋtè', 'mvfò lyɛ̌ʼ', 'mbɔ́ɔntè mvfò lyɛ̌ʼ', 'tsètsɛ̀ɛ lyɛ̌ʼ', 'mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ', 'mvfò màga lyɛ̌ʼ', 'màga lyɛ̌ʼ'],
+ 'weekdays_min' => null,
+ 'months' => null,
+ 'months_short' => ['saŋ tsetsɛ̀ɛ lùm', 'saŋ kàg ngwóŋ', 'saŋ lepyè shúm', 'saŋ cÿó', 'saŋ tsɛ̀ɛ cÿó', 'saŋ njÿoláʼ', 'saŋ tyɛ̀b tyɛ̀b mbʉ̀ŋ', 'saŋ mbʉ̀ŋ', 'saŋ ngwɔ̀ʼ mbÿɛ', 'saŋ tàŋa tsetsáʼ', 'saŋ mejwoŋó', 'saŋ lùm'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/yy',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => '[lyɛ]̌ʼ d [na] MMMM, YYYY HH:mm',
+ 'LLLL' => 'dddd , [lyɛ]̌ʼ d [na] MMMM, YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/no.php b/vendor/nesbot/carbon/src/Carbon/Lang/no.php
new file mode 100644
index 0000000..f4497c7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/no.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Daniel S. Billing
+ * - Paul
+ * - Jimmie Johansson
+ * - Jens Herlevsen
+ */
+return array_replace_recursive(require __DIR__.'/nb.php', [
+ 'formats' => [
+ 'LLL' => 'D. MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY [kl.] HH:mm',
+ ],
+ 'calendar' => [
+ 'nextWeek' => 'på dddd [kl.] LT',
+ 'lastWeek' => '[i] dddd[s kl.] LT',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nr.php b/vendor/nesbot/carbon/src/Carbon/Lang/nr.php
new file mode 100644
index 0000000..1bc999f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nr.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/nr_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php
new file mode 100644
index 0000000..f9a7be8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nr_ZA.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Janabari', 'uFeberbari', 'uMatjhi', 'u-Apreli', 'Meyi', 'Juni', 'Julayi', 'Arhostosi', 'Septemba', 'Oktoba', 'Usinyikhaba', 'Disemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mat', 'Apr', 'Mey', 'Jun', 'Jul', 'Arh', 'Sep', 'Okt', 'Usi', 'Dis'],
+ 'weekdays' => ['uSonto', 'uMvulo', 'uLesibili', 'lesithathu', 'uLesine', 'ngoLesihlanu', 'umGqibelo'],
+ 'weekdays_short' => ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'],
+ 'weekdays_min' => ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nso.php b/vendor/nesbot/carbon/src/Carbon/Lang/nso.php
new file mode 100644
index 0000000..2a6cabb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nso.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/nso_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php
new file mode 100644
index 0000000..b08fe6d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nso_ZA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Janaware', 'Febereware', 'Matšhe', 'Aprele', 'Mei', 'June', 'Julae', 'Agostose', 'Setemere', 'Oktobere', 'Nofemere', 'Disemere'],
+ 'months_short' => ['Jan', 'Feb', 'Mat', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Set', 'Okt', 'Nof', 'Dis'],
+ 'weekdays' => ['LaMorena', 'Mošupologo', 'Labobedi', 'Laboraro', 'Labone', 'Labohlano', 'Mokibelo'],
+ 'weekdays_short' => ['Son', 'Moš', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'],
+ 'weekdays_min' => ['Son', 'Moš', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count ngwaga',
+ 'y' => ':count ngwaga',
+ 'a_year' => ':count ngwaga',
+
+ 'month' => ':count Kgwedi',
+ 'm' => ':count Kgwedi',
+ 'a_month' => ':count Kgwedi',
+
+ 'week' => ':count Beke',
+ 'w' => ':count Beke',
+ 'a_week' => ':count Beke',
+
+ 'day' => ':count Letšatši',
+ 'd' => ':count Letšatši',
+ 'a_day' => ':count Letšatši',
+
+ 'hour' => ':count Iri',
+ 'h' => ':count Iri',
+ 'a_hour' => ':count Iri',
+
+ 'minute' => ':count Motsotso',
+ 'min' => ':count Motsotso',
+ 'a_minute' => ':count Motsotso',
+
+ 'second' => ':count motsotswana',
+ 's' => ':count motsotswana',
+ 'a_second' => ':count motsotswana',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nus.php b/vendor/nesbot/carbon/src/Carbon/Lang/nus.php
new file mode 100644
index 0000000..789bc39
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nus.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['RW', 'TŊ'],
+ 'weekdays' => ['Cäŋ kuɔth', 'Jiec la̱t', 'Rɛw lätni', 'Diɔ̱k lätni', 'Ŋuaan lätni', 'Dhieec lätni', 'Bäkɛl lätni'],
+ 'weekdays_short' => ['Cäŋ', 'Jiec', 'Rɛw', 'Diɔ̱k', 'Ŋuaan', 'Dhieec', 'Bäkɛl'],
+ 'weekdays_min' => ['Cäŋ', 'Jiec', 'Rɛw', 'Diɔ̱k', 'Ŋuaan', 'Dhieec', 'Bäkɛl'],
+ 'months' => ['Tiop thar pɛt', 'Pɛt', 'Duɔ̱ɔ̱ŋ', 'Guak', 'Duät', 'Kornyoot', 'Pay yie̱tni', 'Tho̱o̱r', 'Tɛɛr', 'Laath', 'Kur', 'Tio̱p in di̱i̱t'],
+ 'months_short' => ['Tiop', 'Pɛt', 'Duɔ̱ɔ̱', 'Guak', 'Duä', 'Kor', 'Pay', 'Thoo', 'Tɛɛ', 'Laa', 'Kur', 'Tid'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd D MMMM YYYY h:mm a',
+ ],
+
+ 'year' => ':count jiök', // less reliable
+ 'y' => ':count jiök', // less reliable
+ 'a_year' => ':count jiök', // less reliable
+
+ 'month' => ':count pay', // less reliable
+ 'm' => ':count pay', // less reliable
+ 'a_month' => ':count pay', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/nyn.php b/vendor/nesbot/carbon/src/Carbon/Lang/nyn.php
new file mode 100644
index 0000000..8660ea4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/nyn.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu', 'Orwakana', 'Orwakataano', 'Orwamukaaga'],
+ 'weekdays_short' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
+ 'weekdays_min' => ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
+ 'months' => ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana', 'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda', 'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'],
+ 'months_short' => ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS', 'KMN', 'KMW', 'KKM', 'KNK', 'KNB'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/oc.php b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php
new file mode 100644
index 0000000..c9411d6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/oc.php
@@ -0,0 +1,100 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Quentí
+ */
+// @codeCoverageIgnoreStart
+use Symfony\Component\Translation\PluralizationRules;
+
+if (class_exists('Symfony\\Component\\Translation\\PluralizationRules')) {
+ PluralizationRules::set(static function ($number) {
+ return $number == 1 ? 0 : 1;
+ }, 'oc');
+}
+// @codeCoverageIgnoreEnd
+
+return [
+ 'year' => ':count an|:count ans',
+ 'a_year' => 'un an|:count ans',
+ 'y' => ':count an|:count ans',
+ 'month' => ':count mes|:count meses',
+ 'a_month' => 'un mes|:count meses',
+ 'm' => ':count mes|:count meses',
+ 'week' => ':count setmana|:count setmanas',
+ 'a_week' => 'una setmana|:count setmanas',
+ 'w' => ':count setmana|:count setmanas',
+ 'day' => ':count jorn|:count jorns',
+ 'a_day' => 'un jorn|:count jorns',
+ 'd' => ':count jorn|:count jorns',
+ 'hour' => ':count ora|:count oras',
+ 'a_hour' => 'una ora|:count oras',
+ 'h' => ':count ora|:count oras',
+ 'minute' => ':count minuta|:count minutas',
+ 'a_minute' => 'una minuta|:count minutas',
+ 'min' => ':count minuta|:count minutas',
+ 'second' => ':count segonda|:count segondas',
+ 'a_second' => 'una segonda|:count segondas',
+ 's' => ':count segonda|:count segondas',
+ 'ago' => 'fa :time',
+ 'from_now' => 'd\'aquí :time',
+ 'after' => ':time aprèp',
+ 'before' => ':time abans',
+ 'diff_now' => 'ara meteis',
+ 'diff_today' => 'Uèi',
+ 'diff_today_regexp' => 'Uèi(?:\\s+a)?',
+ 'diff_yesterday' => 'ièr',
+ 'diff_yesterday_regexp' => 'Ièr(?:\\s+a)?',
+ 'diff_tomorrow' => 'deman',
+ 'diff_tomorrow_regexp' => 'Deman(?:\\s+a)?',
+ 'diff_before_yesterday' => 'ièr delà',
+ 'diff_after_tomorrow' => 'deman passat',
+ 'period_recurrences' => ':count còp|:count còps',
+ 'period_interval' => 'cada :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'fins a :date',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM [de] YYYY',
+ 'LLL' => 'D MMMM [de] YYYY [a] H:mm',
+ 'LLLL' => 'dddd D MMMM [de] YYYY [a] H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Uèi a] LT',
+ 'nextDay' => '[Deman a] LT',
+ 'nextWeek' => 'dddd [a] LT',
+ 'lastDay' => '[Ièr a] LT',
+ 'lastWeek' => 'dddd [passat a] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['de genièr', 'de febrièr', 'de març', 'd\'abrial', 'de mai', 'de junh', 'de julhet', 'd\'agost', 'de setembre', 'd’octòbre', 'de novembre', 'de decembre'],
+ 'months_standalone' => ['genièr', 'febrièr', 'març', 'abrial', 'mai', 'junh', 'julh', 'agost', 'setembre', 'octòbre', 'novembre', 'decembre'],
+ 'months_short' => ['gen.', 'feb.', 'març', 'abr.', 'mai', 'junh', 'julh', 'ago.', 'sep.', 'oct.', 'nov.', 'dec.'],
+ 'weekdays' => ['dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte'],
+ 'weekdays_short' => ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'],
+ 'weekdays_min' => ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'],
+ 'ordinal' => function ($number, string $period = '') {
+ $ordinal = [1 => 'èr', 2 => 'nd'][(int) $number] ?? 'en';
+
+ // feminine for year, week, hour, minute, second
+ if (preg_match('/^[yYwWhHgGis]$/', $period)) {
+ $ordinal .= 'a';
+ }
+
+ return $number.$ordinal;
+ },
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' e '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php b/vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php
new file mode 100644
index 0000000..01eb5c1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/oc_FR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/oc.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om.php b/vendor/nesbot/carbon/src/Carbon/Lang/om.php
new file mode 100644
index 0000000..b8d5a0b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/om.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation & Sagalee Oromoo Publishing Co. Inc. locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'dd-MMM-YYYY',
+ 'LLL' => 'dd MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, MMMM D, YYYY HH:mm',
+ ],
+ 'months' => ['Amajjii', 'Guraandhala', 'Bitooteessa', 'Elba', 'Caamsa', 'Waxabajjii', 'Adooleessa', 'Hagayya', 'Fuulbana', 'Onkololeessa', 'Sadaasa', 'Muddee'],
+ 'months_short' => ['Ama', 'Gur', 'Bit', 'Elb', 'Cam', 'Wax', 'Ado', 'Hag', 'Ful', 'Onk', 'Sad', 'Mud'],
+ 'weekdays' => ['Dilbata', 'Wiixata', 'Qibxata', 'Roobii', 'Kamiisa', 'Jimaata', 'Sanbata'],
+ 'weekdays_short' => ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'],
+ 'weekdays_min' => ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['WD', 'WB'],
+
+ 'year' => 'wggoota :count',
+ 'y' => 'wggoota :count',
+ 'a_year' => 'wggoota :count',
+
+ 'month' => 'ji’a :count',
+ 'm' => 'ji’a :count',
+ 'a_month' => 'ji’a :count',
+
+ 'week' => 'torban :count',
+ 'w' => 'torban :count',
+ 'a_week' => 'torban :count',
+
+ 'day' => 'guyyaa :count',
+ 'd' => 'guyyaa :count',
+ 'a_day' => 'guyyaa :count',
+
+ 'hour' => 'saʼaatii :count',
+ 'h' => 'saʼaatii :count',
+ 'a_hour' => 'saʼaatii :count',
+
+ 'minute' => 'daqiiqaa :count',
+ 'min' => 'daqiiqaa :count',
+ 'a_minute' => 'daqiiqaa :count',
+
+ 'second' => 'sekoondii :count',
+ 's' => 'sekoondii :count',
+ 'a_second' => 'sekoondii :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php
new file mode 100644
index 0000000..044760e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/om_ET.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/om.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php
new file mode 100644
index 0000000..f5a4d1c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/om_KE.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/om.php', [
+ 'day_of_first_week_of_year' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/or.php b/vendor/nesbot/carbon/src/Carbon/Lang/or.php
new file mode 100644
index 0000000..3aa7173
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/or.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/or_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php
new file mode 100644
index 0000000..57a89f5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/or_IN.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM AP Linux Technology Center, Yamato Software Laboratory bug-glibc@gnu.org
+ */
+return [
+ 'diff_now' => 'ବର୍ତ୍ତମାନ',
+ 'diff_yesterday' => 'ଗତକାଲି',
+ 'diff_tomorrow' => 'ଆସନ୍ତାକାଲି',
+ 'formats' => [
+ 'LT' => 'Oh:Om A',
+ 'LTS' => 'Oh:Om:Os A',
+ 'L' => 'OD-OM-OY',
+ 'LL' => 'OD MMMM OY',
+ 'LLL' => 'OD MMMM OY Oh:Om A',
+ 'LLLL' => 'dddd OD MMMM OY Oh:Om A',
+ ],
+ 'months' => ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ', 'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ', 'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର', 'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର', 'ଡିସେମ୍ବର'],
+ 'months_short' => ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ', 'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ', 'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର', 'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର', 'ଡିସେମ୍ବର'],
+ 'weekdays' => ['ରବିବାର', 'ସୋମବାର', 'ମଙ୍ଗଳବାର', 'ବୁଧବାର', 'ଗୁରୁବାର', 'ଶୁକ୍ରବାର', 'ଶନିବାର'],
+ 'weekdays_short' => ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ', 'ବୁଧ', 'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'],
+ 'weekdays_min' => ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ', 'ବୁଧ', 'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'],
+ 'day_of_first_week_of_year' => 1,
+ 'alt_numbers' => ['୦', '୧', '୨', '୩', '୪', '୫', '୬', '୭', '୮', '୯', '୧୦', '୧୧', '୧୨', '୧୩', '୧୪', '୧୫', '୧୬', '୧୭', '୧୮', '୧୯', '୨୦', '୨୧', '୨୨', '୨୩', '୨୪', '୨୫', '୨୬', '୨୭', '୨୮', '୨୯', '୩୦', '୩୧', '୩୨', '୩୩', '୩୪', '୩୫', '୩୬', '୩୭', '୩୮', '୩୯', '୪୦', '୪୧', '୪୨', '୪୩', '୪୪', '୪୫', '୪୬', '୪୭', '୪୮', '୪୯', '୫୦', '୫୧', '୫୨', '୫୩', '୫୪', '୫୫', '୫୬', '୫୭', '୫୮', '୫୯', '୬୦', '୬୧', '୬୨', '୬୩', '୬୪', '୬୫', '୬୬', '୬୭', '୬୮', '୬୯', '୭୦', '୭୧', '୭୨', '୭୩', '୭୪', '୭୫', '୭୬', '୭୭', '୭୮', '୭୯', '୮୦', '୮୧', '୮୨', '୮୩', '୮୪', '୮୫', '୮୬', '୮୭', '୮୮', '୮୯', '୯୦', '୯୧', '୯୨', '୯୩', '୯୪', '୯୫', '୯୬', '୯୭', '୯୮', '୯୯'],
+ 'year' => ':count ବର୍ଷ',
+ 'y' => ':count ବ.',
+ 'month' => ':count ମାସ',
+ 'm' => ':count ମା.',
+ 'week' => ':count ସପ୍ତାହ',
+ 'w' => ':count ସପ୍ତା.',
+ 'day' => ':count ଦିନ',
+ 'd' => ':count ଦିନ',
+ 'hour' => ':count ଘଣ୍ତ',
+ 'h' => ':count ଘ.',
+ 'minute' => ':count ମିନଟ',
+ 'min' => ':count ମି.',
+ 'second' => ':count ସେକଣ୍ଢ',
+ 's' => ':count ସେ.',
+ 'ago' => ':time ପୂର୍ବେ',
+ 'from_now' => ':timeରେ',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/os.php b/vendor/nesbot/carbon/src/Carbon/Lang/os.php
new file mode 100644
index 0000000..5f55e8a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/os.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/os_RU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php
new file mode 100644
index 0000000..9592d15
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/os_RU.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['январы', 'февралы', 'мартъийы', 'апрелы', 'майы', 'июны', 'июлы', 'августы', 'сентябры', 'октябры', 'ноябры', 'декабры'],
+ 'months_short' => ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
+ 'weekdays' => ['Хуыцаубон', 'Къуырисæр', 'Дыццæг', 'Æртыццæг', 'Цыппæрæм', 'Майрæмбон', 'Сабат'],
+ 'weekdays_short' => ['Хцб', 'Крс', 'Дцг', 'Æрт', 'Цпр', 'Мрб', 'Сбт'],
+ 'weekdays_min' => ['Хцб', 'Крс', 'Дцг', 'Æрт', 'Цпр', 'Мрб', 'Сбт'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'minute' => ':count гыццыл', // less reliable
+ 'min' => ':count гыццыл', // less reliable
+ 'a_minute' => ':count гыццыл', // less reliable
+
+ 'second' => ':count æндæр', // less reliable
+ 's' => ':count æндæр', // less reliable
+ 'a_second' => ':count æндæр', // less reliable
+
+ 'year' => ':count аз',
+ 'y' => ':count аз',
+ 'a_year' => ':count аз',
+
+ 'month' => ':count мӕй',
+ 'm' => ':count мӕй',
+ 'a_month' => ':count мӕй',
+
+ 'week' => ':count къуыри',
+ 'w' => ':count къуыри',
+ 'a_week' => ':count къуыри',
+
+ 'day' => ':count бон',
+ 'd' => ':count бон',
+ 'a_day' => ':count бон',
+
+ 'hour' => ':count сахат',
+ 'h' => ':count сахат',
+ 'a_hour' => ':count сахат',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa.php
new file mode 100644
index 0000000..48b2033
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - Punjab
+ */
+return [
+ 'year' => 'ਇੱਕ ਸਾਲ|:count ਸਾਲ',
+ 'month' => 'ਇੱਕ ਮਹੀਨਾ|:count ਮਹੀਨੇ',
+ 'week' => 'ਹਫਤਾ|:count ਹਫ਼ਤੇ',
+ 'day' => 'ਇੱਕ ਦਿਨ|:count ਦਿਨ',
+ 'hour' => 'ਇੱਕ ਘੰਟਾ|:count ਘੰਟੇ',
+ 'minute' => 'ਇਕ ਮਿੰਟ|:count ਮਿੰਟ',
+ 'second' => 'ਕੁਝ ਸਕਿੰਟ|:count ਸਕਿੰਟ',
+ 'ago' => ':time ਪਹਿਲਾਂ',
+ 'from_now' => ':time ਵਿੱਚ',
+ 'before' => ':time ਤੋਂ ਪਹਿਲਾਂ',
+ 'after' => ':time ਤੋਂ ਬਾਅਦ',
+ 'diff_now' => 'ਹੁਣ',
+ 'diff_today' => 'ਅਜ',
+ 'diff_yesterday' => 'ਕਲ',
+ 'diff_tomorrow' => 'ਕਲ',
+ 'formats' => [
+ 'LT' => 'A h:mm ਵਜੇ',
+ 'LTS' => 'A h:mm:ss ਵਜੇ',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm ਵਜੇ',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm ਵਜੇ',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ਅਜ] LT',
+ 'nextDay' => '[ਕਲ] LT',
+ 'nextWeek' => '[ਅਗਲਾ] dddd, LT',
+ 'lastDay' => '[ਕਲ] LT',
+ 'lastWeek' => '[ਪਿਛਲੇ] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'ਰਾਤ';
+ }
+ if ($hour < 10) {
+ return 'ਸਵੇਰ';
+ }
+ if ($hour < 17) {
+ return 'ਦੁਪਹਿਰ';
+ }
+ if ($hour < 20) {
+ return 'ਸ਼ਾਮ';
+ }
+
+ return 'ਰਾਤ';
+ },
+ 'months' => ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ', 'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ', 'ਦਸੰਬਰ'],
+ 'months_short' => ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ', 'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ', 'ਦਸੰਬਰ'],
+ 'weekdays' => ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ', 'ਮੰਗਲਵਾਰ', 'ਬੁਧਵਾਰ', 'ਵੀਰਵਾਰ', 'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨੀਚਰਵਾਰ'],
+ 'weekdays_short' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁਧ', 'ਵੀਰ', 'ਸ਼ੁਕਰ', 'ਸ਼ਨੀ'],
+ 'weekdays_min' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁਧ', 'ਵੀਰ', 'ਸ਼ੁਕਰ', 'ਸ਼ਨੀ'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' ਅਤੇ '],
+ 'weekend' => [0, 0],
+ 'alt_numbers' => ['੦', '੧', '੨', '੩', '੪', '੫', '੬', '੭', '੮', '੯'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php
new file mode 100644
index 0000000..39b0653
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Arab.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ur.php', [
+ 'weekdays' => ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات', 'جمعہ', 'ہفتہ'],
+ 'weekdays_short' => ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات', 'جمعہ', 'ہفتہ'],
+ 'weekdays_min' => ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات', 'جمعہ', 'ہفتہ'],
+ 'months' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئ', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئ', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd, DD MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php
new file mode 100644
index 0000000..7adff5c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_Guru.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/pa.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D/M/yy',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY, h:mm a',
+ 'LLLL' => 'dddd, D MMMM YYYY, h:mm a',
+ ],
+ 'months' => ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ', 'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ', 'ਦਸੰਬਰ'],
+ 'months_short' => ['ਜਨ', 'ਫ਼ਰ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾ', 'ਅਗ', 'ਸਤੰ', 'ਅਕਤੂ', 'ਨਵੰ', 'ਦਸੰ'],
+ 'weekdays' => ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ', 'ਮੰਗਲਵਾਰ', 'ਬੁੱਧਵਾਰ', 'ਵੀਰਵਾਰ', 'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨਿੱਚਰਵਾਰ'],
+ 'weekdays_short' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁੱਧ', 'ਵੀਰ', 'ਸ਼ੁੱਕਰ', 'ਸ਼ਨਿੱਚਰ'],
+ 'weekdays_min' => ['ਐਤ', 'ਸੋਮ', 'ਮੰਗ', 'ਬੁੱਧ', 'ਵੀਰ', 'ਸ਼ੁੱਕ', 'ਸ਼ਨਿੱ'],
+ 'weekend' => [0, 0],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php
new file mode 100644
index 0000000..ca67642
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_IN.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Guo Xiang Tan
+ * - Josh Soref
+ * - Ash
+ * - harpreetkhalsagtbit
+ */
+return require __DIR__.'/pa.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php
new file mode 100644
index 0000000..f9af11c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pa_PK.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['جنوري', 'فروري', 'مارچ', 'اپريل', 'مٓی', 'جون', 'جولاي', 'اگست', 'ستمبر', 'اكتوبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنوري', 'فروري', 'مارچ', 'اپريل', 'مٓی', 'جون', 'جولاي', 'اگست', 'ستمبر', 'اكتوبر', 'نومبر', 'دسمبر'],
+ 'weekdays' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'],
+ 'weekdays_short' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'],
+ 'weekdays_min' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ص', 'ش'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pap.php b/vendor/nesbot/carbon/src/Carbon/Lang/pap.php
new file mode 100644
index 0000000..b4c1706
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pap.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return [
+ 'formats' => [
+ 'LT' => 'HH.mm',
+ 'LTS' => 'HH.mm:ss',
+ 'L' => 'DD-MM-YY',
+ 'LL' => 'MMMM [di] DD, YYYY',
+ 'LLL' => 'DD MMM HH.mm',
+ 'LLLL' => 'MMMM DD, YYYY HH.mm',
+ ],
+ 'months' => ['yanüari', 'febrüari', 'mart', 'aprel', 'mei', 'yüni', 'yüli', 'ougùstùs', 'sèptèmber', 'oktober', 'novèmber', 'desèmber'],
+ 'months_short' => ['yan', 'feb', 'mar', 'apr', 'mei', 'yün', 'yül', 'oug', 'sèp', 'okt', 'nov', 'des'],
+ 'weekdays' => ['djadomingo', 'djaluna', 'djamars', 'djawebs', 'djarason', 'djabierne', 'djasabra'],
+ 'weekdays_short' => ['do', 'lu', 'ma', 'we', 'ra', 'bi', 'sa'],
+ 'weekdays_min' => ['do', 'lu', 'ma', 'we', 'ra', 'bi', 'sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'year' => ':count aña',
+ 'month' => ':count luna',
+ 'week' => ':count siman',
+ 'day' => ':count dia',
+ 'hour' => ':count ora',
+ 'minute' => ':count minüt',
+ 'second' => ':count sekònde',
+ 'list' => [', ', ' i '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php b/vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php
new file mode 100644
index 0000000..e9a48ff
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pap_AW.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - information from native speaker Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return require __DIR__.'/pap.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php b/vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php
new file mode 100644
index 0000000..e9a48ff
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pap_CW.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - information from native speaker Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return require __DIR__.'/pap.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pl.php b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php
new file mode 100644
index 0000000..b720535
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pl.php
@@ -0,0 +1,126 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Wacław Jacek
+ * - François B
+ * - Tim Fish
+ * - Serhan Apaydın
+ * - Massimiliano Caniparoli
+ * - JD Isaacks
+ * - Jakub Szwacz
+ * - Jan
+ * - Paul
+ * - damlys
+ * - Marek (marast78)
+ * - Peter (UnrulyNatives)
+ * - Qrzysio
+ * - Jan (aso824)
+ * - diverpl
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count rok|:count lata|:count lat',
+ 'a_year' => 'rok|:count lata|:count lat',
+ 'y' => ':count r|:count l|:count l',
+ 'month' => ':count miesiąc|:count miesiące|:count miesięcy',
+ 'a_month' => 'miesiąc|:count miesiące|:count miesięcy',
+ 'm' => ':count mies.',
+ 'week' => ':count tydzień|:count tygodnie|:count tygodni',
+ 'a_week' => 'tydzień|:count tygodnie|:count tygodni',
+ 'w' => ':count tyg.',
+ 'day' => ':count dzień|:count dni|:count dni',
+ 'a_day' => 'dzień|:count dni|:count dni',
+ 'd' => ':count d',
+ 'hour' => ':count godzina|:count godziny|:count godzin',
+ 'a_hour' => 'godzina|:count godziny|:count godzin',
+ 'h' => ':count godz.',
+ 'minute' => ':count minuta|:count minuty|:count minut',
+ 'a_minute' => 'minuta|:count minuty|:count minut',
+ 'min' => ':count min',
+ 'second' => ':count sekunda|:count sekundy|:count sekund',
+ 'a_second' => '{1}kilka sekund|:count sekunda|:count sekundy|:count sekund',
+ 's' => ':count sek.',
+ 'ago' => ':time temu',
+ 'from_now' => static function ($time) {
+ return 'za '.strtr($time, [
+ 'godzina' => 'godzinę',
+ 'minuta' => 'minutę',
+ 'sekunda' => 'sekundę',
+ ]);
+ },
+ 'after' => ':time po',
+ 'before' => ':time przed',
+ 'diff_now' => 'teraz',
+ 'diff_today' => 'Dziś',
+ 'diff_today_regexp' => 'Dziś(?:\\s+o)?',
+ 'diff_yesterday' => 'wczoraj',
+ 'diff_yesterday_regexp' => 'Wczoraj(?:\\s+o)?',
+ 'diff_tomorrow' => 'jutro',
+ 'diff_tomorrow_regexp' => 'Jutro(?:\\s+o)?',
+ 'diff_before_yesterday' => 'przedwczoraj',
+ 'diff_after_tomorrow' => 'pojutrze',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Dziś o] LT',
+ 'nextDay' => '[Jutro o] LT',
+ 'nextWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[W niedzielę o] LT';
+ case 2:
+ return '[We wtorek o] LT';
+ case 3:
+ return '[W środę o] LT';
+ case 6:
+ return '[W sobotę o] LT';
+ default:
+ return '[W] dddd [o] LT';
+ }
+ },
+ 'lastDay' => '[Wczoraj o] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[W zeszłą niedzielę o] LT';
+ case 3:
+ return '[W zeszłą środę o] LT';
+ case 6:
+ return '[W zeszłą sobotę o] LT';
+ default:
+ return '[W zeszły] dddd [o] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['stycznia', 'lutego', 'marca', 'kwietnia', 'maja', 'czerwca', 'lipca', 'sierpnia', 'września', 'października', 'listopada', 'grudnia'],
+ 'months_standalone' => ['styczeń', 'luty', 'marzec', 'kwiecień', 'maj', 'czerwiec', 'lipiec', 'sierpień', 'wrzesień', 'październik', 'listopad', 'grudzień'],
+ 'months_short' => ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz', 'paź', 'lis', 'gru'],
+ 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['niedziela', 'poniedziałek', 'wtorek', 'środa', 'czwartek', 'piątek', 'sobota'],
+ 'weekdays_short' => ['ndz', 'pon', 'wt', 'śr', 'czw', 'pt', 'sob'],
+ 'weekdays_min' => ['Nd', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'So'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' i '],
+ 'meridiem' => ['przed południem', 'po południu'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php b/vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php
new file mode 100644
index 0000000..222bcdb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pl_PL.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/prg.php b/vendor/nesbot/carbon/src/Carbon/Lang/prg.php
new file mode 100644
index 0000000..6e63f4a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/prg.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'months' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'],
+ 'months_short' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'YYYY MMMM D, dddd HH:mm',
+ ],
+
+ 'year' => ':count meta',
+ 'y' => ':count meta',
+ 'a_year' => ':count meta',
+
+ 'month' => ':count mēniks', // less reliable
+ 'm' => ':count mēniks', // less reliable
+ 'a_month' => ':count mēniks', // less reliable
+
+ 'week' => ':count sawaītin', // less reliable
+ 'w' => ':count sawaītin', // less reliable
+ 'a_week' => ':count sawaītin', // less reliable
+
+ 'day' => ':count di',
+ 'd' => ':count di',
+ 'a_day' => ':count di',
+
+ 'hour' => ':count bruktēt', // less reliable
+ 'h' => ':count bruktēt', // less reliable
+ 'a_hour' => ':count bruktēt', // less reliable
+
+ 'minute' => ':count līkuts', // less reliable
+ 'min' => ':count līkuts', // less reliable
+ 'a_minute' => ':count līkuts', // less reliable
+
+ 'second' => ':count kitan', // less reliable
+ 's' => ':count kitan', // less reliable
+ 'a_second' => ':count kitan', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ps.php b/vendor/nesbot/carbon/src/Carbon/Lang/ps.php
new file mode 100644
index 0000000..a928b28
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ps.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Muhammad Nasir Rahimi
+ * - Nassim Nasibullah (spinzar)
+ */
+return [
+ 'year' => ':count کال|:count کاله',
+ 'y' => ':countکال|:countکاله',
+ 'month' => ':count مياشت|:count مياشتي',
+ 'm' => ':countمياشت|:countمياشتي',
+ 'week' => ':count اونۍ|:count اونۍ',
+ 'w' => ':countاونۍ|:countاونۍ',
+ 'day' => ':count ورځ|:count ورځي',
+ 'd' => ':countورځ|:countورځي',
+ 'hour' => ':count ساعت|:count ساعته',
+ 'h' => ':countساعت|:countساعته',
+ 'minute' => ':count دقيقه|:count دقيقې',
+ 'min' => ':countدقيقه|:countدقيقې',
+ 'second' => ':count ثانيه|:count ثانيې',
+ 's' => ':countثانيه|:countثانيې',
+ 'ago' => ':time دمخه',
+ 'from_now' => ':time له اوس څخه',
+ 'after' => ':time وروسته',
+ 'before' => ':time دمخه',
+ 'list' => ['، ', ' او '],
+ 'meridiem' => ['غ.م.', 'غ.و.'],
+ 'weekdays' => ['اتوار', 'ګل', 'نهه', 'شورو', 'زيارت', 'جمعه', 'خالي'],
+ 'weekdays_short' => ['ا', 'ګ', 'ن', 'ش', 'ز', 'ج', 'خ'],
+ 'weekdays_min' => ['ا', 'ګ', 'ن', 'ش', 'ز', 'ج', 'خ'],
+ 'months' => ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سېپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سېپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_standalone' => ['جنوري', 'فېبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_short_standalone' => ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'مۍ', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'first_day_of_week' => 6,
+ 'weekend' => [4, 5],
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'YYYY/M/d',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'د YYYY د MMMM D H:mm',
+ 'LLLL' => 'dddd د YYYY د MMMM D H:mm',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php b/vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php
new file mode 100644
index 0000000..6ec5180
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ps_AF.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ps.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php
new file mode 100644
index 0000000..bb6359b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt.php
@@ -0,0 +1,116 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Cassiano Montanari
+ * - Matt Pope
+ * - François B
+ * - Prodis
+ * - JD Isaacks
+ * - Raphael Amorim
+ * - João Magalhães
+ * - victortobias
+ * - Paulo Freitas
+ * - Sebastian Thierer
+ * - Claudson Martins (claudsonm)
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count ano|:count anos',
+ 'a_year' => 'um ano|:count anos',
+ 'y' => ':counta',
+ 'month' => ':count mês|:count meses',
+ 'a_month' => 'um mês|:count meses',
+ 'm' => ':countm',
+ 'week' => ':count semana|:count semanas',
+ 'a_week' => 'uma semana|:count semanas',
+ 'w' => ':countsem',
+ 'day' => ':count dia|:count dias',
+ 'a_day' => 'um dia|:count dias',
+ 'd' => ':countd',
+ 'hour' => ':count hora|:count horas',
+ 'a_hour' => 'uma hora|:count horas',
+ 'h' => ':counth',
+ 'minute' => ':count minuto|:count minutos',
+ 'a_minute' => 'um minuto|:count minutos',
+ 'min' => ':countmin',
+ 'second' => ':count segundo|:count segundos',
+ 'a_second' => 'alguns segundos|:count segundos',
+ 's' => ':counts',
+ 'millisecond' => ':count milissegundo|:count milissegundos',
+ 'a_millisecond' => 'um milissegundo|:count milissegundos',
+ 'ms' => ':countms',
+ 'microsecond' => ':count microssegundo|:count microssegundos',
+ 'a_microsecond' => 'um microssegundo|:count microssegundos',
+ 'µs' => ':countµs',
+ 'ago' => 'há :time',
+ 'from_now' => 'em :time',
+ 'after' => ':time depois',
+ 'before' => ':time antes',
+ 'diff_now' => 'agora',
+ 'diff_today' => 'Hoje',
+ 'diff_today_regexp' => 'Hoje(?:\\s+às)?',
+ 'diff_yesterday' => 'ontem',
+ 'diff_yesterday_regexp' => 'Ontem(?:\\s+às)?',
+ 'diff_tomorrow' => 'amanhã',
+ 'diff_tomorrow_regexp' => 'Amanhã(?:\\s+às)?',
+ 'diff_before_yesterday' => 'anteontem',
+ 'diff_after_tomorrow' => 'depois de amanhã',
+ 'period_recurrences' => 'uma vez|:count vezes',
+ 'period_interval' => 'cada :interval',
+ 'period_start_date' => 'de :date',
+ 'period_end_date' => 'até :date',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D [de] MMMM [de] YYYY',
+ 'LLL' => 'D [de] MMMM [de] YYYY HH:mm',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Hoje às] LT',
+ 'nextDay' => '[Amanhã às] LT',
+ 'nextWeek' => 'dddd [às] LT',
+ 'lastDay' => '[Ontem às] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ case 6:
+ return '[Último] dddd [às] LT';
+ default:
+ return '[Última] dddd [às] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberº',
+ 'months' => ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
+ 'months_short' => ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'],
+ 'weekdays' => ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
+ 'weekdays_short' => ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
+ 'weekdays_min' => ['Do', '2ª', '3ª', '4ª', '5ª', '6ª', 'Sá'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' e '],
+ 'ordinal_words' => [
+ 'of' => 'de',
+ 'first' => 'primeira',
+ 'second' => 'segunda',
+ 'third' => 'terceira',
+ 'fourth' => 'quarta',
+ 'fifth' => 'quinta',
+ 'last' => 'última',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_AO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php
new file mode 100644
index 0000000..e917c5c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_BR.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Cassiano Montanari
+ * - Eduardo Dalla Vecchia
+ * - David Rodrigues
+ * - Matt Pope
+ * - François B
+ * - Prodis
+ * - Marlon Maxwel
+ * - JD Isaacks
+ * - Raphael Amorim
+ * - Rafael Raupp
+ * - felipeleite1
+ * - swalker
+ * - Lucas Macedo
+ * - Paulo Freitas
+ * - Sebastian Thierer
+ */
+return array_replace_recursive(require __DIR__.'/pt.php', [
+ 'period_recurrences' => 'uma|:count vez',
+ 'period_interval' => 'toda :interval',
+ 'formats' => [
+ 'LLL' => 'D [de] MMMM [de] YYYY [às] HH:mm',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY [às] HH:mm',
+ ],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CH.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_CV.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GQ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_GW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_LU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php
new file mode 100644
index 0000000..f2b5eab
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MO.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/pt.php', [
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'LLL' => 'D [de] MMMM [de] YYYY, h:mm a',
+ 'LLLL' => 'dddd, D [de] MMMM [de] YYYY, h:mm a',
+ ],
+ 'first_day_of_week' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php
new file mode 100644
index 0000000..fbc0c97
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_MZ.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/pt.php', [
+ 'first_day_of_week' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php
new file mode 100644
index 0000000..2a76fc1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_PT.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RAP bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/pt.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
+ 'months_short' => ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'],
+ 'weekdays' => ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
+ 'weekdays_short' => ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
+ 'weekdays_min' => ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_ST.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php b/vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php
new file mode 100644
index 0000000..22c01ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/pt_TL.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/pt.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/qu.php b/vendor/nesbot/carbon/src/Carbon/Lang/qu.php
new file mode 100644
index 0000000..65278cd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/qu.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/es_UY.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM, YYYY HH:mm',
+ ],
+ 'first_day_of_week' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php b/vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php
new file mode 100644
index 0000000..d5db6bf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/qu_BO.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/qu.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php b/vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php
new file mode 100644
index 0000000..d5db6bf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/qu_EC.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/qu.php', [
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/quz.php b/vendor/nesbot/carbon/src/Carbon/Lang/quz.php
new file mode 100644
index 0000000..1640c02
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/quz.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/quz_PE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php b/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php
new file mode 100644
index 0000000..d322918
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/quz_PE.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Sugar Labs // OLPC sugarlabs.org libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['iniru', 'phiwriru', 'marsu', 'awril', 'mayu', 'huniyu', 'huliyu', 'agustu', 'siptiyimri', 'uktuwri', 'nuwiyimri', 'tisiyimri'],
+ 'months_short' => ['ini', 'phi', 'mar', 'awr', 'may', 'hun', 'hul', 'agu', 'sip', 'ukt', 'nuw', 'tis'],
+ 'weekdays' => ['tuminku', 'lunis', 'martis', 'miyirkulis', 'juywis', 'wiyirnis', 'sawatu'],
+ 'weekdays_short' => ['tum', 'lun', 'mar', 'miy', 'juy', 'wiy', 'saw'],
+ 'weekdays_min' => ['tum', 'lun', 'mar', 'miy', 'juy', 'wiy', 'saw'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'minute' => ':count uchuy', // less reliable
+ 'min' => ':count uchuy', // less reliable
+ 'a_minute' => ':count uchuy', // less reliable
+
+ 'year' => ':count wata',
+ 'y' => ':count wata',
+ 'a_year' => ':count wata',
+
+ 'month' => ':count killa',
+ 'm' => ':count killa',
+ 'a_month' => ':count killa',
+
+ 'week' => ':count simana',
+ 'w' => ':count simana',
+ 'a_week' => ':count simana',
+
+ 'day' => ':count pʼunchaw',
+ 'd' => ':count pʼunchaw',
+ 'a_day' => ':count pʼunchaw',
+
+ 'hour' => ':count ura',
+ 'h' => ':count ura',
+ 'a_hour' => ':count ura',
+
+ 'second' => ':count iskay ñiqin',
+ 's' => ':count iskay ñiqin',
+ 'a_second' => ':count iskay ñiqin',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/raj.php b/vendor/nesbot/carbon/src/Carbon/Lang/raj.php
new file mode 100644
index 0000000..26138c9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/raj.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/raj_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php
new file mode 100644
index 0000000..7b4589c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/raj_IN.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - meghrajsuthar03@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रैल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितंबर', 'अक्टूबर', 'नवंबर', 'दिसंबर'],
+ 'months_short' => ['जन', 'फर', 'मार्च', 'अप्रै', 'मई', 'जून', 'जुल', 'अग', 'सित', 'अक्टू', 'नव', 'दिस'],
+ 'weekdays' => ['रविवार', 'सोमवार', 'मंगल्लवार', 'बुधवार', 'बृहस्पतिवार', 'शुक्रवार', 'शनिवार'],
+ 'weekdays_short' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'],
+ 'weekdays_min' => ['रवि', 'सोम', 'मंगल', 'बुध', 'बृहस्पति', 'शुक्र', 'शनि'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+
+ 'year' => ':count आंहू', // less reliable
+ 'y' => ':count आंहू', // less reliable
+ 'a_year' => ':count आंहू', // less reliable
+
+ 'month' => ':count सूरज', // less reliable
+ 'm' => ':count सूरज', // less reliable
+ 'a_month' => ':count सूरज', // less reliable
+
+ 'week' => ':count निवाज', // less reliable
+ 'w' => ':count निवाज', // less reliable
+ 'a_week' => ':count निवाज', // less reliable
+
+ 'day' => ':count अेक', // less reliable
+ 'd' => ':count अेक', // less reliable
+ 'a_day' => ':count अेक', // less reliable
+
+ 'hour' => ':count दुनियांण', // less reliable
+ 'h' => ':count दुनियांण', // less reliable
+ 'a_hour' => ':count दुनियांण', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rm.php b/vendor/nesbot/carbon/src/Carbon/Lang/rm.php
new file mode 100644
index 0000000..1843f45
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/rm.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - tjku
+ * - Max Melentiev
+ * - Juanito Fatas
+ * - Tsutomu Kuroda
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Nicolás Hock Isaza
+ * - sebastian de castelberg
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'Do MMMM YYYY',
+ 'LLL' => 'Do MMMM, HH:mm [Uhr]',
+ 'LLLL' => 'dddd, Do MMMM YYYY, HH:mm [Uhr]',
+ ],
+ 'year' => ':count onn|:count onns',
+ 'month' => ':count mais',
+ 'week' => ':count emna|:count emnas',
+ 'day' => ':count di|:count dis',
+ 'hour' => ':count oura|:count ouras',
+ 'minute' => ':count minuta|:count minutas',
+ 'second' => ':count secunda|:count secundas',
+ 'weekdays' => ['dumengia', 'glindesdi', 'mardi', 'mesemna', 'gievgia', 'venderdi', 'sonda'],
+ 'weekdays_short' => ['du', 'gli', 'ma', 'me', 'gie', 've', 'so'],
+ 'weekdays_min' => ['du', 'gli', 'ma', 'me', 'gie', 've', 'so'],
+ 'months' => ['schaner', 'favrer', 'mars', 'avrigl', 'matg', 'zercladur', 'fanadur', 'avust', 'settember', 'october', 'november', 'december'],
+ 'months_short' => ['schan', 'favr', 'mars', 'avr', 'matg', 'zercl', 'fan', 'avust', 'sett', 'oct', 'nov', 'dec'],
+ 'meridiem' => ['avantmezdi', 'suentermezdi'],
+ 'list' => [', ', ' e '],
+ 'first_day_of_week' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rn.php b/vendor/nesbot/carbon/src/Carbon/Lang/rn.php
new file mode 100644
index 0000000..8ab958e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/rn.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Z.MU.', 'Z.MW.'],
+ 'weekdays' => ['Ku w’indwi', 'Ku wa mbere', 'Ku wa kabiri', 'Ku wa gatatu', 'Ku wa kane', 'Ku wa gatanu', 'Ku wa gatandatu'],
+ 'weekdays_short' => ['cu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.', 'gnd.'],
+ 'weekdays_min' => ['cu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.', 'gnd.'],
+ 'months' => ['Nzero', 'Ruhuhuma', 'Ntwarante', 'Ndamukiza', 'Rusama', 'Ruheshi', 'Mukakaro', 'Nyandagaro', 'Nyakanga', 'Gitugutu', 'Munyonyo', 'Kigarama'],
+ 'months_short' => ['Mut.', 'Gas.', 'Wer.', 'Mat.', 'Gic.', 'Kam.', 'Nya.', 'Kan.', 'Nze.', 'Ukw.', 'Ugu.', 'Uku.'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => 'imyaka :count',
+ 'y' => 'imyaka :count',
+ 'a_year' => 'imyaka :count',
+
+ 'month' => 'amezi :count',
+ 'm' => 'amezi :count',
+ 'a_month' => 'amezi :count',
+
+ 'week' => 'indwi :count',
+ 'w' => 'indwi :count',
+ 'a_week' => 'indwi :count',
+
+ 'day' => 'imisi :count',
+ 'd' => 'imisi :count',
+ 'a_day' => 'imisi :count',
+
+ 'hour' => 'amasaha :count',
+ 'h' => 'amasaha :count',
+ 'a_hour' => 'amasaha :count',
+
+ 'minute' => 'iminuta :count',
+ 'min' => 'iminuta :count',
+ 'a_minute' => 'iminuta :count',
+
+ 'second' => 'inguvu :count', // less reliable
+ 's' => 'inguvu :count', // less reliable
+ 'a_second' => 'inguvu :count', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro.php
new file mode 100644
index 0000000..868a327
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ * - Cătălin Georgescu
+ * - Valentin Ivaşcu (oriceon)
+ */
+return [
+ 'year' => ':count an|:count ani|:count ani',
+ 'a_year' => 'un an|:count ani|:count ani',
+ 'y' => ':count a.',
+ 'month' => ':count lună|:count luni|:count luni',
+ 'a_month' => 'o lună|:count luni|:count luni',
+ 'm' => ':count l.',
+ 'week' => ':count săptămână|:count săptămâni|:count săptămâni',
+ 'a_week' => 'o săptămână|:count săptămâni|:count săptămâni',
+ 'w' => ':count săp.',
+ 'day' => ':count zi|:count zile|:count zile',
+ 'a_day' => 'o zi|:count zile|:count zile',
+ 'd' => ':count z.',
+ 'hour' => ':count oră|:count ore|:count ore',
+ 'a_hour' => 'o oră|:count ore|:count ore',
+ 'h' => ':count o.',
+ 'minute' => ':count minut|:count minute|:count minute',
+ 'a_minute' => 'un minut|:count minute|:count minute',
+ 'min' => ':count m.',
+ 'second' => ':count secundă|:count secunde|:count secunde',
+ 'a_second' => 'câteva secunde|:count secunde|:count secunde',
+ 's' => ':count sec.',
+ 'ago' => ':time în urmă',
+ 'from_now' => 'peste :time',
+ 'after' => 'peste :time',
+ 'before' => 'acum :time',
+ 'diff_now' => 'acum',
+ 'diff_today' => 'azi',
+ 'diff_today_regexp' => 'azi(?:\\s+la)?',
+ 'diff_yesterday' => 'ieri',
+ 'diff_yesterday_regexp' => 'ieri(?:\\s+la)?',
+ 'diff_tomorrow' => 'mâine',
+ 'diff_tomorrow_regexp' => 'mâine(?:\\s+la)?',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[azi la] LT',
+ 'nextDay' => '[mâine la] LT',
+ 'nextWeek' => 'dddd [la] LT',
+ 'lastDay' => '[ieri la] LT',
+ 'lastWeek' => '[fosta] dddd [la] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie', 'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'],
+ 'months_short' => ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.', 'aug.', 'sept.', 'oct.', 'nov.', 'dec.'],
+ 'weekdays' => ['duminică', 'luni', 'marți', 'miercuri', 'joi', 'vineri', 'sâmbătă'],
+ 'weekdays_short' => ['dum', 'lun', 'mar', 'mie', 'joi', 'vin', 'sâm'],
+ 'weekdays_min' => ['du', 'lu', 'ma', 'mi', 'jo', 'vi', 'sâ'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' și '],
+ 'meridiem' => ['a.m.', 'p.m.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php
new file mode 100644
index 0000000..ad1d2fa
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro_MD.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ro.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php b/vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php
new file mode 100644
index 0000000..102afcd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ro_RO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ro.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rof.php b/vendor/nesbot/carbon/src/Carbon/Lang/rof.php
new file mode 100644
index 0000000..205fc26
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/rof.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['kang’ama', 'kingoto'],
+ 'weekdays' => ['Ijumapili', 'Ijumatatu', 'Ijumanne', 'Ijumatano', 'Alhamisi', 'Ijumaa', 'Ijumamosi'],
+ 'weekdays_short' => ['Ijp', 'Ijt', 'Ijn', 'Ijtn', 'Alh', 'Iju', 'Ijm'],
+ 'weekdays_min' => ['Ijp', 'Ijt', 'Ijn', 'Ijtn', 'Alh', 'Iju', 'Ijm'],
+ 'months' => ['Mweri wa kwanza', 'Mweri wa kaili', 'Mweri wa katatu', 'Mweri wa kaana', 'Mweri wa tanu', 'Mweri wa sita', 'Mweri wa saba', 'Mweri wa nane', 'Mweri wa tisa', 'Mweri wa ikumi', 'Mweri wa ikumi na moja', 'Mweri wa ikumi na mbili'],
+ 'months_short' => ['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10', 'M11', 'M12'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php
new file mode 100644
index 0000000..673b043
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru.php
@@ -0,0 +1,191 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Bari Badamshin
+ * - Jørn Ølmheim
+ * - François B
+ * - Tim Fish
+ * - Коренберг Марк (imac)
+ * - Serhan Apaydın
+ * - RomeroMsk
+ * - vsn4ik
+ * - JD Isaacks
+ * - Bari Badamshin
+ * - Jørn Ølmheim
+ * - François B
+ * - Коренберг Марк (imac)
+ * - Serhan Apaydın
+ * - RomeroMsk
+ * - vsn4ik
+ * - JD Isaacks
+ * - Fellzo
+ * - andrey-helldar
+ * - Pavel Skripkin (psxx)
+ * - AlexWalkerson
+ * - Vladislav UnsealedOne
+ * - dima-bzz
+ */
+
+use Carbon\CarbonInterface;
+
+$transformDiff = function ($input) {
+ return strtr($input, [
+ 'неделя' => 'неделю',
+ 'секунда' => 'секунду',
+ 'минута' => 'минуту',
+ ]);
+};
+
+return [
+ 'year' => ':count год|:count года|:count лет',
+ 'y' => ':count г.|:count г.|:count л.',
+ 'a_year' => '{1}год|:count год|:count года|:count лет',
+ 'month' => ':count месяц|:count месяца|:count месяцев',
+ 'm' => ':count мес.',
+ 'a_month' => '{1}месяц|:count месяц|:count месяца|:count месяцев',
+ 'week' => ':count неделя|:count недели|:count недель',
+ 'w' => ':count нед.',
+ 'a_week' => '{1}неделя|:count неделю|:count недели|:count недель',
+ 'day' => ':count день|:count дня|:count дней',
+ 'd' => ':count д.',
+ 'a_day' => '{1}день|:count день|:count дня|:count дней',
+ 'hour' => ':count час|:count часа|:count часов',
+ 'h' => ':count ч.',
+ 'a_hour' => '{1}час|:count час|:count часа|:count часов',
+ 'minute' => ':count минута|:count минуты|:count минут',
+ 'min' => ':count мин.',
+ 'a_minute' => '{1}минута|:count минута|:count минуты|:count минут',
+ 'second' => ':count секунда|:count секунды|:count секунд',
+ 's' => ':count сек.',
+ 'a_second' => '{1}несколько секунд|:count секунду|:count секунды|:count секунд',
+ 'ago' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' назад';
+ },
+ 'from_now' => function ($time) use ($transformDiff) {
+ return 'через '.$transformDiff($time);
+ },
+ 'after' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' после';
+ },
+ 'before' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' до';
+ },
+ 'diff_now' => 'только что',
+ 'diff_today' => 'Сегодня,',
+ 'diff_today_regexp' => 'Сегодня,?(?:\\s+в)?',
+ 'diff_yesterday' => 'вчера',
+ 'diff_yesterday_regexp' => 'Вчера,?(?:\\s+в)?',
+ 'diff_tomorrow' => 'завтра',
+ 'diff_tomorrow_regexp' => 'Завтра,?(?:\\s+в)?',
+ 'diff_before_yesterday' => 'позавчера',
+ 'diff_after_tomorrow' => 'послезавтра',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY г.',
+ 'LLL' => 'D MMMM YYYY г., H:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY г., H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Сегодня, в] LT',
+ 'nextDay' => '[Завтра, в] LT',
+ 'nextWeek' => function (CarbonInterface $current, CarbonInterface $other) {
+ if ($current->week !== $other->week) {
+ switch ($current->dayOfWeek) {
+ case 0:
+ return '[В следующее] dddd, [в] LT';
+ case 1:
+ case 2:
+ case 4:
+ return '[В следующий] dddd, [в] LT';
+ case 3:
+ case 5:
+ case 6:
+ return '[В следующую] dddd, [в] LT';
+ }
+ }
+
+ if ($current->dayOfWeek === 2) {
+ return '[Во] dddd, [в] LT';
+ }
+
+ return '[В] dddd, [в] LT';
+ },
+ 'lastDay' => '[Вчера, в] LT',
+ 'lastWeek' => function (CarbonInterface $current, CarbonInterface $other) {
+ if ($current->week !== $other->week) {
+ switch ($current->dayOfWeek) {
+ case 0:
+ return '[В прошлое] dddd, [в] LT';
+ case 1:
+ case 2:
+ case 4:
+ return '[В прошлый] dddd, [в] LT';
+ case 3:
+ case 5:
+ case 6:
+ return '[В прошлую] dddd, [в] LT';
+ }
+ }
+
+ if ($current->dayOfWeek === 2) {
+ return '[Во] dddd, [в] LT';
+ }
+
+ return '[В] dddd, [в] LT';
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'M':
+ case 'd':
+ case 'DDD':
+ return $number.'-й';
+ case 'D':
+ return $number.'-го';
+ case 'w':
+ case 'W':
+ return $number.'-я';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'ночи';
+ }
+ if ($hour < 12) {
+ return 'утра';
+ }
+ if ($hour < 17) {
+ return 'дня';
+ }
+
+ return 'вечера';
+ },
+ 'months' => ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'],
+ 'months_standalone' => ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
+ 'months_short' => ['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'months_short_standalone' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'months_regexp' => '/(DD?o?\.?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => ['воскресенье', 'понедельник', 'вторник', 'среду', 'четверг', 'пятницу', 'субботу'],
+ 'weekdays_standalone' => ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'],
+ 'weekdays_short' => ['вск', 'пнд', 'втр', 'срд', 'чтв', 'птн', 'сбт'],
+ 'weekdays_min' => ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+ 'weekdays_regexp' => '/\[\s*(В|в)\s*((?:прошлую|следующую|эту)\s*)?\]\s*dddd/',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' и '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php
new file mode 100644
index 0000000..8ca7df3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_BY.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ru.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php
new file mode 100644
index 0000000..8ca7df3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ru.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php
new file mode 100644
index 0000000..8ca7df3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_KZ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ru.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php
new file mode 100644
index 0000000..8ca7df3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_MD.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ru.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php
new file mode 100644
index 0000000..8ca7df3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_RU.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ru.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php
new file mode 100644
index 0000000..db958d6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ru_UA.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - RFC 2319 bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/ru.php', [
+ 'weekdays' => ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'],
+ 'weekdays_short' => ['вск', 'пнд', 'вто', 'срд', 'чтв', 'птн', 'суб'],
+ 'weekdays_min' => ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'су'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rw.php b/vendor/nesbot/carbon/src/Carbon/Lang/rw.php
new file mode 100644
index 0000000..bc4a347
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/rw.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/rw_RW.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php b/vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php
new file mode 100644
index 0000000..9b3e068
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/rw_RW.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Rwanda Steve Murphy murf@e-tools.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Mutarama', 'Gashyantare', 'Werurwe', 'Mata', 'Gicuransi', 'Kamena', 'Nyakanga', 'Kanama', 'Nzeli', 'Ukwakira', 'Ugushyingo', 'Ukuboza'],
+ 'months_short' => ['Mut', 'Gas', 'Wer', 'Mat', 'Gic', 'Kam', 'Nya', 'Kan', 'Nze', 'Ukw', 'Ugu', 'Uku'],
+ 'weekdays' => ['Ku cyumweru', 'Kuwa mbere', 'Kuwa kabiri', 'Kuwa gatatu', 'Kuwa kane', 'Kuwa gatanu', 'Kuwa gatandatu'],
+ 'weekdays_short' => ['Mwe', 'Mbe', 'Kab', 'Gtu', 'Kan', 'Gnu', 'Gnd'],
+ 'weekdays_min' => ['Mwe', 'Mbe', 'Kab', 'Gtu', 'Kan', 'Gnu', 'Gnd'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'second' => ':count vuna', // less reliable
+ 's' => ':count vuna', // less reliable
+ 'a_second' => ':count vuna', // less reliable
+
+ 'year' => 'aka :count',
+ 'y' => 'aka :count',
+ 'a_year' => 'aka :count',
+
+ 'month' => 'ezi :count',
+ 'm' => 'ezi :count',
+ 'a_month' => 'ezi :count',
+
+ 'week' => ':count icyumweru',
+ 'w' => ':count icyumweru',
+ 'a_week' => ':count icyumweru',
+
+ 'day' => ':count nsi',
+ 'd' => ':count nsi',
+ 'a_day' => ':count nsi',
+
+ 'hour' => 'saha :count',
+ 'h' => 'saha :count',
+ 'a_hour' => 'saha :count',
+
+ 'minute' => ':count -nzinya',
+ 'min' => ':count -nzinya',
+ 'a_minute' => ':count -nzinya',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/rwk.php b/vendor/nesbot/carbon/src/Carbon/Lang/rwk.php
new file mode 100644
index 0000000..ed92e8e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/rwk.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['utuko', 'kyiukonyi'],
+ 'weekdays' => ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sa.php b/vendor/nesbot/carbon/src/Carbon/Lang/sa.php
new file mode 100644
index 0000000..1357c03
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sa.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sa_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php
new file mode 100644
index 0000000..cfda9a6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sa_IN.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - The Debian project Christian Perrier bubulle@debian.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D-MM-YY',
+ ],
+ 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'weekdays' => ['रविवासर:', 'सोमवासर:', 'मंगलवासर:', 'बुधवासर:', 'बृहस्पतिवासरः', 'शुक्रवासर', 'शनिवासर:'],
+ 'weekdays_short' => ['रविः', 'सोम:', 'मंगल:', 'बुध:', 'बृहस्पतिः', 'शुक्र', 'शनि:'],
+ 'weekdays_min' => ['रविः', 'सोम:', 'मंगल:', 'बुध:', 'बृहस्पतिः', 'शुक्र', 'शनि:'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+
+ 'minute' => ':count होरा', // less reliable
+ 'min' => ':count होरा', // less reliable
+ 'a_minute' => ':count होरा', // less reliable
+
+ 'year' => ':count वर्ष',
+ 'y' => ':count वर्ष',
+ 'a_year' => ':count वर्ष',
+
+ 'month' => ':count मास',
+ 'm' => ':count मास',
+ 'a_month' => ':count मास',
+
+ 'week' => ':count सप्ताहः saptahaĥ',
+ 'w' => ':count सप्ताहः saptahaĥ',
+ 'a_week' => ':count सप्ताहः saptahaĥ',
+
+ 'day' => ':count दिन',
+ 'd' => ':count दिन',
+ 'a_day' => ':count दिन',
+
+ 'hour' => ':count घण्टा',
+ 'h' => ':count घण्टा',
+ 'a_hour' => ':count घण्टा',
+
+ 'second' => ':count द्वितीयः',
+ 's' => ':count द्वितीयः',
+ 'a_second' => ':count द्वितीयः',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sah.php b/vendor/nesbot/carbon/src/Carbon/Lang/sah.php
new file mode 100644
index 0000000..b828824
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sah.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sah_RU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php
new file mode 100644
index 0000000..94cc0cb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sah_RU.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Valery Timiriliyev Valery Timiriliyev timiriliyev@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/ru.php', [
+ 'formats' => [
+ 'L' => 'YYYY.MM.DD',
+ ],
+ 'months' => ['тохсунньу', 'олунньу', 'кулун тутар', 'муус устар', 'ыам ыйын', 'бэс ыйын', 'от ыйын', 'атырдьах ыйын', 'балаҕан ыйын', 'алтынньы', 'сэтинньи', 'ахсынньы'],
+ 'months_short' => ['тохс', 'олун', 'кул', 'муус', 'ыам', 'бэс', 'от', 'атыр', 'бал', 'алт', 'сэт', 'ахс'],
+ 'weekdays' => ['баскыһыанньа', 'бэнидиэнньик', 'оптуорунньук', 'сэрэдэ', 'чэппиэр', 'бээтинсэ', 'субуота'],
+ 'weekdays_short' => ['бс', 'бн', 'оп', 'ср', 'чп', 'бт', 'сб'],
+ 'weekdays_min' => ['бс', 'бн', 'оп', 'ср', 'чп', 'бт', 'сб'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/saq.php b/vendor/nesbot/carbon/src/Carbon/Lang/saq.php
new file mode 100644
index 0000000..ff8bf60
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/saq.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Tesiran', 'Teipa'],
+ 'weekdays' => ['Mderot ee are', 'Mderot ee kuni', 'Mderot ee ong’wan', 'Mderot ee inet', 'Mderot ee ile', 'Mderot ee sapa', 'Mderot ee kwe'],
+ 'weekdays_short' => ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'],
+ 'weekdays_min' => ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'],
+ 'months' => ['Lapa le obo', 'Lapa le waare', 'Lapa le okuni', 'Lapa le ong’wan', 'Lapa le imet', 'Lapa le ile', 'Lapa le sapa', 'Lapa le isiet', 'Lapa le saal', 'Lapa le tomon', 'Lapa le tomon obo', 'Lapa le tomon waare'],
+ 'months_short' => ['Obo', 'Waa', 'Oku', 'Ong', 'Ime', 'Ile', 'Sap', 'Isi', 'Saa', 'Tom', 'Tob', 'Tow'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sat.php b/vendor/nesbot/carbon/src/Carbon/Lang/sat.php
new file mode 100644
index 0000000..c9914c6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sat.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sat_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php
new file mode 100644
index 0000000..632b1af
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sat_IN.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat Pune libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रेल', 'मई', 'जुन', 'जुलाई', 'अगस्त', 'सितम्बर', 'अखथबर', 'नवम्बर', 'दिसम्बर'],
+ 'months_short' => ['जनवरी', 'फरवरी', 'मार्च', 'अप्रेल', 'मई', 'जुन', 'जुलाई', 'अगस्त', 'सितम्बर', 'अखथबर', 'नवम्बर', 'दिसम्बर'],
+ 'weekdays' => ['सिंगेमाँहाँ', 'ओतेमाँहाँ', 'बालेमाँहाँ', 'सागुनमाँहाँ', 'सारदीमाँहाँ', 'जारुममाँहाँ', 'ञुहुममाँहाँ'],
+ 'weekdays_short' => ['सिंगे', 'ओते', 'बाले', 'सागुन', 'सारदी', 'जारुम', 'ञुहुम'],
+ 'weekdays_min' => ['सिंगे', 'ओते', 'बाले', 'सागुन', 'सारदी', 'जारुम', 'ञुहुम'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'month' => ':count ńindạ cando', // less reliable
+ 'm' => ':count ńindạ cando', // less reliable
+ 'a_month' => ':count ńindạ cando', // less reliable
+
+ 'week' => ':count mãhã', // less reliable
+ 'w' => ':count mãhã', // less reliable
+ 'a_week' => ':count mãhã', // less reliable
+
+ 'hour' => ':count ᱥᱳᱱᱚ', // less reliable
+ 'h' => ':count ᱥᱳᱱᱚ', // less reliable
+ 'a_hour' => ':count ᱥᱳᱱᱚ', // less reliable
+
+ 'minute' => ':count ᱯᱤᱞᱪᱩ', // less reliable
+ 'min' => ':count ᱯᱤᱞᱪᱩ', // less reliable
+ 'a_minute' => ':count ᱯᱤᱞᱪᱩ', // less reliable
+
+ 'second' => ':count ar', // less reliable
+ 's' => ':count ar', // less reliable
+ 'a_second' => ':count ar', // less reliable
+
+ 'year' => ':count ne̲s',
+ 'y' => ':count ne̲s',
+ 'a_year' => ':count ne̲s',
+
+ 'day' => ':count ᱫᱤᱱ',
+ 'd' => ':count ᱫᱤᱱ',
+ 'a_day' => ':count ᱫᱤᱱ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sbp.php b/vendor/nesbot/carbon/src/Carbon/Lang/sbp.php
new file mode 100644
index 0000000..e29ca37
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sbp.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Lwamilawu', 'Pashamihe'],
+ 'weekdays' => ['Mulungu', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alahamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Mul', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Mul', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'months' => ['Mupalangulwa', 'Mwitope', 'Mushende', 'Munyi', 'Mushende Magali', 'Mujimbi', 'Mushipepo', 'Mupuguto', 'Munyense', 'Mokhu', 'Musongandembwe', 'Muhaano'],
+ 'months_short' => ['Mup', 'Mwi', 'Msh', 'Mun', 'Mag', 'Muj', 'Msp', 'Mpg', 'Mye', 'Mok', 'Mus', 'Muh'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sc.php b/vendor/nesbot/carbon/src/Carbon/Lang/sc.php
new file mode 100644
index 0000000..7178cf4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sc.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sc_IT.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php b/vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php
new file mode 100644
index 0000000..5d1e4ce
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sc_IT.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Sardinian Translators Team Massimeddu Cireddu massimeddu@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD. MM. YY',
+ ],
+ 'months' => ['Ghennàrgiu', 'Freàrgiu', 'Martzu', 'Abrile', 'Maju', 'Làmpadas', 'Argiolas//Trìulas', 'Austu', 'Cabudanni', 'Santugaine//Ladàmine', 'Onniasantu//Santandria', 'Nadale//Idas'],
+ 'months_short' => ['Ghe', 'Fre', 'Mar', 'Abr', 'Maj', 'Làm', 'Arg', 'Aus', 'Cab', 'Lad', 'Onn', 'Nad'],
+ 'weekdays' => ['Domìnigu', 'Lunis', 'Martis', 'Mèrcuris', 'Giòbia', 'Chenàbura', 'Sàbadu'],
+ 'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mèr', 'Giò', 'Che', 'Sàb'],
+ 'weekdays_min' => ['Dom', 'Lun', 'Mar', 'Mèr', 'Giò', 'Che', 'Sàb'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'minute' => ':count mementu', // less reliable
+ 'min' => ':count mementu', // less reliable
+ 'a_minute' => ':count mementu', // less reliable
+
+ 'year' => ':count annu',
+ 'y' => ':count annu',
+ 'a_year' => ':count annu',
+
+ 'month' => ':count mese',
+ 'm' => ':count mese',
+ 'a_month' => ':count mese',
+
+ 'week' => ':count chida',
+ 'w' => ':count chida',
+ 'a_week' => ':count chida',
+
+ 'day' => ':count dí',
+ 'd' => ':count dí',
+ 'a_day' => ':count dí',
+
+ 'hour' => ':count ora',
+ 'h' => ':count ora',
+ 'a_hour' => ':count ora',
+
+ 'second' => ':count secundu',
+ 's' => ':count secundu',
+ 'a_second' => ':count secundu',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd.php
new file mode 100644
index 0000000..0022c5a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd.php
@@ -0,0 +1,81 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+$months = [
+ 'جنوري',
+ 'فيبروري',
+ 'مارچ',
+ 'اپريل',
+ 'مئي',
+ 'جون',
+ 'جولاءِ',
+ 'آگسٽ',
+ 'سيپٽمبر',
+ 'آڪٽوبر',
+ 'نومبر',
+ 'ڊسمبر',
+];
+
+$weekdays = [
+ 'آچر',
+ 'سومر',
+ 'اڱارو',
+ 'اربع',
+ 'خميس',
+ 'جمع',
+ 'ڇنڇر',
+];
+
+/*
+ * Authors:
+ * - Narain Sagar
+ * - Sawood Alam
+ * - Narain Sagar
+ */
+return [
+ 'year' => '{1}'.'هڪ سال'.'|:count '.'سال',
+ 'month' => '{1}'.'هڪ مهينو'.'|:count '.'مهينا',
+ 'week' => '{1}'.'ھڪ ھفتو'.'|:count '.'هفتا',
+ 'day' => '{1}'.'هڪ ڏينهن'.'|:count '.'ڏينهن',
+ 'hour' => '{1}'.'هڪ ڪلاڪ'.'|:count '.'ڪلاڪ',
+ 'minute' => '{1}'.'هڪ منٽ'.'|:count '.'منٽ',
+ 'second' => '{1}'.'چند سيڪنڊ'.'|:count '.'سيڪنڊ',
+ 'ago' => ':time اڳ',
+ 'from_now' => ':time پوء',
+ 'diff_yesterday' => 'ڪالهه',
+ 'diff_today' => 'اڄ',
+ 'diff_tomorrow' => 'سڀاڻي',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd، D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[اڄ] LT',
+ 'nextDay' => '[سڀاڻي] LT',
+ 'nextWeek' => 'dddd [اڳين هفتي تي] LT',
+ 'lastDay' => '[ڪالهه] LT',
+ 'lastWeek' => '[گزريل هفتي] dddd [تي] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['صبح', 'شام'],
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => $weekdays,
+ 'weekdays_short' => $weekdays,
+ 'weekdays_min' => $weekdays,
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => ['، ', ' ۽ '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php
new file mode 100644
index 0000000..de1dad0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat, Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/sd.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['جنوري', 'فبروري', 'مارچ', 'اپريل', 'مي', 'جون', 'جولاءِ', 'آگسٽ', 'سيپٽيمبر', 'آڪٽوبر', 'نومبر', 'ڊسمبر'],
+ 'months_short' => ['جنوري', 'فبروري', 'مارچ', 'اپريل', 'مي', 'جون', 'جولاءِ', 'آگسٽ', 'سيپٽيمبر', 'آڪٽوبر', 'نومبر', 'ڊسمبر'],
+ 'weekdays' => ['آرتوارُ', 'سومرُ', 'منگلُ', 'ٻُڌرُ', 'وسپت', 'جُمو', 'ڇنڇر'],
+ 'weekdays_short' => ['آرتوارُ', 'سومرُ', 'منگلُ', 'ٻُڌرُ', 'وسپت', 'جُمو', 'ڇنڇر'],
+ 'weekdays_min' => ['آرتوارُ', 'سومرُ', 'منگلُ', 'ٻُڌرُ', 'وسپت', 'جُمو', 'ڇنڇر'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php
new file mode 100644
index 0000000..061fcc1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sd_IN@devanagari.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat, Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/sd.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['जनवरी', 'फबरवरी', 'मार्चि', 'अप्रेल', 'मे', 'जूनि', 'जूलाइ', 'आगस्टु', 'सेप्टेंबरू', 'आक्टूबरू', 'नवंबरू', 'ॾिसंबरू'],
+ 'months_short' => ['जनवरी', 'फबरवरी', 'मार्चि', 'अप्रेल', 'मे', 'जूनि', 'जूलाइ', 'आगस्टु', 'सेप्टेंबरू', 'आक्टूबरू', 'नवंबरू', 'ॾिसंबरू'],
+ 'weekdays' => ['आर्तवारू', 'सूमरू', 'मंगलू', 'ॿुधरू', 'विस्पति', 'जुमो', 'छंछस'],
+ 'weekdays_short' => ['आर्तवारू', 'सूमरू', 'मंगलू', 'ॿुधरू', 'विस्पति', 'जुमो', 'छंछस'],
+ 'weekdays_min' => ['आर्तवारू', 'सूमरू', 'मंगलू', 'ॿुधरू', 'विस्पति', 'जुमो', 'छंछस'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['म.पू.', 'म.नं.'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se.php b/vendor/nesbot/carbon/src/Carbon/Lang/se.php
new file mode 100644
index 0000000..7c4b92a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/se.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Karamell
+ */
+return [
+ 'year' => '{1}:count jahki|:count jagit',
+ 'a_year' => '{1}okta jahki|:count jagit',
+ 'y' => ':count j.',
+ 'month' => '{1}:count mánnu|:count mánut',
+ 'a_month' => '{1}okta mánnu|:count mánut',
+ 'm' => ':count mán.',
+ 'week' => '{1}:count vahkku|:count vahkku',
+ 'a_week' => '{1}okta vahkku|:count vahkku',
+ 'w' => ':count v.',
+ 'day' => '{1}:count beaivi|:count beaivvit',
+ 'a_day' => '{1}okta beaivi|:count beaivvit',
+ 'd' => ':count b.',
+ 'hour' => '{1}:count diimmu|:count diimmut',
+ 'a_hour' => '{1}okta diimmu|:count diimmut',
+ 'h' => ':count d.',
+ 'minute' => '{1}:count minuhta|:count minuhtat',
+ 'a_minute' => '{1}okta minuhta|:count minuhtat',
+ 'min' => ':count min.',
+ 'second' => '{1}:count sekunddat|:count sekunddat',
+ 'a_second' => '{1}moadde sekunddat|:count sekunddat',
+ 's' => ':count s.',
+ 'ago' => 'maŋit :time',
+ 'from_now' => ':time geažes',
+ 'diff_yesterday' => 'ikte',
+ 'diff_yesterday_regexp' => 'ikte(?:\\s+ti)?',
+ 'diff_today' => 'otne',
+ 'diff_today_regexp' => 'otne(?:\\s+ti)?',
+ 'diff_tomorrow' => 'ihttin',
+ 'diff_tomorrow_regexp' => 'ihttin(?:\\s+ti)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'MMMM D. [b.] YYYY',
+ 'LLL' => 'MMMM D. [b.] YYYY [ti.] HH:mm',
+ 'LLLL' => 'dddd, MMMM D. [b.] YYYY [ti.] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[otne ti] LT',
+ 'nextDay' => '[ihttin ti] LT',
+ 'nextWeek' => 'dddd [ti] LT',
+ 'lastDay' => '[ikte ti] LT',
+ 'lastWeek' => '[ovddit] dddd [ti] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu', 'cuoŋománnu', 'miessemánnu', 'geassemánnu', 'suoidnemánnu', 'borgemánnu', 'čakčamánnu', 'golggotmánnu', 'skábmamánnu', 'juovlamánnu'],
+ 'months_short' => ['ođđj', 'guov', 'njuk', 'cuo', 'mies', 'geas', 'suoi', 'borg', 'čakč', 'golg', 'skáb', 'juov'],
+ 'weekdays' => ['sotnabeaivi', 'vuossárga', 'maŋŋebárga', 'gaskavahkku', 'duorastat', 'bearjadat', 'lávvardat'],
+ 'weekdays_short' => ['sotn', 'vuos', 'maŋ', 'gask', 'duor', 'bear', 'láv'],
+ 'weekdays_min' => ['s', 'v', 'm', 'g', 'd', 'b', 'L'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' ja '],
+ 'meridiem' => ['i.b.', 'e.b.'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php
new file mode 100644
index 0000000..cf01805
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/se_FI.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/se.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'months' => ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu', 'cuoŋománnu', 'miessemánnu', 'geassemánnu', 'suoidnemánnu', 'borgemánnu', 'čakčamánnu', 'golggotmánnu', 'skábmamánnu', 'juovlamánnu'],
+ 'months_short' => ['ođđj', 'guov', 'njuk', 'cuoŋ', 'mies', 'geas', 'suoi', 'borg', 'čakč', 'golg', 'skáb', 'juov'],
+ 'weekdays' => ['sotnabeaivi', 'mánnodat', 'disdat', 'gaskavahkku', 'duorastat', 'bearjadat', 'lávvordat'],
+ 'weekdays_short' => ['so', 'má', 'di', 'ga', 'du', 'be', 'lá'],
+ 'weekdays_min' => ['so', 'má', 'di', 'ga', 'du', 'be', 'lá'],
+ 'meridiem' => ['i', 'e'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php b/vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php
new file mode 100644
index 0000000..177c7e9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/se_NO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/se.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php
new file mode 100644
index 0000000..177c7e9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/se_SE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/se.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/seh.php b/vendor/nesbot/carbon/src/Carbon/Lang/seh.php
new file mode 100644
index 0000000..babf9af
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/seh.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['Dimingu', 'Chiposi', 'Chipiri', 'Chitatu', 'Chinai', 'Chishanu', 'Sabudu'],
+ 'weekdays_short' => ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'],
+ 'weekdays_min' => ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'],
+ 'months' => ['Janeiro', 'Fevreiro', 'Marco', 'Abril', 'Maio', 'Junho', 'Julho', 'Augusto', 'Setembro', 'Otubro', 'Novembro', 'Decembro'],
+ 'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Aug', 'Set', 'Otu', 'Nov', 'Dec'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'd [de] MMM [de] YYYY',
+ 'LLL' => 'd [de] MMMM [de] YYYY HH:mm',
+ 'LLLL' => 'dddd, d [de] MMMM [de] YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ses.php b/vendor/nesbot/carbon/src/Carbon/Lang/ses.php
new file mode 100644
index 0000000..e1099e6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ses.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Adduha', 'Aluula'],
+ 'weekdays' => ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamiisa', 'Alzuma', 'Asibti'],
+ 'weekdays_short' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
+ 'weekdays_min' => ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
+ 'months' => ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
+ 'months_short' => ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'month' => ':count alaada', // less reliable
+ 'm' => ':count alaada', // less reliable
+ 'a_month' => ':count alaada', // less reliable
+
+ 'hour' => ':count ɲaajin', // less reliable
+ 'h' => ':count ɲaajin', // less reliable
+ 'a_hour' => ':count ɲaajin', // less reliable
+
+ 'minute' => ':count zarbu', // less reliable
+ 'min' => ':count zarbu', // less reliable
+ 'a_minute' => ':count zarbu', // less reliable
+
+ 'year' => ':count jiiri',
+ 'y' => ':count jiiri',
+ 'a_year' => ':count jiiri',
+
+ 'week' => ':count jirbiiyye',
+ 'w' => ':count jirbiiyye',
+ 'a_week' => ':count jirbiiyye',
+
+ 'day' => ':count zaari',
+ 'd' => ':count zaari',
+ 'a_day' => ':count zaari',
+
+ 'second' => ':count ihinkante',
+ 's' => ':count ihinkante',
+ 'a_second' => ':count ihinkante',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sg.php b/vendor/nesbot/carbon/src/Carbon/Lang/sg.php
new file mode 100644
index 0000000..9264e89
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sg.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['ND', 'LK'],
+ 'weekdays' => ['Bikua-ôko', 'Bïkua-ûse', 'Bïkua-ptâ', 'Bïkua-usïö', 'Bïkua-okü', 'Lâpôsö', 'Lâyenga'],
+ 'weekdays_short' => ['Bk1', 'Bk2', 'Bk3', 'Bk4', 'Bk5', 'Lâp', 'Lây'],
+ 'weekdays_min' => ['Bk1', 'Bk2', 'Bk3', 'Bk4', 'Bk5', 'Lâp', 'Lây'],
+ 'months' => ['Nyenye', 'Fulundïgi', 'Mbängü', 'Ngubùe', 'Bêläwü', 'Föndo', 'Lengua', 'Kükürü', 'Mvuka', 'Ngberere', 'Nabändüru', 'Kakauka'],
+ 'months_short' => ['Nye', 'Ful', 'Mbä', 'Ngu', 'Bêl', 'Fön', 'Len', 'Kük', 'Mvu', 'Ngb', 'Nab', 'Kak'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count dā', // less reliable
+ 'y' => ':count dā', // less reliable
+ 'a_year' => ':count dā', // less reliable
+
+ 'week' => ':count bïkua-okü', // less reliable
+ 'w' => ':count bïkua-okü', // less reliable
+ 'a_week' => ':count bïkua-okü', // less reliable
+
+ 'day' => ':count ziggawâ', // less reliable
+ 'd' => ':count ziggawâ', // less reliable
+ 'a_day' => ':count ziggawâ', // less reliable
+
+ 'hour' => ':count yângâködörö', // less reliable
+ 'h' => ':count yângâködörö', // less reliable
+ 'a_hour' => ':count yângâködörö', // less reliable
+
+ 'second' => ':count bïkua-ôko', // less reliable
+ 's' => ':count bïkua-ôko', // less reliable
+ 'a_second' => ':count bïkua-ôko', // less reliable
+
+ 'month' => ':count Nze tî ngu',
+ 'm' => ':count Nze tî ngu',
+ 'a_month' => ':count Nze tî ngu',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sgs.php b/vendor/nesbot/carbon/src/Carbon/Lang/sgs.php
new file mode 100644
index 0000000..864b989
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sgs.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sgs_LT.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php b/vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php
new file mode 100644
index 0000000..aa9e942
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sgs_LT.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Arnas Udovičius bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY.MM.DD',
+ ],
+ 'months' => ['sausė', 'vasarė', 'kuova', 'balondė', 'gegožės', 'bėrželė', 'lëpas', 'rogpjūtė', 'siejės', 'spalė', 'lapkrėstė', 'grůdė'],
+ 'months_short' => ['Sau', 'Vas', 'Kuo', 'Bal', 'Geg', 'Bėr', 'Lëp', 'Rgp', 'Sie', 'Spa', 'Lap', 'Grd'],
+ 'weekdays' => ['nedielės dëna', 'panedielis', 'oterninks', 'sereda', 'četvergs', 'petnīčė', 'sobata'],
+ 'weekdays_short' => ['Nd', 'Pn', 'Ot', 'Sr', 'Čt', 'Pt', 'Sb'],
+ 'weekdays_min' => ['Nd', 'Pn', 'Ot', 'Sr', 'Čt', 'Pt', 'Sb'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'minute' => ':count mažos', // less reliable
+ 'min' => ':count mažos', // less reliable
+ 'a_minute' => ':count mažos', // less reliable
+
+ 'year' => ':count metā',
+ 'y' => ':count metā',
+ 'a_year' => ':count metā',
+
+ 'month' => ':count mienou',
+ 'm' => ':count mienou',
+ 'a_month' => ':count mienou',
+
+ 'week' => ':count nedielė',
+ 'w' => ':count nedielė',
+ 'a_week' => ':count nedielė',
+
+ 'day' => ':count dīna',
+ 'd' => ':count dīna',
+ 'a_day' => ':count dīna',
+
+ 'hour' => ':count adīna',
+ 'h' => ':count adīna',
+ 'a_hour' => ':count adīna',
+
+ 'second' => ':count Sekondė',
+ 's' => ':count Sekondė',
+ 'a_second' => ':count Sekondė',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sh.php b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php
new file mode 100644
index 0000000..e03b506
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sh.php
@@ -0,0 +1,68 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+// @codeCoverageIgnoreStart
+use Symfony\Component\Translation\PluralizationRules;
+
+if (class_exists('Symfony\\Component\\Translation\\PluralizationRules')) {
+ PluralizationRules::set(static function ($number) {
+ return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+ }, 'sh');
+}
+// @codeCoverageIgnoreEnd
+
+/*
+ * Authors:
+ * - Томица Кораћ
+ * - Enrique Vidal
+ * - Christopher Dell
+ * - dmilisic
+ * - danijel
+ * - Miroslav Matkovic (mikki021)
+ */
+return [
+ 'diff_now' => 'sada',
+ 'diff_yesterday' => 'juče',
+ 'diff_tomorrow' => 'sutra',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'MMMM D, YYYY',
+ 'LLL' => 'DD MMM HH:mm',
+ 'LLLL' => 'MMMM DD, YYYY HH:mm',
+ ],
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count g.',
+ 'month' => ':count mesec|:count meseca|:count meseci',
+ 'm' => ':count m.',
+ 'week' => ':count nedelja|:count nedelje|:count nedelja',
+ 'w' => ':count n.',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count d.',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count č.',
+ 'minute' => ':count minut|:count minuta|:count minuta',
+ 'min' => ':count min.',
+ 'second' => ':count sekund|:count sekunde|:count sekundi',
+ 's' => ':count s.',
+ 'ago' => 'pre :time',
+ 'from_now' => 'za :time',
+ 'after' => 'nakon :time',
+ 'before' => ':time raniјe',
+ 'weekdays' => ['Nedelja', 'Ponedeljak', 'Utorak', 'Sreda', 'Četvrtak', 'Petak', 'Subota'],
+ 'weekdays_short' => ['Ned', 'Pon', 'Uto', 'Sre', 'Čet', 'Pet', 'Sub'],
+ 'weekdays_min' => ['Ned', 'Pon', 'Uto', 'Sre', 'Čet', 'Pet', 'Sub'],
+ 'months' => ['Januar', 'Februar', 'Mart', 'April', 'Maj', 'Jun', 'Jul', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
+ 'list' => [', ', ' i '],
+ 'meridiem' => ['pre podne', 'po podne'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shi.php b/vendor/nesbot/carbon/src/Carbon/Lang/shi.php
new file mode 100644
index 0000000..7815186
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shi.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['ⵜⵉⴼⴰⵡⵜ', 'ⵜⴰⴷⴳⴳⵯⴰⵜ'],
+ 'weekdays' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵕⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'],
+ 'weekdays_short' => ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ', 'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'],
+ 'weekdays_min' => ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ', 'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'],
+ 'months' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵜⵓⴱⵔ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'],
+ 'months_short' => ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ', 'ⵎⴰⵢ', 'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ', 'ⴽⵜⵓ', 'ⵏⵓⵡ', 'ⴷⵓⵊ'],
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 6],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'year' => ':count aseggwas',
+ 'y' => ':count aseggwas',
+ 'a_year' => ':count aseggwas',
+
+ 'month' => ':count ayyur',
+ 'm' => ':count ayyur',
+ 'a_month' => ':count ayyur',
+
+ 'week' => ':count imalass',
+ 'w' => ':count imalass',
+ 'a_week' => ':count imalass',
+
+ 'day' => ':count ass',
+ 'd' => ':count ass',
+ 'a_day' => ':count ass',
+
+ 'hour' => ':count urɣ', // less reliable
+ 'h' => ':count urɣ', // less reliable
+ 'a_hour' => ':count urɣ', // less reliable
+
+ 'minute' => ':count ⴰⵎⵥⵉ', // less reliable
+ 'min' => ':count ⴰⵎⵥⵉ', // less reliable
+ 'a_minute' => ':count ⴰⵎⵥⵉ', // less reliable
+
+ 'second' => ':count sin', // less reliable
+ 's' => ':count sin', // less reliable
+ 'a_second' => ':count sin', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php
new file mode 100644
index 0000000..cddfb24
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Latn.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/shi.php', [
+ 'meridiem' => ['tifawt', 'tadggʷat'],
+ 'weekdays' => ['asamas', 'aynas', 'asinas', 'akṛas', 'akwas', 'asimwas', 'asiḍyas'],
+ 'weekdays_short' => ['asa', 'ayn', 'asi', 'akṛ', 'akw', 'asim', 'asiḍ'],
+ 'weekdays_min' => ['asa', 'ayn', 'asi', 'akṛ', 'akw', 'asim', 'asiḍ'],
+ 'months' => ['innayr', 'bṛayṛ', 'maṛṣ', 'ibrir', 'mayyu', 'yunyu', 'yulyuz', 'ɣuct', 'cutanbir', 'ktubr', 'nuwanbir', 'dujanbir'],
+ 'months_short' => ['inn', 'bṛa', 'maṛ', 'ibr', 'may', 'yun', 'yul', 'ɣuc', 'cut', 'ktu', 'nuw', 'duj'],
+ 'first_day_of_week' => 6,
+ 'weekend' => [5, 6],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+
+ 'minute' => ':count agur', // less reliable
+ 'min' => ':count agur', // less reliable
+ 'a_minute' => ':count agur', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php
new file mode 100644
index 0000000..f3df1f2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shi_Tfng.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/shi.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shn.php b/vendor/nesbot/carbon/src/Carbon/Lang/shn.php
new file mode 100644
index 0000000..fe7b1ea
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shn.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/shn_MM.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php b/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php
new file mode 100644
index 0000000..f399acf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shn_MM.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - ubuntu Myanmar LoCo Team https://ubuntu-mm.net Bone Pyae Sone bone.burma@mail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'OY MMM OD dddd',
+ ],
+ 'months' => ['လိူၼ်ၵမ်', 'လိူၼ်သၢမ်', 'လိူၼ်သီ', 'လိူၼ်ႁႃႈ', 'လိူၼ်ႁူၵ်း', 'လိူၼ်ၸဵတ်း', 'လိူၼ်ပႅတ်ႇ', 'လိူၼ်ၵဝ်ႈ', 'လိူၼ်သိပ်း', 'လိူၼ်သိပ်းဢိတ်း', 'လိူၼ်သိပ်းဢိတ်းသွင်', 'လိူၼ်ၸဵင်'],
+ 'months_short' => ['လိူၼ်ၵမ်', 'လိူၼ်သၢမ်', 'လိူၼ်သီ', 'လိူၼ်ႁႃႈ', 'လိူၼ်ႁူၵ်း', 'လိူၼ်ၸဵတ်း', 'လိူၼ်ပႅတ်ႇ', 'လိူၼ်ၵဝ်ႈ', 'လိူၼ်သိပ်း', 'လိူၼ်သိပ်းဢိတ်း', 'လိူၼ်သိပ်းဢိတ်းသွင်', 'လိူၼ်ၸဵင်'],
+ 'weekdays' => ['ဝၼ်းဢႃးတိတ်ႉ', 'ဝၼ်းၸၼ်', 'ဝၼ်းဢၢင်းၵၢၼ်း', 'ဝၼ်းပူတ်ႉ', 'ဝၼ်းၽတ်း', 'ဝၼ်းသုၵ်း', 'ဝၼ်းသဝ်'],
+ 'weekdays_short' => ['တိတ့်', 'ၸၼ်', 'ၵၢၼ်း', 'ပုတ့်', 'ၽတ်း', 'သုၵ်း', 'သဝ်'],
+ 'weekdays_min' => ['တိတ့်', 'ၸၼ်', 'ၵၢၼ်း', 'ပုတ့်', 'ၽတ်း', 'သုၵ်း', 'သဝ်'],
+ 'alt_numbers' => ['႐႐', '႐႑', '႐႒', '႐႓', '႐႔', '႐႕', '႐႖', '႐႗', '႐႘', '႐႙', '႑႐', '႑႑', '႑႒', '႑႓', '႑႔', '႑႕', '႑႖', '႑႗', '႑႘', '႑႙', '႒႐', '႒႑', '႒႒', '႒႓', '႒႔', '႒႕', '႒႖', '႒႗', '႒႘', '႒႙', '႓႐', '႓႑', '႓႒', '႓႓', '႓႔', '႓႕', '႓႖', '႓႗', '႓႘', '႓႙', '႔႐', '႔႑', '႔႒', '႔႓', '႔႔', '႔႕', '႔႖', '႔႗', '႔႘', '႔႙', '႕႐', '႕႑', '႕႒', '႕႓', '႕႔', '႕႕', '႕႖', '႕႗', '႕႘', '႕႙', '႖႐', '႖႑', '႖႒', '႖႓', '႖႔', '႖႕', '႖႖', '႖႗', '႖႘', '႖႙', '႗႐', '႗႑', '႗႒', '႗႓', '႗႔', '႗႕', '႗႖', '႗႗', '႗႘', '႗႙', '႘႐', '႘႑', '႘႒', '႘႓', '႘႔', '႘႕', '႘႖', '႘႗', '႘႘', '႘႙', '႙႐', '႙႑', '႙႒', '႙႓', '႙႔', '႙႕', '႙႖', '႙႗', '႙႘', '႙႙'],
+ 'meridiem' => ['ၵၢင်ၼႂ်', 'တၢမ်းၶမ်ႈ'],
+
+ 'month' => ':count လိူၼ်', // less reliable
+ 'm' => ':count လိူၼ်', // less reliable
+ 'a_month' => ':count လိူၼ်', // less reliable
+
+ 'week' => ':count ဝၼ်း', // less reliable
+ 'w' => ':count ဝၼ်း', // less reliable
+ 'a_week' => ':count ဝၼ်း', // less reliable
+
+ 'hour' => ':count ຕີ', // less reliable
+ 'h' => ':count ຕີ', // less reliable
+ 'a_hour' => ':count ຕີ', // less reliable
+
+ 'minute' => ':count ເດັກ', // less reliable
+ 'min' => ':count ເດັກ', // less reliable
+ 'a_minute' => ':count ເດັກ', // less reliable
+
+ 'second' => ':count ဢိုၼ်ႇ', // less reliable
+ 's' => ':count ဢိုၼ်ႇ', // less reliable
+ 'a_second' => ':count ဢိုၼ်ႇ', // less reliable
+
+ 'year' => ':count ပီ',
+ 'y' => ':count ပီ',
+ 'a_year' => ':count ပီ',
+
+ 'day' => ':count ກາງວັນ',
+ 'd' => ':count ກາງວັນ',
+ 'a_day' => ':count ກາງວັນ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shs.php b/vendor/nesbot/carbon/src/Carbon/Lang/shs.php
new file mode 100644
index 0000000..8d2e1d7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shs.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/shs_CA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php b/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php
new file mode 100644
index 0000000..08d385e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/shs_CA.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Neskie Manuel bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Pellkwet̓min', 'Pelctsipwen̓ten', 'Pellsqépts', 'Peslléwten', 'Pell7ell7é7llqten', 'Pelltspéntsk', 'Pelltqwelq̓wél̓t', 'Pellct̓éxel̓cten', 'Pesqelqlélten', 'Pesllwélsten', 'Pellc7ell7é7llcwten̓', 'Pelltetétq̓em'],
+ 'months_short' => ['Kwe', 'Tsi', 'Sqe', 'Éwt', 'Ell', 'Tsp', 'Tqw', 'Ct̓é', 'Qel', 'Wél', 'U7l', 'Tet'],
+ 'weekdays' => ['Sxetspesq̓t', 'Spetkesq̓t', 'Selesq̓t', 'Skellesq̓t', 'Smesesq̓t', 'Stselkstesq̓t', 'Stqmekstesq̓t'],
+ 'weekdays_short' => ['Sxe', 'Spe', 'Sel', 'Ske', 'Sme', 'Sts', 'Stq'],
+ 'weekdays_min' => ['Sxe', 'Spe', 'Sel', 'Ske', 'Sme', 'Sts', 'Stq'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count sqlélten', // less reliable
+ 'y' => ':count sqlélten', // less reliable
+ 'a_year' => ':count sqlélten', // less reliable
+
+ 'month' => ':count swewll', // less reliable
+ 'm' => ':count swewll', // less reliable
+ 'a_month' => ':count swewll', // less reliable
+
+ 'hour' => ':count seqwlút', // less reliable
+ 'h' => ':count seqwlút', // less reliable
+ 'a_hour' => ':count seqwlút', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/si.php b/vendor/nesbot/carbon/src/Carbon/Lang/si.php
new file mode 100644
index 0000000..636bf69
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/si.php
@@ -0,0 +1,78 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - Malinda Weerasinghe (MalindaWMD)
+ */
+return [
+ 'year' => '{1}වසර 1|වසර :count',
+ 'a_year' => '{1}වසරක්|වසර :count',
+ 'month' => '{1}මාස 1|මාස :count',
+ 'a_month' => '{1}මාසය|මාස :count',
+ 'week' => '{1}සති 1|සති :count',
+ 'a_week' => '{1}සතියක්|සති :count',
+ 'day' => '{1}දින 1|දින :count',
+ 'a_day' => '{1}දිනක්|දින :count',
+ 'hour' => '{1}පැය 1|පැය :count',
+ 'a_hour' => '{1}පැයක්|පැය :count',
+ 'minute' => '{1}මිනිත්තු 1|මිනිත්තු :count',
+ 'a_minute' => '{1}මිනිත්තුවක්|මිනිත්තු :count',
+ 'second' => '{1}තත්පර 1|තත්පර :count',
+ 'a_second' => '{1}තත්පර කිහිපයකට|තත්පර :count',
+ 'ago' => ':time කට පෙර',
+ 'from_now' => function ($time) {
+ if (preg_match('/දින \d/u', $time)) {
+ return $time.' න්';
+ }
+
+ return $time.' කින්';
+ },
+ 'before' => ':time කට පෙර',
+ 'after' => function ($time) {
+ if (preg_match('/දින \d/u', $time)) {
+ return $time.' න්';
+ }
+
+ return $time.' කින්';
+ },
+ 'diff_now' => 'දැන්',
+ 'diff_today' => 'අද',
+ 'diff_yesterday' => 'ඊයේ',
+ 'diff_tomorrow' => 'හෙට',
+ 'formats' => [
+ 'LT' => 'a h:mm',
+ 'LTS' => 'a h:mm:ss',
+ 'L' => 'YYYY/MM/DD',
+ 'LL' => 'YYYY MMMM D',
+ 'LLL' => 'YYYY MMMM D, a h:mm',
+ 'LLLL' => 'YYYY MMMM D [වැනි] dddd, a h:mm:ss',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[අද] LT[ට]',
+ 'nextDay' => '[හෙට] LT[ට]',
+ 'nextWeek' => 'dddd LT[ට]',
+ 'lastDay' => '[ඊයේ] LT[ට]',
+ 'lastWeek' => '[පසුගිය] dddd LT[ට]',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number වැනි',
+ 'meridiem' => ['පෙර වරු', 'පස් වරු', 'පෙ.ව.', 'ප.ව.'],
+ 'months' => ['ජනවාරි', 'පෙබරවාරි', 'මාර්තු', 'අප්රේල්', 'මැයි', 'ජූනි', 'ජූලි', 'අගෝස්තු', 'සැප්තැම්බර්', 'ඔක්තෝබර්', 'නොවැම්බර්', 'දෙසැම්බර්'],
+ 'months_short' => ['ජන', 'පෙබ', 'මාර්', 'අප්', 'මැයි', 'ජූනි', 'ජූලි', 'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'],
+ 'weekdays' => ['ඉරිදා', 'සඳුදා', 'අඟහරුවාදා', 'බදාදා', 'බ්රහස්පතින්දා', 'සිකුරාදා', 'සෙනසුරාදා'],
+ 'weekdays_short' => ['ඉරි', 'සඳු', 'අඟ', 'බදා', 'බ්රහ', 'සිකු', 'සෙන'],
+ 'weekdays_min' => ['ඉ', 'ස', 'අ', 'බ', 'බ්ර', 'සි', 'සෙ'],
+ 'first_day_of_week' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php b/vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php
new file mode 100644
index 0000000..81c44e0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/si_LK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/si.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sid.php b/vendor/nesbot/carbon/src/Carbon/Lang/sid.php
new file mode 100644
index 0000000..b1c6521
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sid.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sid_ET.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php
new file mode 100644
index 0000000..1296f9b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sid_ET.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ 'weekdays' => ['Sambata', 'Sanyo', 'Maakisanyo', 'Roowe', 'Hamuse', 'Arbe', 'Qidaame'],
+ 'weekdays_short' => ['Sam', 'San', 'Mak', 'Row', 'Ham', 'Arb', 'Qid'],
+ 'weekdays_min' => ['Sam', 'San', 'Mak', 'Row', 'Ham', 'Arb', 'Qid'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['soodo', 'hawwaro'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sk.php b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php
new file mode 100644
index 0000000..08af197
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sk.php
@@ -0,0 +1,83 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Martin Suja
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Max Melentiev
+ * - Juanito Fatas
+ * - Ivan Stana
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - James McKinney
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Jozef Fulop
+ * - Nicolás Hock Isaza
+ * - Tom Hughes
+ * - Simon Hürlimann (CyT)
+ * - jofi
+ * - Jakub ADAMEC
+ * - Marek Adamický
+ * - AlterwebStudio
+ */
+return [
+ 'year' => 'rok|:count roky|:count rokov',
+ 'y' => ':count r',
+ 'month' => 'mesiac|:count mesiace|:count mesiacov',
+ 'm' => ':count m',
+ 'week' => 'týždeň|:count týždne|:count týždňov',
+ 'w' => ':count t',
+ 'day' => 'deň|:count dni|:count dní',
+ 'd' => ':count d',
+ 'hour' => 'hodinu|:count hodiny|:count hodín',
+ 'h' => ':count h',
+ 'minute' => 'minútu|:count minúty|:count minút',
+ 'min' => ':count min',
+ 'second' => 'sekundu|:count sekundy|:count sekúnd',
+ 'a_second' => 'pár sekúnd|:count sekundy|:count sekúnd',
+ 's' => ':count s',
+ 'ago' => 'pred :time',
+ 'from_now' => 'o :time',
+ 'after' => ':time po',
+ 'before' => ':time pred',
+ 'year_ago' => 'rokom|:count rokmi|:count rokmi',
+ 'month_ago' => 'mesiacom|:count mesiacmi|:count mesiacmi',
+ 'week_ago' => 'týždňom|:count týždňami|:count týždňami',
+ 'day_ago' => 'dňom|:count dňami|:count dňami',
+ 'hour_ago' => 'hodinou|:count hodinami|:count hodinami',
+ 'minute_ago' => 'minútou|:count minútami|:count minútami',
+ 'second_ago' => 'sekundou|:count sekundami|:count sekundami',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' a '],
+ 'diff_now' => 'teraz',
+ 'diff_yesterday' => 'včera',
+ 'diff_tomorrow' => 'zajtra',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'DD. MMMM YYYY',
+ 'LLL' => 'D. M. HH:mm',
+ 'LLLL' => 'dddd D. MMMM YYYY HH:mm',
+ ],
+ 'weekdays' => ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok', 'piatok', 'sobota'],
+ 'weekdays_short' => ['ned', 'pod', 'uto', 'str', 'štv', 'pia', 'sob'],
+ 'weekdays_min' => ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
+ 'months' => ['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug', 'sep', 'okt', 'nov', 'dec'],
+ 'meridiem' => ['dopoludnia', 'popoludní'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php
new file mode 100644
index 0000000..0515601
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sk_SK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sk.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php
new file mode 100644
index 0000000..1f1d1b3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sl.php
@@ -0,0 +1,129 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Max Melentiev
+ * - Juanito Fatas
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Nicolás Hock Isaza
+ * - Miha Rebernik
+ * - Gal Jakič (morpheus7CS)
+ * - Glavić
+ * - Anže Časar
+ * - Lovro Tramšek (Lovro1107)
+ * - burut13
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count leto|:count leti|:count leta|:count let',
+ 'y' => ':count leto|:count leti|:count leta|:count let',
+ 'month' => ':count mesec|:count meseca|:count mesece|:count mesecev',
+ 'm' => ':count mes.',
+ 'week' => ':count teden|:count tedna|:count tedne|:count tednov',
+ 'w' => ':count ted.',
+ 'day' => ':count dan|:count dni|:count dni|:count dni',
+ 'd' => ':count dan|:count dni|:count dni|:count dni',
+ 'hour' => ':count ura|:count uri|:count ure|:count ur',
+ 'h' => ':count h',
+ 'minute' => ':count minuta|:count minuti|:count minute|:count minut',
+ 'min' => ':count min.',
+ 'second' => ':count sekunda|:count sekundi|:count sekunde|:count sekund',
+ 'a_second' => '{1}nekaj sekund|:count sekunda|:count sekundi|:count sekunde|:count sekund',
+ 's' => ':count s',
+
+ 'year_ago' => ':count letom|:count letoma|:count leti|:count leti',
+ 'y_ago' => ':count letom|:count letoma|:count leti|:count leti',
+ 'month_ago' => ':count mesecem|:count mesecema|:count meseci|:count meseci',
+ 'week_ago' => ':count tednom|:count tednoma|:count tedni|:count tedni',
+ 'day_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi',
+ 'd_ago' => ':count dnem|:count dnevoma|:count dnevi|:count dnevi',
+ 'hour_ago' => ':count uro|:count urama|:count urami|:count urami',
+ 'minute_ago' => ':count minuto|:count minutama|:count minutami|:count minutami',
+ 'second_ago' => ':count sekundo|:count sekundama|:count sekundami|:count sekundami',
+
+ 'day_from_now' => ':count dan|:count dneva|:count dni|:count dni',
+ 'd_from_now' => ':count dan|:count dneva|:count dni|:count dni',
+ 'hour_from_now' => ':count uro|:count uri|:count ure|:count ur',
+ 'minute_from_now' => ':count minuto|:count minuti|:count minute|:count minut',
+ 'second_from_now' => ':count sekundo|:count sekundi|:count sekunde|:count sekund',
+
+ 'ago' => 'pred :time',
+ 'from_now' => 'čez :time',
+ 'after' => ':time kasneje',
+ 'before' => ':time prej',
+
+ 'diff_now' => 'ravnokar',
+ 'diff_today' => 'danes',
+ 'diff_today_regexp' => 'danes(?:\\s+ob)?',
+ 'diff_yesterday' => 'včeraj',
+ 'diff_yesterday_regexp' => 'včeraj(?:\\s+ob)?',
+ 'diff_tomorrow' => 'jutri',
+ 'diff_tomorrow_regexp' => 'jutri(?:\\s+ob)?',
+ 'diff_before_yesterday' => 'predvčerajšnjim',
+ 'diff_after_tomorrow' => 'pojutrišnjem',
+
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'period_start_date' => 'od :date',
+ 'period_end_date' => 'do :date',
+
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[danes ob] LT',
+ 'nextDay' => '[jutri ob] LT',
+ 'nextWeek' => 'dddd [ob] LT',
+ 'lastDay' => '[včeraj ob] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[preteklo] [nedeljo] [ob] LT';
+ case 1:
+ return '[pretekli] [ponedeljek] [ob] LT';
+ case 2:
+ return '[pretekli] [torek] [ob] LT';
+ case 3:
+ return '[preteklo] [sredo] [ob] LT';
+ case 4:
+ return '[pretekli] [četrtek] [ob] LT';
+ case 5:
+ return '[pretekli] [petek] [ob] LT';
+ case 6:
+ return '[preteklo] [soboto] [ob] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['januar', 'februar', 'marec', 'april', 'maj', 'junij', 'julij', 'avgust', 'september', 'oktober', 'november', 'december'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'avg', 'sep', 'okt', 'nov', 'dec'],
+ 'weekdays' => ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek', 'petek', 'sobota'],
+ 'weekdays_short' => ['ned', 'pon', 'tor', 'sre', 'čet', 'pet', 'sob'],
+ 'weekdays_min' => ['ne', 'po', 'to', 'sr', 'če', 'pe', 'so'],
+ 'list' => [', ', ' in '],
+ 'meridiem' => ['dopoldan', 'popoldan'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php b/vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php
new file mode 100644
index 0000000..5dad8c8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sl_SI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sm.php b/vendor/nesbot/carbon/src/Carbon/Lang/sm.php
new file mode 100644
index 0000000..e8c118a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sm.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/sm_WS.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php b/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php
new file mode 100644
index 0000000..f066068
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sm_WS.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Ianuari', 'Fepuari', 'Mati', 'Aperila', 'Me', 'Iuni', 'Iulai', 'Auguso', 'Setema', 'Oketopa', 'Novema', 'Tesema'],
+ 'months_short' => ['Ian', 'Fep', 'Mat', 'Ape', 'Me', 'Iun', 'Iul', 'Aug', 'Set', 'Oke', 'Nov', 'Tes'],
+ 'weekdays' => ['Aso Sa', 'Aso Gafua', 'Aso Lua', 'Aso Lulu', 'Aso Tofi', 'Aso Farail', 'Aso To\'ana\'i'],
+ 'weekdays_short' => ['Aso Sa', 'Aso Gaf', 'Aso Lua', 'Aso Lul', 'Aso Tof', 'Aso Far', 'Aso To\''],
+ 'weekdays_min' => ['Aso Sa', 'Aso Gaf', 'Aso Lua', 'Aso Lul', 'Aso Tof', 'Aso Far', 'Aso To\''],
+
+ 'hour' => ':count uati', // less reliable
+ 'h' => ':count uati', // less reliable
+ 'a_hour' => ':count uati', // less reliable
+
+ 'minute' => ':count itiiti', // less reliable
+ 'min' => ':count itiiti', // less reliable
+ 'a_minute' => ':count itiiti', // less reliable
+
+ 'second' => ':count lua', // less reliable
+ 's' => ':count lua', // less reliable
+ 'a_second' => ':count lua', // less reliable
+
+ 'year' => ':count tausaga',
+ 'y' => ':count tausaga',
+ 'a_year' => ':count tausaga',
+
+ 'month' => ':count māsina',
+ 'm' => ':count māsina',
+ 'a_month' => ':count māsina',
+
+ 'week' => ':count vaiaso',
+ 'w' => ':count vaiaso',
+ 'a_week' => ':count vaiaso',
+
+ 'day' => ':count aso',
+ 'd' => ':count aso',
+ 'a_day' => ':count aso',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/smn.php b/vendor/nesbot/carbon/src/Carbon/Lang/smn.php
new file mode 100644
index 0000000..20add02
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/smn.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['ip.', 'ep.'],
+ 'weekdays' => ['pasepeeivi', 'vuossaargâ', 'majebaargâ', 'koskoho', 'tuorâstuv', 'vástuppeeivi', 'lávurduv'],
+ 'weekdays_short' => ['pas', 'vuo', 'maj', 'kos', 'tuo', 'vás', 'láv'],
+ 'weekdays_min' => ['pa', 'vu', 'ma', 'ko', 'tu', 'vá', 'lá'],
+ 'weekdays_standalone' => ['pasepeivi', 'vuossargâ', 'majebargâ', 'koskokko', 'tuorâstâh', 'vástuppeivi', 'lávurdâh'],
+ 'months' => ['uđđâivemáánu', 'kuovâmáánu', 'njuhčâmáánu', 'cuáŋuimáánu', 'vyesimáánu', 'kesimáánu', 'syeinimáánu', 'porgemáánu', 'čohčâmáánu', 'roovvâdmáánu', 'skammâmáánu', 'juovlâmáánu'],
+ 'months_short' => ['uđiv', 'kuovâ', 'njuhčâ', 'cuáŋui', 'vyesi', 'kesi', 'syeini', 'porge', 'čohčâ', 'roovvâd', 'skammâ', 'juovlâ'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'H.mm',
+ 'LTS' => 'H.mm.ss',
+ 'L' => 'D.M.YYYY',
+ 'LL' => 'MMM D. YYYY',
+ 'LLL' => 'MMMM D. YYYY H.mm',
+ 'LLLL' => 'dddd, MMMM D. YYYY H.mm',
+ ],
+
+ 'hour' => ':count äigi', // less reliable
+ 'h' => ':count äigi', // less reliable
+ 'a_hour' => ':count äigi', // less reliable
+
+ 'year' => ':count ihe',
+ 'y' => ':count ihe',
+ 'a_year' => ':count ihe',
+
+ 'month' => ':count mánuppaje',
+ 'm' => ':count mánuppaje',
+ 'a_month' => ':count mánuppaje',
+
+ 'week' => ':count okko',
+ 'w' => ':count okko',
+ 'a_week' => ':count okko',
+
+ 'day' => ':count peivi',
+ 'd' => ':count peivi',
+ 'a_day' => ':count peivi',
+
+ 'minute' => ':count miinut',
+ 'min' => ':count miinut',
+ 'a_minute' => ':count miinut',
+
+ 'second' => ':count nubbe',
+ 's' => ':count nubbe',
+ 'a_second' => ':count nubbe',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sn.php b/vendor/nesbot/carbon/src/Carbon/Lang/sn.php
new file mode 100644
index 0000000..4f25028
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sn.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['a', 'p'],
+ 'weekdays' => ['Svondo', 'Muvhuro', 'Chipiri', 'Chitatu', 'China', 'Chishanu', 'Mugovera'],
+ 'weekdays_short' => ['Svo', 'Muv', 'Chp', 'Cht', 'Chn', 'Chs', 'Mug'],
+ 'weekdays_min' => ['Sv', 'Mu', 'Cp', 'Ct', 'Cn', 'Cs', 'Mg'],
+ 'months' => ['Ndira', 'Kukadzi', 'Kurume', 'Kubvumbi', 'Chivabvu', 'Chikumi', 'Chikunguru', 'Nyamavhuvhu', 'Gunyana', 'Gumiguru', 'Mbudzi', 'Zvita'],
+ 'months_short' => ['Ndi', 'Kuk', 'Kur', 'Kub', 'Chv', 'Chk', 'Chg', 'Nya', 'Gun', 'Gum', 'Mbu', 'Zvi'],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'YYYY MMMM D, dddd HH:mm',
+ ],
+
+ 'year' => 'makore :count',
+ 'y' => 'makore :count',
+ 'a_year' => 'makore :count',
+
+ 'month' => 'mwedzi :count',
+ 'm' => 'mwedzi :count',
+ 'a_month' => 'mwedzi :count',
+
+ 'week' => 'vhiki :count',
+ 'w' => 'vhiki :count',
+ 'a_week' => 'vhiki :count',
+
+ 'day' => 'mazuva :count',
+ 'd' => 'mazuva :count',
+ 'a_day' => 'mazuva :count',
+
+ 'hour' => 'maawa :count',
+ 'h' => 'maawa :count',
+ 'a_hour' => 'maawa :count',
+
+ 'minute' => 'minitsi :count',
+ 'min' => 'minitsi :count',
+ 'a_minute' => 'minitsi :count',
+
+ 'second' => 'sekonzi :count',
+ 's' => 'sekonzi :count',
+ 'a_second' => 'sekonzi :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so.php b/vendor/nesbot/carbon/src/Carbon/Lang/so.php
new file mode 100644
index 0000000..5785271
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/so.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Author:
+ * - Abdifatah Abdilahi(@abdifatahz)
+ */
+return [
+ 'year' => ':count sanad|:count sanadood',
+ 'a_year' => 'sanad|:count sanadood',
+ 'y' => '{1}:countsn|{0}:countsns|]1,Inf[:countsn',
+ 'month' => ':count bil|:count bilood',
+ 'a_month' => 'bil|:count bilood',
+ 'm' => ':countbil',
+ 'week' => ':count isbuuc',
+ 'a_week' => 'isbuuc|:count isbuuc',
+ 'w' => ':countis',
+ 'day' => ':count maalin|:count maalmood',
+ 'a_day' => 'maalin|:count maalmood',
+ 'd' => ':countml',
+ 'hour' => ':count saac',
+ 'a_hour' => 'saacad|:count saac',
+ 'h' => ':countsc',
+ 'minute' => ':count daqiiqo',
+ 'a_minute' => 'daqiiqo|:count daqiiqo',
+ 'min' => ':countdq',
+ 'second' => ':count ilbidhiqsi',
+ 'a_second' => 'xooga ilbidhiqsiyo|:count ilbidhiqsi',
+ 's' => ':countil',
+ 'ago' => ':time kahor',
+ 'from_now' => ':time gudahood',
+ 'after' => ':time kedib',
+ 'before' => ':time kahor',
+ 'diff_now' => 'hada',
+ 'diff_today' => 'maanta',
+ 'diff_today_regexp' => 'maanta(?:\s+markay\s+(?:tahay|ahayd))?',
+ 'diff_yesterday' => 'shalayto',
+ 'diff_yesterday_regexp' => 'shalayto(?:\s+markay\s+ahayd)?',
+ 'diff_tomorrow' => 'beri',
+ 'diff_tomorrow_regexp' => 'beri(?:\s+markay\s+tahay)?',
+ 'diff_before_yesterday' => 'doraato',
+ 'diff_after_tomorrow' => 'saadanbe',
+ 'period_recurrences' => 'mar|:count jeer',
+ 'period_interval' => ':interval kasta',
+ 'period_start_date' => 'laga bilaabo :date',
+ 'period_end_date' => 'ilaa :date',
+ 'months' => ['Janaayo', 'Febraayo', 'Abriil', 'Maajo', 'Juun', 'Luuliyo', 'Agoosto', 'Sebteembar', 'Oktoobar', 'Nofeembar', 'Diseembar'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Abr', 'Mjo', 'Jun', 'Lyo', 'Agt', 'Seb', 'Okt', 'Nof', 'Dis'],
+ 'weekdays' => ['Axad', 'Isniin', 'Talaada', 'Arbaca', 'Khamiis', 'Jimce', 'Sabti'],
+ 'weekdays_short' => ['Axd', 'Isn', 'Tal', 'Arb', 'Kha', 'Jim', 'Sbt'],
+ 'weekdays_min' => ['Ax', 'Is', 'Ta', 'Ar', 'Kh', 'Ji', 'Sa'],
+ 'list' => [', ', ' and '],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Maanta markay tahay] LT',
+ 'nextDay' => '[Beri markay tahay] LT',
+ 'nextWeek' => 'dddd [markay tahay] LT',
+ 'lastDay' => '[Shalay markay ahayd] LT',
+ 'lastWeek' => '[Hore] dddd [Markay ahayd] LT',
+ 'sameElse' => 'L',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php
new file mode 100644
index 0000000..273dda8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_DJ.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/so.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php
new file mode 100644
index 0000000..7b69971
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_ET.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return require __DIR__.'/so.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php
new file mode 100644
index 0000000..7b69971
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_KE.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return require __DIR__.'/so.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php b/vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php
new file mode 100644
index 0000000..7b69971
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/so_SO.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return require __DIR__.'/so.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq.php
new file mode 100644
index 0000000..ffa592e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - JD Isaacks
+ * - Fadion Dashi
+ */
+return [
+ 'year' => ':count vit|:count vjet',
+ 'a_year' => 'një vit|:count vite',
+ 'y' => ':count v.',
+ 'month' => ':count muaj',
+ 'a_month' => 'një muaj|:count muaj',
+ 'm' => ':count muaj',
+ 'week' => ':count javë',
+ 'a_week' => ':count javë|:count javë',
+ 'w' => ':count j.',
+ 'day' => ':count ditë',
+ 'a_day' => 'një ditë|:count ditë',
+ 'd' => ':count d.',
+ 'hour' => ':count orë',
+ 'a_hour' => 'një orë|:count orë',
+ 'h' => ':count o.',
+ 'minute' => ':count minutë|:count minuta',
+ 'a_minute' => 'një minutë|:count minuta',
+ 'min' => ':count min.',
+ 'second' => ':count sekondë|:count sekonda',
+ 'a_second' => 'disa sekonda|:count sekonda',
+ 's' => ':count s.',
+ 'ago' => ':time më parë',
+ 'from_now' => 'në :time',
+ 'after' => ':time pas',
+ 'before' => ':time para',
+ 'diff_now' => 'tani',
+ 'diff_today' => 'Sot',
+ 'diff_today_regexp' => 'Sot(?:\\s+në)?',
+ 'diff_yesterday' => 'dje',
+ 'diff_yesterday_regexp' => 'Dje(?:\\s+në)?',
+ 'diff_tomorrow' => 'nesër',
+ 'diff_tomorrow_regexp' => 'Nesër(?:\\s+në)?',
+ 'diff_before_yesterday' => 'pardje',
+ 'diff_after_tomorrow' => 'pasnesër',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Sot në] LT',
+ 'nextDay' => '[Nesër në] LT',
+ 'nextWeek' => 'dddd [në] LT',
+ 'lastDay' => '[Dje në] LT',
+ 'lastWeek' => 'dddd [e kaluar në] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'meridiem' => ['PD', 'MD'],
+ 'months' => ['janar', 'shkurt', 'mars', 'prill', 'maj', 'qershor', 'korrik', 'gusht', 'shtator', 'tetor', 'nëntor', 'dhjetor'],
+ 'months_short' => ['jan', 'shk', 'mar', 'pri', 'maj', 'qer', 'kor', 'gus', 'sht', 'tet', 'nën', 'dhj'],
+ 'weekdays' => ['e diel', 'e hënë', 'e martë', 'e mërkurë', 'e enjte', 'e premte', 'e shtunë'],
+ 'weekdays_short' => ['die', 'hën', 'mar', 'mër', 'enj', 'pre', 'sht'],
+ 'weekdays_min' => ['d', 'h', 'ma', 'më', 'e', 'p', 'sh'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' dhe '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php
new file mode 100644
index 0000000..ea5df3f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq_AL.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sq.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php
new file mode 100644
index 0000000..62f752c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq_MK.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/sq.php', [
+ 'formats' => [
+ 'L' => 'D.M.YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php
new file mode 100644
index 0000000..62f752c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sq_XK.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/sq.php', [
+ 'formats' => [
+ 'L' => 'D.M.YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php
new file mode 100644
index 0000000..68ba663
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr.php
@@ -0,0 +1,112 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - shaishavgandhi05
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - Glavić
+ * - Milos Sakovic
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count godina|:count godine|:count godina',
+ 'y' => ':count g.',
+ 'month' => ':count mesec|:count meseca|:count meseci',
+ 'm' => ':count mj.',
+ 'week' => ':count nedelja|:count nedelje|:count nedelja',
+ 'w' => ':count ned.',
+ 'day' => ':count dan|:count dana|:count dana',
+ 'd' => ':count d.',
+ 'hour' => ':count sat|:count sata|:count sati',
+ 'h' => ':count č.',
+ 'minute' => ':count minut|:count minuta|:count minuta',
+ 'min' => ':count min.',
+ 'second' => ':count sekundu|:count sekunde|:count sekundi',
+ 's' => ':count sek.',
+ 'ago' => 'pre :time',
+ 'from_now' => 'za :time',
+ 'after' => 'nakon :time',
+ 'before' => 'pre :time',
+
+ 'year_from_now' => ':count godinu|:count godine|:count godina',
+ 'year_ago' => ':count godinu|:count godine|:count godina',
+ 'week_from_now' => ':count nedelju|:count nedelje|:count nedelja',
+ 'week_ago' => ':count nedelju|:count nedelje|:count nedelja',
+
+ 'diff_now' => 'upravo sada',
+ 'diff_today' => 'danas',
+ 'diff_today_regexp' => 'danas(?:\\s+u)?',
+ 'diff_yesterday' => 'juče',
+ 'diff_yesterday_regexp' => 'juče(?:\\s+u)?',
+ 'diff_tomorrow' => 'sutra',
+ 'diff_tomorrow_regexp' => 'sutra(?:\\s+u)?',
+ 'diff_before_yesterday' => 'prekjuče',
+ 'diff_after_tomorrow' => 'preksutra',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[danas u] LT',
+ 'nextDay' => '[sutra u] LT',
+ 'nextWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[u nedelju u] LT';
+ case 3:
+ return '[u sredu u] LT';
+ case 6:
+ return '[u subotu u] LT';
+ default:
+ return '[u] dddd [u] LT';
+ }
+ },
+ 'lastDay' => '[juče u] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[prošle nedelje u] LT';
+ case 1:
+ return '[prošlog ponedeljka u] LT';
+ case 2:
+ return '[prošlog utorka u] LT';
+ case 3:
+ return '[prošle srede u] LT';
+ case 4:
+ return '[prošlog četvrtka u] LT';
+ case 5:
+ return '[prošlog petka u] LT';
+ default:
+ return '[prošle subote u] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul', 'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'],
+ 'months_short' => ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun', 'jul', 'avg.', 'sep.', 'okt.', 'nov.', 'dec.'],
+ 'weekdays' => ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak', 'subota'],
+ 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sre.', 'čet.', 'pet.', 'sub.'],
+ 'weekdays_min' => ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' i '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php
new file mode 100644
index 0000000..8becbc5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl.php
@@ -0,0 +1,112 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - shaishavgandhi05
+ * - Serhan Apaydın
+ * - JD Isaacks
+ * - Glavić
+ * - Nikola Zeravcic
+ * - Milos Sakovic
+ */
+
+use Carbon\CarbonInterface;
+
+return [
+ 'year' => ':count година|:count године|:count година',
+ 'y' => ':count г.',
+ 'month' => ':count месец|:count месеца|:count месеци',
+ 'm' => ':count м.',
+ 'week' => ':count недеља|:count недеље|:count недеља',
+ 'w' => ':count нед.',
+ 'day' => ':count дан|:count дана|:count дана',
+ 'd' => ':count д.',
+ 'hour' => ':count сат|:count сата|:count сати',
+ 'h' => ':count ч.',
+ 'minute' => ':count минут|:count минута|:count минута',
+ 'min' => ':count мин.',
+ 'second' => ':count секунд|:count секунде|:count секунди',
+ 's' => ':count сек.',
+ 'ago' => 'пре :time',
+ 'from_now' => 'за :time',
+ 'after' => ':time након',
+ 'before' => ':time пре',
+ 'year_from_now' => ':count годину|:count године|:count година',
+ 'year_ago' => ':count годину|:count године|:count година',
+ 'week_from_now' => ':count недељу|:count недеље|:count недеља',
+ 'week_ago' => ':count недељу|:count недеље|:count недеља',
+ 'diff_now' => 'управо сада',
+ 'diff_today' => 'данас',
+ 'diff_today_regexp' => 'данас(?:\\s+у)?',
+ 'diff_yesterday' => 'јуче',
+ 'diff_yesterday_regexp' => 'јуче(?:\\s+у)?',
+ 'diff_tomorrow' => 'сутра',
+ 'diff_tomorrow_regexp' => 'сутра(?:\\s+у)?',
+ 'diff_before_yesterday' => 'прекјуче',
+ 'diff_after_tomorrow' => 'прекосутра',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[данас у] LT',
+ 'nextDay' => '[сутра у] LT',
+ 'nextWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[у недељу у] LT';
+ case 3:
+ return '[у среду у] LT';
+ case 6:
+ return '[у суботу у] LT';
+ default:
+ return '[у] dddd [у] LT';
+ }
+ },
+ 'lastDay' => '[јуче у] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[прошле недеље у] LT';
+ case 1:
+ return '[прошлог понедељка у] LT';
+ case 2:
+ return '[прошлог уторка у] LT';
+ case 3:
+ return '[прошле среде у] LT';
+ case 4:
+ return '[прошлог четвртка у] LT';
+ case 5:
+ return '[прошлог петка у] LT';
+ default:
+ return '[прошле суботе у] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'],
+ 'months_short' => ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'],
+ 'weekdays' => ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'],
+ 'weekdays_short' => ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'],
+ 'weekdays_min' => ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' и '],
+ 'meridiem' => ['АМ', 'ПМ'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php
new file mode 100644
index 0000000..4b29a45
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_BA.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'sr');
+ }, 'sr_Cyrl_BA');
+}
+// @codeCoverageIgnoreEnd
+
+return array_replace_recursive(require __DIR__.'/sr_Cyrl.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D.M.yy.',
+ 'LL' => 'DD.MM.YYYY.',
+ 'LLL' => 'DD. MMMM YYYY. HH:mm',
+ 'LLLL' => 'dddd, DD. MMMM YYYY. HH:mm',
+ ],
+ 'weekdays' => ['недјеља', 'понедељак', 'уторак', 'сриједа', 'четвртак', 'петак', 'субота'],
+ 'weekdays_short' => ['нед.', 'пон.', 'ут.', 'ср.', 'чет.', 'пет.', 'суб.'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php
new file mode 100644
index 0000000..28d22fd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_ME.php
@@ -0,0 +1,118 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Glavić
+ * - Milos Sakovic
+ */
+
+use Carbon\CarbonInterface;
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'sr');
+ }, 'sr_Cyrl_ME');
+}
+// @codeCoverageIgnoreEnd
+
+return [
+ 'year' => ':count година|:count године|:count година',
+ 'y' => ':count г.',
+ 'month' => ':count мјесец|:count мјесеца|:count мјесеци',
+ 'm' => ':count мј.',
+ 'week' => ':count недјеља|:count недјеље|:count недјеља',
+ 'w' => ':count нед.',
+ 'day' => ':count дан|:count дана|:count дана',
+ 'd' => ':count д.',
+ 'hour' => ':count сат|:count сата|:count сати',
+ 'h' => ':count ч.',
+ 'minute' => ':count минут|:count минута|:count минута',
+ 'min' => ':count мин.',
+ 'second' => ':count секунд|:count секунде|:count секунди',
+ 's' => ':count сек.',
+ 'ago' => 'прије :time',
+ 'from_now' => 'за :time',
+ 'after' => ':time након',
+ 'before' => ':time прије',
+
+ 'year_from_now' => ':count годину|:count године|:count година',
+ 'year_ago' => ':count годину|:count године|:count година',
+
+ 'week_from_now' => ':count недјељу|:count недјеље|:count недјеља',
+ 'week_ago' => ':count недјељу|:count недјеље|:count недјеља',
+
+ 'diff_now' => 'управо сада',
+ 'diff_today' => 'данас',
+ 'diff_today_regexp' => 'данас(?:\\s+у)?',
+ 'diff_yesterday' => 'јуче',
+ 'diff_yesterday_regexp' => 'јуче(?:\\s+у)?',
+ 'diff_tomorrow' => 'сутра',
+ 'diff_tomorrow_regexp' => 'сутра(?:\\s+у)?',
+ 'diff_before_yesterday' => 'прекјуче',
+ 'diff_after_tomorrow' => 'прекосјутра',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM YYYY',
+ 'LLL' => 'D. MMMM YYYY H:mm',
+ 'LLLL' => 'dddd, D. MMMM YYYY H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[данас у] LT',
+ 'nextDay' => '[сутра у] LT',
+ 'nextWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[у недељу у] LT';
+ case 3:
+ return '[у среду у] LT';
+ case 6:
+ return '[у суботу у] LT';
+ default:
+ return '[у] dddd [у] LT';
+ }
+ },
+ 'lastDay' => '[јуче у] LT',
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[прошле недеље у] LT';
+ case 1:
+ return '[прошлог понедељка у] LT';
+ case 2:
+ return '[прошлог уторка у] LT';
+ case 3:
+ return '[прошле среде у] LT';
+ case 4:
+ return '[прошлог четвртка у] LT';
+ case 5:
+ return '[прошлог петка у] LT';
+ default:
+ return '[прошле суботе у] LT';
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'],
+ 'months_short' => ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'],
+ 'weekdays' => ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'],
+ 'weekdays_short' => ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'],
+ 'weekdays_min' => ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' и '],
+ 'meridiem' => ['АМ', 'ПМ'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php
new file mode 100644
index 0000000..d6e29b8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Cyrl_XK.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'sr');
+ }, 'sr_Cyrl_XK');
+}
+// @codeCoverageIgnoreEnd
+
+return array_replace_recursive(require __DIR__.'/sr_Cyrl_BA.php', [
+ 'weekdays' => ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php
new file mode 100644
index 0000000..9971674
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php
new file mode 100644
index 0000000..95b2770
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_BA.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'sr');
+ }, 'sr_Latn_BA');
+}
+// @codeCoverageIgnoreEnd
+
+return array_replace_recursive(require __DIR__.'/sr_Latn.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D.M.yy.',
+ 'LL' => 'DD.MM.YYYY.',
+ 'LLL' => 'DD. MMMM YYYY. HH:mm',
+ 'LLLL' => 'dddd, DD. MMMM YYYY. HH:mm',
+ ],
+ 'weekdays' => ['nedjelja', 'ponedeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'],
+ 'weekdays_short' => ['ned.', 'pon.', 'ut.', 'sr.', 'čet.', 'pet.', 'sub.'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php
new file mode 100644
index 0000000..5b8f2d0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_ME.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Glavić
+ * - Milos Sakovic
+ */
+
+use Carbon\CarbonInterface;
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'sr');
+ }, 'sr_Latn_ME');
+}
+// @codeCoverageIgnoreEnd
+
+return array_replace_recursive(require __DIR__.'/sr.php', [
+ 'month' => ':count mjesec|:count mjeseca|:count mjeseci',
+ 'week' => ':count nedjelja|:count nedjelje|:count nedjelja',
+ 'second' => ':count sekund|:count sekunde|:count sekundi',
+ 'ago' => 'prije :time',
+ 'from_now' => 'za :time',
+ 'after' => ':time nakon',
+ 'before' => ':time prije',
+ 'week_from_now' => ':count nedjelju|:count nedjelje|:count nedjelja',
+ 'week_ago' => ':count nedjelju|:count nedjelje|:count nedjelja',
+ 'second_ago' => ':count sekund|:count sekunde|:count sekundi',
+ 'diff_tomorrow' => 'sjutra',
+ 'calendar' => [
+ 'nextDay' => '[sjutra u] LT',
+ 'nextWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[u nedjelju u] LT';
+ case 3:
+ return '[u srijedu u] LT';
+ case 6:
+ return '[u subotu u] LT';
+ default:
+ return '[u] dddd [u] LT';
+ }
+ },
+ 'lastWeek' => function (CarbonInterface $date) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ return '[prošle nedjelje u] LT';
+ case 1:
+ return '[prošle nedjelje u] LT';
+ case 2:
+ return '[prošlog utorka u] LT';
+ case 3:
+ return '[prošle srijede u] LT';
+ case 4:
+ return '[prošlog četvrtka u] LT';
+ case 5:
+ return '[prošlog petka u] LT';
+ default:
+ return '[prošle subote u] LT';
+ }
+ },
+ ],
+ 'weekdays' => ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota'],
+ 'weekdays_short' => ['ned.', 'pon.', 'uto.', 'sri.', 'čet.', 'pet.', 'sub.'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php
new file mode 100644
index 0000000..5278e2e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_Latn_XK.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Component\Translation\PluralizationRules;
+
+// @codeCoverageIgnoreStart
+if (class_exists(PluralizationRules::class)) {
+ PluralizationRules::set(static function ($number) {
+ return PluralizationRules::get($number, 'sr');
+ }, 'sr_Latn_XK');
+}
+// @codeCoverageIgnoreEnd
+
+return array_replace_recursive(require __DIR__.'/sr_Latn_BA.php', [
+ 'weekdays' => ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak', 'subota'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php
new file mode 100644
index 0000000..d7c65b9
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_ME.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sr_Latn_ME.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php
new file mode 100644
index 0000000..bc5e04b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - sr_YU, sr_CS locale Danilo Segan bug-glibc-locales@gnu.org
+ */
+return require __DIR__.'/sr_Cyrl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php
new file mode 100644
index 0000000..9971674
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sr_RS@latin.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ss.php b/vendor/nesbot/carbon/src/Carbon/Lang/ss.php
new file mode 100644
index 0000000..1c52c9b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ss.php
@@ -0,0 +1,78 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Nicolai Davies
+ */
+return [
+ 'year' => '{1}umnyaka|:count iminyaka',
+ 'month' => '{1}inyanga|:count tinyanga',
+ 'week' => '{1}:count liviki|:count emaviki',
+ 'day' => '{1}lilanga|:count emalanga',
+ 'hour' => '{1}lihora|:count emahora',
+ 'minute' => '{1}umzuzu|:count emizuzu',
+ 'second' => '{1}emizuzwana lomcane|:count mzuzwana',
+ 'ago' => 'wenteka nga :time',
+ 'from_now' => 'nga :time',
+ 'diff_yesterday' => 'Itolo',
+ 'diff_yesterday_regexp' => 'Itolo(?:\\s+nga)?',
+ 'diff_today' => 'Namuhla',
+ 'diff_today_regexp' => 'Namuhla(?:\\s+nga)?',
+ 'diff_tomorrow' => 'Kusasa',
+ 'diff_tomorrow_regexp' => 'Kusasa(?:\\s+nga)?',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm A',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm A',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Namuhla nga] LT',
+ 'nextDay' => '[Kusasa nga] LT',
+ 'nextWeek' => 'dddd [nga] LT',
+ 'lastDay' => '[Itolo nga] LT',
+ 'lastWeek' => 'dddd [leliphelile] [nga] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ $lastDigit = $number % 10;
+
+ return $number.(
+ ((int) ($number % 100 / 10) === 1) ? 'e' : (
+ ($lastDigit === 1 || $lastDigit === 2) ? 'a' : 'e'
+ )
+ );
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 11) {
+ return 'ekuseni';
+ }
+ if ($hour < 15) {
+ return 'emini';
+ }
+ if ($hour < 19) {
+ return 'entsambama';
+ }
+
+ return 'ebusuku';
+ },
+ 'months' => ['Bhimbidvwane', 'Indlovana', 'Indlov\'lenkhulu', 'Mabasa', 'Inkhwekhweti', 'Inhlaba', 'Kholwane', 'Ingci', 'Inyoni', 'Imphala', 'Lweti', 'Ingongoni'],
+ 'months_short' => ['Bhi', 'Ina', 'Inu', 'Mab', 'Ink', 'Inh', 'Kho', 'Igc', 'Iny', 'Imp', 'Lwe', 'Igo'],
+ 'weekdays' => ['Lisontfo', 'Umsombuluko', 'Lesibili', 'Lesitsatfu', 'Lesine', 'Lesihlanu', 'Umgcibelo'],
+ 'weekdays_short' => ['Lis', 'Umb', 'Lsb', 'Les', 'Lsi', 'Lsh', 'Umg'],
+ 'weekdays_min' => ['Li', 'Us', 'Lb', 'Lt', 'Ls', 'Lh', 'Ug'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php
new file mode 100644
index 0000000..ba89527
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ss_ZA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/ss.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/st.php b/vendor/nesbot/carbon/src/Carbon/Lang/st.php
new file mode 100644
index 0000000..b065445
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/st.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/st_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php
new file mode 100644
index 0000000..5bce7f2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/st_ZA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Pherekgong', 'Hlakola', 'Tlhakubele', 'Mmese', 'Motsheanong', 'Phupjane', 'Phupu', 'Phato', 'Leotse', 'Mphalane', 'Pudungwana', 'Tshitwe'],
+ 'months_short' => ['Phe', 'Hla', 'TlH', 'Mme', 'Mot', 'Jan', 'Upu', 'Pha', 'Leo', 'Mph', 'Pud', 'Tsh'],
+ 'weekdays' => ['Sontaha', 'Mantaha', 'Labobedi', 'Laboraro', 'Labone', 'Labohlano', 'Moqebelo'],
+ 'weekdays_short' => ['Son', 'Mma', 'Bed', 'Rar', 'Ne', 'Hla', 'Moq'],
+ 'weekdays_min' => ['Son', 'Mma', 'Bed', 'Rar', 'Ne', 'Hla', 'Moq'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'week' => ':count Sontaha', // less reliable
+ 'w' => ':count Sontaha', // less reliable
+ 'a_week' => ':count Sontaha', // less reliable
+
+ 'day' => ':count letsatsi', // less reliable
+ 'd' => ':count letsatsi', // less reliable
+ 'a_day' => ':count letsatsi', // less reliable
+
+ 'hour' => ':count sešupanako', // less reliable
+ 'h' => ':count sešupanako', // less reliable
+ 'a_hour' => ':count sešupanako', // less reliable
+
+ 'minute' => ':count menyane', // less reliable
+ 'min' => ':count menyane', // less reliable
+ 'a_minute' => ':count menyane', // less reliable
+
+ 'second' => ':count thusa', // less reliable
+ 's' => ':count thusa', // less reliable
+ 'a_second' => ':count thusa', // less reliable
+
+ 'year' => ':count selemo',
+ 'y' => ':count selemo',
+ 'a_year' => ':count selemo',
+
+ 'month' => ':count kgwedi',
+ 'm' => ':count kgwedi',
+ 'a_month' => ':count kgwedi',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php
new file mode 100644
index 0000000..1706c71
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv.php
@@ -0,0 +1,87 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Kristoffer Snabb
+ * - JD Isaacks
+ * - Jens Herlevsen
+ * - Nightpine
+ * - Anders Nygren (litemerafrukt)
+ */
+return [
+ 'year' => ':count år',
+ 'a_year' => 'ett år|:count år',
+ 'y' => ':count år',
+ 'month' => ':count månad|:count månader',
+ 'a_month' => 'en månad|:count månader',
+ 'm' => ':count mån',
+ 'week' => ':count vecka|:count veckor',
+ 'a_week' => 'en vecka|:count veckor',
+ 'w' => ':count v',
+ 'day' => ':count dag|:count dagar',
+ 'a_day' => 'en dag|:count dagar',
+ 'd' => ':count dgr',
+ 'hour' => ':count timme|:count timmar',
+ 'a_hour' => 'en timme|:count timmar',
+ 'h' => ':count tim',
+ 'minute' => ':count minut|:count minuter',
+ 'a_minute' => 'en minut|:count minuter',
+ 'min' => ':count min',
+ 'second' => ':count sekund|:count sekunder',
+ 'a_second' => 'några sekunder|:count sekunder',
+ 's' => ':count s',
+ 'ago' => 'för :time sedan',
+ 'from_now' => 'om :time',
+ 'after' => ':time efter',
+ 'before' => ':time före',
+ 'diff_now' => 'nu',
+ 'diff_today' => 'I dag',
+ 'diff_yesterday' => 'i går',
+ 'diff_yesterday_regexp' => 'I går',
+ 'diff_tomorrow' => 'i morgon',
+ 'diff_tomorrow_regexp' => 'I morgon',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY [kl.] HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY [kl.] HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[I dag] LT',
+ 'nextDay' => '[I morgon] LT',
+ 'nextWeek' => '[På] dddd LT',
+ 'lastDay' => '[I går] LT',
+ 'lastWeek' => '[I] dddd[s] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ $lastDigit = $number % 10;
+
+ return $number.(
+ ((int) ($number % 100 / 10) === 1) ? 'e' : (
+ ($lastDigit === 1 || $lastDigit === 2) ? 'a' : 'e'
+ )
+ );
+ },
+ 'months' => ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'],
+ 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
+ 'weekdays' => ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag'],
+ 'weekdays_short' => ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'],
+ 'weekdays_min' => ['sö', 'må', 'ti', 'on', 'to', 'fr', 'lö'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' och '],
+ 'meridiem' => ['fm', 'em'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php
new file mode 100644
index 0000000..70cc558
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv_AX.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/sv.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php
new file mode 100644
index 0000000..d7182c8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv_FI.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sv.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php b/vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php
new file mode 100644
index 0000000..d7182c8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sv_SE.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/sv.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw.php
new file mode 100644
index 0000000..f8630d5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - leyluj
+ * - Josh Soref
+ * - ryanhart2
+ */
+return [
+ 'year' => 'mwaka :count|miaka :count',
+ 'a_year' => 'mwaka mmoja|miaka :count',
+ 'y' => 'mwaka :count|miaka :count',
+ 'month' => 'mwezi :count|miezi :count',
+ 'a_month' => 'mwezi mmoja|miezi :count',
+ 'm' => 'mwezi :count|miezi :count',
+ 'week' => 'wiki :count',
+ 'a_week' => 'wiki mmoja|wiki :count',
+ 'w' => 'w. :count',
+ 'day' => 'siku :count',
+ 'a_day' => 'siku moja|masiku :count',
+ 'd' => 'si. :count',
+ 'hour' => 'saa :count|masaa :count',
+ 'a_hour' => 'saa limoja|masaa :count',
+ 'h' => 'saa :count|masaa :count',
+ 'minute' => 'dakika :count',
+ 'a_minute' => 'dakika moja|dakika :count',
+ 'min' => 'd. :count',
+ 'second' => 'sekunde :count',
+ 'a_second' => 'hivi punde|sekunde :count',
+ 's' => 'se. :count',
+ 'ago' => 'tokea :time',
+ 'from_now' => ':time baadaye',
+ 'after' => ':time baada',
+ 'before' => ':time kabla',
+ 'diff_now' => 'sasa hivi',
+ 'diff_today' => 'leo',
+ 'diff_today_regexp' => 'leo(?:\\s+saa)?',
+ 'diff_yesterday' => 'jana',
+ 'diff_tomorrow' => 'kesho',
+ 'diff_tomorrow_regexp' => 'kesho(?:\\s+saa)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[leo saa] LT',
+ 'nextDay' => '[kesho saa] LT',
+ 'nextWeek' => '[wiki ijayo] dddd [saat] LT',
+ 'lastDay' => '[jana] LT',
+ 'lastWeek' => '[wiki iliyopita] dddd [saat] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Jpl', 'Jtat', 'Jnne', 'Jtan', 'Alh', 'Ijm', 'Jmos'],
+ 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' na '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php
new file mode 100644
index 0000000..ec9117b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_CD.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/sw.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php
new file mode 100644
index 0000000..2ace0db
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_KE.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kamusi Project Martin Benjamin locales@kamusi.org
+ */
+return array_replace_recursive(require __DIR__.'/sw.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'],
+ 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['asubuhi', 'alasiri'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php
new file mode 100644
index 0000000..fab3cd6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_TZ.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kamusi Project Martin Benjamin locales@kamusi.org
+ */
+return array_replace_recursive(require __DIR__.'/sw.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'],
+ 'weekdays_min' => ['J2', 'J3', 'J4', 'J5', 'Alh', 'Ij', 'J1'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['asubuhi', 'alasiri'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php b/vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php
new file mode 100644
index 0000000..ec9117b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/sw_UG.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/sw.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/szl.php b/vendor/nesbot/carbon/src/Carbon/Lang/szl.php
new file mode 100644
index 0000000..4429c4f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/szl.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/szl_PL.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php b/vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php
new file mode 100644
index 0000000..9adddcf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/szl_PL.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - szl_PL locale Przemyslaw Buczkowski libc-alpha@sourceware.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['styczyń', 'luty', 'merc', 'kwjeciyń', 'moj', 'czyrwjyń', 'lipjyń', 'siyrpjyń', 'wrzesiyń', 'październik', 'listopad', 'grudziyń'],
+ 'months_short' => ['sty', 'lut', 'mer', 'kwj', 'moj', 'czy', 'lip', 'siy', 'wrz', 'paź', 'lis', 'gru'],
+ 'weekdays' => ['niydziela', 'pyńdziŏek', 'wtŏrek', 'strzŏda', 'sztwortek', 'pjōntek', 'sobŏta'],
+ 'weekdays_short' => ['niy', 'pyń', 'wtŏ', 'str', 'szt', 'pjō', 'sob'],
+ 'weekdays_min' => ['niy', 'pyń', 'wtŏ', 'str', 'szt', 'pjō', 'sob'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count rok',
+ 'y' => ':count rok',
+ 'a_year' => ':count rok',
+
+ 'month' => ':count mjeśůnc',
+ 'm' => ':count mjeśůnc',
+ 'a_month' => ':count mjeśůnc',
+
+ 'week' => ':count tydźyń',
+ 'w' => ':count tydźyń',
+ 'a_week' => ':count tydźyń',
+
+ 'day' => ':count dźyń',
+ 'd' => ':count dźyń',
+ 'a_day' => ':count dźyń',
+
+ 'hour' => ':count godzina',
+ 'h' => ':count godzina',
+ 'a_hour' => ':count godzina',
+
+ 'minute' => ':count minuta',
+ 'min' => ':count minuta',
+ 'a_minute' => ':count minuta',
+
+ 'second' => ':count sekůnda',
+ 's' => ':count sekůnda',
+ 'a_second' => ':count sekůnda',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta.php
new file mode 100644
index 0000000..c1d89cb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta.php
@@ -0,0 +1,97 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - François B
+ * - JD Isaacks
+ * - Satheez
+ */
+return [
+ 'year' => ':count வருடம்|:count ஆண்டுகள்',
+ 'a_year' => 'ஒரு வருடம்|:count ஆண்டுகள்',
+ 'y' => ':count வருட.|:count ஆண்.',
+ 'month' => ':count மாதம்|:count மாதங்கள்',
+ 'a_month' => 'ஒரு மாதம்|:count மாதங்கள்',
+ 'm' => ':count மாத.',
+ 'week' => ':count வாரம்|:count வாரங்கள்',
+ 'a_week' => 'ஒரு வாரம்|:count வாரங்கள்',
+ 'w' => ':count வார.',
+ 'day' => ':count நாள்|:count நாட்கள்',
+ 'a_day' => 'ஒரு நாள்|:count நாட்கள்',
+ 'd' => ':count நாள்|:count நாட்.',
+ 'hour' => ':count மணி நேரம்|:count மணி நேரம்',
+ 'a_hour' => 'ஒரு மணி நேரம்|:count மணி நேரம்',
+ 'h' => ':count மணி.',
+ 'minute' => ':count நிமிடம்|:count நிமிடங்கள்',
+ 'a_minute' => 'ஒரு நிமிடம்|:count நிமிடங்கள்',
+ 'min' => ':count நிமி.',
+ 'second' => ':count சில விநாடிகள்|:count விநாடிகள்',
+ 'a_second' => 'ஒரு சில விநாடிகள்|:count விநாடிகள்',
+ 's' => ':count விநா.',
+ 'ago' => ':time முன்',
+ 'from_now' => ':time இல்',
+ 'before' => ':time முன்',
+ 'after' => ':time பின்',
+ 'diff_now' => 'இப்போது',
+ 'diff_today' => 'இன்று',
+ 'diff_yesterday' => 'நேற்று',
+ 'diff_tomorrow' => 'நாளை',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[இன்று] LT',
+ 'nextDay' => '[நாளை] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[நேற்று] LT',
+ 'lastWeek' => '[கடந்த வாரம்] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberவது',
+ 'meridiem' => function ($hour) {
+ if ($hour < 2) {
+ return ' யாமம்';
+ }
+ if ($hour < 6) {
+ return ' வைகறை';
+ }
+ if ($hour < 10) {
+ return ' காலை';
+ }
+ if ($hour < 14) {
+ return ' நண்பகல்';
+ }
+ if ($hour < 18) {
+ return ' எற்பாடு';
+ }
+ if ($hour < 22) {
+ return ' மாலை';
+ }
+
+ return ' யாமம்';
+ },
+ 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டெம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'],
+ 'months_short' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டெம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'],
+ 'weekdays' => ['ஞாயிற்றுக்கிழமை', 'திங்கட்கிழமை', 'செவ்வாய்கிழமை', 'புதன்கிழமை', 'வியாழக்கிழமை', 'வெள்ளிக்கிழமை', 'சனிக்கிழமை'],
+ 'weekdays_short' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'],
+ 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' மற்றும் '],
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php
new file mode 100644
index 0000000..492d4c5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_IN.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/ta.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'],
+ 'months_short' => ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.', 'அக்.', 'நவ.', 'டிச.'],
+ 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'],
+ 'weekdays_short' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['காலை', 'மாலை'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php
new file mode 100644
index 0000000..8e2afbf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_LK.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - J.Yogaraj 94-777-315206 yogaraj.ubuntu@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/ta.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'],
+ 'months_short' => ['ஜன', 'பிப்', 'மார்', 'ஏப்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக', 'செப்', 'அக்', 'நவ', 'டிச'],
+ 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'],
+ 'weekdays_short' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['காலை', 'மாலை'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php
new file mode 100644
index 0000000..a6cd8b5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_MY.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ta.php', [
+ 'formats' => [
+ 'LT' => 'a h:mm',
+ 'LTS' => 'a h:mm:ss',
+ 'L' => 'D/M/yy',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM, YYYY, a h:mm',
+ 'LLLL' => 'dddd, D MMMM, YYYY, a h:mm',
+ ],
+ 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'],
+ 'months_short' => ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.', 'அக்.', 'நவ.', 'டிச.'],
+ 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'],
+ 'weekdays_short' => ['ஞாயி.', 'திங்.', 'செவ்.', 'புத.', 'வியா.', 'வெள்.', 'சனி'],
+ 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'first_day_of_week' => 1,
+ 'meridiem' => ['மு.ப', 'பி.ப'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php
new file mode 100644
index 0000000..7dbedee
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ta_SG.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ta.php', [
+ 'formats' => [
+ 'LT' => 'a h:mm',
+ 'LTS' => 'a h:mm:ss',
+ 'L' => 'D/M/yy',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM, YYYY, a h:mm',
+ 'LLLL' => 'dddd, D MMMM, YYYY, a h:mm',
+ ],
+ 'months' => ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை', 'ஆகஸ்ட்', 'செப்டம்பர்', 'அக்டோபர்', 'நவம்பர்', 'டிசம்பர்'],
+ 'months_short' => ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.', 'அக்.', 'நவ.', 'டிச.'],
+ 'weekdays' => ['ஞாயிறு', 'திங்கள்', 'செவ்வாய்', 'புதன்', 'வியாழன்', 'வெள்ளி', 'சனி'],
+ 'weekdays_short' => ['ஞாயி.', 'திங்.', 'செவ்.', 'புத.', 'வியா.', 'வெள்.', 'சனி'],
+ 'weekdays_min' => ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+ 'meridiem' => ['மு.ப', 'பி.ப'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tcy.php b/vendor/nesbot/carbon/src/Carbon/Lang/tcy.php
new file mode 100644
index 0000000..2eb9905
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tcy.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/tcy_IN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php
new file mode 100644
index 0000000..2ff20e0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tcy_IN.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IndLinux.org, Samsung Electronics Co., Ltd. alexey.merzlyakov@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['ಜನವರಿ', 'ಫೆಬ್ರುವರಿ', 'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್', 'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್', 'ಡಿಸೆಂಬರ್'],
+ 'months_short' => ['ಜ', 'ಫೆ', 'ಮಾ', 'ಏ', 'ಮೇ', 'ಜೂ', 'ಜು', 'ಆ', 'ಸೆ', 'ಅ', 'ನ', 'ಡಿ'],
+ 'weekdays' => ['ಐಥಾರ', 'ಸೋಮಾರ', 'ಅಂಗರೆ', 'ಬುಧಾರ', 'ಗುರುವಾರ', 'ಶುಕ್ರರ', 'ಶನಿವಾರ'],
+ 'weekdays_short' => ['ಐ', 'ಸೋ', 'ಅಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'],
+ 'weekdays_min' => ['ಐ', 'ಸೋ', 'ಅಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ಕಾಂಡೆ', 'ಬಯ್ಯ'],
+
+ 'year' => ':count ನೀರ್', // less reliable
+ 'y' => ':count ನೀರ್', // less reliable
+ 'a_year' => ':count ನೀರ್', // less reliable
+
+ 'month' => ':count ಮೀನ್', // less reliable
+ 'm' => ':count ಮೀನ್', // less reliable
+ 'a_month' => ':count ಮೀನ್', // less reliable
+
+ 'day' => ':count ಸುಗ್ಗಿ', // less reliable
+ 'd' => ':count ಸುಗ್ಗಿ', // less reliable
+ 'a_day' => ':count ಸುಗ್ಗಿ', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/te.php b/vendor/nesbot/carbon/src/Carbon/Lang/te.php
new file mode 100644
index 0000000..ac38218
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/te.php
@@ -0,0 +1,89 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - Josh Soref
+ * - François B
+ * - kc
+ */
+return [
+ 'year' => ':count సంవత్సరం|:count సంవత్సరాలు',
+ 'a_year' => 'ఒక సంవత్సరం|:count సంవత్సరాలు',
+ 'y' => ':count సం.',
+ 'month' => ':count నెల|:count నెలలు',
+ 'a_month' => 'ఒక నెల|:count నెలలు',
+ 'm' => ':count నెల|:count నెల.',
+ 'week' => ':count వారం|:count వారాలు',
+ 'a_week' => 'ఒక వారం|:count వారాలు',
+ 'w' => ':count వార.|:count వారా.',
+ 'day' => ':count రోజు|:count రోజులు',
+ 'a_day' => 'ఒక రోజు|:count రోజులు',
+ 'd' => ':count రోజు|:count రోజు.',
+ 'hour' => ':count గంట|:count గంటలు',
+ 'a_hour' => 'ఒక గంట|:count గంటలు',
+ 'h' => ':count గం.',
+ 'minute' => ':count నిమిషం|:count నిమిషాలు',
+ 'a_minute' => 'ఒక నిమిషం|:count నిమిషాలు',
+ 'min' => ':count నిమి.',
+ 'second' => ':count సెకను|:count సెకన్లు',
+ 'a_second' => 'కొన్ని క్షణాలు|:count సెకన్లు',
+ 's' => ':count సెక.',
+ 'ago' => ':time క్రితం',
+ 'from_now' => ':time లో',
+ 'diff_now' => 'ప్రస్తుతం',
+ 'diff_today' => 'నేడు',
+ 'diff_yesterday' => 'నిన్న',
+ 'diff_tomorrow' => 'రేపు',
+ 'formats' => [
+ 'LT' => 'A h:mm',
+ 'LTS' => 'A h:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, A h:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, A h:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[నేడు] LT',
+ 'nextDay' => '[రేపు] LT',
+ 'nextWeek' => 'dddd, LT',
+ 'lastDay' => '[నిన్న] LT',
+ 'lastWeek' => '[గత] dddd, LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberవ',
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'రాత్రి';
+ }
+ if ($hour < 10) {
+ return 'ఉదయం';
+ }
+ if ($hour < 17) {
+ return 'మధ్యాహ్నం';
+ }
+ if ($hour < 20) {
+ return 'సాయంత్రం';
+ }
+
+ return ' రాత్రి';
+ },
+ 'months' => ['జనవరి', 'ఫిబ్రవరి', 'మార్చి', 'ఏప్రిల్', 'మే', 'జూన్', 'జూలై', 'ఆగస్టు', 'సెప్టెంబర్', 'అక్టోబర్', 'నవంబర్', 'డిసెంబర్'],
+ 'months_short' => ['జన.', 'ఫిబ్ర.', 'మార్చి', 'ఏప్రి.', 'మే', 'జూన్', 'జూలై', 'ఆగ.', 'సెప్.', 'అక్టో.', 'నవ.', 'డిసె.'],
+ 'weekdays' => ['ఆదివారం', 'సోమవారం', 'మంగళవారం', 'బుధవారం', 'గురువారం', 'శుక్రవారం', 'శనివారం'],
+ 'weekdays_short' => ['ఆది', 'సోమ', 'మంగళ', 'బుధ', 'గురు', 'శుక్ర', 'శని'],
+ 'weekdays_min' => ['ఆ', 'సో', 'మం', 'బు', 'గు', 'శు', 'శ'],
+ 'list' => ', ',
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'weekend' => [0, 0],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php
new file mode 100644
index 0000000..3963f8d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/te_IN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/te.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/teo.php b/vendor/nesbot/carbon/src/Carbon/Lang/teo.php
new file mode 100644
index 0000000..ca30c37
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/teo.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ta.php', [
+ 'meridiem' => ['Taparachu', 'Ebongi'],
+ 'weekdays' => ['Nakaejuma', 'Nakaebarasa', 'Nakaare', 'Nakauni', 'Nakaung’on', 'Nakakany', 'Nakasabiti'],
+ 'weekdays_short' => ['Jum', 'Bar', 'Aar', 'Uni', 'Ung', 'Kan', 'Sab'],
+ 'weekdays_min' => ['Jum', 'Bar', 'Aar', 'Uni', 'Ung', 'Kan', 'Sab'],
+ 'months' => ['Orara', 'Omuk', 'Okwamg’', 'Odung’el', 'Omaruk', 'Omodok’king’ol', 'Ojola', 'Opedel', 'Osokosokoma', 'Otibar', 'Olabor', 'Opoo'],
+ 'months_short' => ['Rar', 'Muk', 'Kwa', 'Dun', 'Mar', 'Mod', 'Jol', 'Ped', 'Sok', 'Tib', 'Lab', 'Poo'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php b/vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php
new file mode 100644
index 0000000..010a04f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/teo_KE.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/teo.php', [
+ 'first_day_of_week' => 0,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tet.php b/vendor/nesbot/carbon/src/Carbon/Lang/tet.php
new file mode 100644
index 0000000..d0544d4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tet.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Joshua Brooks
+ * - François B
+ */
+return [
+ 'year' => 'tinan :count',
+ 'a_year' => '{1}tinan ida|tinan :count',
+ 'month' => 'fulan :count',
+ 'a_month' => '{1}fulan ida|fulan :count',
+ 'week' => 'semana :count',
+ 'a_week' => '{1}semana ida|semana :count',
+ 'day' => 'loron :count',
+ 'a_day' => '{1}loron ida|loron :count',
+ 'hour' => 'oras :count',
+ 'a_hour' => '{1}oras ida|oras :count',
+ 'minute' => 'minutu :count',
+ 'a_minute' => '{1}minutu ida|minutu :count',
+ 'second' => 'segundu :count',
+ 'a_second' => '{1}segundu balun|segundu :count',
+ 'ago' => ':time liuba',
+ 'from_now' => 'iha :time',
+ 'diff_yesterday' => 'Horiseik',
+ 'diff_yesterday_regexp' => 'Horiseik(?:\\s+iha)?',
+ 'diff_today' => 'Ohin',
+ 'diff_today_regexp' => 'Ohin(?:\\s+iha)?',
+ 'diff_tomorrow' => 'Aban',
+ 'diff_tomorrow_regexp' => 'Aban(?:\\s+iha)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Ohin iha] LT',
+ 'nextDay' => '[Aban iha] LT',
+ 'nextWeek' => 'dddd [iha] LT',
+ 'lastDay' => '[Horiseik iha] LT',
+ 'lastWeek' => 'dddd [semana kotuk] [iha] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':numberº',
+ 'months' => ['Janeiru', 'Fevereiru', 'Marsu', 'Abril', 'Maiu', 'Juñu', 'Jullu', 'Agustu', 'Setembru', 'Outubru', 'Novembru', 'Dezembru'],
+ 'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
+ 'weekdays' => ['Domingu', 'Segunda', 'Tersa', 'Kuarta', 'Kinta', 'Sesta', 'Sabadu'],
+ 'weekdays_short' => ['Dom', 'Seg', 'Ters', 'Kua', 'Kint', 'Sest', 'Sab'],
+ 'weekdays_min' => ['Do', 'Seg', 'Te', 'Ku', 'Ki', 'Ses', 'Sa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tg.php b/vendor/nesbot/carbon/src/Carbon/Lang/tg.php
new file mode 100644
index 0000000..b7df893
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tg.php
@@ -0,0 +1,104 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Orif N. Jr
+ */
+return [
+ 'year' => '{1}як сол|:count сол',
+ 'month' => '{1}як моҳ|:count моҳ',
+ 'week' => '{1}як ҳафта|:count ҳафта',
+ 'day' => '{1}як рӯз|:count рӯз',
+ 'hour' => '{1}як соат|:count соат',
+ 'minute' => '{1}як дақиқа|:count дақиқа',
+ 'second' => '{1}якчанд сония|:count сония',
+ 'ago' => ':time пеш',
+ 'from_now' => 'баъди :time',
+ 'diff_today' => 'Имрӯз',
+ 'diff_yesterday' => 'Дирӯз',
+ 'diff_yesterday_regexp' => 'Дирӯз(?:\\s+соати)?',
+ 'diff_tomorrow' => 'Пагоҳ',
+ 'diff_tomorrow_regexp' => 'Пагоҳ(?:\\s+соати)?',
+ 'diff_today_regexp' => 'Имрӯз(?:\\s+соати)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Имрӯз соати] LT',
+ 'nextDay' => '[Пагоҳ соати] LT',
+ 'nextWeek' => 'dddd[и] [ҳафтаи оянда соати] LT',
+ 'lastDay' => '[Дирӯз соати] LT',
+ 'lastWeek' => 'dddd[и] [ҳафтаи гузашта соати] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number) {
+ if ($number === 0) { // special case for zero
+ return "$number-ıncı";
+ }
+
+ static $suffixes = [
+ 0 => '-ум',
+ 1 => '-ум',
+ 2 => '-юм',
+ 3 => '-юм',
+ 4 => '-ум',
+ 5 => '-ум',
+ 6 => '-ум',
+ 7 => '-ум',
+ 8 => '-ум',
+ 9 => '-ум',
+ 10 => '-ум',
+ 12 => '-ум',
+ 13 => '-ум',
+ 20 => '-ум',
+ 30 => '-юм',
+ 40 => '-ум',
+ 50 => '-ум',
+ 60 => '-ум',
+ 70 => '-ум',
+ 80 => '-ум',
+ 90 => '-ум',
+ 100 => '-ум',
+ ];
+
+ return $number.($suffixes[$number] ?? $suffixes[$number % 10] ?? $suffixes[$number >= 100 ? 100 : -1] ?? '');
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'шаб';
+ }
+ if ($hour < 11) {
+ return 'субҳ';
+ }
+ if ($hour < 16) {
+ return 'рӯз';
+ }
+ if ($hour < 19) {
+ return 'бегоҳ';
+ }
+
+ return 'шаб';
+ },
+ 'months' => ['январ', 'феврал', 'март', 'апрел', 'май', 'июн', 'июл', 'август', 'сентябр', 'октябр', 'ноябр', 'декабр'],
+ 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'weekdays' => ['якшанбе', 'душанбе', 'сешанбе', 'чоршанбе', 'панҷшанбе', 'ҷумъа', 'шанбе'],
+ 'weekdays_short' => ['яшб', 'дшб', 'сшб', 'чшб', 'пшб', 'ҷум', 'шнб'],
+ 'weekdays_min' => ['яш', 'дш', 'сш', 'чш', 'пш', 'ҷм', 'шб'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' ва '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php
new file mode 100644
index 0000000..badc7d1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tg_TJ.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/tg.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/th.php b/vendor/nesbot/carbon/src/Carbon/Lang/th.php
new file mode 100644
index 0000000..6397f6e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/th.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Nate Whittaker
+ * - John MacAslan
+ * - Chanintorn Asavavichairoj
+ * - JD Isaacks
+ * - ROKAISAKKON
+ * - RO'KAISAKKON
+ * - Andreas Möller
+ * - nithisa
+ */
+return [
+ 'year' => ':count ปี',
+ 'y' => ':count ปี',
+ 'month' => ':count เดือน',
+ 'm' => ':count เดือน',
+ 'week' => ':count สัปดาห์',
+ 'w' => ':count สัปดาห์',
+ 'day' => ':count วัน',
+ 'd' => ':count วัน',
+ 'hour' => ':count ชั่วโมง',
+ 'h' => ':count ชั่วโมง',
+ 'minute' => ':count นาที',
+ 'min' => ':count นาที',
+ 'second' => ':count วินาที',
+ 'a_second' => '{1}ไม่กี่วินาที|]1,Inf[:count วินาที',
+ 's' => ':count วินาที',
+ 'ago' => ':timeที่แล้ว',
+ 'from_now' => 'อีก :time',
+ 'after' => ':timeหลังจากนี้',
+ 'before' => ':timeก่อน',
+ 'diff_now' => 'ขณะนี้',
+ 'diff_today' => 'วันนี้',
+ 'diff_today_regexp' => 'วันนี้(?:\\s+เวลา)?',
+ 'diff_yesterday' => 'เมื่อวาน',
+ 'diff_yesterday_regexp' => 'เมื่อวานนี้(?:\\s+เวลา)?',
+ 'diff_tomorrow' => 'พรุ่งนี้',
+ 'diff_tomorrow_regexp' => 'พรุ่งนี้(?:\\s+เวลา)?',
+ 'formats' => [
+ 'LT' => 'H:mm',
+ 'LTS' => 'H:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY เวลา H:mm',
+ 'LLLL' => 'วันddddที่ D MMMM YYYY เวลา H:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[วันนี้ เวลา] LT',
+ 'nextDay' => '[พรุ่งนี้ เวลา] LT',
+ 'nextWeek' => 'dddd[หน้า เวลา] LT',
+ 'lastDay' => '[เมื่อวานนี้ เวลา] LT',
+ 'lastWeek' => '[วัน]dddd[ที่แล้ว เวลา] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ก่อนเที่ยง', 'หลังเที่ยง'],
+ 'months' => ['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'],
+ 'months_short' => ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
+ 'weekdays' => ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัสบดี', 'ศุกร์', 'เสาร์'],
+ 'weekdays_short' => ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัส', 'ศุกร์', 'เสาร์'],
+ 'weekdays_min' => ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
+ 'list' => [', ', ' และ '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php b/vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php
new file mode 100644
index 0000000..b9f94b2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/th_TH.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/th.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/the.php b/vendor/nesbot/carbon/src/Carbon/Lang/the.php
new file mode 100644
index 0000000..85f8333
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/the.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/the_NP.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php b/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php
new file mode 100644
index 0000000..34da162
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/the_NP.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Chitwanix OS Development info@chitwanix.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'dddd DD MMM YYYY',
+ ],
+ 'months' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'months_short' => ['जनवरी', 'फ़रवरी', 'मार्च', 'अप्रेल', 'मई', 'जून', 'जुलाई', 'अगस्त', 'सितम्बर', 'अक्टूबर', 'नवम्बर', 'दिसम्बर'],
+ 'weekdays' => ['आइतबार', 'सोमबार', 'मंगलबार', 'बुधबार', 'बिहिबार', 'शुक्रबार', 'शनिबार'],
+ 'weekdays_short' => ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि'],
+ 'weekdays_min' => ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['पूर्वाह्न', 'अपराह्न'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti.php
new file mode 100644
index 0000000..ffd3236
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ti_ER.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php
new file mode 100644
index 0000000..310c51c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ER.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጥሪ', 'ለካቲት', 'መጋቢት', 'ሚያዝያ', 'ግንቦት', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከረም', 'ጥቅምቲ', 'ሕዳር', 'ታሕሳስ'],
+ 'months_short' => ['ጥሪ ', 'ለካቲ', 'መጋቢ', 'ሚያዝ', 'ግንቦ', 'ሰነ ', 'ሓምለ', 'ነሓሰ', 'መስከ', 'ጥቅም', 'ሕዳር', 'ታሕሳ'],
+ 'weekdays' => ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
+ 'weekdays_short' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
+ 'weekdays_min' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'],
+
+ 'year' => ':count ዓመት',
+ 'y' => ':count ዓመት',
+ 'a_year' => ':count ዓመት',
+
+ 'month' => 'ወርሒ :count',
+ 'm' => 'ወርሒ :count',
+ 'a_month' => 'ወርሒ :count',
+
+ 'week' => ':count ሰሙን',
+ 'w' => ':count ሰሙን',
+ 'a_week' => ':count ሰሙን',
+
+ 'day' => ':count መዓልቲ',
+ 'd' => ':count መዓልቲ',
+ 'a_day' => ':count መዓልቲ',
+
+ 'hour' => ':count ሰዓት',
+ 'h' => ':count ሰዓት',
+ 'a_hour' => ':count ሰዓት',
+
+ 'minute' => ':count ደቒቕ',
+ 'min' => ':count ደቒቕ',
+ 'a_minute' => ':count ደቒቕ',
+
+ 'second' => ':count ሰከንድ',
+ 's' => ':count ሰከንድ',
+ 'a_second' => ':count ሰከንድ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php
new file mode 100644
index 0000000..024217f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ti_ET.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'],
+ 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'],
+ 'weekdays' => ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
+ 'weekdays_short' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
+ 'weekdays_min' => ['ሰንበ', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tig.php b/vendor/nesbot/carbon/src/Carbon/Lang/tig.php
new file mode 100644
index 0000000..186fe71
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tig.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/tig_ER.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php b/vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php
new file mode 100644
index 0000000..46887b0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tig_ER.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጥሪ', 'ለካቲት', 'መጋቢት', 'ሚያዝያ', 'ግንቦት', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከረም', 'ጥቅምቲ', 'ሕዳር', 'ታሕሳስ'],
+ 'months_short' => ['ጥሪ ', 'ለካቲ', 'መጋቢ', 'ሚያዝ', 'ግንቦ', 'ሰነ ', 'ሓምለ', 'ነሓሰ', 'መስከ', 'ጥቅም', 'ሕዳር', 'ታሕሳ'],
+ 'weekdays' => ['ሰንበት ዓባይ', 'ሰኖ', 'ታላሸኖ', 'ኣረርባዓ', 'ከሚሽ', 'ጅምዓት', 'ሰንበት ንኢሽ'],
+ 'weekdays_short' => ['ሰ//ዓ', 'ሰኖ ', 'ታላሸ', 'ኣረር', 'ከሚሽ', 'ጅምዓ', 'ሰ//ን'],
+ 'weekdays_min' => ['ሰ//ዓ', 'ሰኖ ', 'ታላሸ', 'ኣረር', 'ከሚሽ', 'ጅምዓ', 'ሰ//ን'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ቀደም ሰር ምዕል', 'ሓቆ ሰር ምዕል'],
+
+ 'year' => ':count ማይ', // less reliable
+ 'y' => ':count ማይ', // less reliable
+ 'a_year' => ':count ማይ', // less reliable
+
+ 'month' => ':count ሸምሽ', // less reliable
+ 'm' => ':count ሸምሽ', // less reliable
+ 'a_month' => ':count ሸምሽ', // less reliable
+
+ 'week' => ':count ሰቡዕ', // less reliable
+ 'w' => ':count ሰቡዕ', // less reliable
+ 'a_week' => ':count ሰቡዕ', // less reliable
+
+ 'day' => ':count ዎሮ', // less reliable
+ 'd' => ':count ዎሮ', // less reliable
+ 'a_day' => ':count ዎሮ', // less reliable
+
+ 'hour' => ':count ሰዓት', // less reliable
+ 'h' => ':count ሰዓት', // less reliable
+ 'a_hour' => ':count ሰዓት', // less reliable
+
+ 'minute' => ':count ካልኣይት', // less reliable
+ 'min' => ':count ካልኣይት', // less reliable
+ 'a_minute' => ':count ካልኣይት', // less reliable
+
+ 'second' => ':count ካልኣይ',
+ 's' => ':count ካልኣይ',
+ 'a_second' => ':count ካልኣይ',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tk.php b/vendor/nesbot/carbon/src/Carbon/Lang/tk.php
new file mode 100644
index 0000000..d8f7d19
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tk.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/tk_TM.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php b/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php
new file mode 100644
index 0000000..f949a43
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tk_TM.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Authors:
+ * - Ghorban M. Tavakoly Pablo Saratxaga & Ghorban M. Tavakoly pablo@walon.org & gmt314@yahoo.com
+ * - SuperManPHP
+ * - Maksat Meredow (isadma)
+ */
+$transformDiff = function ($input) {
+ return strtr($input, [
+ 'sekunt' => 'sekunt',
+ 'hepde' => 'hepde',
+ ]);
+};
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Ýanwar', 'Fewral', 'Mart', 'Aprel', 'Maý', 'Iýun', 'Iýul', 'Awgust', 'Sentýabr', 'Oktýabr', 'Noýabr', 'Dekabr'],
+ 'months_short' => ['Ýan', 'Few', 'Mar', 'Apr', 'Maý', 'Iýn', 'Iýl', 'Awg', 'Sen', 'Okt', 'Noý', 'Dek'],
+ 'weekdays' => ['Duşenbe', 'Sişenbe', 'Çarşenbe', 'Penşenbe', 'Anna', 'Şenbe', 'Ýekşenbe'],
+ 'weekdays_short' => ['Duş', 'Siş', 'Çar', 'Pen', 'Ann', 'Şen', 'Ýek'],
+ 'weekdays_min' => ['Du', 'Si', 'Ça', 'Pe', 'An', 'Şe', 'Ýe'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count ýyl',
+ 'y' => ':count ýyl',
+ 'a_year' => ':count ýyl',
+
+ 'month' => ':count aý',
+ 'm' => ':count aý',
+ 'a_month' => ':count aý',
+
+ 'week' => ':count hepde',
+ 'w' => ':count hepde',
+ 'a_week' => ':count hepde',
+
+ 'day' => ':count gün',
+ 'd' => ':count gün',
+ 'a_day' => ':count gün',
+
+ 'hour' => ':count sagat',
+ 'h' => ':count sagat',
+ 'a_hour' => ':count sagat',
+
+ 'minute' => ':count minut',
+ 'min' => ':count minut',
+ 'a_minute' => ':count minut',
+
+ 'second' => ':count sekunt',
+ 's' => ':count sekunt',
+ 'a_second' => ':count sekunt',
+
+ 'ago' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' ozal';
+ },
+ 'from_now' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' soňra';
+ },
+ 'after' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' soň';
+ },
+ 'before' => function ($time) use ($transformDiff) {
+ return $transformDiff($time).' öň';
+ },
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tl.php b/vendor/nesbot/carbon/src/Carbon/Lang/tl.php
new file mode 100644
index 0000000..410a266
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tl.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+ 'year' => ':count taon',
+ 'a_year' => '{1}isang taon|:count taon',
+ 'month' => ':count buwan',
+ 'a_month' => '{1}isang buwan|:count buwan',
+ 'week' => ':count linggo',
+ 'a_week' => '{1}isang linggo|:count linggo',
+ 'day' => ':count araw',
+ 'a_day' => '{1}isang araw|:count araw',
+ 'hour' => ':count oras',
+ 'a_hour' => '{1}isang oras|:count oras',
+ 'minute' => ':count minuto',
+ 'a_minute' => '{1}isang minuto|:count minuto',
+ 'min' => ':count min.',
+ 'second' => ':count segundo',
+ 'a_second' => '{1}ilang segundo|:count segundo',
+ 's' => ':count seg.',
+ 'ago' => ':time ang nakalipas',
+ 'from_now' => 'sa loob ng :time',
+ 'diff_now' => 'ngayon',
+ 'diff_today' => 'ngayong',
+ 'diff_today_regexp' => 'ngayong(?:\\s+araw)?',
+ 'diff_yesterday' => 'kahapon',
+ 'diff_tomorrow' => 'bukas',
+ 'diff_tomorrow_regexp' => 'Bukas(?:\\s+ng)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'MM/D/YYYY',
+ 'LL' => 'MMMM D, YYYY',
+ 'LLL' => 'MMMM D, YYYY HH:mm',
+ 'LLLL' => 'dddd, MMMM DD, YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => 'LT [ngayong araw]',
+ 'nextDay' => '[Bukas ng] LT',
+ 'nextWeek' => 'LT [sa susunod na] dddd',
+ 'lastDay' => 'LT [kahapon]',
+ 'lastWeek' => 'LT [noong nakaraang] dddd',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
+ 'months_short' => ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set', 'Okt', 'Nob', 'Dis'],
+ 'weekdays' => ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes', 'Biyernes', 'Sabado'],
+ 'weekdays_short' => ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
+ 'weekdays_min' => ['Li', 'Lu', 'Ma', 'Mi', 'Hu', 'Bi', 'Sab'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' at '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php b/vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php
new file mode 100644
index 0000000..95f508c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tl_PH.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Ian De La Cruz
+ * - JD Isaacks
+ */
+return require __DIR__.'/tl.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php b/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php
new file mode 100644
index 0000000..fbf9e6f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tlh.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Serhan Apaydın
+ * - Dominika
+ */
+return [
+ 'year' => '{1}wa’ DIS|:count DIS',
+ 'month' => '{1}wa’ jar|:count jar',
+ 'week' => '{1}wa’ hogh|:count hogh',
+ 'day' => '{1}wa’ jaj|:count jaj',
+ 'hour' => '{1}wa’ rep|:count rep',
+ 'minute' => '{1}wa’ tup|:count tup',
+ 'second' => '{1}puS lup|:count lup',
+ 'ago' => function ($time) {
+ $output = strtr($time, [
+ 'jaj' => 'Hu’',
+ 'jar' => 'wen',
+ 'DIS' => 'ben',
+ ]);
+
+ return $output === $time ? "$time ret" : $output;
+ },
+ 'from_now' => function ($time) {
+ $output = strtr($time, [
+ 'jaj' => 'leS',
+ 'jar' => 'waQ',
+ 'DIS' => 'nem',
+ ]);
+
+ return $output === $time ? "$time pIq" : $output;
+ },
+ 'diff_yesterday' => 'wa’Hu’',
+ 'diff_today' => 'DaHjaj',
+ 'diff_tomorrow' => 'wa’leS',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[DaHjaj] LT',
+ 'nextDay' => '[wa’leS] LT',
+ 'nextWeek' => 'LLL',
+ 'lastDay' => '[wa’Hu’] LT',
+ 'lastWeek' => 'LLL',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => ':number.',
+ 'months' => ['tera’ jar wa’', 'tera’ jar cha’', 'tera’ jar wej', 'tera’ jar loS', 'tera’ jar vagh', 'tera’ jar jav', 'tera’ jar Soch', 'tera’ jar chorgh', 'tera’ jar Hut', 'tera’ jar wa’maH', 'tera’ jar wa’maH wa’', 'tera’ jar wa’maH cha’'],
+ 'months_short' => ['jar wa’', 'jar cha’', 'jar wej', 'jar loS', 'jar vagh', 'jar jav', 'jar Soch', 'jar chorgh', 'jar Hut', 'jar wa’maH', 'jar wa’maH wa’', 'jar wa’maH cha’'],
+ 'weekdays' => ['lojmItjaj', 'DaSjaj', 'povjaj', 'ghItlhjaj', 'loghjaj', 'buqjaj', 'ghInjaj'],
+ 'weekdays_short' => ['lojmItjaj', 'DaSjaj', 'povjaj', 'ghItlhjaj', 'loghjaj', 'buqjaj', 'ghInjaj'],
+ 'weekdays_min' => ['lojmItjaj', 'DaSjaj', 'povjaj', 'ghItlhjaj', 'loghjaj', 'buqjaj', 'ghInjaj'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' ’ej '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tn.php b/vendor/nesbot/carbon/src/Carbon/Lang/tn.php
new file mode 100644
index 0000000..f29bdf6
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tn.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/tn_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php
new file mode 100644
index 0000000..aada7db
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tn_ZA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Ferikgong', 'Tlhakole', 'Mopitlwe', 'Moranang', 'Motsheganong', 'Seetebosigo', 'Phukwi', 'Phatwe', 'Lwetse', 'Diphalane', 'Ngwanatsele', 'Sedimonthole'],
+ 'months_short' => ['Fer', 'Tlh', 'Mop', 'Mor', 'Mot', 'See', 'Phu', 'Pha', 'Lwe', 'Dip', 'Ngw', 'Sed'],
+ 'weekdays' => ['laTshipi', 'Mosupologo', 'Labobedi', 'Laboraro', 'Labone', 'Labotlhano', 'Lamatlhatso'],
+ 'weekdays_short' => ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tlh', 'Mat'],
+ 'weekdays_min' => ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tlh', 'Mat'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => 'dingwaga di le :count',
+ 'y' => 'dingwaga di le :count',
+ 'a_year' => 'dingwaga di le :count',
+
+ 'month' => 'dikgwedi di le :count',
+ 'm' => 'dikgwedi di le :count',
+ 'a_month' => 'dikgwedi di le :count',
+
+ 'week' => 'dibeke di le :count',
+ 'w' => 'dibeke di le :count',
+ 'a_week' => 'dibeke di le :count',
+
+ 'day' => 'malatsi :count',
+ 'd' => 'malatsi :count',
+ 'a_day' => 'malatsi :count',
+
+ 'hour' => 'diura di le :count',
+ 'h' => 'diura di le :count',
+ 'a_hour' => 'diura di le :count',
+
+ 'minute' => 'metsotso e le :count',
+ 'min' => 'metsotso e le :count',
+ 'a_minute' => 'metsotso e le :count',
+
+ 'second' => 'metsotswana e le :count',
+ 's' => 'metsotswana e le :count',
+ 'a_second' => 'metsotswana e le :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/to.php b/vendor/nesbot/carbon/src/Carbon/Lang/to.php
new file mode 100644
index 0000000..20581bb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/to.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/to_TO.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php b/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php
new file mode 100644
index 0000000..335c69a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/to_TO.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - International Components for Unicode akhilesh.k@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'dddd DD MMM YYYY',
+ ],
+ 'months' => ['Sānuali', 'Fēpueli', 'Maʻasi', 'ʻEpeleli', 'Mē', 'Sune', 'Siulai', 'ʻAokosi', 'Sepitema', 'ʻOkatopa', 'Nōvema', 'Tīsema'],
+ 'months_short' => ['Sān', 'Fēp', 'Maʻa', 'ʻEpe', 'Mē', 'Sun', 'Siu', 'ʻAok', 'Sep', 'ʻOka', 'Nōv', 'Tīs'],
+ 'weekdays' => ['Sāpate', 'Mōnite', 'Tūsite', 'Pulelulu', 'Tuʻapulelulu', 'Falaite', 'Tokonaki'],
+ 'weekdays_short' => ['Sāp', 'Mōn', 'Tūs', 'Pul', 'Tuʻa', 'Fal', 'Tok'],
+ 'weekdays_min' => ['Sāp', 'Mōn', 'Tūs', 'Pul', 'Tuʻa', 'Fal', 'Tok'],
+ 'meridiem' => ['hengihengi', 'efiafi'],
+
+ 'year' => ':count fitu', // less reliable
+ 'y' => ':count fitu', // less reliable
+ 'a_year' => ':count fitu', // less reliable
+
+ 'month' => ':count mahina', // less reliable
+ 'm' => ':count mahina', // less reliable
+ 'a_month' => ':count mahina', // less reliable
+
+ 'week' => ':count Sapate', // less reliable
+ 'w' => ':count Sapate', // less reliable
+ 'a_week' => ':count Sapate', // less reliable
+
+ 'day' => ':count ʻaho', // less reliable
+ 'd' => ':count ʻaho', // less reliable
+ 'a_day' => ':count ʻaho', // less reliable
+
+ 'hour' => ':count houa',
+ 'h' => ':count houa',
+ 'a_hour' => ':count houa',
+
+ 'minute' => ':count miniti',
+ 'min' => ':count miniti',
+ 'a_minute' => ':count miniti',
+
+ 'second' => ':count sekoni',
+ 's' => ':count sekoni',
+ 'a_second' => ':count sekoni',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tpi.php b/vendor/nesbot/carbon/src/Carbon/Lang/tpi.php
new file mode 100644
index 0000000..7d38dae
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tpi.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/tpi_PG.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php
new file mode 100644
index 0000000..5f58c44
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tpi_PG.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Samsung Electronics Co., Ltd. akhilesh.k@samsung.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Janueri', 'Februeri', 'Mas', 'Epril', 'Me', 'Jun', 'Julai', 'Ogas', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mas', 'Epr', 'Me', 'Jun', 'Jul', 'Oga', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'weekdays' => ['Sande', 'Mande', 'Tunde', 'Trinde', 'Fonde', 'Fraide', 'Sarere'],
+ 'weekdays_short' => ['San', 'Man', 'Tun', 'Tri', 'Fon', 'Fra', 'Sar'],
+ 'weekdays_min' => ['San', 'Man', 'Tun', 'Tri', 'Fon', 'Fra', 'Sar'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['biknait', 'apinun'],
+
+ 'year' => 'yia :count',
+ 'y' => 'yia :count',
+ 'a_year' => 'yia :count',
+
+ 'month' => ':count mun',
+ 'm' => ':count mun',
+ 'a_month' => ':count mun',
+
+ 'week' => ':count wik',
+ 'w' => ':count wik',
+ 'a_week' => ':count wik',
+
+ 'day' => ':count de',
+ 'd' => ':count de',
+ 'a_day' => ':count de',
+
+ 'hour' => ':count aua',
+ 'h' => ':count aua',
+ 'a_hour' => ':count aua',
+
+ 'minute' => ':count minit',
+ 'min' => ':count minit',
+ 'a_minute' => ':count minit',
+
+ 'second' => ':count namba tu',
+ 's' => ':count namba tu',
+ 'a_second' => ':count namba tu',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php
new file mode 100644
index 0000000..f5d9f4c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr.php
@@ -0,0 +1,121 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Alan Agius
+ * - Erhan Gundogan
+ * - François B
+ * - JD Isaacks
+ * - Murat Yüksel
+ * - Baran Şengül
+ * - Selami (selamialtin)
+ * - TeomanBey
+ */
+return [
+ 'year' => ':count yıl',
+ 'a_year' => '{1}bir yıl|]1,Inf[:count yıl',
+ 'y' => ':county',
+ 'month' => ':count ay',
+ 'a_month' => '{1}bir ay|]1,Inf[:count ay',
+ 'm' => ':countay',
+ 'week' => ':count hafta',
+ 'a_week' => '{1}bir hafta|]1,Inf[:count hafta',
+ 'w' => ':counth',
+ 'day' => ':count gün',
+ 'a_day' => '{1}bir gün|]1,Inf[:count gün',
+ 'd' => ':countg',
+ 'hour' => ':count saat',
+ 'a_hour' => '{1}bir saat|]1,Inf[:count saat',
+ 'h' => ':countsa',
+ 'minute' => ':count dakika',
+ 'a_minute' => '{1}bir dakika|]1,Inf[:count dakika',
+ 'min' => ':countdk',
+ 'second' => ':count saniye',
+ 'a_second' => '{1}birkaç saniye|]1,Inf[:count saniye',
+ 's' => ':countsn',
+ 'ago' => ':time önce',
+ 'from_now' => ':time sonra',
+ 'after' => ':time sonra',
+ 'before' => ':time önce',
+ 'diff_now' => 'şimdi',
+ 'diff_today' => 'bugün',
+ 'diff_today_regexp' => 'bugün(?:\\s+saat)?',
+ 'diff_yesterday' => 'dün',
+ 'diff_tomorrow' => 'yarın',
+ 'diff_tomorrow_regexp' => 'yarın(?:\\s+saat)?',
+ 'diff_before_yesterday' => 'evvelsi gün',
+ 'diff_after_tomorrow' => 'öbür gün',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[bugün saat] LT',
+ 'nextDay' => '[yarın saat] LT',
+ 'nextWeek' => '[gelecek] dddd [saat] LT',
+ 'lastDay' => '[dün] LT',
+ 'lastWeek' => '[geçen] dddd [saat] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'd':
+ case 'D':
+ case 'Do':
+ case 'DD':
+ return $number;
+ default:
+ if ($number === 0) { // special case for zero
+ return "$number'ıncı";
+ }
+
+ static $suffixes = [
+ 1 => '\'inci',
+ 5 => '\'inci',
+ 8 => '\'inci',
+ 70 => '\'inci',
+ 80 => '\'inci',
+ 2 => '\'nci',
+ 7 => '\'nci',
+ 20 => '\'nci',
+ 50 => '\'nci',
+ 3 => '\'üncü',
+ 4 => '\'üncü',
+ 100 => '\'üncü',
+ 6 => '\'ncı',
+ 9 => '\'uncu',
+ 10 => '\'uncu',
+ 30 => '\'uncu',
+ 60 => '\'ıncı',
+ 90 => '\'ıncı',
+ ];
+
+ $lastDigit = $number % 10;
+
+ return $number.($suffixes[$lastDigit] ?? $suffixes[$number % 100 - $lastDigit] ?? $suffixes[$number >= 100 ? 100 : -1] ?? '');
+ }
+ },
+ 'meridiem' => ['ÖÖ', 'ÖS', 'öö', 'ös'],
+ 'months' => ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
+ 'months_short' => ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
+ 'weekdays' => ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
+ 'weekdays_short' => ['Paz', 'Pts', 'Sal', 'Çar', 'Per', 'Cum', 'Cts'],
+ 'weekdays_min' => ['Pz', 'Pt', 'Sa', 'Ça', 'Pe', 'Cu', 'Ct'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' ve '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php
new file mode 100644
index 0000000..23f1144
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr_CY.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/tr.php', [
+ 'weekdays_short' => ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
+ 'weekdays_min' => ['Pa', 'Pt', 'Sa', 'Ça', 'Pe', 'Cu', 'Ct'],
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'D.MM.YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'D MMMM YYYY dddd h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php b/vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php
new file mode 100644
index 0000000..9e99482
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tr_TR.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/tr.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ts.php b/vendor/nesbot/carbon/src/Carbon/Lang/ts.php
new file mode 100644
index 0000000..525736b
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ts.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ts_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php
new file mode 100644
index 0000000..37a24ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ts_ZA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Sunguti', 'Nyenyenyani', 'Nyenyankulu', 'Dzivamisoko', 'Mudyaxihi', 'Khotavuxika', 'Mawuwani', 'Mhawuri', 'Ndzhati', 'Nhlangula', 'Hukuri', 'N\'wendzamhala'],
+ 'months_short' => ['Sun', 'Yan', 'Kul', 'Dzi', 'Mud', 'Kho', 'Maw', 'Mha', 'Ndz', 'Nhl', 'Huk', 'N\'w'],
+ 'weekdays' => ['Sonto', 'Musumbhunuku', 'Ravumbirhi', 'Ravunharhu', 'Ravumune', 'Ravuntlhanu', 'Mugqivela'],
+ 'weekdays_short' => ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'],
+ 'weekdays_min' => ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => 'malembe ya :count',
+ 'y' => 'malembe ya :count',
+ 'a_year' => 'malembe ya :count',
+
+ 'month' => 'tin’hweti ta :count',
+ 'm' => 'tin’hweti ta :count',
+ 'a_month' => 'tin’hweti ta :count',
+
+ 'week' => 'mavhiki ya :count',
+ 'w' => 'mavhiki ya :count',
+ 'a_week' => 'mavhiki ya :count',
+
+ 'day' => 'masiku :count',
+ 'd' => 'masiku :count',
+ 'a_day' => 'masiku :count',
+
+ 'hour' => 'tiawara ta :count',
+ 'h' => 'tiawara ta :count',
+ 'a_hour' => 'tiawara ta :count',
+
+ 'minute' => 'timinete ta :count',
+ 'min' => 'timinete ta :count',
+ 'a_minute' => 'timinete ta :count',
+
+ 'second' => 'tisekoni ta :count',
+ 's' => 'tisekoni ta :count',
+ 'a_second' => 'tisekoni ta :count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tt.php b/vendor/nesbot/carbon/src/Carbon/Lang/tt.php
new file mode 100644
index 0000000..d67d896
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tt.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/tt_RU.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php
new file mode 100644
index 0000000..38e42d0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Rinat Norkin Pablo Saratxaga, Rinat Norkin pablo@mandrakesoft.com, rinat@taif.ru
+ */
+return [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'DD MMM, HH:mm',
+ 'LLLL' => 'DD MMMM YYYY, HH:mm',
+ ],
+ 'months' => ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'],
+ 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'weekdays' => ['якшәмбе', 'дышәмбе', 'сишәмбе', 'чәршәәмбе', 'пәнҗешмбе', 'җомга', 'шимбә'],
+ 'weekdays_short' => ['якш', 'дыш', 'сиш', 'чәрш', 'пәнҗ', 'җом', 'шим'],
+ 'weekdays_min' => ['якш', 'дыш', 'сиш', 'чәрш', 'пәнҗ', 'җом', 'шим'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'year' => ':count ел',
+ 'month' => ':count ай',
+ 'week' => ':count атна',
+ 'day' => ':count көн',
+ 'hour' => ':count сәгать',
+ 'minute' => ':count минут',
+ 'second' => ':count секунд',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php
new file mode 100644
index 0000000..16b8efb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tt_RU@iqtelif.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Reshat Sabiq tatar.iqtelif.i18n@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD.MM.YYYY',
+ ],
+ 'months' => ['Ğınwar', 'Fiwral\'', 'Mart', 'April', 'May', 'Yün', 'Yül', 'Awgust', 'Sintebír', 'Üktebír', 'Noyebír', 'Dikebír'],
+ 'months_short' => ['Ğın', 'Fiw', 'Mar', 'Apr', 'May', 'Yün', 'Yül', 'Awg', 'Sin', 'Ükt', 'Noy', 'Dik'],
+ 'weekdays' => ['Yekşembí', 'Düşembí', 'Sişembí', 'Çerşembí', 'Pencíşembí', 'Comğa', 'Şimbe'],
+ 'weekdays_short' => ['Yek', 'Düş', 'Siş', 'Çer', 'Pen', 'Com', 'Şim'],
+ 'weekdays_min' => ['Yek', 'Düş', 'Siş', 'Çer', 'Pen', 'Com', 'Şim'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ÖA', 'ÖS'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/twq.php b/vendor/nesbot/carbon/src/Carbon/Lang/twq.php
new file mode 100644
index 0000000..5cbb46e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/twq.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/ses.php', [
+ 'meridiem' => ['Subbaahi', 'Zaarikay b'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tzl.php b/vendor/nesbot/carbon/src/Carbon/Lang/tzl.php
new file mode 100644
index 0000000..50bf26d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tzl.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+ 'year' => '[0,1]:count ar|:count ars',
+ 'y' => '[0,1]:count ar|:count ars',
+ 'month' => '[0,1]:count mes|:count mesen',
+ 'm' => '[0,1]:count mes|:count mesen',
+ 'week' => '[0,1]:count seifetziua|:count seifetziuas',
+ 'w' => '[0,1]:count seifetziua|:count seifetziuas',
+ 'day' => '[0,1]:count ziua|:count ziuas',
+ 'd' => '[0,1]:count ziua|:count ziuas',
+ 'hour' => '[0,1]:count þora|:count þoras',
+ 'h' => '[0,1]:count þora|:count þoras',
+ 'minute' => '[0,1]:count míut|:count míuts',
+ 'min' => '[0,1]:count míut|:count míuts',
+ 'second' => ':count secunds',
+ 's' => ':count secunds',
+
+ 'ago' => 'ja :time',
+ 'from_now' => 'osprei :time',
+
+ 'diff_yesterday' => 'ieiri',
+ 'diff_yesterday_regexp' => 'ieiri(?:\\s+à)?',
+ 'diff_today' => 'oxhi',
+ 'diff_today_regexp' => 'oxhi(?:\\s+à)?',
+ 'diff_tomorrow' => 'demà',
+ 'diff_tomorrow_regexp' => 'demà(?:\\s+à)?',
+
+ 'formats' => [
+ 'LT' => 'HH.mm',
+ 'LTS' => 'HH.mm.ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D. MMMM [dallas] YYYY',
+ 'LLL' => 'D. MMMM [dallas] YYYY HH.mm',
+ 'LLLL' => 'dddd, [li] D. MMMM [dallas] YYYY HH.mm',
+ ],
+
+ 'calendar' => [
+ 'sameDay' => '[oxhi à] LT',
+ 'nextDay' => '[demà à] LT',
+ 'nextWeek' => 'dddd [à] LT',
+ 'lastDay' => '[ieiri à] LT',
+ 'lastWeek' => '[sür el] dddd [lasteu à] LT',
+ 'sameElse' => 'L',
+ ],
+
+ 'meridiem' => ["D'A", "D'O"],
+ 'months' => ['Januar', 'Fevraglh', 'Març', 'Avrïu', 'Mai', 'Gün', 'Julia', 'Guscht', 'Setemvar', 'Listopäts', 'Noemvar', 'Zecemvar'],
+ 'months_short' => ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Gün', 'Jul', 'Gus', 'Set', 'Lis', 'Noe', 'Zec'],
+ 'weekdays' => ['Súladi', 'Lúneçi', 'Maitzi', 'Márcuri', 'Xhúadi', 'Viénerçi', 'Sáturi'],
+ 'weekdays_short' => ['Súl', 'Lún', 'Mai', 'Már', 'Xhú', 'Vié', 'Sát'],
+ 'weekdays_min' => ['Sú', 'Lú', 'Ma', 'Má', 'Xh', 'Vi', 'Sá'],
+ 'ordinal' => ':number.',
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tzm.php b/vendor/nesbot/carbon/src/Carbon/Lang/tzm.php
new file mode 100644
index 0000000..2a1a0f2
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tzm.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ */
+return [
+ 'year' => '{1}ⴰⵙⴳⴰⵙ|:count ⵉⵙⴳⴰⵙⵏ',
+ 'month' => '{1}ⴰⵢoⵓⵔ|:count ⵉⵢⵢⵉⵔⵏ',
+ 'week' => ':count ⵉⵎⴰⵍⴰⵙⵙ',
+ 'day' => '{1}ⴰⵙⵙ|:count oⵙⵙⴰⵏ',
+ 'hour' => '{1}ⵙⴰⵄⴰ|:count ⵜⴰⵙⵙⴰⵄⵉⵏ',
+ 'minute' => '{1}ⵎⵉⵏⵓⴺ|:count ⵎⵉⵏⵓⴺ',
+ 'second' => '{1}ⵉⵎⵉⴽ|:count ⵉⵎⵉⴽ',
+ 'ago' => 'ⵢⴰⵏ :time',
+ 'from_now' => 'ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ :time',
+ 'diff_today' => 'ⴰⵙⴷⵅ',
+ 'diff_yesterday' => 'ⴰⵚⴰⵏⵜ',
+ 'diff_yesterday_regexp' => 'ⴰⵚⴰⵏⵜ(?:\\s+ⴴ)?',
+ 'diff_tomorrow' => 'ⴰⵙⴽⴰ',
+ 'diff_tomorrow_regexp' => 'ⴰⵙⴽⴰ(?:\\s+ⴴ)?',
+ 'diff_today_regexp' => 'ⴰⵙⴷⵅ(?:\\s+ⴴ)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ⴰⵙⴷⵅ ⴴ] LT',
+ 'nextDay' => '[ⴰⵙⴽⴰ ⴴ] LT',
+ 'nextWeek' => 'dddd [ⴴ] LT',
+ 'lastDay' => '[ⴰⵚⴰⵏⵜ ⴴ] LT',
+ 'lastWeek' => 'dddd [ⴴ] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵟⵓⴱⵕ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⵏⴱⵉⵔ'],
+ 'months_short' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵟⵓⴱⵕ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⵏⴱⵉⵔ'],
+ 'weekdays' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵔⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'],
+ 'weekdays_short' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵔⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'],
+ 'weekdays_min' => ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵔⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+ 'weekend' => [5, 6],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php
new file mode 100644
index 0000000..5840d20
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/tzm_Latn.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - JD Isaacks
+ */
+return [
+ 'year' => '{1}:count asgas|:count isgasn',
+ 'a_year' => 'asgas|:count isgasn',
+ 'month' => '{1}:count ayowr|:count iyyirn',
+ 'a_month' => 'ayowr|:count iyyirn',
+ 'week' => ':count imalass',
+ 'a_week' => ':imalass',
+ 'day' => '{1}:count ass|:count ossan',
+ 'a_day' => 'ass|:count ossan',
+ 'hour' => '{1}:count saɛa|:count tassaɛin',
+ 'a_hour' => '{1}saɛa|:count tassaɛin',
+ 'minute' => ':count minuḍ',
+ 'a_minute' => '{1}minuḍ|:count minuḍ',
+ 'second' => ':count imik',
+ 'a_second' => '{1}imik|:count imik',
+ 'ago' => 'yan :time',
+ 'from_now' => 'dadkh s yan :time',
+ 'diff_yesterday' => 'assant',
+ 'diff_yesterday_regexp' => 'assant(?:\\s+g)?',
+ 'diff_today' => 'asdkh',
+ 'diff_today_regexp' => 'asdkh(?:\\s+g)?',
+ 'diff_tomorrow' => 'aska',
+ 'diff_tomorrow_regexp' => 'aska(?:\\s+g)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[asdkh g] LT',
+ 'nextDay' => '[aska g] LT',
+ 'nextWeek' => 'dddd [g] LT',
+ 'lastDay' => '[assant g] LT',
+ 'lastWeek' => 'dddd [g] LT',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['innayr', 'brˤayrˤ', 'marˤsˤ', 'ibrir', 'mayyw', 'ywnyw', 'ywlywz', 'ɣwšt', 'šwtanbir', 'ktˤwbrˤ', 'nwwanbir', 'dwjnbir'],
+ 'months_short' => ['innayr', 'brˤayrˤ', 'marˤsˤ', 'ibrir', 'mayyw', 'ywnyw', 'ywlywz', 'ɣwšt', 'šwtanbir', 'ktˤwbrˤ', 'nwwanbir', 'dwjnbir'],
+ 'weekdays' => ['asamas', 'aynas', 'asinas', 'akras', 'akwas', 'asimwas', 'asiḍyas'],
+ 'weekdays_short' => ['asamas', 'aynas', 'asinas', 'akras', 'akwas', 'asimwas', 'asiḍyas'],
+ 'weekdays_min' => ['asamas', 'aynas', 'asinas', 'akras', 'akwas', 'asimwas', 'asiḍyas'],
+ 'meridiem' => ['Zdat azal', 'Ḍeffir aza'],
+ 'first_day_of_week' => 6,
+ 'day_of_first_week_of_year' => 1,
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ug.php b/vendor/nesbot/carbon/src/Carbon/Lang/ug.php
new file mode 100644
index 0000000..259b99a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ug.php
@@ -0,0 +1,90 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - yasinn
+ */
+return [
+ 'year' => '{1}'.'بىر يىل'.'|:count '.'يىل',
+ 'month' => '{1}'.'بىر ئاي'.'|:count '.'ئاي',
+ 'week' => '{1}'.'بىر ھەپتە'.'|:count '.'ھەپتە',
+ 'day' => '{1}'.'بىر كۈن'.'|:count '.'كۈن',
+ 'hour' => '{1}'.'بىر سائەت'.'|:count '.'سائەت',
+ 'minute' => '{1}'.'بىر مىنۇت'.'|:count '.'مىنۇت',
+ 'second' => '{1}'.'نەچچە سېكونت'.'|:count '.'سېكونت',
+ 'ago' => ':time بۇرۇن',
+ 'from_now' => ':time كېيىن',
+ 'diff_today' => 'بۈگۈن',
+ 'diff_yesterday' => 'تۆنۈگۈن',
+ 'diff_tomorrow' => 'ئەتە',
+ 'diff_tomorrow_regexp' => 'ئەتە(?:\\s+سائەت)?',
+ 'diff_today_regexp' => 'بۈگۈن(?:\\s+سائەت)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-DD',
+ 'LL' => 'YYYY-يىلىM-ئاينىڭD-كۈنى',
+ 'LLL' => 'YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm',
+ 'LLLL' => 'dddd، YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[بۈگۈن سائەت] LT',
+ 'nextDay' => '[ئەتە سائەت] LT',
+ 'nextWeek' => '[كېلەركى] dddd [سائەت] LT',
+ 'lastDay' => '[تۆنۈگۈن] LT',
+ 'lastWeek' => '[ئالدىنقى] dddd [سائەت] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'd':
+ case 'D':
+ case 'DDD':
+ return $number.'-كۈنى';
+ case 'w':
+ case 'W':
+ return $number.'-ھەپتە';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour, $minute) {
+ $time = $hour * 100 + $minute;
+ if ($time < 600) {
+ return 'يېرىم كېچە';
+ }
+ if ($time < 900) {
+ return 'سەھەر';
+ }
+ if ($time < 1130) {
+ return 'چۈشتىن بۇرۇن';
+ }
+ if ($time < 1230) {
+ return 'چۈش';
+ }
+ if ($time < 1800) {
+ return 'چۈشتىن كېيىن';
+ }
+
+ return 'كەچ';
+ },
+ 'months' => ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل', 'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر', 'ئۆكتەبىر', 'نويابىر', 'دېكابىر'],
+ 'months_short' => ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل', 'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر', 'ئۆكتەبىر', 'نويابىر', 'دېكابىر'],
+ 'weekdays' => ['يەكشەنبە', 'دۈشەنبە', 'سەيشەنبە', 'چارشەنبە', 'پەيشەنبە', 'جۈمە', 'شەنبە'],
+ 'weekdays_short' => ['يە', 'دۈ', 'سە', 'چا', 'پە', 'جۈ', 'شە'],
+ 'weekdays_min' => ['يە', 'دۈ', 'سە', 'چا', 'پە', 'جۈ', 'شە'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' ۋە '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php
new file mode 100644
index 0000000..deb828c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ug_CN.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - Alim Boyaq
+ */
+return require __DIR__.'/ug.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uk.php b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php
new file mode 100644
index 0000000..1d5ba70
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uk.php
@@ -0,0 +1,212 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Carbon\CarbonInterface;
+
+$processHoursFunction = function (CarbonInterface $date, string $format) {
+ return $format.'о'.($date->hour === 11 ? 'б' : '').'] LT';
+};
+
+/*
+ * Authors:
+ * - Kunal Marwaha
+ * - Josh Soref
+ * - François B
+ * - Tim Fish
+ * - Serhan Apaydın
+ * - Max Mykhailenko
+ * - JD Isaacks
+ * - Max Kovpak
+ * - AucT
+ * - Philippe Vaucher
+ * - Ilya Shaplyko
+ * - Vadym Ievsieiev
+ * - Denys Kurets
+ * - Igor Kasyanchuk
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Max Melentiev
+ * - Oleh
+ * - epaminond
+ * - Juanito Fatas
+ * - Vitalii Khustochka
+ * - Akira Matsuda
+ * - Christopher Dell
+ * - Enrique Vidal
+ * - Simone Carletti
+ * - Aaron Patterson
+ * - Andriy Tyurnikov
+ * - Nicolás Hock Isaza
+ * - Iwakura Taro
+ * - Andrii Ponomarov
+ * - alecrabbit
+ * - vystepanenko
+ * - AlexWalkerson
+ * - Andre Havryliuk (Andrend)
+ * - Max Datsenko (datsenko-md)
+ */
+return [
+ 'year' => ':count рік|:count роки|:count років',
+ 'y' => ':countр',
+ 'a_year' => '{1}рік|:count рік|:count роки|:count років',
+ 'month' => ':count місяць|:count місяці|:count місяців',
+ 'm' => ':countм',
+ 'a_month' => '{1}місяць|:count місяць|:count місяці|:count місяців',
+ 'week' => ':count тиждень|:count тижні|:count тижнів',
+ 'w' => ':countт',
+ 'a_week' => '{1}тиждень|:count тиждень|:count тижні|:count тижнів',
+ 'day' => ':count день|:count дні|:count днів',
+ 'd' => ':countд',
+ 'a_day' => '{1}день|:count день|:count дні|:count днів',
+ 'hour' => ':count година|:count години|:count годин',
+ 'h' => ':countг',
+ 'a_hour' => '{1}година|:count година|:count години|:count годин',
+ 'minute' => ':count хвилина|:count хвилини|:count хвилин',
+ 'min' => ':countхв',
+ 'a_minute' => '{1}хвилина|:count хвилина|:count хвилини|:count хвилин',
+ 'second' => ':count секунда|:count секунди|:count секунд',
+ 's' => ':countсек',
+ 'a_second' => '{1}декілька секунд|:count секунда|:count секунди|:count секунд',
+
+ 'hour_ago' => ':count годину|:count години|:count годин',
+ 'a_hour_ago' => '{1}годину|:count годину|:count години|:count годин',
+ 'minute_ago' => ':count хвилину|:count хвилини|:count хвилин',
+ 'a_minute_ago' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин',
+ 'second_ago' => ':count секунду|:count секунди|:count секунд',
+ 'a_second_ago' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд',
+
+ 'hour_from_now' => ':count годину|:count години|:count годин',
+ 'a_hour_from_now' => '{1}годину|:count годину|:count години|:count годин',
+ 'minute_from_now' => ':count хвилину|:count хвилини|:count хвилин',
+ 'a_minute_from_now' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин',
+ 'second_from_now' => ':count секунду|:count секунди|:count секунд',
+ 'a_second_from_now' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд',
+
+ 'hour_after' => ':count годину|:count години|:count годин',
+ 'a_hour_after' => '{1}годину|:count годину|:count години|:count годин',
+ 'minute_after' => ':count хвилину|:count хвилини|:count хвилин',
+ 'a_minute_after' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин',
+ 'second_after' => ':count секунду|:count секунди|:count секунд',
+ 'a_second_after' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд',
+
+ 'hour_before' => ':count годину|:count години|:count годин',
+ 'a_hour_before' => '{1}годину|:count годину|:count години|:count годин',
+ 'minute_before' => ':count хвилину|:count хвилини|:count хвилин',
+ 'a_minute_before' => '{1}хвилину|:count хвилину|:count хвилини|:count хвилин',
+ 'second_before' => ':count секунду|:count секунди|:count секунд',
+ 'a_second_before' => '{1}декілька секунд|:count секунду|:count секунди|:count секунд',
+
+ 'ago' => ':time тому',
+ 'from_now' => 'за :time',
+ 'after' => ':time після',
+ 'before' => ':time до',
+ 'diff_now' => 'щойно',
+ 'diff_today' => 'Сьогодні',
+ 'diff_today_regexp' => 'Сьогодні(?:\\s+о)?',
+ 'diff_yesterday' => 'вчора',
+ 'diff_yesterday_regexp' => 'Вчора(?:\\s+о)?',
+ 'diff_tomorrow' => 'завтра',
+ 'diff_tomorrow_regexp' => 'Завтра(?:\\s+о)?',
+ 'diff_before_yesterday' => 'позавчора',
+ 'diff_after_tomorrow' => 'післязавтра',
+ 'period_recurrences' => 'один раз|:count рази|:count разів',
+ 'period_interval' => 'кожні :interval',
+ 'period_start_date' => 'з :date',
+ 'period_end_date' => 'до :date',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY, HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY, HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => function (CarbonInterface $date) use ($processHoursFunction) {
+ return $processHoursFunction($date, '[Сьогодні ');
+ },
+ 'nextDay' => function (CarbonInterface $date) use ($processHoursFunction) {
+ return $processHoursFunction($date, '[Завтра ');
+ },
+ 'nextWeek' => function (CarbonInterface $date) use ($processHoursFunction) {
+ return $processHoursFunction($date, '[У] dddd [');
+ },
+ 'lastDay' => function (CarbonInterface $date) use ($processHoursFunction) {
+ return $processHoursFunction($date, '[Вчора ');
+ },
+ 'lastWeek' => function (CarbonInterface $date) use ($processHoursFunction) {
+ switch ($date->dayOfWeek) {
+ case 0:
+ case 3:
+ case 5:
+ case 6:
+ return $processHoursFunction($date, '[Минулої] dddd [');
+ default:
+ return $processHoursFunction($date, '[Минулого] dddd [');
+ }
+ },
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'M':
+ case 'd':
+ case 'DDD':
+ case 'w':
+ case 'W':
+ return $number.'-й';
+ case 'D':
+ return $number.'-го';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour) {
+ if ($hour < 4) {
+ return 'ночі';
+ }
+ if ($hour < 12) {
+ return 'ранку';
+ }
+ if ($hour < 17) {
+ return 'дня';
+ }
+
+ return 'вечора';
+ },
+ 'months' => ['січня', 'лютого', 'березня', 'квітня', 'травня', 'червня', 'липня', 'серпня', 'вересня', 'жовтня', 'листопада', 'грудня'],
+ 'months_standalone' => ['січень', 'лютий', 'березень', 'квітень', 'травень', 'червень', 'липень', 'серпень', 'вересень', 'жовтень', 'листопад', 'грудень'],
+ 'months_short' => ['січ', 'лют', 'бер', 'кві', 'тра', 'чер', 'лип', 'сер', 'вер', 'жов', 'лис', 'гру'],
+ 'months_regexp' => '/(D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|L{2,4}|l{2,4})/',
+ 'weekdays' => function (CarbonInterface $date, $format, $index) {
+ static $words = [
+ 'nominative' => ['неділя', 'понеділок', 'вівторок', 'середа', 'четвер', 'п’ятниця', 'субота'],
+ 'accusative' => ['неділю', 'понеділок', 'вівторок', 'середу', 'четвер', 'п’ятницю', 'суботу'],
+ 'genitive' => ['неділі', 'понеділка', 'вівторка', 'середи', 'четверга', 'п’ятниці', 'суботи'],
+ ];
+
+ $format = $format ?? '';
+ $nounCase = preg_match('/(\[(В|в|У|у)\])\s+dddd/u', $format)
+ ? 'accusative'
+ : (
+ preg_match('/\[?(?:минулої|наступної)?\s*\]\s+dddd/u', $format)
+ ? 'genitive'
+ : 'nominative'
+ );
+
+ return $words[$nounCase][$index] ?? null;
+ },
+ 'weekdays_short' => ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+ 'weekdays_min' => ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' i '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php b/vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php
new file mode 100644
index 0000000..bd11d86
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uk_UA.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/uk.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/unm.php b/vendor/nesbot/carbon/src/Carbon/Lang/unm.php
new file mode 100644
index 0000000..d3f19f0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/unm.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/unm_US.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php
new file mode 100644
index 0000000..fa5c374
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/unm_US.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['enikwsi', 'chkwali', 'xamokhwite', 'kwetayoxe', 'tainipen', 'kichinipen', 'lainipen', 'winaminke', 'kichitahkok', 'puksit', 'wini', 'muxkotae'],
+ 'months_short' => ['eni', 'chk', 'xam', 'kwe', 'tai', 'nip', 'lai', 'win', 'tah', 'puk', 'kun', 'mux'],
+ 'weekdays' => ['kentuwei', 'manteke', 'tusteke', 'lelai', 'tasteke', 'pelaiteke', 'sateteke'],
+ 'weekdays_short' => ['ken', 'man', 'tus', 'lel', 'tas', 'pel', 'sat'],
+ 'weekdays_min' => ['ken', 'man', 'tus', 'lel', 'tas', 'pel', 'sat'],
+ 'day_of_first_week_of_year' => 1,
+
+ // Too unreliable
+ /*
+ 'year' => ':count kaxtëne',
+ 'y' => ':count kaxtëne',
+ 'a_year' => ':count kaxtëne',
+
+ 'month' => ':count piskewëni kishux', // less reliable
+ 'm' => ':count piskewëni kishux', // less reliable
+ 'a_month' => ':count piskewëni kishux', // less reliable
+
+ 'week' => ':count kishku', // less reliable
+ 'w' => ':count kishku', // less reliable
+ 'a_week' => ':count kishku', // less reliable
+
+ 'day' => ':count kishku',
+ 'd' => ':count kishku',
+ 'a_day' => ':count kishku',
+
+ 'hour' => ':count xkuk', // less reliable
+ 'h' => ':count xkuk', // less reliable
+ 'a_hour' => ':count xkuk', // less reliable
+
+ 'minute' => ':count txituwàk', // less reliable
+ 'min' => ':count txituwàk', // less reliable
+ 'a_minute' => ':count txituwàk', // less reliable
+
+ 'second' => ':count nisha', // less reliable
+ 's' => ':count nisha', // less reliable
+ 'a_second' => ':count nisha', // less reliable
+ */
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php
new file mode 100644
index 0000000..dc16c2c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+$months = [
+ 'جنوری',
+ 'فروری',
+ 'مارچ',
+ 'اپریل',
+ 'مئی',
+ 'جون',
+ 'جولائی',
+ 'اگست',
+ 'ستمبر',
+ 'اکتوبر',
+ 'نومبر',
+ 'دسمبر',
+];
+
+$weekdays = [
+ 'اتوار',
+ 'پیر',
+ 'منگل',
+ 'بدھ',
+ 'جمعرات',
+ 'جمعہ',
+ 'ہفتہ',
+];
+
+/*
+ * Authors:
+ * - Sawood Alam
+ * - Mehshan
+ * - Philippe Vaucher
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Zaid Akram
+ * - Max Melentiev
+ * - hafezdivandari
+ * - Hossein Jabbari
+ * - nimamo
+ */
+return [
+ 'year' => 'ایک سال|:count سال',
+ 'month' => 'ایک ماہ|:count ماہ',
+ 'week' => ':count ہفتے',
+ 'day' => 'ایک دن|:count دن',
+ 'hour' => 'ایک گھنٹہ|:count گھنٹے',
+ 'minute' => 'ایک منٹ|:count منٹ',
+ 'second' => 'چند سیکنڈ|:count سیکنڈ',
+ 'ago' => ':time قبل',
+ 'from_now' => ':time بعد',
+ 'after' => ':time بعد',
+ 'before' => ':time پہلے',
+ 'diff_now' => 'اب',
+ 'diff_today' => 'آج',
+ 'diff_today_regexp' => 'آج(?:\\s+بوقت)?',
+ 'diff_yesterday' => 'گزشتہ کل',
+ 'diff_yesterday_regexp' => 'گذشتہ(?:\\s+روز)?(?:\\s+بوقت)?',
+ 'diff_tomorrow' => 'آئندہ کل',
+ 'diff_tomorrow_regexp' => 'کل(?:\\s+بوقت)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd، D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[آج بوقت] LT',
+ 'nextDay' => '[کل بوقت] LT',
+ 'nextWeek' => 'dddd [بوقت] LT',
+ 'lastDay' => '[گذشتہ روز بوقت] LT',
+ 'lastWeek' => '[گذشتہ] dddd [بوقت] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['صبح', 'شام'],
+ 'months' => $months,
+ 'months_short' => $months,
+ 'weekdays' => $weekdays,
+ 'weekdays_short' => $weekdays,
+ 'weekdays_min' => $weekdays,
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => ['، ', ' اور '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php
new file mode 100644
index 0000000..f81c84d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur_IN.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Red Hat, Pune bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/ur.php', [
+ 'formats' => [
+ 'L' => 'D/M/YY',
+ ],
+ 'months' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'weekdays' => ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'سنیچر'],
+ 'weekdays_short' => ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'سنیچر'],
+ 'weekdays_min' => ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'سنیچر'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php b/vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php
new file mode 100644
index 0000000..8cd593d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ur_PK.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/ur.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'weekdays' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'],
+ 'weekdays_short' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'],
+ 'weekdays_min' => ['اتوار', 'پير', 'منگل', 'بدھ', 'جمعرات', 'جمعه', 'هفته'],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ص', 'ش'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz.php
new file mode 100644
index 0000000..61f3b64
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Dmitriy Shabanov
+ * - JD Isaacks
+ * - Inoyatulloh
+ * - Jamshid
+ * - aarkhipov
+ * - Philippe Vaucher
+ * - felixthemagnificent
+ * - Tsutomu Kuroda
+ * - tjku
+ * - Max Melentiev
+ * - Juanito Fatas
+ * - Alisher Ulugbekov
+ * - Ergashev Adizbek
+ */
+return [
+ 'year' => ':count йил',
+ 'a_year' => '{1}бир йил|:count йил',
+ 'y' => ':count й',
+ 'month' => ':count ой',
+ 'a_month' => '{1}бир ой|:count ой',
+ 'm' => ':count о',
+ 'week' => ':count ҳафта',
+ 'a_week' => '{1}бир ҳафта|:count ҳафта',
+ 'w' => ':count ҳ',
+ 'day' => ':count кун',
+ 'a_day' => '{1}бир кун|:count кун',
+ 'd' => ':count к',
+ 'hour' => ':count соат',
+ 'a_hour' => '{1}бир соат|:count соат',
+ 'h' => ':count с',
+ 'minute' => ':count дақиқа',
+ 'a_minute' => '{1}бир дақиқа|:count дақиқа',
+ 'min' => ':count д',
+ 'second' => ':count сония',
+ 'a_second' => '{1}сония|:count сония',
+ 's' => ':count с',
+ 'ago' => ':time аввал',
+ 'from_now' => 'Якин :time ичида',
+ 'after' => ':timeдан кейин',
+ 'before' => ':time олдин',
+ 'diff_now' => 'ҳозир',
+ 'diff_today' => 'Бугун',
+ 'diff_today_regexp' => 'Бугун(?:\\s+соат)?',
+ 'diff_yesterday' => 'Кеча',
+ 'diff_yesterday_regexp' => 'Кеча(?:\\s+соат)?',
+ 'diff_tomorrow' => 'Эртага',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'D MMMM YYYY, dddd HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Бугун соат] LT [да]',
+ 'nextDay' => '[Эртага] LT [да]',
+ 'nextWeek' => 'dddd [куни соат] LT [да]',
+ 'lastDay' => '[Кеча соат] LT [да]',
+ 'lastWeek' => '[Утган] dddd [куни соат] LT [да]',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['январ', 'феврал', 'март', 'апрел', 'май', 'июн', 'июл', 'август', 'сентябр', 'октябр', 'ноябр', 'декабр'],
+ 'months_short' => ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
+ 'weekdays' => ['якшанба', 'душанба', 'сешанба', 'чоршанба', 'пайшанба', 'жума', 'шанба'],
+ 'weekdays_short' => ['якш', 'душ', 'сеш', 'чор', 'пай', 'жум', 'шан'],
+ 'weekdays_min' => ['як', 'ду', 'се', 'чо', 'па', 'жу', 'ша'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['эрталаб', 'кечаси'],
+ 'list' => [', ', ' ва '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php
new file mode 100644
index 0000000..ffb5131
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Arab.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/fa.php', [
+ 'weekdays' => ['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
+ 'weekdays_short' => ['ی.', 'د.', 'س.', 'چ.', 'پ.', 'ج.', 'ش.'],
+ 'weekdays_min' => ['ی.', 'د.', 'س.', 'چ.', 'پ.', 'ج.', 'ش.'],
+ 'months' => ['جنوری', 'فبروری', 'مارچ', 'اپریل', 'می', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
+ 'months_short' => ['جنو', 'فبر', 'مار', 'اپر', 'می', 'جون', 'جول', 'اگس', 'سپت', 'اکت', 'نوم', 'دسم'],
+ 'first_day_of_week' => 6,
+ 'weekend' => [4, 5],
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'YYYY MMMM D, dddd HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php
new file mode 100644
index 0000000..89e9971
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Cyrl.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/uz.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/yy',
+ 'LL' => 'D MMM, YYYY',
+ 'LLL' => 'D MMMM, YYYY HH:mm',
+ 'LLLL' => 'dddd, DD MMMM, YYYY HH:mm',
+ ],
+ 'meridiem' => ['ТО', 'ТК'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php
new file mode 100644
index 0000000..ecceeaa
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_Latn.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Josh Soref
+ * - Rasulbek
+ * - Ilyosjon Kamoldinov (ilyosjon09)
+ */
+return [
+ 'year' => ':count yil',
+ 'a_year' => '{1}bir yil|:count yil',
+ 'y' => ':count y',
+ 'month' => ':count oy',
+ 'a_month' => '{1}bir oy|:count oy',
+ 'm' => ':count o',
+ 'week' => ':count hafta',
+ 'a_week' => '{1}bir hafta|:count hafta',
+ 'w' => ':count h',
+ 'day' => ':count kun',
+ 'a_day' => '{1}bir kun|:count kun',
+ 'd' => ':count k',
+ 'hour' => ':count soat',
+ 'a_hour' => '{1}bir soat|:count soat',
+ 'h' => ':count soat',
+ 'minute' => ':count daqiqa',
+ 'a_minute' => '{1}bir daqiqa|:count daqiqa',
+ 'min' => ':count d',
+ 'second' => ':count soniya',
+ 'a_second' => '{1}soniya|:count soniya',
+ 's' => ':count son.',
+ 'ago' => ':time avval',
+ 'from_now' => 'Yaqin :time ichida',
+ 'after' => ':timedan keyin',
+ 'before' => ':time oldin',
+ 'diff_yesterday' => 'Kecha',
+ 'diff_yesterday_regexp' => 'Kecha(?:\\s+soat)?',
+ 'diff_today' => 'Bugun',
+ 'diff_today_regexp' => 'Bugun(?:\\s+soat)?',
+ 'diff_tomorrow' => 'Ertaga',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'D MMMM YYYY, dddd HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Bugun soat] LT [da]',
+ 'nextDay' => '[Ertaga] LT [da]',
+ 'nextWeek' => 'dddd [kuni soat] LT [da]',
+ 'lastDay' => '[Kecha soat] LT [da]',
+ 'lastWeek' => '[O\'tgan] dddd [kuni soat] LT [da]',
+ 'sameElse' => 'L',
+ ],
+ 'months' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul', 'Avgust', 'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'],
+ 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'Iyun', 'Iyul', 'Avg', 'Sen', 'Okt', 'Noy', 'Dek'],
+ 'weekdays' => ['Yakshanba', 'Dushanba', 'Seshanba', 'Chorshanba', 'Payshanba', 'Juma', 'Shanba'],
+ 'weekdays_short' => ['Yak', 'Dush', 'Sesh', 'Chor', 'Pay', 'Jum', 'Shan'],
+ 'weekdays_min' => ['Ya', 'Du', 'Se', 'Cho', 'Pa', 'Ju', 'Sha'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' va '],
+ 'meridiem' => ['TO', 'TK'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php
new file mode 100644
index 0000000..d41bfee
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Bobir Ismailov Bobir Ismailov, Pablo Saratxaga, Mashrab Kuvatov bobir_is@yahoo.com, pablo@mandrakesoft.com, kmashrab@uni-bremen.de
+ */
+return array_replace_recursive(require __DIR__.'/uz_Latn.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul', 'Avgust', 'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'],
+ 'months_short' => ['Yan', 'Fev', 'Mar', 'Apr', 'May', 'Iyn', 'Iyl', 'Avg', 'Sen', 'Okt', 'Noy', 'Dek'],
+ 'weekdays' => ['Yakshanba', 'Dushanba', 'Seshanba', 'Chorshanba', 'Payshanba', 'Juma', 'Shanba'],
+ 'weekdays_short' => ['Yak', 'Du', 'Se', 'Cho', 'Pay', 'Ju', 'Sha'],
+ 'weekdays_min' => ['Yak', 'Du', 'Se', 'Cho', 'Pay', 'Ju', 'Sha'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php
new file mode 100644
index 0000000..2fa967c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/uz_UZ@cyrillic.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Mashrab Kuvatov Mashrab Kuvatov, Pablo Saratxaga kmashrab@uni-bremen.de, pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/uz.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['Январ', 'Феврал', 'Март', 'Апрел', 'Май', 'Июн', 'Июл', 'Август', 'Сентябр', 'Октябр', 'Ноябр', 'Декабр'],
+ 'months_short' => ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
+ 'weekdays' => ['Якшанба', 'Душанба', 'Сешанба', 'Чоршанба', 'Пайшанба', 'Жума', 'Шанба'],
+ 'weekdays_short' => ['Якш', 'Душ', 'Сеш', 'Чор', 'Пай', 'Жум', 'Шан'],
+ 'weekdays_min' => ['Якш', 'Душ', 'Сеш', 'Чор', 'Пай', 'Жум', 'Шан'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vai.php b/vendor/nesbot/carbon/src/Carbon/Lang/vai.php
new file mode 100644
index 0000000..3c378df
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vai.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
+ 'weekdays_short' => ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
+ 'weekdays_min' => ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
+ 'months' => ['ꖨꖕ ꕪꕴ ꔞꔀꕮꕊ', 'ꕒꕡꖝꖕ', 'ꕾꖺ', 'ꖢꖕ', 'ꖑꕱ', 'ꖱꘋ', 'ꖱꕞꔤ', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋꕔꕿ ꕸꖃꗏ', 'ꖨꖕ ꕪꕴ ꗏꖺꕮꕊ'],
+ 'months_short' => ['ꖨꖕꔞ', 'ꕒꕡ', 'ꕾꖺ', 'ꖢꖕ', 'ꖑꕱ', 'ꖱꘋ', 'ꖱꕞ', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋ', 'ꖨꖕꗏ'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm a',
+ ],
+
+ 'year' => ':count ꕀ', // less reliable
+ 'y' => ':count ꕀ', // less reliable
+ 'a_year' => ':count ꕀ', // less reliable
+
+ 'second' => ':count ꗱꕞꕯꕊ', // less reliable
+ 's' => ':count ꗱꕞꕯꕊ', // less reliable
+ 'a_second' => ':count ꗱꕞꕯꕊ', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php
new file mode 100644
index 0000000..51e83cc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Latn.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'weekdays' => ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima', 'siɓiti'],
+ 'weekdays_short' => ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima', 'siɓiti'],
+ 'weekdays_min' => ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima', 'siɓiti'],
+ 'months' => ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6', '7', 'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'],
+ 'months_short' => ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6', '7', 'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'h:mm a',
+ 'LTS' => 'h:mm:ss a',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm a',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm a',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php
new file mode 100644
index 0000000..b4bb533
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vai_Vaii.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/vai.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ve.php b/vendor/nesbot/carbon/src/Carbon/Lang/ve.php
new file mode 100644
index 0000000..7f10aeb
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ve.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/ve_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php
new file mode 100644
index 0000000..5eb2b91
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/ve_ZA.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Phando', 'Luhuhi', 'Ṱhafamuhwe', 'Lambamai', 'Shundunthule', 'Fulwi', 'Fulwana', 'Ṱhangule', 'Khubvumedzi', 'Tshimedzi', 'Ḽara', 'Nyendavhusiku'],
+ 'months_short' => ['Pha', 'Luh', 'Fam', 'Lam', 'Shu', 'Lwi', 'Lwa', 'Ngu', 'Khu', 'Tsh', 'Ḽar', 'Nye'],
+ 'weekdays' => ['Swondaha', 'Musumbuluwo', 'Ḽavhuvhili', 'Ḽavhuraru', 'Ḽavhuṋa', 'Ḽavhuṱanu', 'Mugivhela'],
+ 'weekdays_short' => ['Swo', 'Mus', 'Vhi', 'Rar', 'ṋa', 'Ṱan', 'Mug'],
+ 'weekdays_min' => ['Swo', 'Mus', 'Vhi', 'Rar', 'ṋa', 'Ṱan', 'Mug'],
+ 'day_of_first_week_of_year' => 1,
+
+ // Too unreliable
+ /*
+ 'day' => ':count vhege', // less reliable
+ 'd' => ':count vhege', // less reliable
+ 'a_day' => ':count vhege', // less reliable
+
+ 'hour' => ':count watshi', // less reliable
+ 'h' => ':count watshi', // less reliable
+ 'a_hour' => ':count watshi', // less reliable
+
+ 'minute' => ':count watshi', // less reliable
+ 'min' => ':count watshi', // less reliable
+ 'a_minute' => ':count watshi', // less reliable
+
+ 'second' => ':count Mu', // less reliable
+ 's' => ':count Mu', // less reliable
+ 'a_second' => ':count Mu', // less reliable
+
+ 'week' => ':count vhege',
+ 'w' => ':count vhege',
+ 'a_week' => ':count vhege',
+ */
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vi.php b/vendor/nesbot/carbon/src/Carbon/Lang/vi.php
new file mode 100644
index 0000000..73e2852
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vi.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Andre Polykanine A.K.A. Menelion Elensúlë
+ * - JD Isaacks
+ */
+return [
+ 'year' => ':count năm',
+ 'a_year' => '{1}một năm|]1, Inf[:count năm',
+ 'y' => ':count năm',
+ 'month' => ':count tháng',
+ 'a_month' => '{1}một tháng|]1, Inf[:count tháng',
+ 'm' => ':count tháng',
+ 'week' => ':count tuần',
+ 'a_week' => '{1}một tuần|]1, Inf[:count tuần',
+ 'w' => ':count tuần',
+ 'day' => ':count ngày',
+ 'a_day' => '{1}một ngày|]1, Inf[:count ngày',
+ 'd' => ':count ngày',
+ 'hour' => ':count giờ',
+ 'a_hour' => '{1}một giờ|]1, Inf[:count giờ',
+ 'h' => ':count giờ',
+ 'minute' => ':count phút',
+ 'a_minute' => '{1}một phút|]1, Inf[:count phút',
+ 'min' => ':count phút',
+ 'second' => ':count giây',
+ 'a_second' => '{1}vài giây|]1, Inf[:count giây',
+ 's' => ':count giây',
+ 'ago' => ':time trước',
+ 'from_now' => ':time tới',
+ 'after' => ':time sau',
+ 'before' => ':time trước',
+ 'diff_now' => 'bây giờ',
+ 'diff_today' => 'Hôm',
+ 'diff_today_regexp' => 'Hôm(?:\\s+nay)?(?:\\s+lúc)?',
+ 'diff_yesterday' => 'Hôm qua',
+ 'diff_yesterday_regexp' => 'Hôm(?:\\s+qua)?(?:\\s+lúc)?',
+ 'diff_tomorrow' => 'Ngày mai',
+ 'diff_tomorrow_regexp' => 'Ngày(?:\\s+mai)?(?:\\s+lúc)?',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM [năm] YYYY',
+ 'LLL' => 'D MMMM [năm] YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM [năm] YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Hôm nay lúc] LT',
+ 'nextDay' => '[Ngày mai lúc] LT',
+ 'nextWeek' => 'dddd [tuần tới lúc] LT',
+ 'lastDay' => '[Hôm qua lúc] LT',
+ 'lastWeek' => 'dddd [tuần trước lúc] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['SA', 'CH'],
+ 'months' => ['tháng 1', 'tháng 2', 'tháng 3', 'tháng 4', 'tháng 5', 'tháng 6', 'tháng 7', 'tháng 8', 'tháng 9', 'tháng 10', 'tháng 11', 'tháng 12'],
+ 'months_short' => ['Th01', 'Th02', 'Th03', 'Th04', 'Th05', 'Th06', 'Th07', 'Th08', 'Th09', 'Th10', 'Th11', 'Th12'],
+ 'weekdays' => ['chủ nhật', 'thứ hai', 'thứ ba', 'thứ tư', 'thứ năm', 'thứ sáu', 'thứ bảy'],
+ 'weekdays_short' => ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
+ 'weekdays_min' => ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => [', ', ' và '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php b/vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php
new file mode 100644
index 0000000..18d8987
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vi_VN.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/vi.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vo.php b/vendor/nesbot/carbon/src/Carbon/Lang/vo.php
new file mode 100644
index 0000000..e273033
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vo.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'months' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'],
+ 'months_short' => ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10', 'M11', 'M12'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY-MM-dd',
+ 'LL' => 'YYYY MMM D',
+ 'LLL' => 'YYYY MMMM D HH:mm',
+ 'LLLL' => 'YYYY MMMM D, dddd HH:mm',
+ ],
+
+ 'year' => ':count yel',
+ 'y' => ':count yel',
+ 'a_year' => ':count yel',
+
+ 'month' => ':count mul',
+ 'm' => ':count mul',
+ 'a_month' => ':count mul',
+
+ 'week' => ':count vig',
+ 'w' => ':count vig',
+ 'a_week' => ':count vig',
+
+ 'day' => ':count del',
+ 'd' => ':count del',
+ 'a_day' => ':count del',
+
+ 'hour' => ':count düp',
+ 'h' => ':count düp',
+ 'a_hour' => ':count düp',
+
+ 'minute' => ':count minut',
+ 'min' => ':count minut',
+ 'a_minute' => ':count minut',
+
+ 'second' => ':count sekun',
+ 's' => ':count sekun',
+ 'a_second' => ':count sekun',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/vun.php b/vendor/nesbot/carbon/src/Carbon/Lang/vun.php
new file mode 100644
index 0000000..ed92e8e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/vun.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['utuko', 'kyiukonyi'],
+ 'weekdays' => ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi', 'Ijumaa', 'Jumamosi'],
+ 'weekdays_short' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'weekdays_min' => ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
+ 'months' => ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wa.php b/vendor/nesbot/carbon/src/Carbon/Lang/wa.php
new file mode 100644
index 0000000..f6dc4cc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wa.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/wa_BE.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php b/vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php
new file mode 100644
index 0000000..a76d80d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wa_BE.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Djan SACRE Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['di djanvî', 'di fevrî', 'di måss', 'd’ avri', 'di may', 'di djun', 'di djulete', 'd’ awousse', 'di setimbe', 'd’ octôbe', 'di nôvimbe', 'di decimbe'],
+ 'months_short' => ['dja', 'fev', 'mås', 'avr', 'may', 'djn', 'djl', 'awo', 'set', 'oct', 'nôv', 'dec'],
+ 'weekdays' => ['dimegne', 'londi', 'mårdi', 'mierkidi', 'djudi', 'vénrdi', 'semdi'],
+ 'weekdays_short' => ['dim', 'lon', 'mår', 'mie', 'dju', 'vén', 'sem'],
+ 'weekdays_min' => ['dim', 'lon', 'mår', 'mie', 'dju', 'vén', 'sem'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'year' => ':count anêye',
+ 'y' => ':count anêye',
+ 'a_year' => ':count anêye',
+
+ 'month' => ':count meûs',
+ 'm' => ':count meûs',
+ 'a_month' => ':count meûs',
+
+ 'week' => ':count samwinne',
+ 'w' => ':count samwinne',
+ 'a_week' => ':count samwinne',
+
+ 'day' => ':count djoû',
+ 'd' => ':count djoû',
+ 'a_day' => ':count djoû',
+
+ 'hour' => ':count eure',
+ 'h' => ':count eure',
+ 'a_hour' => ':count eure',
+
+ 'minute' => ':count munute',
+ 'min' => ':count munute',
+ 'a_minute' => ':count munute',
+
+ 'second' => ':count Sigonde',
+ 's' => ':count Sigonde',
+ 'a_second' => ':count Sigonde',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wae.php b/vendor/nesbot/carbon/src/Carbon/Lang/wae.php
new file mode 100644
index 0000000..bf57f23
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wae.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/wae_CH.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php b/vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php
new file mode 100644
index 0000000..2af50b4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wae_CH.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Walser Translation Team ml@translate-wae.ch
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+ 'months' => ['Jenner', 'Hornig', 'Märze', 'Abrille', 'Meije', 'Bráčet', 'Heiwet', 'Öigšte', 'Herbštmánet', 'Wímánet', 'Wintermánet', 'Chrištmánet'],
+ 'months_short' => ['Jen', 'Hor', 'Mär', 'Abr', 'Mei', 'Brá', 'Hei', 'Öig', 'Her', 'Wím', 'Win', 'Chr'],
+ 'weekdays' => ['Suntag', 'Mäntag', 'Zischtag', 'Mittwuch', 'Frontag', 'Fritag', 'Samschtag'],
+ 'weekdays_short' => ['Sun', 'Män', 'Zis', 'Mit', 'Fro', 'Fri', 'Sam'],
+ 'weekdays_min' => ['Sun', 'Män', 'Zis', 'Mit', 'Fro', 'Fri', 'Sam'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+
+ 'month' => ':count Maano', // less reliable
+ 'm' => ':count Maano', // less reliable
+ 'a_month' => ':count Maano', // less reliable
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wal.php b/vendor/nesbot/carbon/src/Carbon/Lang/wal.php
new file mode 100644
index 0000000..e8ec40f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wal.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/wal_ET.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php b/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php
new file mode 100644
index 0000000..a4e619a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wal_ET.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Ge'ez Frontier Foundation locales@geez.org
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'],
+ 'months_short' => ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ ', 'ጁን ', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም', 'ዲሴም'],
+ 'weekdays' => ['ወጋ', 'ሳይኖ', 'ማቆሳኛ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ'],
+ 'weekdays_short' => ['ወጋ ', 'ሳይኖ', 'ማቆሳ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ '],
+ 'weekdays_min' => ['ወጋ ', 'ሳይኖ', 'ማቆሳ', 'አሩዋ', 'ሃሙሳ', 'አርባ', 'ቄራ '],
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['ማለዶ', 'ቃማ'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wo.php b/vendor/nesbot/carbon/src/Carbon/Lang/wo.php
new file mode 100644
index 0000000..74b95df
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wo.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/wo_SN.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php b/vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php
new file mode 100644
index 0000000..f8a85b3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/wo_SN.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - The Debian Project Christian Perrier bubulle@debian.org
+ */
+return [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD.MM.YYYY',
+ 'LL' => 'MMMM DD, YYYY',
+ 'LLL' => 'DD MMM HH:mm',
+ 'LLLL' => 'MMMM DD, YYYY HH:mm',
+ ],
+ 'months' => ['sanwiy\'e', 'feebriy\'e', 'mars', 'awril', 'me', 'suwen', 'sulet', 'uut', 'septaambar', 'oktoobar', 'nowaambar', 'desaambar'],
+ 'months_short' => ['san', 'fee', 'mar', 'awr', 'me ', 'suw', 'sul', 'uut', 'sep', 'okt', 'now', 'des'],
+ 'weekdays' => ['dib\'eer', 'altine', 'talaata', 'allarba', 'alxames', 'ajjuma', 'gaawu'],
+ 'weekdays_short' => ['dib', 'alt', 'tal', 'all', 'alx', 'ajj', 'gaa'],
+ 'weekdays_min' => ['dib', 'alt', 'tal', 'all', 'alx', 'ajj', 'gaa'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'year' => ':count at',
+ 'month' => ':count wèr',
+ 'week' => ':count ayubés',
+ 'day' => ':count bés',
+ 'hour' => ':count waxtu',
+ 'minute' => ':count simili',
+ 'second' => ':count saa',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xh.php b/vendor/nesbot/carbon/src/Carbon/Lang/xh.php
new file mode 100644
index 0000000..e88c78d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/xh.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/xh_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php
new file mode 100644
index 0000000..910f831
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/xh_ZA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['eyoMqungu', 'eyoMdumba', 'eyoKwindla', 'uTshazimpuzi', 'uCanzibe', 'eyeSilimela', 'eyeKhala', 'eyeThupa', 'eyoMsintsi', 'eyeDwarha', 'eyeNkanga', 'eyoMnga'],
+ 'months_short' => ['Mqu', 'Mdu', 'Kwi', 'Tsh', 'Can', 'Sil', 'Kha', 'Thu', 'Msi', 'Dwa', 'Nka', 'Mng'],
+ 'weekdays' => ['iCawa', 'uMvulo', 'lwesiBini', 'lwesiThathu', 'ulweSine', 'lwesiHlanu', 'uMgqibelo'],
+ 'weekdays_short' => ['Caw', 'Mvu', 'Bin', 'Tha', 'Sin', 'Hla', 'Mgq'],
+ 'weekdays_min' => ['Caw', 'Mvu', 'Bin', 'Tha', 'Sin', 'Hla', 'Mgq'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count ihlobo', // less reliable
+ 'y' => ':count ihlobo', // less reliable
+ 'a_year' => ':count ihlobo', // less reliable
+
+ 'hour' => ':count iwotshi', // less reliable
+ 'h' => ':count iwotshi', // less reliable
+ 'a_hour' => ':count iwotshi', // less reliable
+
+ 'minute' => ':count ingqalelo', // less reliable
+ 'min' => ':count ingqalelo', // less reliable
+ 'a_minute' => ':count ingqalelo', // less reliable
+
+ 'second' => ':count nceda', // less reliable
+ 's' => ':count nceda', // less reliable
+ 'a_second' => ':count nceda', // less reliable
+
+ 'month' => ':count inyanga',
+ 'm' => ':count inyanga',
+ 'a_month' => ':count inyanga',
+
+ 'week' => ':count veki',
+ 'w' => ':count veki',
+ 'a_week' => ':count veki',
+
+ 'day' => ':count imini',
+ 'd' => ':count imini',
+ 'a_day' => ':count imini',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/xog.php b/vendor/nesbot/carbon/src/Carbon/Lang/xog.php
new file mode 100644
index 0000000..eb55b4a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/xog.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['Munkyo', 'Eigulo'],
+ 'weekdays' => ['Sabiiti', 'Balaza', 'Owokubili', 'Owokusatu', 'Olokuna', 'Olokutaanu', 'Olomukaaga'],
+ 'weekdays_short' => ['Sabi', 'Bala', 'Kubi', 'Kusa', 'Kuna', 'Kuta', 'Muka'],
+ 'weekdays_min' => ['Sabi', 'Bala', 'Kubi', 'Kusa', 'Kuna', 'Kuta', 'Muka'],
+ 'months' => ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi', 'Juuni', 'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba', 'Desemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul', 'Agu', 'Seb', 'Oki', 'Nov', 'Des'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yav.php b/vendor/nesbot/carbon/src/Carbon/Lang/yav.php
new file mode 100644
index 0000000..225a20d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yav.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'meridiem' => ['kiɛmɛ́ɛm', 'kisɛ́ndɛ'],
+ 'weekdays' => ['sɔ́ndiɛ', 'móndie', 'muányáŋmóndie', 'metúkpíápɛ', 'kúpélimetúkpiapɛ', 'feléte', 'séselé'],
+ 'weekdays_short' => ['sd', 'md', 'mw', 'et', 'kl', 'fl', 'ss'],
+ 'weekdays_min' => ['sd', 'md', 'mw', 'et', 'kl', 'fl', 'ss'],
+ 'months' => ['pikítíkítie, oólí ú kutúan', 'siɛyɛ́, oóli ú kándíɛ', 'ɔnsúmbɔl, oóli ú kátátúɛ', 'mesiŋ, oóli ú kénie', 'ensil, oóli ú kátánuɛ', 'ɔsɔn', 'efute', 'pisuyú', 'imɛŋ i puɔs', 'imɛŋ i putúk,oóli ú kátíɛ', 'makandikɛ', 'pilɔndɔ́'],
+ 'months_short' => ['o.1', 'o.2', 'o.3', 'o.4', 'o.5', 'o.6', 'o.7', 'o.8', 'o.9', 'o.10', 'o.11', 'o.12'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'D/M/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yi.php b/vendor/nesbot/carbon/src/Carbon/Lang/yi.php
new file mode 100644
index 0000000..8f32022
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yi.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/yi_US.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php b/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php
new file mode 100644
index 0000000..f764d36
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yi_US.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - http://www.uyip.org/ Pablo Saratxaga pablo@mandrakesoft.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['יאַנואַר', 'פֿעברואַר', 'מערץ', 'אַפּריל', 'מיי', 'יוני', 'יולי', 'אויגוסט', 'סעפּטעמבער', 'אקטאבער', 'נאוועמבער', 'דעצעמבער'],
+ 'months_short' => ['יאַנ', 'פֿעב', 'מאַר', 'אַפּר', 'מײַ ', 'יונ', 'יול', 'אױג', 'סעפּ', 'אָקט', 'נאָװ', 'דעצ'],
+ 'weekdays' => ['זונטיק', 'מאָנטיק', 'דינסטיק', 'מיטװאָך', 'דאָנערשטיק', 'פֿרײַטיק', 'שבת'],
+ 'weekdays_short' => ['זונ\'', 'מאָנ\'', 'דינ\'', 'מיט\'', 'דאָנ\'', 'פֿרײַ\'', 'שבת'],
+ 'weekdays_min' => ['זונ\'', 'מאָנ\'', 'דינ\'', 'מיט\'', 'דאָנ\'', 'פֿרײַ\'', 'שבת'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => ':count יאר',
+ 'y' => ':count יאר',
+ 'a_year' => ':count יאר',
+
+ 'month' => ':count חודש',
+ 'm' => ':count חודש',
+ 'a_month' => ':count חודש',
+
+ 'week' => ':count וואָך',
+ 'w' => ':count וואָך',
+ 'a_week' => ':count וואָך',
+
+ 'day' => ':count טאָג',
+ 'd' => ':count טאָג',
+ 'a_day' => ':count טאָג',
+
+ 'hour' => ':count שעה',
+ 'h' => ':count שעה',
+ 'a_hour' => ':count שעה',
+
+ 'minute' => ':count מינוט',
+ 'min' => ':count מינוט',
+ 'a_minute' => ':count מינוט',
+
+ 'second' => ':count סעקונדע',
+ 's' => ':count סעקונדע',
+ 'a_second' => ':count סעקונדע',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yo.php b/vendor/nesbot/carbon/src/Carbon/Lang/yo.php
new file mode 100644
index 0000000..0a82981
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yo.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - François B
+ * - Atolagbe Abisoye
+ */
+return [
+ 'year' => 'ọdún :count',
+ 'a_year' => '{1}ọdún kan|ọdún :count',
+ 'month' => 'osù :count',
+ 'a_month' => '{1}osù kan|osù :count',
+ 'week' => 'ọsẹ :count',
+ 'a_week' => '{1}ọsẹ kan|ọsẹ :count',
+ 'day' => 'ọjọ́ :count',
+ 'a_day' => '{1}ọjọ́ kan|ọjọ́ :count',
+ 'hour' => 'wákati :count',
+ 'a_hour' => '{1}wákati kan|wákati :count',
+ 'minute' => 'ìsẹjú :count',
+ 'a_minute' => '{1}ìsẹjú kan|ìsẹjú :count',
+ 'second' => 'iaayá :count',
+ 'a_second' => '{1}ìsẹjú aayá die|aayá :count',
+ 'ago' => ':time kọjá',
+ 'from_now' => 'ní :time',
+ 'diff_yesterday' => 'Àna',
+ 'diff_yesterday_regexp' => 'Àna(?:\\s+ni)?',
+ 'diff_today' => 'Ònì',
+ 'diff_today_regexp' => 'Ònì(?:\\s+ni)?',
+ 'diff_tomorrow' => 'Ọ̀la',
+ 'diff_tomorrow_regexp' => 'Ọ̀la(?:\\s+ni)?',
+ 'formats' => [
+ 'LT' => 'h:mm A',
+ 'LTS' => 'h:mm:ss A',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY h:mm A',
+ 'LLLL' => 'dddd, D MMMM YYYY h:mm A',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[Ònì ni] LT',
+ 'nextDay' => '[Ọ̀la ni] LT',
+ 'nextWeek' => 'dddd [Ọsẹ̀ tón\'bọ] [ni] LT',
+ 'lastDay' => '[Àna ni] LT',
+ 'lastWeek' => 'dddd [Ọsẹ̀ tólọ́] [ni] LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => 'ọjọ́ :number',
+ 'months' => ['Sẹ́rẹ́', 'Èrèlè', 'Ẹrẹ̀nà', 'Ìgbé', 'Èbibi', 'Òkùdu', 'Agẹmo', 'Ògún', 'Owewe', 'Ọ̀wàrà', 'Bélú', 'Ọ̀pẹ̀̀'],
+ 'months_short' => ['Sẹ́r', 'Èrl', 'Ẹrn', 'Ìgb', 'Èbi', 'Òkù', 'Agẹ', 'Ògú', 'Owe', 'Ọ̀wà', 'Bél', 'Ọ̀pẹ̀̀'],
+ 'weekdays' => ['Àìkú', 'Ajé', 'Ìsẹ́gun', 'Ọjọ́rú', 'Ọjọ́bọ', 'Ẹtì', 'Àbámẹ́ta'],
+ 'weekdays_short' => ['Àìk', 'Ajé', 'Ìsẹ́', 'Ọjr', 'Ọjb', 'Ẹtì', 'Àbá'],
+ 'weekdays_min' => ['Àì', 'Aj', 'Ìs', 'Ọr', 'Ọb', 'Ẹt', 'Àb'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'meridiem' => ['Àárọ̀', 'Ọ̀sán'],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php b/vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php
new file mode 100644
index 0000000..12b9e81
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yo_BJ.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array_replace_recursive(require __DIR__.'/yo.php', [
+ 'meridiem' => ['Àárɔ̀', 'Ɔ̀sán'],
+ 'weekdays' => ['Ɔjɔ́ Àìkú', 'Ɔjɔ́ Ajé', 'Ɔjɔ́ Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɔjɔ́ Ɛtì', 'Ɔjɔ́ Àbámɛ́ta'],
+ 'weekdays_short' => ['Àìkú', 'Ajé', 'Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɛtì', 'Àbámɛ́ta'],
+ 'weekdays_min' => ['Àìkú', 'Ajé', 'Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɛtì', 'Àbámɛ́ta'],
+ 'months' => ['Oshù Shɛ́rɛ́', 'Oshù Èrèlè', 'Oshù Ɛrɛ̀nà', 'Oshù Ìgbé', 'Oshù Ɛ̀bibi', 'Oshù Òkúdu', 'Oshù Agɛmɔ', 'Oshù Ògún', 'Oshù Owewe', 'Oshù Ɔ̀wàrà', 'Oshù Bélú', 'Oshù Ɔ̀pɛ̀'],
+ 'months_short' => ['Shɛ́rɛ́', 'Èrèlè', 'Ɛrɛ̀nà', 'Ìgbé', 'Ɛ̀bibi', 'Òkúdu', 'Agɛmɔ', 'Ògún', 'Owewe', 'Ɔ̀wàrà', 'Bélú', 'Ɔ̀pɛ̀'],
+ 'first_day_of_week' => 1,
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd, D MMMM YYYY HH:mm',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php b/vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php
new file mode 100644
index 0000000..6860bc1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yo_NG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/yo.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue.php
new file mode 100644
index 0000000..ce233a4
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/yue_HK.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php
new file mode 100644
index 0000000..4e7d5c3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue_HK.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/zh_HK.php', [
+ 'formats' => [
+ 'L' => 'YYYY年MM月DD日 dddd',
+ ],
+ 'months' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+ 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+ 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+ 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'first_day_of_week' => 0,
+ 'day_of_first_week_of_year' => 1,
+ 'meridiem' => ['上午', '下午'],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php
new file mode 100644
index 0000000..db913ca
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hans.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hans.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php
new file mode 100644
index 0000000..e2526f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yue_Hant.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hant.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yuw.php b/vendor/nesbot/carbon/src/Carbon/Lang/yuw.php
new file mode 100644
index 0000000..8efdc93
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yuw.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/yuw_PG.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php b/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php
new file mode 100644
index 0000000..b99ad2e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/yuw_PG.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Information from native speakers Hannah Sarvasy nungon.localization@gmail.com
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YY',
+ ],
+ 'months' => ['jenuari', 'febuari', 'mas', 'epril', 'mei', 'jun', 'julai', 'ögus', 'septemba', 'öktoba', 'nöwemba', 'diksemba'],
+ 'months_short' => ['jen', 'feb', 'mas', 'epr', 'mei', 'jun', 'jul', 'ögu', 'sep', 'ökt', 'nöw', 'dis'],
+ 'weekdays' => ['sönda', 'mönda', 'sinda', 'mitiwö', 'sogipbono', 'nenggo', 'söndanggie'],
+ 'weekdays_short' => ['sön', 'mön', 'sin', 'mit', 'soi', 'nen', 'sab'],
+ 'weekdays_min' => ['sön', 'mön', 'sin', 'mit', 'soi', 'nen', 'sab'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zgh.php b/vendor/nesbot/carbon/src/Carbon/Lang/zgh.php
new file mode 100644
index 0000000..4d2c3b3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zgh.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - BAKTETE Miloud
+ */
+return [
+ 'year' => ':count ⵓⵙⴳⴳⵯⴰⵙ|:count ⵉⵙⴳⴳⵓⵙⴰ',
+ 'a_year' => 'ⵓⵙⴳⴳⵯⴰⵙ|:count ⵉⵙⴳⴳⵓⵙⴰ',
+ 'y' => ':count ⵓⵙⴳⴳⵯⴰⵙ|:count ⵉⵙⴳⴳⵓⵙⴰ',
+ 'month' => ':count ⵡⴰⵢⵢⵓⵔ|:count ⴰⵢⵢⵓⵔⵏ',
+ 'a_month' => 'ⵉⴷⵊ ⵡⴰⵢⵢⵓⵔ|:count ⴰⵢⵢⵓⵔⵏ',
+ 'm' => ':count ⴰⵢⵢⵓⵔⵏ',
+ 'week' => ':count ⵉⵎⴰⵍⴰⵙⵙ|:count ⵉⵎⴰⵍⴰⵙⵙⵏ',
+ 'a_week' => 'ⵉⵛⵜ ⵉⵎⴰⵍⴰⵙⵙ|:count ⵉⵎⴰⵍⴰⵙⵙⵏ',
+ 'w' => ':count ⵉⵎⴰⵍⴰⵙⵙ.',
+ 'day' => ':count ⵡⴰⵙⵙ|:count ⵓⵙⵙⴰⵏ',
+ 'a_day' => 'ⵉⴷⵊ ⵡⴰⵙⵙ|:count ⵓⵙⵙⴰⵏ',
+ 'd' => ':count ⵓ',
+ 'hour' => ':count ⵜⵙⵔⴰⴳⵜ|:count ⵜⵉⵙⵔⴰⴳⵉⵏ',
+ 'a_hour' => 'ⵉⵛⵜ ⵜⵙⵔⴰⴳⵜ|:count ⵜⵉⵙⵔⴰⴳⵉⵏ',
+ 'h' => ':count ⵜ',
+ 'minute' => ':count ⵜⵓⵙⴷⵉⴷⵜ|:count ⵜⵓⵙⴷⵉⴷⵉⵏ',
+ 'a_minute' => 'ⵉⵛⵜ ⵜⵓⵙⴷⵉⴷⵜ|:count ⵜⵓⵙⴷⵉⴷⵉⵏ',
+ 'min' => ':count ⵜⵓⵙ',
+ 'second' => ':count ⵜⵙⵉⵏⵜ|:count ⵜⵉⵙⵉⵏⴰ',
+ 'a_second' => 'ⴽⵔⴰ ⵜⵉⵙⵉⵏⴰ|:count ⵜⵉⵙⵉⵏⴰ',
+ 's' => ':count ⵜ',
+ 'ago' => 'ⵣⴳ :time',
+ 'from_now' => 'ⴷⴳ :time',
+ 'after' => ':time ⴰⵡⴰⵔ',
+ 'before' => ':time ⴷⴰⵜ',
+ 'diff_now' => 'ⴰⴷⵡⴰⵍⵉ',
+ 'diff_today' => 'ⴰⵙⵙ',
+ 'diff_today_regexp' => 'ⴰⵙⵙ(?:\\s+ⴰ/ⴰⴷ)?(?:\\s+ⴳ)?',
+ 'diff_yesterday' => 'ⴰⵙⵙⵏⵏⴰⵟ',
+ 'diff_yesterday_regexp' => 'ⴰⵙⵙⵏⵏⴰⵟ(?:\\s+ⴳ)?',
+ 'diff_tomorrow' => 'ⴰⵙⴽⴽⴰ',
+ 'diff_tomorrow_regexp' => 'ⴰⵙⴽⴽⴰ(?:\\s+ⴳ)?',
+ 'diff_before_yesterday' => 'ⴼⵔ ⵉⴹⵏⵏⴰⵟ',
+ 'diff_after_tomorrow' => 'ⵏⴰⴼ ⵓⵙⴽⴽⴰ',
+ 'period_recurrences' => ':count ⵜⵉⴽⴽⴰⵍ',
+ 'period_interval' => 'ⴽⵓ :interval',
+ 'period_start_date' => 'ⴳ :date',
+ 'period_end_date' => 'ⵉ :date',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'DD/MM/YYYY',
+ 'LL' => 'D MMMM YYYY',
+ 'LLL' => 'D MMMM YYYY HH:mm',
+ 'LLLL' => 'dddd D MMMM YYYY HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[ⴰⵙⵙ ⴰ/ⴰⴷ ⴳ] LT',
+ 'nextDay' => '[ⴰⵙⴽⴽⴰ ⴳ] LT',
+ 'nextWeek' => 'dddd [ⴳ] LT',
+ 'lastDay' => '[ⴰⵙⵙⵏⵏⴰⵟ ⴳ] LT',
+ 'lastWeek' => 'dddd [ⴰⵎⴳⴳⴰⵔⵓ ⴳ] LT',
+ 'sameElse' => 'L',
+ ],
+ 'meridiem' => ['ⵜⵉⴼⴰⵡⵜ', 'ⵜⴰⴷⴳⴳⵯⴰⵜ'],
+ 'months' => ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ', 'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ', 'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ', 'ⴽⵟⵓⴱⵕ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'],
+ 'months_short' => ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ', 'ⵎⴰⵢ', 'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ', 'ⴽⵟⵓ', 'ⵏⵓⵡ', 'ⴷⵓⵊ'],
+ 'weekdays' => ['ⵓⵙⴰⵎⴰⵙ', 'ⵡⴰⵢⵏⴰⵙ', 'ⵓⵙⵉⵏⴰⵙ', 'ⵡⴰⴽⵕⴰⵙ', 'ⵓⴽⵡⴰⵙ', 'ⵓⵙⵉⵎⵡⴰⵙ', 'ⵓⵙⵉⴹⵢⴰⵙ'],
+ 'weekdays_short' => ['ⵓⵙⴰ', 'ⵡⴰⵢ', 'ⵓⵙⵉ', 'ⵡⴰⴽ', 'ⵓⴽⵡ', 'ⵓⵙⵉⵎ', 'ⵓⵙⵉⴹ'],
+ 'weekdays_min' => ['ⵓⵙⴰ', 'ⵡⴰⵢ', 'ⵓⵙⵉ', 'ⵡⴰⴽ', 'ⵓⴽⵡ', 'ⵓⵙⵉⵎ', 'ⵓⵙⵉⴹ'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 1,
+ 'list' => [', ', ' ⴷ '],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh.php
new file mode 100644
index 0000000..1187c3d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - xuri
+ * - sycuato
+ * - bokideckonja
+ * - Luo Ning
+ * - William Yang (williamyang233)
+ */
+return array_merge(require __DIR__.'/zh_Hans.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY/MM/DD',
+ 'LL' => 'YYYY年M月D日',
+ 'LLL' => 'YYYY年M月D日 A h点mm分',
+ 'LLLL' => 'YYYY年M月D日dddd A h点mm分',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php
new file mode 100644
index 0000000..9c05d5a
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_CN.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - monkeycon
+ * - François B
+ * - Jason Katz-Brown
+ * - Serhan Apaydın
+ * - Matt Johnson
+ * - JD Isaacks
+ * - Zeno Zeng
+ * - Chris Hemp
+ * - shankesgk2
+ */
+return array_merge(require __DIR__.'/zh.php', [
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY/MM/DD',
+ 'LL' => 'YYYY年M月D日',
+ 'LLL' => 'YYYY年M月D日Ah点mm分',
+ 'LLLL' => 'YYYY年M月D日ddddAh点mm分',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php
new file mode 100644
index 0000000..c3ee9fc
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_HK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hant_HK.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php
new file mode 100644
index 0000000..9b91785
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans.php
@@ -0,0 +1,109 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - monkeycon
+ * - François B
+ * - Jason Katz-Brown
+ * - Konstantin Konev
+ * - Chris Lam
+ * - Serhan Apaydın
+ * - Gary Lo
+ * - JD Isaacks
+ * - Chris Hemp
+ * - shankesgk2
+ * - Daniel Cheung (danvim)
+ */
+return [
+ 'year' => ':count:optional-space年',
+ 'y' => ':count:optional-space年',
+ 'month' => ':count:optional-space个月',
+ 'm' => ':count:optional-space个月',
+ 'week' => ':count:optional-space周',
+ 'w' => ':count:optional-space周',
+ 'day' => ':count:optional-space天',
+ 'd' => ':count:optional-space天',
+ 'hour' => ':count:optional-space小时',
+ 'h' => ':count:optional-space小时',
+ 'minute' => ':count:optional-space分钟',
+ 'min' => ':count:optional-space分钟',
+ 'second' => ':count:optional-space秒',
+ 'a_second' => '{1}几秒|]1,Inf[:count:optional-space秒',
+ 's' => ':count:optional-space秒',
+ 'ago' => ':time前',
+ 'from_now' => ':time后',
+ 'after' => ':time后',
+ 'before' => ':time前',
+ 'diff_now' => '现在',
+ 'diff_today' => '今天',
+ 'diff_yesterday' => '昨天',
+ 'diff_tomorrow' => '明天',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY/MM/DD',
+ 'LL' => 'YYYY年M月D日',
+ 'LLL' => 'YYYY年M月D日 HH:mm',
+ 'LLLL' => 'YYYY年M月D日dddd HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[今天]LT',
+ 'nextDay' => '[明天]LT',
+ 'nextWeek' => '[下]ddddLT',
+ 'lastDay' => '[昨天]LT',
+ 'lastWeek' => '[上]ddddLT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'd':
+ case 'D':
+ case 'DDD':
+ return $number.'日';
+ case 'M':
+ return $number.'月';
+ case 'w':
+ case 'W':
+ return $number.'周';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour, $minute) {
+ $time = $hour * 100 + $minute;
+ if ($time < 600) {
+ return '凌晨';
+ }
+ if ($time < 900) {
+ return '早上';
+ }
+ if ($time < 1130) {
+ return '上午';
+ }
+ if ($time < 1230) {
+ return '中午';
+ }
+ if ($time < 1800) {
+ return '下午';
+ }
+
+ return '晚上';
+ },
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+ 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+ 'weekdays_short' => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => '',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php
new file mode 100644
index 0000000..db913ca
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_HK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hans.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php
new file mode 100644
index 0000000..db913ca
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_MO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hans.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php
new file mode 100644
index 0000000..db913ca
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hans_SG.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hans.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php
new file mode 100644
index 0000000..a27b610
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant.php
@@ -0,0 +1,111 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Adam
+ * - monkeycon
+ * - François B
+ * - Jason Katz-Brown
+ * - Chris Lam
+ * - Serhan Apaydın
+ * - Gary Lo
+ * - JD Isaacks
+ * - Chris Hemp
+ * - Eddie
+ * - KID
+ * - shankesgk2
+ * - Daniel Cheung (danvim)
+ */
+return [
+ 'year' => ':count:optional-space年',
+ 'y' => ':count:optional-space年',
+ 'month' => ':count:optional-space個月',
+ 'm' => ':count:optional-space月',
+ 'week' => ':count:optional-space週',
+ 'w' => ':count:optional-space週',
+ 'day' => ':count:optional-space天',
+ 'd' => ':count:optional-space天',
+ 'hour' => ':count:optional-space小時',
+ 'h' => ':count:optional-space小時',
+ 'minute' => ':count:optional-space分鐘',
+ 'min' => ':count:optional-space分鐘',
+ 'second' => ':count:optional-space秒',
+ 'a_second' => '{1}幾秒|]1,Inf[:count:optional-space秒',
+ 's' => ':count:optional-space秒',
+ 'ago' => ':time前',
+ 'from_now' => ':time後',
+ 'after' => ':time後',
+ 'before' => ':time前',
+ 'diff_now' => '現在',
+ 'diff_today' => '今天',
+ 'diff_yesterday' => '昨天',
+ 'diff_tomorrow' => '明天',
+ 'formats' => [
+ 'LT' => 'HH:mm',
+ 'LTS' => 'HH:mm:ss',
+ 'L' => 'YYYY/MM/DD',
+ 'LL' => 'YYYY年M月D日',
+ 'LLL' => 'YYYY年M月D日 HH:mm',
+ 'LLLL' => 'YYYY年M月D日dddd HH:mm',
+ ],
+ 'calendar' => [
+ 'sameDay' => '[今天] LT',
+ 'nextDay' => '[明天] LT',
+ 'nextWeek' => '[下]dddd LT',
+ 'lastDay' => '[昨天] LT',
+ 'lastWeek' => '[上]dddd LT',
+ 'sameElse' => 'L',
+ ],
+ 'ordinal' => function ($number, $period) {
+ switch ($period) {
+ case 'd':
+ case 'D':
+ case 'DDD':
+ return $number.'日';
+ case 'M':
+ return $number.'月';
+ case 'w':
+ case 'W':
+ return $number.'周';
+ default:
+ return $number;
+ }
+ },
+ 'meridiem' => function ($hour, $minute) {
+ $time = $hour * 100 + $minute;
+ if ($time < 600) {
+ return '凌晨';
+ }
+ if ($time < 900) {
+ return '早上';
+ }
+ if ($time < 1130) {
+ return '上午';
+ }
+ if ($time < 1230) {
+ return '中午';
+ }
+ if ($time < 1800) {
+ return '下午';
+ }
+
+ return '晚上';
+ },
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
+ 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+ 'weekdays_short' => ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'first_day_of_week' => 1,
+ 'day_of_first_week_of_year' => 4,
+ 'list' => '',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php
new file mode 100644
index 0000000..e2526f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_HK.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hant.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php
new file mode 100644
index 0000000..e2526f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_MO.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hant.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php
new file mode 100644
index 0000000..e2526f1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_Hant_TW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hant.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php
new file mode 100644
index 0000000..1c86d47
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_MO.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - tarunvelli
+ * - Eddie
+ * - KID
+ * - shankesgk2
+ */
+return array_replace_recursive(require __DIR__.'/zh_Hant.php', [
+ 'after' => ':time后',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php
new file mode 100644
index 0000000..c451a56
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_SG.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/zh.php', [
+ 'formats' => [
+ 'L' => 'YYYY年MM月DD日',
+ ],
+ 'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'months_short' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+ 'weekdays' => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+ 'weekdays_short' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'weekdays_min' => ['日', '一', '二', '三', '四', '五', '六'],
+ 'day_of_first_week_of_year' => 1,
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php
new file mode 100644
index 0000000..c6789ed
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php
@@ -0,0 +1,12 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return require __DIR__.'/zh_Hant_TW.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php b/vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php
new file mode 100644
index 0000000..b0d9ba8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zh_YUE.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - IBM Globalization Center of Competency, Yamato Software Laboratory bug-glibc-locales@gnu.org
+ */
+return array_replace_recursive(require __DIR__.'/zh.php', [
+ 'formats' => [
+ 'L' => 'YYYY-MM-DD',
+ ],
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zu.php b/vendor/nesbot/carbon/src/Carbon/Lang/zu.php
new file mode 100644
index 0000000..9a6cce0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zu.php
@@ -0,0 +1,15 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Unknown default region, use the first alphabetically.
+ */
+return require __DIR__.'/zu_ZA.php';
diff --git a/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php b/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php
new file mode 100644
index 0000000..6bfb72f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Lang/zu_ZA.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Authors:
+ * - Zuza Software Foundation (Translate.org.za) Dwayne Bailey dwayne@translate.org.za
+ */
+return array_replace_recursive(require __DIR__.'/en.php', [
+ 'formats' => [
+ 'L' => 'DD/MM/YYYY',
+ ],
+ 'months' => ['Januwari', 'Februwari', 'Mashi', 'Ephreli', 'Meyi', 'Juni', 'Julayi', 'Agasti', 'Septhemba', 'Okthoba', 'Novemba', 'Disemba'],
+ 'months_short' => ['Jan', 'Feb', 'Mas', 'Eph', 'Mey', 'Jun', 'Jul', 'Aga', 'Sep', 'Okt', 'Nov', 'Dis'],
+ 'weekdays' => ['iSonto', 'uMsombuluko', 'uLwesibili', 'uLwesithathu', 'uLwesine', 'uLwesihlanu', 'uMgqibelo'],
+ 'weekdays_short' => ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
+ 'weekdays_min' => ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
+ 'day_of_first_week_of_year' => 1,
+
+ 'year' => 'kweminyaka engu-:count',
+ 'y' => 'kweminyaka engu-:count',
+ 'a_year' => 'kweminyaka engu-:count',
+
+ 'month' => 'izinyanga ezingu-:count',
+ 'm' => 'izinyanga ezingu-:count',
+ 'a_month' => 'izinyanga ezingu-:count',
+
+ 'week' => 'lwamasonto angu-:count',
+ 'w' => 'lwamasonto angu-:count',
+ 'a_week' => 'lwamasonto angu-:count',
+
+ 'day' => 'ezingaba ngu-:count',
+ 'd' => 'ezingaba ngu-:count',
+ 'a_day' => 'ezingaba ngu-:count',
+
+ 'hour' => 'amahora angu-:count',
+ 'h' => 'amahora angu-:count',
+ 'a_hour' => 'amahora angu-:count',
+
+ 'minute' => 'ngemizuzu engu-:count',
+ 'min' => 'ngemizuzu engu-:count',
+ 'a_minute' => 'ngemizuzu engu-:count',
+
+ 'second' => 'imizuzwana engu-:count',
+ 's' => 'imizuzwana engu-:count',
+ 'a_second' => 'imizuzwana engu-:count',
+]);
diff --git a/vendor/nesbot/carbon/src/Carbon/Language.php b/vendor/nesbot/carbon/src/Carbon/Language.php
new file mode 100644
index 0000000..1fb5baf
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Language.php
@@ -0,0 +1,342 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use JsonSerializable;
+use ReturnTypeWillChange;
+
+class Language implements JsonSerializable
+{
+ /**
+ * @var array
+ */
+ protected static $languagesNames;
+
+ /**
+ * @var array
+ */
+ protected static $regionsNames;
+
+ /**
+ * @var string
+ */
+ protected $id;
+
+ /**
+ * @var string
+ */
+ protected $code;
+
+ /**
+ * @var string|null
+ */
+ protected $variant;
+
+ /**
+ * @var string|null
+ */
+ protected $region;
+
+ /**
+ * @var array
+ */
+ protected $names;
+
+ /**
+ * @var string
+ */
+ protected $isoName;
+
+ /**
+ * @var string
+ */
+ protected $nativeName;
+
+ public function __construct(string $id)
+ {
+ $this->id = str_replace('-', '_', $id);
+ $parts = explode('_', $this->id);
+ $this->code = $parts[0];
+
+ if (isset($parts[1])) {
+ if (!preg_match('/^[A-Z]+$/', $parts[1])) {
+ $this->variant = $parts[1];
+ $parts[1] = $parts[2] ?? null;
+ }
+ if ($parts[1]) {
+ $this->region = $parts[1];
+ }
+ }
+ }
+
+ /**
+ * Get the list of the known languages.
+ *
+ * @return array
+ */
+ public static function all()
+ {
+ if (!static::$languagesNames) {
+ static::$languagesNames = require __DIR__.'/List/languages.php';
+ }
+
+ return static::$languagesNames;
+ }
+
+ /**
+ * Get the list of the known regions.
+ *
+ * @return array
+ */
+ public static function regions()
+ {
+ if (!static::$regionsNames) {
+ static::$regionsNames = require __DIR__.'/List/regions.php';
+ }
+
+ return static::$regionsNames;
+ }
+
+ /**
+ * Get both isoName and nativeName as an array.
+ *
+ * @return array
+ */
+ public function getNames(): array
+ {
+ if (!$this->names) {
+ $this->names = static::all()[$this->code] ?? [
+ 'isoName' => $this->code,
+ 'nativeName' => $this->code,
+ ];
+ }
+
+ return $this->names;
+ }
+
+ /**
+ * Returns the original locale ID.
+ *
+ * @return string
+ */
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ /**
+ * Returns the code of the locale "en"/"fr".
+ *
+ * @return string
+ */
+ public function getCode(): string
+ {
+ return $this->code;
+ }
+
+ /**
+ * Returns the variant code such as cyrl/latn.
+ *
+ * @return string|null
+ */
+ public function getVariant(): ?string
+ {
+ return $this->variant;
+ }
+
+ /**
+ * Returns the variant such as Cyrillic/Latin.
+ *
+ * @return string|null
+ */
+ public function getVariantName(): ?string
+ {
+ if ($this->variant === 'Latn') {
+ return 'Latin';
+ }
+
+ if ($this->variant === 'Cyrl') {
+ return 'Cyrillic';
+ }
+
+ return $this->variant;
+ }
+
+ /**
+ * Returns the region part of the locale.
+ *
+ * @return string|null
+ */
+ public function getRegion(): ?string
+ {
+ return $this->region;
+ }
+
+ /**
+ * Returns the region name for the current language.
+ *
+ * @return string|null
+ */
+ public function getRegionName(): ?string
+ {
+ return $this->region ? (static::regions()[$this->region] ?? $this->region) : null;
+ }
+
+ /**
+ * Returns the long ISO language name.
+ *
+ * @return string
+ */
+ public function getFullIsoName(): string
+ {
+ if (!$this->isoName) {
+ $this->isoName = $this->getNames()['isoName'];
+ }
+
+ return $this->isoName;
+ }
+
+ /**
+ * Set the ISO language name.
+ *
+ * @param string $isoName
+ */
+ public function setIsoName(string $isoName): self
+ {
+ $this->isoName = $isoName;
+
+ return $this;
+ }
+
+ /**
+ * Return the full name of the language in this language.
+ *
+ * @return string
+ */
+ public function getFullNativeName(): string
+ {
+ if (!$this->nativeName) {
+ $this->nativeName = $this->getNames()['nativeName'];
+ }
+
+ return $this->nativeName;
+ }
+
+ /**
+ * Set the name of the language in this language.
+ *
+ * @param string $nativeName
+ */
+ public function setNativeName(string $nativeName): self
+ {
+ $this->nativeName = $nativeName;
+
+ return $this;
+ }
+
+ /**
+ * Returns the short ISO language name.
+ *
+ * @return string
+ */
+ public function getIsoName(): string
+ {
+ $name = $this->getFullIsoName();
+
+ return trim(strstr($name, ',', true) ?: $name);
+ }
+
+ /**
+ * Get the short name of the language in this language.
+ *
+ * @return string
+ */
+ public function getNativeName(): string
+ {
+ $name = $this->getFullNativeName();
+
+ return trim(strstr($name, ',', true) ?: $name);
+ }
+
+ /**
+ * Get a string with short ISO name, region in parentheses if applicable, variant in parentheses if applicable.
+ *
+ * @return string
+ */
+ public function getIsoDescription()
+ {
+ $region = $this->getRegionName();
+ $variant = $this->getVariantName();
+
+ return $this->getIsoName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : '');
+ }
+
+ /**
+ * Get a string with short native name, region in parentheses if applicable, variant in parentheses if applicable.
+ *
+ * @return string
+ */
+ public function getNativeDescription()
+ {
+ $region = $this->getRegionName();
+ $variant = $this->getVariantName();
+
+ return $this->getNativeName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : '');
+ }
+
+ /**
+ * Get a string with long ISO name, region in parentheses if applicable, variant in parentheses if applicable.
+ *
+ * @return string
+ */
+ public function getFullIsoDescription()
+ {
+ $region = $this->getRegionName();
+ $variant = $this->getVariantName();
+
+ return $this->getFullIsoName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : '');
+ }
+
+ /**
+ * Get a string with long native name, region in parentheses if applicable, variant in parentheses if applicable.
+ *
+ * @return string
+ */
+ public function getFullNativeDescription()
+ {
+ $region = $this->getRegionName();
+ $variant = $this->getVariantName();
+
+ return $this->getFullNativeName().($region ? ' ('.$region.')' : '').($variant ? ' ('.$variant.')' : '');
+ }
+
+ /**
+ * Returns the original locale ID.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getId();
+ }
+
+ /**
+ * Get a string with short ISO name, region in parentheses if applicable, variant in parentheses if applicable.
+ *
+ * @return string
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->getIsoDescription();
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php
new file mode 100644
index 0000000..84e241e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php
@@ -0,0 +1,127 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Laravel;
+
+use Carbon\Carbon;
+use Carbon\CarbonImmutable;
+use Carbon\CarbonInterval;
+use Carbon\CarbonPeriod;
+use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
+use Illuminate\Events\Dispatcher;
+use Illuminate\Events\EventDispatcher;
+use Illuminate\Support\Carbon as IlluminateCarbon;
+use Illuminate\Support\Facades\Date;
+use Throwable;
+
+class ServiceProvider extends \Illuminate\Support\ServiceProvider
+{
+ /** @var callable|null */
+ protected $appGetter = null;
+
+ /** @var callable|null */
+ protected $localeGetter = null;
+
+ public function setAppGetter(?callable $appGetter): void
+ {
+ $this->appGetter = $appGetter;
+ }
+
+ public function setLocaleGetter(?callable $localeGetter): void
+ {
+ $this->localeGetter = $localeGetter;
+ }
+
+ public function boot()
+ {
+ $this->updateLocale();
+
+ if (!$this->app->bound('events')) {
+ return;
+ }
+
+ $service = $this;
+ $events = $this->app['events'];
+
+ if ($this->isEventDispatcher($events)) {
+ $events->listen(class_exists('Illuminate\Foundation\Events\LocaleUpdated') ? 'Illuminate\Foundation\Events\LocaleUpdated' : 'locale.changed', function () use ($service) {
+ $service->updateLocale();
+ });
+ }
+ }
+
+ public function updateLocale()
+ {
+ $locale = $this->getLocale();
+
+ if ($locale === null) {
+ return;
+ }
+
+ Carbon::setLocale($locale);
+ CarbonImmutable::setLocale($locale);
+ CarbonPeriod::setLocale($locale);
+ CarbonInterval::setLocale($locale);
+
+ if (class_exists(IlluminateCarbon::class)) {
+ IlluminateCarbon::setLocale($locale);
+ }
+
+ if (class_exists(Date::class)) {
+ try {
+ $root = Date::getFacadeRoot();
+ $root->setLocale($locale);
+ } catch (Throwable $e) {
+ // Non Carbon class in use in Date facade
+ }
+ }
+ }
+
+ public function register()
+ {
+ // Needed for Laravel < 5.3 compatibility
+ }
+
+ protected function getLocale()
+ {
+ if ($this->localeGetter) {
+ return ($this->localeGetter)();
+ }
+
+ $app = $this->getApp();
+ $app = $app && method_exists($app, 'getLocale')
+ ? $app
+ : $this->getGlobalApp('translator');
+
+ return $app ? $app->getLocale() : null;
+ }
+
+ protected function getApp()
+ {
+ if ($this->appGetter) {
+ return ($this->appGetter)();
+ }
+
+ return $this->app ?? $this->getGlobalApp();
+ }
+
+ protected function getGlobalApp(...$args)
+ {
+ return \function_exists('app') ? \app(...$args) : null;
+ }
+
+ protected function isEventDispatcher($instance)
+ {
+ return $instance instanceof EventDispatcher
+ || $instance instanceof Dispatcher
+ || $instance instanceof DispatcherContract;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/List/languages.php b/vendor/nesbot/carbon/src/Carbon/List/languages.php
new file mode 100644
index 0000000..5b5d9a1
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/List/languages.php
@@ -0,0 +1,1239 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+ /*
+ * ISO 639-2
+ */
+ 'ab' => [
+ 'isoName' => 'Abkhazian',
+ 'nativeName' => 'аҧсуа бызшәа, аҧсшәа',
+ ],
+ 'aa' => [
+ 'isoName' => 'Afar',
+ 'nativeName' => 'Afaraf',
+ ],
+ 'af' => [
+ 'isoName' => 'Afrikaans',
+ 'nativeName' => 'Afrikaans',
+ ],
+ 'ak' => [
+ 'isoName' => 'Akan',
+ 'nativeName' => 'Akan',
+ ],
+ 'sq' => [
+ 'isoName' => 'Albanian',
+ 'nativeName' => 'Shqip',
+ ],
+ 'am' => [
+ 'isoName' => 'Amharic',
+ 'nativeName' => 'አማርኛ',
+ ],
+ 'ar' => [
+ 'isoName' => 'Arabic',
+ 'nativeName' => 'العربية',
+ ],
+ 'an' => [
+ 'isoName' => 'Aragonese',
+ 'nativeName' => 'aragonés',
+ ],
+ 'hy' => [
+ 'isoName' => 'Armenian',
+ 'nativeName' => 'Հայերեն',
+ ],
+ 'as' => [
+ 'isoName' => 'Assamese',
+ 'nativeName' => 'অসমীয়া',
+ ],
+ 'av' => [
+ 'isoName' => 'Avaric',
+ 'nativeName' => 'авар мацӀ, магӀарул мацӀ',
+ ],
+ 'ae' => [
+ 'isoName' => 'Avestan',
+ 'nativeName' => 'avesta',
+ ],
+ 'ay' => [
+ 'isoName' => 'Aymara',
+ 'nativeName' => 'aymar aru',
+ ],
+ 'az' => [
+ 'isoName' => 'Azerbaijani',
+ 'nativeName' => 'azərbaycan dili',
+ ],
+ 'bm' => [
+ 'isoName' => 'Bambara',
+ 'nativeName' => 'bamanankan',
+ ],
+ 'ba' => [
+ 'isoName' => 'Bashkir',
+ 'nativeName' => 'башҡорт теле',
+ ],
+ 'eu' => [
+ 'isoName' => 'Basque',
+ 'nativeName' => 'euskara, euskera',
+ ],
+ 'be' => [
+ 'isoName' => 'Belarusian',
+ 'nativeName' => 'беларуская мова',
+ ],
+ 'bn' => [
+ 'isoName' => 'Bengali',
+ 'nativeName' => 'বাংলা',
+ ],
+ 'bh' => [
+ 'isoName' => 'Bihari languages',
+ 'nativeName' => 'भोजपुरी',
+ ],
+ 'bi' => [
+ 'isoName' => 'Bislama',
+ 'nativeName' => 'Bislama',
+ ],
+ 'bs' => [
+ 'isoName' => 'Bosnian',
+ 'nativeName' => 'bosanski jezik',
+ ],
+ 'br' => [
+ 'isoName' => 'Breton',
+ 'nativeName' => 'brezhoneg',
+ ],
+ 'bg' => [
+ 'isoName' => 'Bulgarian',
+ 'nativeName' => 'български език',
+ ],
+ 'my' => [
+ 'isoName' => 'Burmese',
+ 'nativeName' => 'ဗမာစာ',
+ ],
+ 'ca' => [
+ 'isoName' => 'Catalan, Valencian',
+ 'nativeName' => 'català, valencià',
+ ],
+ 'ch' => [
+ 'isoName' => 'Chamorro',
+ 'nativeName' => 'Chamoru',
+ ],
+ 'ce' => [
+ 'isoName' => 'Chechen',
+ 'nativeName' => 'нохчийн мотт',
+ ],
+ 'ny' => [
+ 'isoName' => 'Chichewa, Chewa, Nyanja',
+ 'nativeName' => 'chiCheŵa, chinyanja',
+ ],
+ 'zh' => [
+ 'isoName' => 'Chinese',
+ 'nativeName' => '中文 (Zhōngwén), 汉语, 漢語',
+ ],
+ 'cv' => [
+ 'isoName' => 'Chuvash',
+ 'nativeName' => 'чӑваш чӗлхи',
+ ],
+ 'kw' => [
+ 'isoName' => 'Cornish',
+ 'nativeName' => 'Kernewek',
+ ],
+ 'co' => [
+ 'isoName' => 'Corsican',
+ 'nativeName' => 'corsu, lingua corsa',
+ ],
+ 'cr' => [
+ 'isoName' => 'Cree',
+ 'nativeName' => 'ᓀᐦᐃᔭᐍᐏᐣ',
+ ],
+ 'hr' => [
+ 'isoName' => 'Croatian',
+ 'nativeName' => 'hrvatski jezik',
+ ],
+ 'cs' => [
+ 'isoName' => 'Czech',
+ 'nativeName' => 'čeština, český jazyk',
+ ],
+ 'da' => [
+ 'isoName' => 'Danish',
+ 'nativeName' => 'dansk',
+ ],
+ 'dv' => [
+ 'isoName' => 'Divehi, Dhivehi, Maldivian',
+ 'nativeName' => 'ދިވެހި',
+ ],
+ 'nl' => [
+ 'isoName' => 'Dutch, Flemish',
+ 'nativeName' => 'Nederlands, Vlaams',
+ ],
+ 'dz' => [
+ 'isoName' => 'Dzongkha',
+ 'nativeName' => 'རྫོང་ཁ',
+ ],
+ 'en' => [
+ 'isoName' => 'English',
+ 'nativeName' => 'English',
+ ],
+ 'eo' => [
+ 'isoName' => 'Esperanto',
+ 'nativeName' => 'Esperanto',
+ ],
+ 'et' => [
+ 'isoName' => 'Estonian',
+ 'nativeName' => 'eesti, eesti keel',
+ ],
+ 'ee' => [
+ 'isoName' => 'Ewe',
+ 'nativeName' => 'Eʋegbe',
+ ],
+ 'fo' => [
+ 'isoName' => 'Faroese',
+ 'nativeName' => 'føroyskt',
+ ],
+ 'fj' => [
+ 'isoName' => 'Fijian',
+ 'nativeName' => 'vosa Vakaviti',
+ ],
+ 'fi' => [
+ 'isoName' => 'Finnish',
+ 'nativeName' => 'suomi, suomen kieli',
+ ],
+ 'fr' => [
+ 'isoName' => 'French',
+ 'nativeName' => 'français',
+ ],
+ 'ff' => [
+ 'isoName' => 'Fulah',
+ 'nativeName' => 'Fulfulde, Pulaar, Pular',
+ ],
+ 'gl' => [
+ 'isoName' => 'Galician',
+ 'nativeName' => 'Galego',
+ ],
+ 'ka' => [
+ 'isoName' => 'Georgian',
+ 'nativeName' => 'ქართული',
+ ],
+ 'de' => [
+ 'isoName' => 'German',
+ 'nativeName' => 'Deutsch',
+ ],
+ 'el' => [
+ 'isoName' => 'Greek (modern)',
+ 'nativeName' => 'ελληνικά',
+ ],
+ 'gn' => [
+ 'isoName' => 'Guaraní',
+ 'nativeName' => 'Avañe\'ẽ',
+ ],
+ 'gu' => [
+ 'isoName' => 'Gujarati',
+ 'nativeName' => 'ગુજરાતી',
+ ],
+ 'ht' => [
+ 'isoName' => 'Haitian, Haitian Creole',
+ 'nativeName' => 'Kreyòl ayisyen',
+ ],
+ 'ha' => [
+ 'isoName' => 'Hausa',
+ 'nativeName' => '(Hausa) هَوُسَ',
+ ],
+ 'he' => [
+ 'isoName' => 'Hebrew (modern)',
+ 'nativeName' => 'עברית',
+ ],
+ 'hz' => [
+ 'isoName' => 'Herero',
+ 'nativeName' => 'Otjiherero',
+ ],
+ 'hi' => [
+ 'isoName' => 'Hindi',
+ 'nativeName' => 'हिन्दी, हिंदी',
+ ],
+ 'ho' => [
+ 'isoName' => 'Hiri Motu',
+ 'nativeName' => 'Hiri Motu',
+ ],
+ 'hu' => [
+ 'isoName' => 'Hungarian',
+ 'nativeName' => 'magyar',
+ ],
+ 'ia' => [
+ 'isoName' => 'Interlingua',
+ 'nativeName' => 'Interlingua',
+ ],
+ 'id' => [
+ 'isoName' => 'Indonesian',
+ 'nativeName' => 'Bahasa Indonesia',
+ ],
+ 'ie' => [
+ 'isoName' => 'Interlingue',
+ 'nativeName' => 'Originally called Occidental; then Interlingue after WWII',
+ ],
+ 'ga' => [
+ 'isoName' => 'Irish',
+ 'nativeName' => 'Gaeilge',
+ ],
+ 'ig' => [
+ 'isoName' => 'Igbo',
+ 'nativeName' => 'Asụsụ Igbo',
+ ],
+ 'ik' => [
+ 'isoName' => 'Inupiaq',
+ 'nativeName' => 'Iñupiaq, Iñupiatun',
+ ],
+ 'io' => [
+ 'isoName' => 'Ido',
+ 'nativeName' => 'Ido',
+ ],
+ 'is' => [
+ 'isoName' => 'Icelandic',
+ 'nativeName' => 'Íslenska',
+ ],
+ 'it' => [
+ 'isoName' => 'Italian',
+ 'nativeName' => 'Italiano',
+ ],
+ 'iu' => [
+ 'isoName' => 'Inuktitut',
+ 'nativeName' => 'ᐃᓄᒃᑎᑐᑦ',
+ ],
+ 'ja' => [
+ 'isoName' => 'Japanese',
+ 'nativeName' => '日本語 (にほんご)',
+ ],
+ 'jv' => [
+ 'isoName' => 'Javanese',
+ 'nativeName' => 'ꦧꦱꦗꦮ, Basa Jawa',
+ ],
+ 'kl' => [
+ 'isoName' => 'Kalaallisut, Greenlandic',
+ 'nativeName' => 'kalaallisut, kalaallit oqaasii',
+ ],
+ 'kn' => [
+ 'isoName' => 'Kannada',
+ 'nativeName' => 'ಕನ್ನಡ',
+ ],
+ 'kr' => [
+ 'isoName' => 'Kanuri',
+ 'nativeName' => 'Kanuri',
+ ],
+ 'ks' => [
+ 'isoName' => 'Kashmiri',
+ 'nativeName' => 'कश्मीरी, كشميري',
+ ],
+ 'kk' => [
+ 'isoName' => 'Kazakh',
+ 'nativeName' => 'қазақ тілі',
+ ],
+ 'km' => [
+ 'isoName' => 'Central Khmer',
+ 'nativeName' => 'ខ្មែរ, ខេមរភាសា, ភាសាខ្មែរ',
+ ],
+ 'ki' => [
+ 'isoName' => 'Kikuyu, Gikuyu',
+ 'nativeName' => 'Gĩkũyũ',
+ ],
+ 'rw' => [
+ 'isoName' => 'Kinyarwanda',
+ 'nativeName' => 'Ikinyarwanda',
+ ],
+ 'ky' => [
+ 'isoName' => 'Kirghiz, Kyrgyz',
+ 'nativeName' => 'Кыргызча, Кыргыз тили',
+ ],
+ 'kv' => [
+ 'isoName' => 'Komi',
+ 'nativeName' => 'коми кыв',
+ ],
+ 'kg' => [
+ 'isoName' => 'Kongo',
+ 'nativeName' => 'Kikongo',
+ ],
+ 'ko' => [
+ 'isoName' => 'Korean',
+ 'nativeName' => '한국어',
+ ],
+ 'ku' => [
+ 'isoName' => 'Kurdish',
+ 'nativeName' => 'Kurdî, کوردی',
+ ],
+ 'kj' => [
+ 'isoName' => 'Kuanyama, Kwanyama',
+ 'nativeName' => 'Kuanyama',
+ ],
+ 'la' => [
+ 'isoName' => 'Latin',
+ 'nativeName' => 'latine, lingua latina',
+ ],
+ 'lb' => [
+ 'isoName' => 'Luxembourgish, Letzeburgesch',
+ 'nativeName' => 'Lëtzebuergesch',
+ ],
+ 'lg' => [
+ 'isoName' => 'Ganda',
+ 'nativeName' => 'Luganda',
+ ],
+ 'li' => [
+ 'isoName' => 'Limburgan, Limburger, Limburgish',
+ 'nativeName' => 'Limburgs',
+ ],
+ 'ln' => [
+ 'isoName' => 'Lingala',
+ 'nativeName' => 'Lingála',
+ ],
+ 'lo' => [
+ 'isoName' => 'Lao',
+ 'nativeName' => 'ພາສາລາວ',
+ ],
+ 'lt' => [
+ 'isoName' => 'Lithuanian',
+ 'nativeName' => 'lietuvių kalba',
+ ],
+ 'lu' => [
+ 'isoName' => 'Luba-Katanga',
+ 'nativeName' => 'Kiluba',
+ ],
+ 'lv' => [
+ 'isoName' => 'Latvian',
+ 'nativeName' => 'latviešu valoda',
+ ],
+ 'gv' => [
+ 'isoName' => 'Manx',
+ 'nativeName' => 'Gaelg, Gailck',
+ ],
+ 'mk' => [
+ 'isoName' => 'Macedonian',
+ 'nativeName' => 'македонски јазик',
+ ],
+ 'mg' => [
+ 'isoName' => 'Malagasy',
+ 'nativeName' => 'fiteny malagasy',
+ ],
+ 'ms' => [
+ 'isoName' => 'Malay',
+ 'nativeName' => 'Bahasa Melayu, بهاس ملايو',
+ ],
+ 'ml' => [
+ 'isoName' => 'Malayalam',
+ 'nativeName' => 'മലയാളം',
+ ],
+ 'mt' => [
+ 'isoName' => 'Maltese',
+ 'nativeName' => 'Malti',
+ ],
+ 'mi' => [
+ 'isoName' => 'Maori',
+ 'nativeName' => 'te reo Māori',
+ ],
+ 'mr' => [
+ 'isoName' => 'Marathi',
+ 'nativeName' => 'मराठी',
+ ],
+ 'mh' => [
+ 'isoName' => 'Marshallese',
+ 'nativeName' => 'Kajin M̧ajeļ',
+ ],
+ 'mn' => [
+ 'isoName' => 'Mongolian',
+ 'nativeName' => 'Монгол хэл',
+ ],
+ 'na' => [
+ 'isoName' => 'Nauru',
+ 'nativeName' => 'Dorerin Naoero',
+ ],
+ 'nv' => [
+ 'isoName' => 'Navajo, Navaho',
+ 'nativeName' => 'Diné bizaad',
+ ],
+ 'nd' => [
+ 'isoName' => 'North Ndebele',
+ 'nativeName' => 'isiNdebele',
+ ],
+ 'ne' => [
+ 'isoName' => 'Nepali',
+ 'nativeName' => 'नेपाली',
+ ],
+ 'ng' => [
+ 'isoName' => 'Ndonga',
+ 'nativeName' => 'Owambo',
+ ],
+ 'nb' => [
+ 'isoName' => 'Norwegian Bokmål',
+ 'nativeName' => 'Norsk Bokmål',
+ ],
+ 'nn' => [
+ 'isoName' => 'Norwegian Nynorsk',
+ 'nativeName' => 'Norsk Nynorsk',
+ ],
+ 'no' => [
+ 'isoName' => 'Norwegian',
+ 'nativeName' => 'Norsk',
+ ],
+ 'ii' => [
+ 'isoName' => 'Sichuan Yi, Nuosu',
+ 'nativeName' => 'ꆈꌠ꒿ Nuosuhxop',
+ ],
+ 'nr' => [
+ 'isoName' => 'South Ndebele',
+ 'nativeName' => 'isiNdebele',
+ ],
+ 'oc' => [
+ 'isoName' => 'Occitan',
+ 'nativeName' => 'occitan, lenga d\'òc',
+ ],
+ 'oj' => [
+ 'isoName' => 'Ojibwa',
+ 'nativeName' => 'ᐊᓂᔑᓈᐯᒧᐎᓐ',
+ ],
+ 'cu' => [
+ 'isoName' => 'Church Slavic, Church Slavonic, Old Church Slavonic, Old Slavonic, Old Bulgarian',
+ 'nativeName' => 'ѩзыкъ словѣньскъ',
+ ],
+ 'om' => [
+ 'isoName' => 'Oromo',
+ 'nativeName' => 'Afaan Oromoo',
+ ],
+ 'or' => [
+ 'isoName' => 'Oriya',
+ 'nativeName' => 'ଓଡ଼ିଆ',
+ ],
+ 'os' => [
+ 'isoName' => 'Ossetian, Ossetic',
+ 'nativeName' => 'ирон æвзаг',
+ ],
+ 'pa' => [
+ 'isoName' => 'Panjabi, Punjabi',
+ 'nativeName' => 'ਪੰਜਾਬੀ',
+ ],
+ 'pi' => [
+ 'isoName' => 'Pali',
+ 'nativeName' => 'पाऴि',
+ ],
+ 'fa' => [
+ 'isoName' => 'Persian',
+ 'nativeName' => 'فارسی',
+ ],
+ 'pl' => [
+ 'isoName' => 'Polish',
+ 'nativeName' => 'język polski, polszczyzna',
+ ],
+ 'ps' => [
+ 'isoName' => 'Pashto, Pushto',
+ 'nativeName' => 'پښتو',
+ ],
+ 'pt' => [
+ 'isoName' => 'Portuguese',
+ 'nativeName' => 'Português',
+ ],
+ 'qu' => [
+ 'isoName' => 'Quechua',
+ 'nativeName' => 'Runa Simi, Kichwa',
+ ],
+ 'rm' => [
+ 'isoName' => 'Romansh',
+ 'nativeName' => 'Rumantsch Grischun',
+ ],
+ 'rn' => [
+ 'isoName' => 'Rundi',
+ 'nativeName' => 'Ikirundi',
+ ],
+ 'ro' => [
+ 'isoName' => 'Romanian, Moldavian, Moldovan',
+ 'nativeName' => 'Română',
+ ],
+ 'ru' => [
+ 'isoName' => 'Russian',
+ 'nativeName' => 'русский',
+ ],
+ 'sa' => [
+ 'isoName' => 'Sanskrit',
+ 'nativeName' => 'संस्कृतम्',
+ ],
+ 'sc' => [
+ 'isoName' => 'Sardinian',
+ 'nativeName' => 'sardu',
+ ],
+ 'sd' => [
+ 'isoName' => 'Sindhi',
+ 'nativeName' => 'सिन्धी, سنڌي، سندھی',
+ ],
+ 'se' => [
+ 'isoName' => 'Northern Sami',
+ 'nativeName' => 'Davvisámegiella',
+ ],
+ 'sm' => [
+ 'isoName' => 'Samoan',
+ 'nativeName' => 'gagana fa\'a Samoa',
+ ],
+ 'sg' => [
+ 'isoName' => 'Sango',
+ 'nativeName' => 'yângâ tî sängö',
+ ],
+ 'sr' => [
+ 'isoName' => 'Serbian',
+ 'nativeName' => 'српски језик',
+ ],
+ 'gd' => [
+ 'isoName' => 'Gaelic, Scottish Gaelic',
+ 'nativeName' => 'Gàidhlig',
+ ],
+ 'sn' => [
+ 'isoName' => 'Shona',
+ 'nativeName' => 'chiShona',
+ ],
+ 'si' => [
+ 'isoName' => 'Sinhala, Sinhalese',
+ 'nativeName' => 'සිංහල',
+ ],
+ 'sk' => [
+ 'isoName' => 'Slovak',
+ 'nativeName' => 'Slovenčina, Slovenský Jazyk',
+ ],
+ 'sl' => [
+ 'isoName' => 'Slovene',
+ 'nativeName' => 'Slovenski Jezik, Slovenščina',
+ ],
+ 'so' => [
+ 'isoName' => 'Somali',
+ 'nativeName' => 'Soomaaliga, af Soomaali',
+ ],
+ 'st' => [
+ 'isoName' => 'Southern Sotho',
+ 'nativeName' => 'Sesotho',
+ ],
+ 'es' => [
+ 'isoName' => 'Spanish, Castilian',
+ 'nativeName' => 'Español',
+ ],
+ 'su' => [
+ 'isoName' => 'Sundanese',
+ 'nativeName' => 'Basa Sunda',
+ ],
+ 'sw' => [
+ 'isoName' => 'Swahili',
+ 'nativeName' => 'Kiswahili',
+ ],
+ 'ss' => [
+ 'isoName' => 'Swati',
+ 'nativeName' => 'SiSwati',
+ ],
+ 'sv' => [
+ 'isoName' => 'Swedish',
+ 'nativeName' => 'Svenska',
+ ],
+ 'ta' => [
+ 'isoName' => 'Tamil',
+ 'nativeName' => 'தமிழ்',
+ ],
+ 'te' => [
+ 'isoName' => 'Telugu',
+ 'nativeName' => 'తెలుగు',
+ ],
+ 'tg' => [
+ 'isoName' => 'Tajik',
+ 'nativeName' => 'тоҷикӣ, toçikī, تاجیکی',
+ ],
+ 'th' => [
+ 'isoName' => 'Thai',
+ 'nativeName' => 'ไทย',
+ ],
+ 'ti' => [
+ 'isoName' => 'Tigrinya',
+ 'nativeName' => 'ትግርኛ',
+ ],
+ 'bo' => [
+ 'isoName' => 'Tibetan',
+ 'nativeName' => 'བོད་ཡིག',
+ ],
+ 'tk' => [
+ 'isoName' => 'Turkmen',
+ 'nativeName' => 'Türkmen, Түркмен',
+ ],
+ 'tl' => [
+ 'isoName' => 'Tagalog',
+ 'nativeName' => 'Wikang Tagalog',
+ ],
+ 'tn' => [
+ 'isoName' => 'Tswana',
+ 'nativeName' => 'Setswana',
+ ],
+ 'to' => [
+ 'isoName' => 'Tongan (Tonga Islands)',
+ 'nativeName' => 'Faka Tonga',
+ ],
+ 'tr' => [
+ 'isoName' => 'Turkish',
+ 'nativeName' => 'Türkçe',
+ ],
+ 'ts' => [
+ 'isoName' => 'Tsonga',
+ 'nativeName' => 'Xitsonga',
+ ],
+ 'tt' => [
+ 'isoName' => 'Tatar',
+ 'nativeName' => 'татар теле, tatar tele',
+ ],
+ 'tw' => [
+ 'isoName' => 'Twi',
+ 'nativeName' => 'Twi',
+ ],
+ 'ty' => [
+ 'isoName' => 'Tahitian',
+ 'nativeName' => 'Reo Tahiti',
+ ],
+ 'ug' => [
+ 'isoName' => 'Uighur, Uyghur',
+ 'nativeName' => 'Uyƣurqə, ئۇيغۇرچ',
+ ],
+ 'uk' => [
+ 'isoName' => 'Ukrainian',
+ 'nativeName' => 'Українська',
+ ],
+ 'ur' => [
+ 'isoName' => 'Urdu',
+ 'nativeName' => 'اردو',
+ ],
+ 'uz' => [
+ 'isoName' => 'Uzbek',
+ 'nativeName' => 'Oʻzbek, Ўзбек, أۇزبېك',
+ ],
+ 've' => [
+ 'isoName' => 'Venda',
+ 'nativeName' => 'Tshivenḓa',
+ ],
+ 'vi' => [
+ 'isoName' => 'Vietnamese',
+ 'nativeName' => 'Tiếng Việt',
+ ],
+ 'vo' => [
+ 'isoName' => 'Volapük',
+ 'nativeName' => 'Volapük',
+ ],
+ 'wa' => [
+ 'isoName' => 'Walloon',
+ 'nativeName' => 'Walon',
+ ],
+ 'cy' => [
+ 'isoName' => 'Welsh',
+ 'nativeName' => 'Cymraeg',
+ ],
+ 'wo' => [
+ 'isoName' => 'Wolof',
+ 'nativeName' => 'Wollof',
+ ],
+ 'fy' => [
+ 'isoName' => 'Western Frisian',
+ 'nativeName' => 'Frysk',
+ ],
+ 'xh' => [
+ 'isoName' => 'Xhosa',
+ 'nativeName' => 'isiXhosa',
+ ],
+ 'yi' => [
+ 'isoName' => 'Yiddish',
+ 'nativeName' => 'ייִדיש',
+ ],
+ 'yo' => [
+ 'isoName' => 'Yoruba',
+ 'nativeName' => 'Yorùbá',
+ ],
+ 'za' => [
+ 'isoName' => 'Zhuang, Chuang',
+ 'nativeName' => 'Saɯ cueŋƅ, Saw cuengh',
+ ],
+ 'zu' => [
+ 'isoName' => 'Zulu',
+ 'nativeName' => 'isiZulu',
+ ],
+ /*
+ * Add ISO 639-3 languages available in Carbon
+ */
+ 'agq' => [
+ 'isoName' => 'Aghem',
+ 'nativeName' => 'Aghem',
+ ],
+ 'agr' => [
+ 'isoName' => 'Aguaruna',
+ 'nativeName' => 'Aguaruna',
+ ],
+ 'anp' => [
+ 'isoName' => 'Angika',
+ 'nativeName' => 'Angika',
+ ],
+ 'asa' => [
+ 'isoName' => 'Asu',
+ 'nativeName' => 'Asu',
+ ],
+ 'ast' => [
+ 'isoName' => 'Asturian',
+ 'nativeName' => 'Asturian',
+ ],
+ 'ayc' => [
+ 'isoName' => 'Southern Aymara',
+ 'nativeName' => 'Southern Aymara',
+ ],
+ 'bas' => [
+ 'isoName' => 'Basaa',
+ 'nativeName' => 'Basaa',
+ ],
+ 'bem' => [
+ 'isoName' => 'Bemba',
+ 'nativeName' => 'Bemba',
+ ],
+ 'bez' => [
+ 'isoName' => 'Bena',
+ 'nativeName' => 'Bena',
+ ],
+ 'bhb' => [
+ 'isoName' => 'Bhili',
+ 'nativeName' => 'Bhili',
+ ],
+ 'bho' => [
+ 'isoName' => 'Bhojpuri',
+ 'nativeName' => 'Bhojpuri',
+ ],
+ 'brx' => [
+ 'isoName' => 'Bodo',
+ 'nativeName' => 'Bodo',
+ ],
+ 'byn' => [
+ 'isoName' => 'Bilin',
+ 'nativeName' => 'Bilin',
+ ],
+ 'ccp' => [
+ 'isoName' => 'Chakma',
+ 'nativeName' => 'Chakma',
+ ],
+ 'cgg' => [
+ 'isoName' => 'Chiga',
+ 'nativeName' => 'Chiga',
+ ],
+ 'chr' => [
+ 'isoName' => 'Cherokee',
+ 'nativeName' => 'Cherokee',
+ ],
+ 'cmn' => [
+ 'isoName' => 'Chinese',
+ 'nativeName' => 'Chinese',
+ ],
+ 'crh' => [
+ 'isoName' => 'Crimean Turkish',
+ 'nativeName' => 'Crimean Turkish',
+ ],
+ 'csb' => [
+ 'isoName' => 'Kashubian',
+ 'nativeName' => 'Kashubian',
+ ],
+ 'dav' => [
+ 'isoName' => 'Taita',
+ 'nativeName' => 'Taita',
+ ],
+ 'dje' => [
+ 'isoName' => 'Zarma',
+ 'nativeName' => 'Zarma',
+ ],
+ 'doi' => [
+ 'isoName' => 'Dogri (macrolanguage)',
+ 'nativeName' => 'Dogri (macrolanguage)',
+ ],
+ 'dsb' => [
+ 'isoName' => 'Lower Sorbian',
+ 'nativeName' => 'Lower Sorbian',
+ ],
+ 'dua' => [
+ 'isoName' => 'Duala',
+ 'nativeName' => 'Duala',
+ ],
+ 'dyo' => [
+ 'isoName' => 'Jola-Fonyi',
+ 'nativeName' => 'Jola-Fonyi',
+ ],
+ 'ebu' => [
+ 'isoName' => 'Embu',
+ 'nativeName' => 'Embu',
+ ],
+ 'ewo' => [
+ 'isoName' => 'Ewondo',
+ 'nativeName' => 'Ewondo',
+ ],
+ 'fil' => [
+ 'isoName' => 'Filipino',
+ 'nativeName' => 'Filipino',
+ ],
+ 'fur' => [
+ 'isoName' => 'Friulian',
+ 'nativeName' => 'Friulian',
+ ],
+ 'gez' => [
+ 'isoName' => 'Geez',
+ 'nativeName' => 'Geez',
+ ],
+ 'gom' => [
+ 'isoName' => 'Konkani, Goan',
+ 'nativeName' => 'ಕೊಂಕಣಿ',
+ ],
+ 'gsw' => [
+ 'isoName' => 'Swiss German',
+ 'nativeName' => 'Swiss German',
+ ],
+ 'guz' => [
+ 'isoName' => 'Gusii',
+ 'nativeName' => 'Gusii',
+ ],
+ 'hak' => [
+ 'isoName' => 'Hakka Chinese',
+ 'nativeName' => 'Hakka Chinese',
+ ],
+ 'haw' => [
+ 'isoName' => 'Hawaiian',
+ 'nativeName' => 'Hawaiian',
+ ],
+ 'hif' => [
+ 'isoName' => 'Fiji Hindi',
+ 'nativeName' => 'Fiji Hindi',
+ ],
+ 'hne' => [
+ 'isoName' => 'Chhattisgarhi',
+ 'nativeName' => 'Chhattisgarhi',
+ ],
+ 'hsb' => [
+ 'isoName' => 'Upper Sorbian',
+ 'nativeName' => 'Upper Sorbian',
+ ],
+ 'jgo' => [
+ 'isoName' => 'Ngomba',
+ 'nativeName' => 'Ngomba',
+ ],
+ 'jmc' => [
+ 'isoName' => 'Machame',
+ 'nativeName' => 'Machame',
+ ],
+ 'kab' => [
+ 'isoName' => 'Kabyle',
+ 'nativeName' => 'Kabyle',
+ ],
+ 'kam' => [
+ 'isoName' => 'Kamba',
+ 'nativeName' => 'Kamba',
+ ],
+ 'kde' => [
+ 'isoName' => 'Makonde',
+ 'nativeName' => 'Makonde',
+ ],
+ 'kea' => [
+ 'isoName' => 'Kabuverdianu',
+ 'nativeName' => 'Kabuverdianu',
+ ],
+ 'khq' => [
+ 'isoName' => 'Koyra Chiini',
+ 'nativeName' => 'Koyra Chiini',
+ ],
+ 'kkj' => [
+ 'isoName' => 'Kako',
+ 'nativeName' => 'Kako',
+ ],
+ 'kln' => [
+ 'isoName' => 'Kalenjin',
+ 'nativeName' => 'Kalenjin',
+ ],
+ 'kok' => [
+ 'isoName' => 'Konkani',
+ 'nativeName' => 'Konkani',
+ ],
+ 'ksb' => [
+ 'isoName' => 'Shambala',
+ 'nativeName' => 'Shambala',
+ ],
+ 'ksf' => [
+ 'isoName' => 'Bafia',
+ 'nativeName' => 'Bafia',
+ ],
+ 'ksh' => [
+ 'isoName' => 'Colognian',
+ 'nativeName' => 'Colognian',
+ ],
+ 'lag' => [
+ 'isoName' => 'Langi',
+ 'nativeName' => 'Langi',
+ ],
+ 'lij' => [
+ 'isoName' => 'Ligurian',
+ 'nativeName' => 'Ligurian',
+ ],
+ 'lkt' => [
+ 'isoName' => 'Lakota',
+ 'nativeName' => 'Lakota',
+ ],
+ 'lrc' => [
+ 'isoName' => 'Northern Luri',
+ 'nativeName' => 'Northern Luri',
+ ],
+ 'luo' => [
+ 'isoName' => 'Luo',
+ 'nativeName' => 'Luo',
+ ],
+ 'luy' => [
+ 'isoName' => 'Luyia',
+ 'nativeName' => 'Luyia',
+ ],
+ 'lzh' => [
+ 'isoName' => 'Literary Chinese',
+ 'nativeName' => 'Literary Chinese',
+ ],
+ 'mag' => [
+ 'isoName' => 'Magahi',
+ 'nativeName' => 'Magahi',
+ ],
+ 'mai' => [
+ 'isoName' => 'Maithili',
+ 'nativeName' => 'Maithili',
+ ],
+ 'mas' => [
+ 'isoName' => 'Masai',
+ 'nativeName' => 'Masai',
+ ],
+ 'mer' => [
+ 'isoName' => 'Meru',
+ 'nativeName' => 'Meru',
+ ],
+ 'mfe' => [
+ 'isoName' => 'Morisyen',
+ 'nativeName' => 'Morisyen',
+ ],
+ 'mgh' => [
+ 'isoName' => 'Makhuwa-Meetto',
+ 'nativeName' => 'Makhuwa-Meetto',
+ ],
+ 'mgo' => [
+ 'isoName' => 'Metaʼ',
+ 'nativeName' => 'Metaʼ',
+ ],
+ 'mhr' => [
+ 'isoName' => 'Eastern Mari',
+ 'nativeName' => 'Eastern Mari',
+ ],
+ 'miq' => [
+ 'isoName' => 'Mískito',
+ 'nativeName' => 'Mískito',
+ ],
+ 'mjw' => [
+ 'isoName' => 'Karbi',
+ 'nativeName' => 'Karbi',
+ ],
+ 'mni' => [
+ 'isoName' => 'Manipuri',
+ 'nativeName' => 'Manipuri',
+ ],
+ 'mua' => [
+ 'isoName' => 'Mundang',
+ 'nativeName' => 'Mundang',
+ ],
+ 'mzn' => [
+ 'isoName' => 'Mazanderani',
+ 'nativeName' => 'Mazanderani',
+ ],
+ 'nan' => [
+ 'isoName' => 'Min Nan Chinese',
+ 'nativeName' => 'Min Nan Chinese',
+ ],
+ 'naq' => [
+ 'isoName' => 'Nama',
+ 'nativeName' => 'Nama',
+ ],
+ 'nds' => [
+ 'isoName' => 'Low German',
+ 'nativeName' => 'Low German',
+ ],
+ 'nhn' => [
+ 'isoName' => 'Central Nahuatl',
+ 'nativeName' => 'Central Nahuatl',
+ ],
+ 'niu' => [
+ 'isoName' => 'Niuean',
+ 'nativeName' => 'Niuean',
+ ],
+ 'nmg' => [
+ 'isoName' => 'Kwasio',
+ 'nativeName' => 'Kwasio',
+ ],
+ 'nnh' => [
+ 'isoName' => 'Ngiemboon',
+ 'nativeName' => 'Ngiemboon',
+ ],
+ 'nso' => [
+ 'isoName' => 'Northern Sotho',
+ 'nativeName' => 'Northern Sotho',
+ ],
+ 'nus' => [
+ 'isoName' => 'Nuer',
+ 'nativeName' => 'Nuer',
+ ],
+ 'nyn' => [
+ 'isoName' => 'Nyankole',
+ 'nativeName' => 'Nyankole',
+ ],
+ 'pap' => [
+ 'isoName' => 'Papiamento',
+ 'nativeName' => 'Papiamento',
+ ],
+ 'prg' => [
+ 'isoName' => 'Prussian',
+ 'nativeName' => 'Prussian',
+ ],
+ 'quz' => [
+ 'isoName' => 'Cusco Quechua',
+ 'nativeName' => 'Cusco Quechua',
+ ],
+ 'raj' => [
+ 'isoName' => 'Rajasthani',
+ 'nativeName' => 'Rajasthani',
+ ],
+ 'rof' => [
+ 'isoName' => 'Rombo',
+ 'nativeName' => 'Rombo',
+ ],
+ 'rwk' => [
+ 'isoName' => 'Rwa',
+ 'nativeName' => 'Rwa',
+ ],
+ 'sah' => [
+ 'isoName' => 'Sakha',
+ 'nativeName' => 'Sakha',
+ ],
+ 'saq' => [
+ 'isoName' => 'Samburu',
+ 'nativeName' => 'Samburu',
+ ],
+ 'sat' => [
+ 'isoName' => 'Santali',
+ 'nativeName' => 'Santali',
+ ],
+ 'sbp' => [
+ 'isoName' => 'Sangu',
+ 'nativeName' => 'Sangu',
+ ],
+ 'scr' => [
+ 'isoName' => 'Serbo Croatian',
+ 'nativeName' => 'Serbo Croatian',
+ ],
+ 'seh' => [
+ 'isoName' => 'Sena',
+ 'nativeName' => 'Sena',
+ ],
+ 'ses' => [
+ 'isoName' => 'Koyraboro Senni',
+ 'nativeName' => 'Koyraboro Senni',
+ ],
+ 'sgs' => [
+ 'isoName' => 'Samogitian',
+ 'nativeName' => 'Samogitian',
+ ],
+ 'shi' => [
+ 'isoName' => 'Tachelhit',
+ 'nativeName' => 'Tachelhit',
+ ],
+ 'shn' => [
+ 'isoName' => 'Shan',
+ 'nativeName' => 'Shan',
+ ],
+ 'shs' => [
+ 'isoName' => 'Shuswap',
+ 'nativeName' => 'Shuswap',
+ ],
+ 'sid' => [
+ 'isoName' => 'Sidamo',
+ 'nativeName' => 'Sidamo',
+ ],
+ 'smn' => [
+ 'isoName' => 'Inari Sami',
+ 'nativeName' => 'Inari Sami',
+ ],
+ 'szl' => [
+ 'isoName' => 'Silesian',
+ 'nativeName' => 'Silesian',
+ ],
+ 'tcy' => [
+ 'isoName' => 'Tulu',
+ 'nativeName' => 'Tulu',
+ ],
+ 'teo' => [
+ 'isoName' => 'Teso',
+ 'nativeName' => 'Teso',
+ ],
+ 'tet' => [
+ 'isoName' => 'Tetum',
+ 'nativeName' => 'Tetum',
+ ],
+ 'the' => [
+ 'isoName' => 'Chitwania Tharu',
+ 'nativeName' => 'Chitwania Tharu',
+ ],
+ 'tig' => [
+ 'isoName' => 'Tigre',
+ 'nativeName' => 'Tigre',
+ ],
+ 'tlh' => [
+ 'isoName' => 'Klingon',
+ 'nativeName' => 'tlhIngan Hol',
+ ],
+ 'tpi' => [
+ 'isoName' => 'Tok Pisin',
+ 'nativeName' => 'Tok Pisin',
+ ],
+ 'twq' => [
+ 'isoName' => 'Tasawaq',
+ 'nativeName' => 'Tasawaq',
+ ],
+ 'tzl' => [
+ 'isoName' => 'Talossan',
+ 'nativeName' => 'Talossan',
+ ],
+ 'tzm' => [
+ 'isoName' => 'Tamazight, Central Atlas',
+ 'nativeName' => 'ⵜⵎⴰⵣⵉⵖⵜ',
+ ],
+ 'unm' => [
+ 'isoName' => 'Unami',
+ 'nativeName' => 'Unami',
+ ],
+ 'vai' => [
+ 'isoName' => 'Vai',
+ 'nativeName' => 'Vai',
+ ],
+ 'vun' => [
+ 'isoName' => 'Vunjo',
+ 'nativeName' => 'Vunjo',
+ ],
+ 'wae' => [
+ 'isoName' => 'Walser',
+ 'nativeName' => 'Walser',
+ ],
+ 'wal' => [
+ 'isoName' => 'Wolaytta',
+ 'nativeName' => 'Wolaytta',
+ ],
+ 'xog' => [
+ 'isoName' => 'Soga',
+ 'nativeName' => 'Soga',
+ ],
+ 'yav' => [
+ 'isoName' => 'Yangben',
+ 'nativeName' => 'Yangben',
+ ],
+ 'yue' => [
+ 'isoName' => 'Cantonese',
+ 'nativeName' => 'Cantonese',
+ ],
+ 'yuw' => [
+ 'isoName' => 'Yau (Morobe Province)',
+ 'nativeName' => 'Yau (Morobe Province)',
+ ],
+ 'zgh' => [
+ 'isoName' => 'Standard Moroccan Tamazight',
+ 'nativeName' => 'Standard Moroccan Tamazight',
+ ],
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/List/regions.php b/vendor/nesbot/carbon/src/Carbon/List/regions.php
new file mode 100644
index 0000000..8ab8a9e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/List/regions.php
@@ -0,0 +1,265 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * ISO 3166-2
+ */
+return [
+ 'AD' => 'Andorra',
+ 'AE' => 'United Arab Emirates',
+ 'AF' => 'Afghanistan',
+ 'AG' => 'Antigua and Barbuda',
+ 'AI' => 'Anguilla',
+ 'AL' => 'Albania',
+ 'AM' => 'Armenia',
+ 'AO' => 'Angola',
+ 'AQ' => 'Antarctica',
+ 'AR' => 'Argentina',
+ 'AS' => 'American Samoa',
+ 'AT' => 'Austria',
+ 'AU' => 'Australia',
+ 'AW' => 'Aruba',
+ 'AX' => 'Åland Islands',
+ 'AZ' => 'Azerbaijan',
+ 'BA' => 'Bosnia and Herzegovina',
+ 'BB' => 'Barbados',
+ 'BD' => 'Bangladesh',
+ 'BE' => 'Belgium',
+ 'BF' => 'Burkina Faso',
+ 'BG' => 'Bulgaria',
+ 'BH' => 'Bahrain',
+ 'BI' => 'Burundi',
+ 'BJ' => 'Benin',
+ 'BL' => 'Saint Barthélemy',
+ 'BM' => 'Bermuda',
+ 'BN' => 'Brunei Darussalam',
+ 'BO' => 'Bolivia (Plurinational State of)',
+ 'BQ' => 'Bonaire, Sint Eustatius and Saba',
+ 'BR' => 'Brazil',
+ 'BS' => 'Bahamas',
+ 'BT' => 'Bhutan',
+ 'BV' => 'Bouvet Island',
+ 'BW' => 'Botswana',
+ 'BY' => 'Belarus',
+ 'BZ' => 'Belize',
+ 'CA' => 'Canada',
+ 'CC' => 'Cocos (Keeling) Islands',
+ 'CD' => 'Congo, Democratic Republic of the',
+ 'CF' => 'Central African Republic',
+ 'CG' => 'Congo',
+ 'CH' => 'Switzerland',
+ 'CI' => 'Côte d\'Ivoire',
+ 'CK' => 'Cook Islands',
+ 'CL' => 'Chile',
+ 'CM' => 'Cameroon',
+ 'CN' => 'China',
+ 'CO' => 'Colombia',
+ 'CR' => 'Costa Rica',
+ 'CU' => 'Cuba',
+ 'CV' => 'Cabo Verde',
+ 'CW' => 'Curaçao',
+ 'CX' => 'Christmas Island',
+ 'CY' => 'Cyprus',
+ 'CZ' => 'Czechia',
+ 'DE' => 'Germany',
+ 'DJ' => 'Djibouti',
+ 'DK' => 'Denmark',
+ 'DM' => 'Dominica',
+ 'DO' => 'Dominican Republic',
+ 'DZ' => 'Algeria',
+ 'EC' => 'Ecuador',
+ 'EE' => 'Estonia',
+ 'EG' => 'Egypt',
+ 'EH' => 'Western Sahara',
+ 'ER' => 'Eritrea',
+ 'ES' => 'Spain',
+ 'ET' => 'Ethiopia',
+ 'FI' => 'Finland',
+ 'FJ' => 'Fiji',
+ 'FK' => 'Falkland Islands (Malvinas)',
+ 'FM' => 'Micronesia (Federated States of)',
+ 'FO' => 'Faroe Islands',
+ 'FR' => 'France',
+ 'GA' => 'Gabon',
+ 'GB' => 'United Kingdom of Great Britain and Northern Ireland',
+ 'GD' => 'Grenada',
+ 'GE' => 'Georgia',
+ 'GF' => 'French Guiana',
+ 'GG' => 'Guernsey',
+ 'GH' => 'Ghana',
+ 'GI' => 'Gibraltar',
+ 'GL' => 'Greenland',
+ 'GM' => 'Gambia',
+ 'GN' => 'Guinea',
+ 'GP' => 'Guadeloupe',
+ 'GQ' => 'Equatorial Guinea',
+ 'GR' => 'Greece',
+ 'GS' => 'South Georgia and the South Sandwich Islands',
+ 'GT' => 'Guatemala',
+ 'GU' => 'Guam',
+ 'GW' => 'Guinea-Bissau',
+ 'GY' => 'Guyana',
+ 'HK' => 'Hong Kong',
+ 'HM' => 'Heard Island and McDonald Islands',
+ 'HN' => 'Honduras',
+ 'HR' => 'Croatia',
+ 'HT' => 'Haiti',
+ 'HU' => 'Hungary',
+ 'ID' => 'Indonesia',
+ 'IE' => 'Ireland',
+ 'IL' => 'Israel',
+ 'IM' => 'Isle of Man',
+ 'IN' => 'India',
+ 'IO' => 'British Indian Ocean Territory',
+ 'IQ' => 'Iraq',
+ 'IR' => 'Iran (Islamic Republic of)',
+ 'IS' => 'Iceland',
+ 'IT' => 'Italy',
+ 'JE' => 'Jersey',
+ 'JM' => 'Jamaica',
+ 'JO' => 'Jordan',
+ 'JP' => 'Japan',
+ 'KE' => 'Kenya',
+ 'KG' => 'Kyrgyzstan',
+ 'KH' => 'Cambodia',
+ 'KI' => 'Kiribati',
+ 'KM' => 'Comoros',
+ 'KN' => 'Saint Kitts and Nevis',
+ 'KP' => 'Korea (Democratic People\'s Republic of)',
+ 'KR' => 'Korea, Republic of',
+ 'KW' => 'Kuwait',
+ 'KY' => 'Cayman Islands',
+ 'KZ' => 'Kazakhstan',
+ 'LA' => 'Lao People\'s Democratic Republic',
+ 'LB' => 'Lebanon',
+ 'LC' => 'Saint Lucia',
+ 'LI' => 'Liechtenstein',
+ 'LK' => 'Sri Lanka',
+ 'LR' => 'Liberia',
+ 'LS' => 'Lesotho',
+ 'LT' => 'Lithuania',
+ 'LU' => 'Luxembourg',
+ 'LV' => 'Latvia',
+ 'LY' => 'Libya',
+ 'MA' => 'Morocco',
+ 'MC' => 'Monaco',
+ 'MD' => 'Moldova, Republic of',
+ 'ME' => 'Montenegro',
+ 'MF' => 'Saint Martin (French part)',
+ 'MG' => 'Madagascar',
+ 'MH' => 'Marshall Islands',
+ 'MK' => 'Macedonia, the former Yugoslav Republic of',
+ 'ML' => 'Mali',
+ 'MM' => 'Myanmar',
+ 'MN' => 'Mongolia',
+ 'MO' => 'Macao',
+ 'MP' => 'Northern Mariana Islands',
+ 'MQ' => 'Martinique',
+ 'MR' => 'Mauritania',
+ 'MS' => 'Montserrat',
+ 'MT' => 'Malta',
+ 'MU' => 'Mauritius',
+ 'MV' => 'Maldives',
+ 'MW' => 'Malawi',
+ 'MX' => 'Mexico',
+ 'MY' => 'Malaysia',
+ 'MZ' => 'Mozambique',
+ 'NA' => 'Namibia',
+ 'NC' => 'New Caledonia',
+ 'NE' => 'Niger',
+ 'NF' => 'Norfolk Island',
+ 'NG' => 'Nigeria',
+ 'NI' => 'Nicaragua',
+ 'NL' => 'Netherlands',
+ 'NO' => 'Norway',
+ 'NP' => 'Nepal',
+ 'NR' => 'Nauru',
+ 'NU' => 'Niue',
+ 'NZ' => 'New Zealand',
+ 'OM' => 'Oman',
+ 'PA' => 'Panama',
+ 'PE' => 'Peru',
+ 'PF' => 'French Polynesia',
+ 'PG' => 'Papua New Guinea',
+ 'PH' => 'Philippines',
+ 'PK' => 'Pakistan',
+ 'PL' => 'Poland',
+ 'PM' => 'Saint Pierre and Miquelon',
+ 'PN' => 'Pitcairn',
+ 'PR' => 'Puerto Rico',
+ 'PS' => 'Palestine, State of',
+ 'PT' => 'Portugal',
+ 'PW' => 'Palau',
+ 'PY' => 'Paraguay',
+ 'QA' => 'Qatar',
+ 'RE' => 'Réunion',
+ 'RO' => 'Romania',
+ 'RS' => 'Serbia',
+ 'RU' => 'Russian Federation',
+ 'RW' => 'Rwanda',
+ 'SA' => 'Saudi Arabia',
+ 'SB' => 'Solomon Islands',
+ 'SC' => 'Seychelles',
+ 'SD' => 'Sudan',
+ 'SE' => 'Sweden',
+ 'SG' => 'Singapore',
+ 'SH' => 'Saint Helena, Ascension and Tristan da Cunha',
+ 'SI' => 'Slovenia',
+ 'SJ' => 'Svalbard and Jan Mayen',
+ 'SK' => 'Slovakia',
+ 'SL' => 'Sierra Leone',
+ 'SM' => 'San Marino',
+ 'SN' => 'Senegal',
+ 'SO' => 'Somalia',
+ 'SR' => 'Suriname',
+ 'SS' => 'South Sudan',
+ 'ST' => 'Sao Tome and Principe',
+ 'SV' => 'El Salvador',
+ 'SX' => 'Sint Maarten (Dutch part)',
+ 'SY' => 'Syrian Arab Republic',
+ 'SZ' => 'Eswatini',
+ 'TC' => 'Turks and Caicos Islands',
+ 'TD' => 'Chad',
+ 'TF' => 'French Southern Territories',
+ 'TG' => 'Togo',
+ 'TH' => 'Thailand',
+ 'TJ' => 'Tajikistan',
+ 'TK' => 'Tokelau',
+ 'TL' => 'Timor-Leste',
+ 'TM' => 'Turkmenistan',
+ 'TN' => 'Tunisia',
+ 'TO' => 'Tonga',
+ 'TR' => 'Turkey',
+ 'TT' => 'Trinidad and Tobago',
+ 'TV' => 'Tuvalu',
+ 'TW' => 'Taiwan, Province of China',
+ 'TZ' => 'Tanzania, United Republic of',
+ 'UA' => 'Ukraine',
+ 'UG' => 'Uganda',
+ 'UM' => 'United States Minor Outlying Islands',
+ 'US' => 'United States of America',
+ 'UY' => 'Uruguay',
+ 'UZ' => 'Uzbekistan',
+ 'VA' => 'Holy See',
+ 'VC' => 'Saint Vincent and the Grenadines',
+ 'VE' => 'Venezuela (Bolivarian Republic of)',
+ 'VG' => 'Virgin Islands (British)',
+ 'VI' => 'Virgin Islands (U.S.)',
+ 'VN' => 'Viet Nam',
+ 'VU' => 'Vanuatu',
+ 'WF' => 'Wallis and Futuna',
+ 'WS' => 'Samoa',
+ 'YE' => 'Yemen',
+ 'YT' => 'Mayotte',
+ 'ZA' => 'South Africa',
+ 'ZM' => 'Zambia',
+ 'ZW' => 'Zimbabwe',
+];
diff --git a/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php b/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php
new file mode 100644
index 0000000..c054808
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\MessageFormatter;
+
+use ReflectionMethod;
+use Symfony\Component\Translation\Formatter\MessageFormatter;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+
+// @codeCoverageIgnoreStart
+$transMethod = new ReflectionMethod(MessageFormatterInterface::class, 'format');
+
+require $transMethod->getParameters()[0]->hasType()
+ ? __DIR__.'/../../../lazy/Carbon/MessageFormatter/MessageFormatterMapperStrongType.php'
+ : __DIR__.'/../../../lazy/Carbon/MessageFormatter/MessageFormatterMapperWeakType.php';
+// @codeCoverageIgnoreEnd
+
+final class MessageFormatterMapper extends LazyMessageFormatter
+{
+ /**
+ * Wrapped formatter.
+ *
+ * @var MessageFormatterInterface
+ */
+ protected $formatter;
+
+ public function __construct(?MessageFormatterInterface $formatter = null)
+ {
+ $this->formatter = $formatter ?? new MessageFormatter();
+ }
+
+ protected function transformLocale(?string $locale): ?string
+ {
+ return $locale ? preg_replace('/[_@][A-Za-z][a-z]{2,}/', '', $locale) : $locale;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php
new file mode 100644
index 0000000..5123d1e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/AbstractMacro.php
@@ -0,0 +1,288 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+use Closure;
+use InvalidArgumentException;
+use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter as AdapterReflectionParameter;
+use PHPStan\BetterReflection\Reflection\Adapter\ReflectionType as AdapterReflectionType;
+use PHPStan\BetterReflection\Reflection\ReflectionClass as BetterReflectionClass;
+use PHPStan\BetterReflection\Reflection\ReflectionFunction as BetterReflectionFunction;
+use PHPStan\BetterReflection\Reflection\ReflectionParameter as BetterReflectionParameter;
+use PHPStan\Reflection\Php\BuiltinMethodReflection;
+use PHPStan\TrinaryLogic;
+use ReflectionClass;
+use ReflectionFunction;
+use ReflectionMethod;
+use ReflectionParameter;
+use ReflectionType;
+use stdClass;
+use Throwable;
+
+abstract class AbstractMacro implements BuiltinMethodReflection
+{
+ /**
+ * The reflection function/method.
+ *
+ * @var ReflectionFunction|ReflectionMethod
+ */
+ protected $reflectionFunction;
+
+ /**
+ * The class name.
+ *
+ * @var class-string
+ */
+ private $className;
+
+ /**
+ * The method name.
+ *
+ * @var string
+ */
+ private $methodName;
+
+ /**
+ * The parameters.
+ *
+ * @var ReflectionParameter[]
+ */
+ private $parameters;
+
+ /**
+ * The is static.
+ *
+ * @var bool
+ */
+ private $static = false;
+
+ /**
+ * Macro constructor.
+ *
+ * @param string $className
+ * @phpstan-param class-string $className
+ *
+ * @param string $methodName
+ * @param callable $macro
+ */
+ public function __construct(string $className, string $methodName, $macro)
+ {
+ $this->className = $className;
+ $this->methodName = $methodName;
+ $rawReflectionFunction = \is_array($macro)
+ ? new ReflectionMethod($macro[0], $macro[1])
+ : new ReflectionFunction($macro);
+ $this->reflectionFunction = self::hasModernParser()
+ ? $this->getReflectionFunction($macro)
+ : $rawReflectionFunction; // @codeCoverageIgnore
+ $this->parameters = array_map(
+ function ($parameter) {
+ if ($parameter instanceof BetterReflectionParameter) {
+ return new AdapterReflectionParameter($parameter);
+ }
+
+ return $parameter; // @codeCoverageIgnore
+ },
+ $this->reflectionFunction->getParameters()
+ );
+
+ if ($rawReflectionFunction->isClosure()) {
+ try {
+ $closure = $rawReflectionFunction->getClosure();
+ $boundClosure = Closure::bind($closure, new stdClass());
+ $this->static = (!$boundClosure || (new ReflectionFunction($boundClosure))->getClosureThis() === null);
+ } catch (Throwable $e) {
+ $this->static = true;
+ }
+ }
+ }
+
+ private function getReflectionFunction($spec)
+ {
+ if (\is_array($spec) && \count($spec) === 2 && \is_string($spec[1])) {
+ \assert($spec[1] !== '');
+
+ if (\is_object($spec[0])) {
+ return BetterReflectionClass::createFromInstance($spec[0])
+ ->getMethod($spec[1]);
+ }
+
+ return BetterReflectionClass::createFromName($spec[0])
+ ->getMethod($spec[1]);
+ }
+
+ if (\is_string($spec)) {
+ return BetterReflectionFunction::createFromName($spec);
+ }
+
+ if ($spec instanceof Closure) {
+ return BetterReflectionFunction::createFromClosure($spec);
+ }
+
+ throw new InvalidArgumentException('Could not create reflection from the spec given'); // @codeCoverageIgnore
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDeclaringClass(): ReflectionClass
+ {
+ return new ReflectionClass($this->className);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isPrivate(): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isPublic(): bool
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isFinal(): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isInternal(): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isAbstract(): bool
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStatic(): bool
+ {
+ return $this->static;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDocComment(): ?string
+ {
+ return $this->reflectionFunction->getDocComment() ?: null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName(): string
+ {
+ return $this->methodName;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getParameters(): array
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getReturnType(): ?ReflectionType
+ {
+ $type = $this->reflectionFunction->getReturnType();
+
+ if ($type instanceof ReflectionType) {
+ return $type; // @codeCoverageIgnore
+ }
+
+ return self::adaptType($type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isDeprecated(): TrinaryLogic
+ {
+ return TrinaryLogic::createFromBoolean(
+ $this->reflectionFunction->isDeprecated() ||
+ preg_match('/@deprecated/i', $this->getDocComment() ?: '')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isVariadic(): bool
+ {
+ return $this->reflectionFunction->isVariadic();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPrototype(): BuiltinMethodReflection
+ {
+ return $this;
+ }
+
+ public function getTentativeReturnType(): ?ReflectionType
+ {
+ return null;
+ }
+
+ public function returnsByReference(): TrinaryLogic
+ {
+ return TrinaryLogic::createNo();
+ }
+
+ private static function adaptType($type)
+ {
+ $method = method_exists(AdapterReflectionType::class, 'fromTypeOrNull')
+ ? 'fromTypeOrNull'
+ : 'fromReturnTypeOrNull'; // @codeCoverageIgnore
+
+ return AdapterReflectionType::$method($type);
+ }
+
+ private static function hasModernParser(): bool
+ {
+ static $modernParser = null;
+
+ if ($modernParser !== null) {
+ return $modernParser;
+ }
+
+ $modernParser = method_exists(AdapterReflectionType::class, 'fromTypeOrNull');
+
+ return $modernParser;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php
new file mode 100644
index 0000000..de3e51f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/Macro.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+use PHPStan\BetterReflection\Reflection\Adapter;
+use PHPStan\Reflection\Php\BuiltinMethodReflection;
+use ReflectionMethod;
+
+$method = new ReflectionMethod(BuiltinMethodReflection::class, 'getReflection');
+
+require $method->hasReturnType() && $method->getReturnType()->getName() === Adapter\ReflectionMethod::class
+ ? __DIR__.'/../../../lazy/Carbon/PHPStan/AbstractMacroStatic.php'
+ : __DIR__.'/../../../lazy/Carbon/PHPStan/AbstractMacroBuiltin.php';
+
+$method = new ReflectionMethod(BuiltinMethodReflection::class, 'getFileName');
+
+require $method->hasReturnType()
+ ? __DIR__.'/../../../lazy/Carbon/PHPStan/MacroStrongType.php'
+ : __DIR__.'/../../../lazy/Carbon/PHPStan/MacroWeakType.php';
+
+final class Macro extends LazyMacro
+{
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php
new file mode 100644
index 0000000..2cd6fce
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+use PHPStan\Reflection\Assertions;
+use PHPStan\Reflection\ClassReflection;
+use PHPStan\Reflection\MethodReflection;
+use PHPStan\Reflection\MethodsClassReflectionExtension;
+use PHPStan\Reflection\Php\PhpMethodReflectionFactory;
+use PHPStan\Reflection\ReflectionProvider;
+use PHPStan\Type\TypehintHelper;
+
+/**
+ * Class MacroExtension.
+ *
+ * @codeCoverageIgnore Pure PHPStan wrapper.
+ */
+final class MacroExtension implements MethodsClassReflectionExtension
+{
+ /**
+ * @var PhpMethodReflectionFactory
+ */
+ protected $methodReflectionFactory;
+
+ /**
+ * @var MacroScanner
+ */
+ protected $scanner;
+
+ /**
+ * Extension constructor.
+ *
+ * @param PhpMethodReflectionFactory $methodReflectionFactory
+ * @param ReflectionProvider $reflectionProvider
+ */
+ public function __construct(
+ PhpMethodReflectionFactory $methodReflectionFactory,
+ ReflectionProvider $reflectionProvider
+ ) {
+ $this->scanner = new MacroScanner($reflectionProvider);
+ $this->methodReflectionFactory = $methodReflectionFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasMethod(ClassReflection $classReflection, string $methodName): bool
+ {
+ return $this->scanner->hasMethod($classReflection->getName(), $methodName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
+ {
+ $builtinMacro = $this->scanner->getMethod($classReflection->getName(), $methodName);
+ $supportAssertions = class_exists(Assertions::class);
+
+ return $this->methodReflectionFactory->create(
+ $classReflection,
+ null,
+ $builtinMacro,
+ $classReflection->getActiveTemplateTypeMap(),
+ [],
+ TypehintHelper::decideTypeFromReflection($builtinMacro->getReturnType()),
+ null,
+ null,
+ $builtinMacro->isDeprecated()->yes(),
+ $builtinMacro->isInternal(),
+ $builtinMacro->isFinal(),
+ $supportAssertions ? null : $builtinMacro->getDocComment(),
+ $supportAssertions ? Assertions::createEmpty() : null,
+ null,
+ $builtinMacro->getDocComment(),
+ []
+ );
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php
new file mode 100644
index 0000000..c88e49e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/PHPStan/MacroScanner.php
@@ -0,0 +1,87 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\PHPStan;
+
+use Carbon\CarbonInterface;
+use PHPStan\Reflection\ReflectionProvider;
+use ReflectionClass;
+use ReflectionException;
+
+final class MacroScanner
+{
+ /**
+ * @var \PHPStan\Reflection\ReflectionProvider
+ */
+ private $reflectionProvider;
+
+ /**
+ * MacroScanner constructor.
+ *
+ * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider
+ */
+ public function __construct(ReflectionProvider $reflectionProvider)
+ {
+ $this->reflectionProvider = $reflectionProvider;
+ }
+
+ /**
+ * Return true if the given pair class-method is a Carbon macro.
+ *
+ * @param string $className
+ * @phpstan-param class-string $className
+ *
+ * @param string $methodName
+ *
+ * @return bool
+ */
+ public function hasMethod(string $className, string $methodName): bool
+ {
+ $classReflection = $this->reflectionProvider->getClass($className);
+
+ if (
+ $classReflection->getName() !== CarbonInterface::class &&
+ !$classReflection->isSubclassOf(CarbonInterface::class)
+ ) {
+ return false;
+ }
+
+ return \is_callable([$className, 'hasMacro']) &&
+ $className::hasMacro($methodName);
+ }
+
+ /**
+ * Return the Macro for a given pair class-method.
+ *
+ * @param string $className
+ * @phpstan-param class-string $className
+ *
+ * @param string $methodName
+ *
+ * @throws ReflectionException
+ *
+ * @return Macro
+ */
+ public function getMethod(string $className, string $methodName): Macro
+ {
+ $reflectionClass = new ReflectionClass($className);
+ $property = $reflectionClass->getProperty('globalMacros');
+
+ $property->setAccessible(true);
+ $macro = $property->getValue()[$methodName];
+
+ return new Macro(
+ $className,
+ $methodName,
+ $macro
+ );
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php b/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php
new file mode 100644
index 0000000..71bbb72
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Boundaries.php
@@ -0,0 +1,443 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Exceptions\UnknownUnitException;
+
+/**
+ * Trait Boundaries.
+ *
+ * startOf, endOf and derived method for each unit.
+ *
+ * Depends on the following properties:
+ *
+ * @property int $year
+ * @property int $month
+ * @property int $daysInMonth
+ * @property int $quarter
+ *
+ * Depends on the following methods:
+ *
+ * @method $this setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0)
+ * @method $this setDate(int $year, int $month, int $day)
+ * @method $this addMonths(int $value = 1)
+ */
+trait Boundaries
+{
+ /**
+ * Resets the time to 00:00:00 start of day
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfDay();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfDay()
+ {
+ return $this->setTime(0, 0, 0, 0);
+ }
+
+ /**
+ * Resets the time to 23:59:59.999999 end of day
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfDay();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfDay()
+ {
+ return $this->setTime(static::HOURS_PER_DAY - 1, static::MINUTES_PER_HOUR - 1, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1);
+ }
+
+ /**
+ * Resets the date to the first day of the month and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfMonth();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfMonth()
+ {
+ return $this->setDate($this->year, $this->month, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the month and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfMonth();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfMonth()
+ {
+ return $this->setDate($this->year, $this->month, $this->daysInMonth)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the quarter and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfQuarter();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfQuarter()
+ {
+ $month = ($this->quarter - 1) * static::MONTHS_PER_QUARTER + 1;
+
+ return $this->setDate($this->year, $month, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the quarter and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfQuarter();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfQuarter()
+ {
+ return $this->startOfQuarter()->addMonths(static::MONTHS_PER_QUARTER - 1)->endOfMonth();
+ }
+
+ /**
+ * Resets the date to the first day of the year and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfYear();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfYear()
+ {
+ return $this->setDate($this->year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the year and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfYear();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfYear()
+ {
+ return $this->setDate($this->year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the decade and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfDecade();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfDecade()
+ {
+ $year = $this->year - $this->year % static::YEARS_PER_DECADE;
+
+ return $this->setDate($year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the decade and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfDecade();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfDecade()
+ {
+ $year = $this->year - $this->year % static::YEARS_PER_DECADE + static::YEARS_PER_DECADE - 1;
+
+ return $this->setDate($year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the century and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfCentury();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfCentury()
+ {
+ $year = $this->year - ($this->year - 1) % static::YEARS_PER_CENTURY;
+
+ return $this->setDate($year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the century and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfCentury();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfCentury()
+ {
+ $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_CENTURY + static::YEARS_PER_CENTURY;
+
+ return $this->setDate($year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of the millennium and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfMillennium();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfMillennium()
+ {
+ $year = $this->year - ($this->year - 1) % static::YEARS_PER_MILLENNIUM;
+
+ return $this->setDate($year, 1, 1)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of the millennium and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfMillennium();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfMillennium()
+ {
+ $year = $this->year - 1 - ($this->year - 1) % static::YEARS_PER_MILLENNIUM + static::YEARS_PER_MILLENNIUM;
+
+ return $this->setDate($year, 12, 31)->endOfDay();
+ }
+
+ /**
+ * Resets the date to the first day of week (defined in $weekStartsAt) and the time to 00:00:00
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->startOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfWeek(Carbon::SUNDAY) . "\n";
+ * ```
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return static
+ */
+ public function startOfWeek($weekStartsAt = null)
+ {
+ return $this->subDays((7 + $this->dayOfWeek - ($weekStartsAt ?? $this->firstWeekDay)) % 7)->startOfDay();
+ }
+
+ /**
+ * Resets the date to end of week (defined in $weekEndsAt) and time to 23:59:59.999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->locale('ar')->endOfWeek() . "\n";
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfWeek(Carbon::SATURDAY) . "\n";
+ * ```
+ *
+ * @param int $weekEndsAt optional start allow you to specify the day of week to use to end the week
+ *
+ * @return static
+ */
+ public function endOfWeek($weekEndsAt = null)
+ {
+ return $this->addDays((7 - $this->dayOfWeek + ($weekEndsAt ?? $this->lastWeekDay)) % 7)->endOfDay();
+ }
+
+ /**
+ * Modify to start of current hour, minutes and seconds become 0
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfHour();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfHour()
+ {
+ return $this->setTime($this->hour, 0, 0, 0);
+ }
+
+ /**
+ * Modify to end of current hour, minutes and seconds become 59
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfHour();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfHour()
+ {
+ return $this->setTime($this->hour, static::MINUTES_PER_HOUR - 1, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1);
+ }
+
+ /**
+ * Modify to start of current minute, seconds become 0
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->startOfMinute();
+ * ```
+ *
+ * @return static
+ */
+ public function startOfMinute()
+ {
+ return $this->setTime($this->hour, $this->minute, 0, 0);
+ }
+
+ /**
+ * Modify to end of current minute, seconds become 59
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16')->endOfMinute();
+ * ```
+ *
+ * @return static
+ */
+ public function endOfMinute()
+ {
+ return $this->setTime($this->hour, $this->minute, static::SECONDS_PER_MINUTE - 1, static::MICROSECONDS_PER_SECOND - 1);
+ }
+
+ /**
+ * Modify to start of current second, microseconds become 0
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->startOfSecond()
+ * ->format('H:i:s.u');
+ * ```
+ *
+ * @return static
+ */
+ public function startOfSecond()
+ {
+ return $this->setTime($this->hour, $this->minute, $this->second, 0);
+ }
+
+ /**
+ * Modify to end of current second, microseconds become 999999
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->endOfSecond()
+ * ->format('H:i:s.u');
+ * ```
+ *
+ * @return static
+ */
+ public function endOfSecond()
+ {
+ return $this->setTime($this->hour, $this->minute, $this->second, static::MICROSECONDS_PER_SECOND - 1);
+ }
+
+ /**
+ * Modify to start of current given unit.
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->startOf('month')
+ * ->endOf('week', Carbon::FRIDAY);
+ * ```
+ *
+ * @param string $unit
+ * @param array $params
+ *
+ * @return static
+ */
+ public function startOf($unit, ...$params)
+ {
+ $ucfUnit = ucfirst(static::singularUnit($unit));
+ $method = "startOf$ucfUnit";
+ if (!method_exists($this, $method)) {
+ throw new UnknownUnitException($unit);
+ }
+
+ return $this->$method(...$params);
+ }
+
+ /**
+ * Modify to end of current given unit.
+ *
+ * @example
+ * ```
+ * echo Carbon::parse('2018-07-25 12:45:16.334455')
+ * ->startOf('month')
+ * ->endOf('week', Carbon::FRIDAY);
+ * ```
+ *
+ * @param string $unit
+ * @param array $params
+ *
+ * @return static
+ */
+ public function endOf($unit, ...$params)
+ {
+ $ucfUnit = ucfirst(static::singularUnit($unit));
+ $method = "endOf$ucfUnit";
+ if (!method_exists($this, $method)) {
+ throw new UnknownUnitException($unit);
+ }
+
+ return $this->$method(...$params);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php b/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php
new file mode 100644
index 0000000..5f7c7c0
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Cast.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Exceptions\InvalidCastException;
+use DateTimeInterface;
+
+/**
+ * Trait Cast.
+ *
+ * Utils to cast into an other class.
+ */
+trait Cast
+{
+ /**
+ * Cast the current instance into the given class.
+ *
+ * @param string $className The $className::instance() method will be called to cast the current object.
+ *
+ * @return DateTimeInterface
+ */
+ public function cast(string $className)
+ {
+ if (!method_exists($className, 'instance')) {
+ if (is_a($className, DateTimeInterface::class, true)) {
+ return new $className($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone());
+ }
+
+ throw new InvalidCastException("$className has not the instance() method needed to cast the date.");
+ }
+
+ return $className::instance($this);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php b/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php
new file mode 100644
index 0000000..f6261d8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Comparison.php
@@ -0,0 +1,1125 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use BadMethodCallException;
+use Carbon\CarbonInterface;
+use Carbon\Exceptions\BadComparisonUnitException;
+use InvalidArgumentException;
+
+/**
+ * Trait Comparison.
+ *
+ * Comparison utils and testers. All the following methods return booleans.
+ * nowWithSameTz
+ *
+ * Depends on the following methods:
+ *
+ * @method static resolveCarbon($date)
+ * @method static copy()
+ * @method static nowWithSameTz()
+ * @method static static yesterday($timezone = null)
+ * @method static static tomorrow($timezone = null)
+ */
+trait Comparison
+{
+ /** @var bool */
+ protected $endOfTime = false;
+
+ /** @var bool */
+ protected $startOfTime = false;
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->eq(Carbon::parse('2018-07-25 12:45:16')); // true
+ * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see equalTo()
+ *
+ * @return bool
+ */
+ public function eq($date): bool
+ {
+ return $this->equalTo($date);
+ }
+
+ /**
+ * Determines if the instance is equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true
+ * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function equalTo($date): bool
+ {
+ $this->discourageNull($date);
+ $this->discourageBoolean($date);
+
+ return $this == $this->resolveCarbon($date);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->ne(Carbon::parse('2018-07-25 12:45:16')); // false
+ * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see notEqualTo()
+ *
+ * @return bool
+ */
+ public function ne($date): bool
+ {
+ return $this->notEqualTo($date);
+ }
+
+ /**
+ * Determines if the instance is not equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false
+ * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function notEqualTo($date): bool
+ {
+ return !$this->equalTo($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see greaterThan()
+ *
+ * @return bool
+ */
+ public function gt($date): bool
+ {
+ return $this->greaterThan($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function greaterThan($date): bool
+ {
+ $this->discourageNull($date);
+ $this->discourageBoolean($date);
+
+ return $this > $this->resolveCarbon($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see greaterThan()
+ *
+ * @return bool
+ */
+ public function isAfter($date): bool
+ {
+ return $this->greaterThan($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see greaterThanOrEqualTo()
+ *
+ * @return bool
+ */
+ public function gte($date): bool
+ {
+ return $this->greaterThanOrEqualTo($date);
+ }
+
+ /**
+ * Determines if the instance is greater (after) than or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:15'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function greaterThanOrEqualTo($date): bool
+ {
+ $this->discourageNull($date);
+ $this->discourageBoolean($date);
+
+ return $this >= $this->resolveCarbon($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lessThan()
+ *
+ * @return bool
+ */
+ public function lt($date): bool
+ {
+ return $this->lessThan($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function lessThan($date): bool
+ {
+ $this->discourageNull($date);
+ $this->discourageBoolean($date);
+
+ return $this < $this->resolveCarbon($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) than another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:16'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lessThan()
+ *
+ * @return bool
+ */
+ public function isBefore($date): bool
+ {
+ return $this->lessThan($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see lessThanOrEqualTo()
+ *
+ * @return bool
+ */
+ public function lte($date): bool
+ {
+ return $this->lessThanOrEqualTo($date);
+ }
+
+ /**
+ * Determines if the instance is less (before) or equal to another
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:15'); // false
+ * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true
+ * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return bool
+ */
+ public function lessThanOrEqualTo($date): bool
+ {
+ $this->discourageNull($date);
+ $this->discourageBoolean($date);
+
+ return $this <= $this->resolveCarbon($date);
+ }
+
+ /**
+ * Determines if the instance is between two others.
+ *
+ * The third argument allow you to specify if bounds are included or not (true by default)
+ * but for when you including/excluding bounds may produce different results in your application,
+ * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->between('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->between('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function between($date1, $date2, $equal = true): bool
+ {
+ $date1 = $this->resolveCarbon($date1);
+ $date2 = $this->resolveCarbon($date2);
+
+ if ($date1->greaterThan($date2)) {
+ [$date1, $date2] = [$date2, $date1];
+ }
+
+ if ($equal) {
+ return $this >= $date1 && $this <= $date2;
+ }
+
+ return $this > $date1 && $this < $date2;
+ }
+
+ /**
+ * Determines if the instance is between two others, bounds included.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return bool
+ */
+ public function betweenIncluded($date1, $date2): bool
+ {
+ return $this->between($date1, $date2, true);
+ }
+
+ /**
+ * Determines if the instance is between two others, bounds excluded.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return bool
+ */
+ public function betweenExcluded($date1, $date2): bool
+ {
+ return $this->between($date1, $date2, false);
+ }
+
+ /**
+ * Determines if the instance is between two others
+ *
+ * @example
+ * ```
+ * Carbon::parse('2018-07-25')->isBetween('2018-07-14', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->isBetween('2018-08-01', '2018-08-20'); // false
+ * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01'); // true
+ * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ * @param bool $equal Indicates if an equal to comparison should be done
+ *
+ * @return bool
+ */
+ public function isBetween($date1, $date2, $equal = true): bool
+ {
+ return $this->between($date1, $date2, $equal);
+ }
+
+ /**
+ * Determines if the instance is a weekday.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-07-14')->isWeekday(); // false
+ * Carbon::parse('2019-07-15')->isWeekday(); // true
+ * ```
+ *
+ * @return bool
+ */
+ public function isWeekday()
+ {
+ return !$this->isWeekend();
+ }
+
+ /**
+ * Determines if the instance is a weekend day.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-07-14')->isWeekend(); // true
+ * Carbon::parse('2019-07-15')->isWeekend(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isWeekend()
+ {
+ return \in_array($this->dayOfWeek, static::$weekendDays, true);
+ }
+
+ /**
+ * Determines if the instance is yesterday.
+ *
+ * @example
+ * ```
+ * Carbon::yesterday()->isYesterday(); // true
+ * Carbon::tomorrow()->isYesterday(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isYesterday()
+ {
+ return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is today.
+ *
+ * @example
+ * ```
+ * Carbon::today()->isToday(); // true
+ * Carbon::tomorrow()->isToday(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isToday()
+ {
+ return $this->toDateString() === $this->nowWithSameTz()->toDateString();
+ }
+
+ /**
+ * Determines if the instance is tomorrow.
+ *
+ * @example
+ * ```
+ * Carbon::tomorrow()->isTomorrow(); // true
+ * Carbon::yesterday()->isTomorrow(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isTomorrow()
+ {
+ return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString();
+ }
+
+ /**
+ * Determines if the instance is in the future, ie. greater (after) than now.
+ *
+ * @example
+ * ```
+ * Carbon::now()->addHours(5)->isFuture(); // true
+ * Carbon::now()->subHours(5)->isFuture(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isFuture()
+ {
+ return $this->greaterThan($this->nowWithSameTz());
+ }
+
+ /**
+ * Determines if the instance is in the past, ie. less (before) than now.
+ *
+ * @example
+ * ```
+ * Carbon::now()->subHours(5)->isPast(); // true
+ * Carbon::now()->addHours(5)->isPast(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isPast()
+ {
+ return $this->lessThan($this->nowWithSameTz());
+ }
+
+ /**
+ * Determines if the instance is a leap year.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2020-01-01')->isLeapYear(); // true
+ * Carbon::parse('2019-01-01')->isLeapYear(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isLeapYear()
+ {
+ return $this->rawFormat('L') === '1';
+ }
+
+ /**
+ * Determines if the instance is a long year (using calendar year).
+ *
+ * ⚠️ This method completely ignores month and day to use the numeric year number,
+ * it's not correct if the exact date matters. For instance as `2019-12-30` is already
+ * in the first week of the 2020 year, if you want to know from this date if ISO week
+ * year 2020 is a long year, use `isLongIsoYear` instead.
+ *
+ * @example
+ * ```
+ * Carbon::create(2015)->isLongYear(); // true
+ * Carbon::create(2016)->isLongYear(); // false
+ * ```
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
+ *
+ * @return bool
+ */
+ public function isLongYear()
+ {
+ return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53;
+ }
+
+ /**
+ * Determines if the instance is a long year (using ISO 8601 year).
+ *
+ * @example
+ * ```
+ * Carbon::parse('2015-01-01')->isLongIsoYear(); // true
+ * Carbon::parse('2016-01-01')->isLongIsoYear(); // true
+ * Carbon::parse('2016-01-03')->isLongIsoYear(); // false
+ * Carbon::parse('2019-12-29')->isLongIsoYear(); // false
+ * Carbon::parse('2019-12-30')->isLongIsoYear(); // true
+ * ```
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
+ *
+ * @return bool
+ */
+ public function isLongIsoYear()
+ {
+ return static::create($this->isoWeekYear, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53;
+ }
+
+ /**
+ * Compares the formatted values of the two dates.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-12-13')); // true
+ * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false
+ * ```
+ *
+ * @param string $format date formats to compare.
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isSameAs($format, $date = null)
+ {
+ return $this->rawFormat($format) === $this->resolveCarbon($date)->rawFormat($format);
+ }
+
+ /**
+ * Determines if the instance is in the current unit given.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-01-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // true
+ * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false
+ * ```
+ *
+ * @param string $unit singular unit string
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day.
+ *
+ * @throws BadComparisonUnitException
+ *
+ * @return bool
+ */
+ public function isSameUnit($unit, $date = null)
+ {
+ $units = [
+ // @call isSameUnit
+ 'year' => 'Y',
+ // @call isSameUnit
+ 'week' => 'o-W',
+ // @call isSameUnit
+ 'day' => 'Y-m-d',
+ // @call isSameUnit
+ 'hour' => 'Y-m-d H',
+ // @call isSameUnit
+ 'minute' => 'Y-m-d H:i',
+ // @call isSameUnit
+ 'second' => 'Y-m-d H:i:s',
+ // @call isSameUnit
+ 'micro' => 'Y-m-d H:i:s.u',
+ // @call isSameUnit
+ 'microsecond' => 'Y-m-d H:i:s.u',
+ ];
+
+ if (isset($units[$unit])) {
+ return $this->isSameAs($units[$unit], $date);
+ }
+
+ if (isset($this->$unit)) {
+ return $this->resolveCarbon($date)->$unit === $this->$unit;
+ }
+
+ if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) {
+ throw new BadComparisonUnitException($unit);
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if the instance is in the current unit given.
+ *
+ * @example
+ * ```
+ * Carbon::now()->isCurrentUnit('hour'); // true
+ * Carbon::now()->subHours(2)->isCurrentUnit('hour'); // false
+ * ```
+ *
+ * @param string $unit The unit to test.
+ *
+ * @throws BadMethodCallException
+ *
+ * @return bool
+ */
+ public function isCurrentUnit($unit)
+ {
+ return $this->{'isSame'.ucfirst($unit)}();
+ }
+
+ /**
+ * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed).
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-03-01')); // true
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-04-01')); // false
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01')); // false
+ * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameQuarter($date = null, $ofSameYear = true)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->quarter === $date->quarter && (!$ofSameYear || $this->isSameYear($date));
+ }
+
+ /**
+ * Checks if the passed in date is in the same month as the instance´s month.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-01-01')); // true
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-02-01')); // false
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01')); // false
+ * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
+ * @param bool $ofSameYear Check if it is the same month in the same year.
+ *
+ * @return bool
+ */
+ public function isSameMonth($date = null, $ofSameYear = true)
+ {
+ return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date);
+ }
+
+ /**
+ * Checks if this day is a specific day of the week.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::WEDNESDAY); // true
+ * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::FRIDAY); // false
+ * Carbon::parse('2019-07-17')->isDayOfWeek('Wednesday'); // true
+ * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false
+ * ```
+ *
+ * @param int $dayOfWeek
+ *
+ * @return bool
+ */
+ public function isDayOfWeek($dayOfWeek)
+ {
+ if (\is_string($dayOfWeek) && \defined($constant = static::class.'::'.strtoupper($dayOfWeek))) {
+ $dayOfWeek = \constant($constant);
+ }
+
+ return $this->dayOfWeek === $dayOfWeek;
+ }
+
+ /**
+ * Check if its the birthday. Compares the date/month values of the two dates.
+ *
+ * @example
+ * ```
+ * Carbon::now()->subYears(5)->isBirthday(); // true
+ * Carbon::now()->subYears(5)->subDay()->isBirthday(); // false
+ * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-05')); // true
+ * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false
+ * ```
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
+ *
+ * @return bool
+ */
+ public function isBirthday($date = null)
+ {
+ return $this->isSameAs('md', $date);
+ }
+
+ /**
+ * Check if today is the last day of the Month
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28')->isLastOfMonth(); // true
+ * Carbon::parse('2019-03-28')->isLastOfMonth(); // false
+ * Carbon::parse('2019-03-30')->isLastOfMonth(); // false
+ * Carbon::parse('2019-03-31')->isLastOfMonth(); // true
+ * Carbon::parse('2019-04-30')->isLastOfMonth(); // true
+ * ```
+ *
+ * @return bool
+ */
+ public function isLastOfMonth()
+ {
+ return $this->day === $this->daysInMonth;
+ }
+
+ /**
+ * Check if the instance is start of day / midnight.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 00:00:00')->isStartOfDay(); // true
+ * Carbon::parse('2019-02-28 00:00:00.999999')->isStartOfDay(); // true
+ * Carbon::parse('2019-02-28 00:00:01')->isStartOfDay(); // false
+ * Carbon::parse('2019-02-28 00:00:00.000000')->isStartOfDay(true); // true
+ * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false
+ * ```
+ *
+ * @param bool $checkMicroseconds check time at microseconds precision
+ *
+ * @return bool
+ */
+ public function isStartOfDay($checkMicroseconds = false)
+ {
+ /* @var CarbonInterface $this */
+ return $checkMicroseconds
+ ? $this->rawFormat('H:i:s.u') === '00:00:00.000000'
+ : $this->rawFormat('H:i:s') === '00:00:00';
+ }
+
+ /**
+ * Check if the instance is end of day.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(); // true
+ * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(); // true
+ * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(); // true
+ * Carbon::parse('2019-02-28 23:59:58.999999')->isEndOfDay(); // false
+ * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(true); // true
+ * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(true); // false
+ * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false
+ * ```
+ *
+ * @param bool $checkMicroseconds check time at microseconds precision
+ *
+ * @return bool
+ */
+ public function isEndOfDay($checkMicroseconds = false)
+ {
+ /* @var CarbonInterface $this */
+ return $checkMicroseconds
+ ? $this->rawFormat('H:i:s.u') === '23:59:59.999999'
+ : $this->rawFormat('H:i:s') === '23:59:59';
+ }
+
+ /**
+ * Check if the instance is start of day / midnight.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 00:00:00')->isMidnight(); // true
+ * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true
+ * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isMidnight()
+ {
+ return $this->isStartOfDay();
+ }
+
+ /**
+ * Check if the instance is midday.
+ *
+ * @example
+ * ```
+ * Carbon::parse('2019-02-28 11:59:59.999999')->isMidday(); // false
+ * Carbon::parse('2019-02-28 12:00:00')->isMidday(); // true
+ * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true
+ * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false
+ * ```
+ *
+ * @return bool
+ */
+ public function isMidday()
+ {
+ /* @var CarbonInterface $this */
+ return $this->rawFormat('G:i:s') === static::$midDayAt.':00:00';
+ }
+
+ /**
+ * Checks if the (date)time string is in a given format.
+ *
+ * @example
+ * ```
+ * Carbon::hasFormat('11:12:45', 'h:i:s'); // true
+ * Carbon::hasFormat('13:12:45', 'h:i:s'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function hasFormat($date, $format)
+ {
+ // createFromFormat() is known to handle edge cases silently.
+ // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format.
+ // To ensure we're really testing against our desired format, perform an additional regex validation.
+
+ return self::matchFormatPattern((string) $date, preg_quote((string) $format, '/'), static::$regexFormats);
+ }
+
+ /**
+ * Checks if the (date)time string is in a given format.
+ *
+ * @example
+ * ```
+ * Carbon::hasFormatWithModifiers('31/08/2015', 'd#m#Y'); // true
+ * Carbon::hasFormatWithModifiers('31/08/2015', 'm#d#Y'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function hasFormatWithModifiers($date, $format): bool
+ {
+ return self::matchFormatPattern((string) $date, (string) $format, array_merge(static::$regexFormats, static::$regexFormatModifiers));
+ }
+
+ /**
+ * Checks if the (date)time string is in a given format and valid to create a
+ * new instance.
+ *
+ * @example
+ * ```
+ * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true
+ * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ *
+ * @return bool
+ */
+ public static function canBeCreatedFromFormat($date, $format)
+ {
+ try {
+ // Try to create a DateTime object. Throws an InvalidArgumentException if the provided time string
+ // doesn't match the format in any way.
+ if (!static::rawCreateFromFormat($format, $date)) {
+ return false;
+ }
+ } catch (InvalidArgumentException $e) {
+ return false;
+ }
+
+ return static::hasFormatWithModifiers($date, $format);
+ }
+
+ /**
+ * Returns true if the current date matches the given string.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2018')); // false
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('06-02')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06-02')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('Sunday')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('June')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:45')); // true
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:00')); // false
+ * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12h')); // true
+ * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3pm')); // true
+ * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3am')); // false
+ * ```
+ *
+ * @param string $tester day name, month name, hour, date, etc. as string
+ *
+ * @return bool
+ */
+ public function is(string $tester)
+ {
+ $tester = trim($tester);
+
+ if (preg_match('/^\d+$/', $tester)) {
+ return $this->year === (int) $tester;
+ }
+
+ if (preg_match('/^\d{3,}-\d{1,2}$/', $tester)) {
+ return $this->isSameMonth(static::parse($tester));
+ }
+
+ if (preg_match('/^\d{1,2}-\d{1,2}$/', $tester)) {
+ return $this->isSameDay(static::parse($this->year.'-'.$tester));
+ }
+
+ $modifier = preg_replace('/(\d)h$/i', '$1:00', $tester);
+
+ /* @var CarbonInterface $max */
+ $median = static::parse('5555-06-15 12:30:30.555555')->modify($modifier);
+ $current = $this->avoidMutation();
+ /* @var CarbonInterface $other */
+ $other = $this->avoidMutation()->modify($modifier);
+
+ if ($current->eq($other)) {
+ return true;
+ }
+
+ if (preg_match('/\d:\d{1,2}:\d{1,2}$/', $tester)) {
+ return $current->startOfSecond()->eq($other);
+ }
+
+ if (preg_match('/\d:\d{1,2}$/', $tester)) {
+ return $current->startOfMinute()->eq($other);
+ }
+
+ if (preg_match('/\d(?:h|am|pm)$/', $tester)) {
+ return $current->startOfHour()->eq($other);
+ }
+
+ if (preg_match(
+ '/^(?:january|february|march|april|may|june|july|august|september|october|november|december)(?:\s+\d+)?$/i',
+ $tester
+ )) {
+ return $current->startOfMonth()->eq($other->startOfMonth());
+ }
+
+ $units = [
+ 'month' => [1, 'year'],
+ 'day' => [1, 'month'],
+ 'hour' => [0, 'day'],
+ 'minute' => [0, 'hour'],
+ 'second' => [0, 'minute'],
+ 'microsecond' => [0, 'second'],
+ ];
+
+ foreach ($units as $unit => [$minimum, $startUnit]) {
+ if ($minimum === $median->$unit) {
+ $current = $current->startOf($startUnit);
+
+ break;
+ }
+ }
+
+ return $current->eq($other);
+ }
+
+ /**
+ * Checks if the (date)time string is in a given format with
+ * given list of pattern replacements.
+ *
+ * @example
+ * ```
+ * Carbon::hasFormat('11:12:45', 'h:i:s'); // true
+ * Carbon::hasFormat('13:12:45', 'h:i:s'); // false
+ * ```
+ *
+ * @param string $date
+ * @param string $format
+ * @param array $replacements
+ *
+ * @return bool
+ */
+ private static function matchFormatPattern(string $date, string $format, array $replacements): bool
+ {
+ // Preg quote, but remove escaped backslashes since we'll deal with escaped characters in the format string.
+ $regex = str_replace('\\\\', '\\', $format);
+ // Replace not-escaped letters
+ $regex = preg_replace_callback(
+ '/(?startOfTime ?? false;
+ }
+
+ /**
+ * Returns true if the date was created using CarbonImmutable::endOfTime()
+ *
+ * @return bool
+ */
+ public function isEndOfTime(): bool
+ {
+ return $this->endOfTime ?? false;
+ }
+
+ private function discourageNull($value): void
+ {
+ if ($value === null) {
+ @trigger_error("Since 2.61.0, it's deprecated to compare a date to null, meaning of such comparison is ambiguous and will no longer be possible in 3.0.0, you should explicitly pass 'now' or make an other check to eliminate null values.", \E_USER_DEPRECATED);
+ }
+ }
+
+ private function discourageBoolean($value): void
+ {
+ if (\is_bool($value)) {
+ @trigger_error("Since 2.61.0, it's deprecated to compare a date to true or false, meaning of such comparison is ambiguous and will no longer be possible in 3.0.0, you should explicitly pass 'now' or make an other check to eliminate boolean values.", \E_USER_DEPRECATED);
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php b/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php
new file mode 100644
index 0000000..fff8a60
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Converter.php
@@ -0,0 +1,639 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Carbon;
+use Carbon\CarbonImmutable;
+use Carbon\CarbonInterface;
+use Carbon\CarbonInterval;
+use Carbon\CarbonPeriod;
+use Carbon\CarbonPeriodImmutable;
+use Carbon\Exceptions\UnitException;
+use Closure;
+use DateTime;
+use DateTimeImmutable;
+use ReturnTypeWillChange;
+
+/**
+ * Trait Converter.
+ *
+ * Change date into different string formats and types and
+ * handle the string cast.
+ *
+ * Depends on the following methods:
+ *
+ * @method static copy()
+ */
+trait Converter
+{
+ use ToStringFormat;
+
+ /**
+ * Returns the formatted date string on success or FALSE on failure.
+ *
+ * @see https://php.net/manual/en/datetime.format.php
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ #[ReturnTypeWillChange]
+ public function format($format)
+ {
+ $function = $this->localFormatFunction ?: static::$formatFunction;
+
+ if (!$function) {
+ return $this->rawFormat($format);
+ }
+
+ if (\is_string($function) && method_exists($this, $function)) {
+ $function = [$this, $function];
+ }
+
+ return $function(...\func_get_args());
+ }
+
+ /**
+ * @see https://php.net/manual/en/datetime.format.php
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function rawFormat($format)
+ {
+ return parent::format($format);
+ }
+
+ /**
+ * Format the instance as a string using the set format
+ *
+ * @example
+ * ```
+ * echo Carbon::now(); // Carbon instances can be cast to string
+ * ```
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $format = $this->localToStringFormat ?? static::$toStringFormat;
+
+ return $format instanceof Closure
+ ? $format($this)
+ : $this->rawFormat($format ?: (
+ \defined('static::DEFAULT_TO_STRING_FORMAT')
+ ? static::DEFAULT_TO_STRING_FORMAT
+ : CarbonInterface::DEFAULT_TO_STRING_FORMAT
+ ));
+ }
+
+ /**
+ * Format the instance as date
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDateString();
+ * ```
+ *
+ * @return string
+ */
+ public function toDateString()
+ {
+ return $this->rawFormat('Y-m-d');
+ }
+
+ /**
+ * Format the instance as a readable date
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toFormattedDateString();
+ * ```
+ *
+ * @return string
+ */
+ public function toFormattedDateString()
+ {
+ return $this->rawFormat('M j, Y');
+ }
+
+ /**
+ * Format the instance with the day, and a readable date
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toFormattedDayDateString();
+ * ```
+ *
+ * @return string
+ */
+ public function toFormattedDayDateString(): string
+ {
+ return $this->rawFormat('D, M j, Y');
+ }
+
+ /**
+ * Format the instance as time
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toTimeString();
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toTimeString($unitPrecision = 'second')
+ {
+ return $this->rawFormat(static::getTimeFormatByPrecision($unitPrecision));
+ }
+
+ /**
+ * Format the instance as date and time
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDateTimeString();
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toDateTimeString($unitPrecision = 'second')
+ {
+ return $this->rawFormat('Y-m-d '.static::getTimeFormatByPrecision($unitPrecision));
+ }
+
+ /**
+ * Return a format from H:i to H:i:s.u according to given unit precision.
+ *
+ * @param string $unitPrecision "minute", "second", "millisecond" or "microsecond"
+ *
+ * @return string
+ */
+ public static function getTimeFormatByPrecision($unitPrecision)
+ {
+ switch (static::singularUnit($unitPrecision)) {
+ case 'minute':
+ return 'H:i';
+ case 'second':
+ return 'H:i:s';
+ case 'm':
+ case 'millisecond':
+ return 'H:i:s.v';
+ case 'µ':
+ case 'microsecond':
+ return 'H:i:s.u';
+ }
+
+ throw new UnitException('Precision unit expected among: minute, second, millisecond and microsecond.');
+ }
+
+ /**
+ * Format the instance as date and time T-separated with no timezone
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDateTimeLocalString();
+ * echo "\n";
+ * echo Carbon::now()->toDateTimeLocalString('minute'); // You can specify precision among: minute, second, millisecond and microsecond
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toDateTimeLocalString($unitPrecision = 'second')
+ {
+ return $this->rawFormat('Y-m-d\T'.static::getTimeFormatByPrecision($unitPrecision));
+ }
+
+ /**
+ * Format the instance with day, date and time
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toDayDateTimeString();
+ * ```
+ *
+ * @return string
+ */
+ public function toDayDateTimeString()
+ {
+ return $this->rawFormat('D, M j, Y g:i A');
+ }
+
+ /**
+ * Format the instance as ATOM
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toAtomString();
+ * ```
+ *
+ * @return string
+ */
+ public function toAtomString()
+ {
+ return $this->rawFormat(DateTime::ATOM);
+ }
+
+ /**
+ * Format the instance as COOKIE
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toCookieString();
+ * ```
+ *
+ * @return string
+ */
+ public function toCookieString()
+ {
+ return $this->rawFormat(DateTime::COOKIE);
+ }
+
+ /**
+ * Format the instance as ISO8601
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toIso8601String();
+ * ```
+ *
+ * @return string
+ */
+ public function toIso8601String()
+ {
+ return $this->toAtomString();
+ }
+
+ /**
+ * Format the instance as RFC822
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc822String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc822String()
+ {
+ return $this->rawFormat(DateTime::RFC822);
+ }
+
+ /**
+ * Convert the instance to UTC and return as Zulu ISO8601
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toIso8601ZuluString();
+ * ```
+ *
+ * @param string $unitPrecision
+ *
+ * @return string
+ */
+ public function toIso8601ZuluString($unitPrecision = 'second')
+ {
+ return $this->avoidMutation()
+ ->utc()
+ ->rawFormat('Y-m-d\T'.static::getTimeFormatByPrecision($unitPrecision).'\Z');
+ }
+
+ /**
+ * Format the instance as RFC850
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc850String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc850String()
+ {
+ return $this->rawFormat(DateTime::RFC850);
+ }
+
+ /**
+ * Format the instance as RFC1036
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc1036String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc1036String()
+ {
+ return $this->rawFormat(DateTime::RFC1036);
+ }
+
+ /**
+ * Format the instance as RFC1123
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc1123String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc1123String()
+ {
+ return $this->rawFormat(DateTime::RFC1123);
+ }
+
+ /**
+ * Format the instance as RFC2822
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc2822String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc2822String()
+ {
+ return $this->rawFormat(DateTime::RFC2822);
+ }
+
+ /**
+ * Format the instance as RFC3339
+ *
+ * @param bool $extended
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc3339String() . "\n";
+ * echo Carbon::now()->toRfc3339String(true) . "\n";
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc3339String($extended = false)
+ {
+ $format = DateTime::RFC3339;
+ if ($extended) {
+ $format = DateTime::RFC3339_EXTENDED;
+ }
+
+ return $this->rawFormat($format);
+ }
+
+ /**
+ * Format the instance as RSS
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRssString();
+ * ```
+ *
+ * @return string
+ */
+ public function toRssString()
+ {
+ return $this->rawFormat(DateTime::RSS);
+ }
+
+ /**
+ * Format the instance as W3C
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toW3cString();
+ * ```
+ *
+ * @return string
+ */
+ public function toW3cString()
+ {
+ return $this->rawFormat(DateTime::W3C);
+ }
+
+ /**
+ * Format the instance as RFC7231
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toRfc7231String();
+ * ```
+ *
+ * @return string
+ */
+ public function toRfc7231String()
+ {
+ return $this->avoidMutation()
+ ->setTimezone('GMT')
+ ->rawFormat(\defined('static::RFC7231_FORMAT') ? static::RFC7231_FORMAT : CarbonInterface::RFC7231_FORMAT);
+ }
+
+ /**
+ * Get default array representation.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toArray());
+ * ```
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'year' => $this->year,
+ 'month' => $this->month,
+ 'day' => $this->day,
+ 'dayOfWeek' => $this->dayOfWeek,
+ 'dayOfYear' => $this->dayOfYear,
+ 'hour' => $this->hour,
+ 'minute' => $this->minute,
+ 'second' => $this->second,
+ 'micro' => $this->micro,
+ 'timestamp' => $this->timestamp,
+ 'formatted' => $this->rawFormat(\defined('static::DEFAULT_TO_STRING_FORMAT') ? static::DEFAULT_TO_STRING_FORMAT : CarbonInterface::DEFAULT_TO_STRING_FORMAT),
+ 'timezone' => $this->timezone,
+ ];
+ }
+
+ /**
+ * Get default object representation.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toObject());
+ * ```
+ *
+ * @return object
+ */
+ public function toObject()
+ {
+ return (object) $this->toArray();
+ }
+
+ /**
+ * Returns english human readable complete date string.
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->toString();
+ * ```
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->avoidMutation()->locale('en')->isoFormat('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
+ }
+
+ /**
+ * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z, if $keepOffset truthy, offset will be kept:
+ * 1977-04-22T01:00:00-05:00).
+ *
+ * @example
+ * ```
+ * echo Carbon::now('America/Toronto')->toISOString() . "\n";
+ * echo Carbon::now('America/Toronto')->toISOString(true) . "\n";
+ * ```
+ *
+ * @param bool $keepOffset Pass true to keep the date offset. Else forced to UTC.
+ *
+ * @return null|string
+ */
+ public function toISOString($keepOffset = false)
+ {
+ if (!$this->isValid()) {
+ return null;
+ }
+
+ $yearFormat = $this->year < 0 || $this->year > 9999 ? 'YYYYYY' : 'YYYY';
+ $tzFormat = $keepOffset ? 'Z' : '[Z]';
+ $date = $keepOffset ? $this : $this->avoidMutation()->utc();
+
+ return $date->isoFormat("$yearFormat-MM-DD[T]HH:mm:ss.SSSSSS$tzFormat");
+ }
+
+ /**
+ * Return the ISO-8601 string (ex: 1977-04-22T06:00:00Z) with UTC timezone.
+ *
+ * @example
+ * ```
+ * echo Carbon::now('America/Toronto')->toJSON();
+ * ```
+ *
+ * @return null|string
+ */
+ public function toJSON()
+ {
+ return $this->toISOString();
+ }
+
+ /**
+ * Return native DateTime PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toDateTime());
+ * ```
+ *
+ * @return DateTime
+ */
+ public function toDateTime()
+ {
+ return new DateTime($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone());
+ }
+
+ /**
+ * Return native toDateTimeImmutable PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toDateTimeImmutable());
+ * ```
+ *
+ * @return DateTimeImmutable
+ */
+ public function toDateTimeImmutable()
+ {
+ return new DateTimeImmutable($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone());
+ }
+
+ /**
+ * @alias toDateTime
+ *
+ * Return native DateTime PHP object matching the current instance.
+ *
+ * @example
+ * ```
+ * var_dump(Carbon::now()->toDate());
+ * ```
+ *
+ * @return DateTime
+ */
+ public function toDate()
+ {
+ return $this->toDateTime();
+ }
+
+ /**
+ * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval).
+ *
+ * @param \DateTimeInterface|Carbon|CarbonImmutable|int|null $end period end date or recurrences count if int
+ * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit
+ * @param string|null $unit if specified, $interval must be an integer
+ *
+ * @return CarbonPeriod
+ */
+ public function toPeriod($end = null, $interval = null, $unit = null)
+ {
+ if ($unit) {
+ $interval = CarbonInterval::make("$interval ".static::pluralUnit($unit));
+ }
+
+ $period = ($this->isMutable() ? new CarbonPeriod() : new CarbonPeriodImmutable())
+ ->setDateClass(static::class)
+ ->setStartDate($this);
+
+ if ($interval) {
+ $period = $period->setDateInterval($interval);
+ }
+
+ if (\is_int($end) || (\is_string($end) && ctype_digit($end))) {
+ $period = $period->setRecurrences($end);
+ } elseif ($end) {
+ $period = $period->setEndDate($end);
+ }
+
+ return $period;
+ }
+
+ /**
+ * Create a iterable CarbonPeriod object from current date to a given end date (and optional interval).
+ *
+ * @param \DateTimeInterface|Carbon|CarbonImmutable|null $end period end date
+ * @param int|\DateInterval|string|null $interval period default interval or number of the given $unit
+ * @param string|null $unit if specified, $interval must be an integer
+ *
+ * @return CarbonPeriod
+ */
+ public function range($end = null, $interval = null, $unit = null)
+ {
+ return $this->toPeriod($end, $interval, $unit);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php b/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php
new file mode 100644
index 0000000..0d611ea
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php
@@ -0,0 +1,977 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Carbon;
+use Carbon\CarbonImmutable;
+use Carbon\CarbonInterface;
+use Carbon\Exceptions\InvalidDateException;
+use Carbon\Exceptions\InvalidFormatException;
+use Carbon\Exceptions\OutOfRangeException;
+use Carbon\Translator;
+use Closure;
+use DateMalformedStringException;
+use DateTimeImmutable;
+use DateTimeInterface;
+use DateTimeZone;
+use Exception;
+use ReturnTypeWillChange;
+
+/**
+ * Trait Creator.
+ *
+ * Static creators.
+ *
+ * Depends on the following methods:
+ *
+ * @method static Carbon|CarbonImmutable getTestNow()
+ */
+trait Creator
+{
+ use ObjectInitialisation;
+
+ /**
+ * The errors that can occur.
+ *
+ * @var array
+ */
+ protected static $lastErrors;
+
+ /**
+ * Create a new Carbon instance.
+ *
+ * Please see the testing aids section (specifically static::setTestNow())
+ * for more on the possibility of this constructor returning a test instance.
+ *
+ * @param DateTimeInterface|string|null $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ */
+ public function __construct($time = null, $tz = null)
+ {
+ if ($time instanceof DateTimeInterface) {
+ $time = $this->constructTimezoneFromDateTime($time, $tz)->format('Y-m-d H:i:s.u');
+ }
+
+ if (is_numeric($time) && (!\is_string($time) || !preg_match('/^\d{1,14}$/', $time))) {
+ $time = static::createFromTimestampUTC($time)->format('Y-m-d\TH:i:s.uP');
+ }
+
+ // If the class has a test now set and we are trying to create a now()
+ // instance then override as required
+ $isNow = empty($time) || $time === 'now';
+
+ if (method_exists(static::class, 'hasTestNow') &&
+ method_exists(static::class, 'getTestNow') &&
+ static::hasTestNow() &&
+ ($isNow || static::hasRelativeKeywords($time))
+ ) {
+ static::mockConstructorParameters($time, $tz);
+ }
+
+ // Work-around for PHP bug https://bugs.php.net/bug.php?id=67127
+ if (!str_contains((string) .1, '.')) {
+ $locale = setlocale(LC_NUMERIC, '0'); // @codeCoverageIgnore
+ setlocale(LC_NUMERIC, 'C'); // @codeCoverageIgnore
+ }
+
+ try {
+ parent::__construct($time ?: 'now', static::safeCreateDateTimeZone($tz) ?: null);
+ } catch (Exception $exception) {
+ throw new InvalidFormatException($exception->getMessage(), 0, $exception);
+ }
+
+ $this->constructedObjectId = spl_object_hash($this);
+
+ if (isset($locale)) {
+ setlocale(LC_NUMERIC, $locale); // @codeCoverageIgnore
+ }
+
+ self::setLastErrors(parent::getLastErrors());
+ }
+
+ /**
+ * Get timezone from a datetime instance.
+ *
+ * @param DateTimeInterface $date
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return DateTimeInterface
+ */
+ private function constructTimezoneFromDateTime(DateTimeInterface $date, &$tz)
+ {
+ if ($tz !== null) {
+ $safeTz = static::safeCreateDateTimeZone($tz);
+
+ if ($safeTz) {
+ return ($date instanceof DateTimeImmutable ? $date : clone $date)->setTimezone($safeTz);
+ }
+
+ return $date;
+ }
+
+ $tz = $date->getTimezone();
+
+ return $date;
+ }
+
+ /**
+ * Update constructedObjectId on cloned.
+ */
+ public function __clone()
+ {
+ $this->constructedObjectId = spl_object_hash($this);
+ }
+
+ /**
+ * Create a Carbon instance from a DateTime one.
+ *
+ * @param DateTimeInterface $date
+ *
+ * @return static
+ */
+ public static function instance($date)
+ {
+ if ($date instanceof static) {
+ return clone $date;
+ }
+
+ static::expectDateTime($date);
+
+ $instance = new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
+
+ if ($date instanceof CarbonInterface) {
+ $settings = $date->getSettings();
+
+ if (!$date->hasLocalTranslator()) {
+ unset($settings['locale']);
+ }
+
+ $instance->settings($settings);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Create a carbon instance from a string.
+ *
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ *
+ * @param string|DateTimeInterface|null $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function rawParse($time = null, $tz = null)
+ {
+ if ($time instanceof DateTimeInterface) {
+ return static::instance($time);
+ }
+
+ try {
+ return new static($time, $tz);
+ } catch (Exception $exception) {
+ // @codeCoverageIgnoreStart
+ try {
+ $date = @static::now($tz)->change($time);
+ } catch (DateMalformedStringException $ignoredException) {
+ $date = null;
+ }
+ // @codeCoverageIgnoreEnd
+
+ if (!$date) {
+ throw new InvalidFormatException("Could not parse '$time': ".$exception->getMessage(), 0, $exception);
+ }
+
+ return $date;
+ }
+ }
+
+ /**
+ * Create a carbon instance from a string.
+ *
+ * This is an alias for the constructor that allows better fluent syntax
+ * as it allows you to do Carbon::parse('Monday next week')->fn() rather
+ * than (new Carbon('Monday next week'))->fn().
+ *
+ * @param string|DateTimeInterface|null $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function parse($time = null, $tz = null)
+ {
+ $function = static::$parseFunction;
+
+ if (!$function) {
+ return static::rawParse($time, $tz);
+ }
+
+ if (\is_string($function) && method_exists(static::class, $function)) {
+ $function = [static::class, $function];
+ }
+
+ return $function(...\func_get_args());
+ }
+
+ /**
+ * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.).
+ *
+ * @param string $time date/time string in the given language (may also contain English).
+ * @param string|null $locale if locale is null or not specified, current global locale will be
+ * used instead.
+ * @param DateTimeZone|string|null $tz optional timezone for the new instance.
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function parseFromLocale($time, $locale = null, $tz = null)
+ {
+ return static::rawParse(static::translateTimeString($time, $locale, 'en'), $tz);
+ }
+
+ /**
+ * Get a Carbon instance for the current date and time.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function now($tz = null)
+ {
+ return new static(null, $tz);
+ }
+
+ /**
+ * Create a Carbon instance for today.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function today($tz = null)
+ {
+ return static::rawParse('today', $tz);
+ }
+
+ /**
+ * Create a Carbon instance for tomorrow.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function tomorrow($tz = null)
+ {
+ return static::rawParse('tomorrow', $tz);
+ }
+
+ /**
+ * Create a Carbon instance for yesterday.
+ *
+ * @param DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function yesterday($tz = null)
+ {
+ return static::rawParse('yesterday', $tz);
+ }
+
+ /**
+ * Create a Carbon instance for the greatest supported date.
+ *
+ * @return static
+ */
+ public static function maxValue()
+ {
+ if (self::$PHPIntSize === 4) {
+ // 32 bit
+ return static::createFromTimestamp(PHP_INT_MAX); // @codeCoverageIgnore
+ }
+
+ // 64 bit
+ return static::create(9999, 12, 31, 23, 59, 59);
+ }
+
+ /**
+ * Create a Carbon instance for the lowest supported date.
+ *
+ * @return static
+ */
+ public static function minValue()
+ {
+ if (self::$PHPIntSize === 4) {
+ // 32 bit
+ return static::createFromTimestamp(~PHP_INT_MAX); // @codeCoverageIgnore
+ }
+
+ // 64 bit
+ return static::create(1, 1, 1, 0, 0, 0);
+ }
+
+ private static function assertBetween($unit, $value, $min, $max)
+ {
+ if (static::isStrictModeEnabled() && ($value < $min || $value > $max)) {
+ throw new OutOfRangeException($unit, $min, $max, $value);
+ }
+ }
+
+ private static function createNowInstance($tz)
+ {
+ if (!static::hasTestNow()) {
+ return static::now($tz);
+ }
+
+ $now = static::getTestNow();
+
+ if ($now instanceof Closure) {
+ return $now(static::now($tz));
+ }
+
+ return $now->avoidMutation()->tz($tz);
+ }
+
+ /**
+ * Create a new Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * @param DateTimeInterface|int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null)
+ {
+ if ((\is_string($year) && !is_numeric($year)) || $year instanceof DateTimeInterface) {
+ return static::parse($year, $tz ?: (\is_string($month) || $month instanceof DateTimeZone ? $month : null));
+ }
+
+ $defaults = null;
+ $getDefault = function ($unit) use ($tz, &$defaults) {
+ if ($defaults === null) {
+ $now = self::createNowInstance($tz);
+
+ $defaults = array_combine([
+ 'year',
+ 'month',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ ], explode('-', $now->rawFormat('Y-n-j-G-i-s.u')));
+ }
+
+ return $defaults[$unit];
+ };
+
+ $year = $year ?? $getDefault('year');
+ $month = $month ?? $getDefault('month');
+ $day = $day ?? $getDefault('day');
+ $hour = $hour ?? $getDefault('hour');
+ $minute = $minute ?? $getDefault('minute');
+ $second = (float) ($second ?? $getDefault('second'));
+
+ self::assertBetween('month', $month, 0, 99);
+ self::assertBetween('day', $day, 0, 99);
+ self::assertBetween('hour', $hour, 0, 99);
+ self::assertBetween('minute', $minute, 0, 99);
+ self::assertBetween('second', $second, 0, 99);
+
+ $fixYear = null;
+
+ if ($year < 0) {
+ $fixYear = $year;
+ $year = 0;
+ } elseif ($year > 9999) {
+ $fixYear = $year - 9999;
+ $year = 9999;
+ }
+
+ $second = ($second < 10 ? '0' : '').number_format($second, 6);
+ $instance = static::rawCreateFromFormat('!Y-n-j G:i:s.u', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz);
+
+ if ($fixYear !== null) {
+ $instance = $instance->addYears($fixYear);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Create a new safe Carbon instance from a specific date and time.
+ *
+ * If any of $year, $month or $day are set to null their now() values will
+ * be used.
+ *
+ * If $hour is null it will be set to its now() value and the default
+ * values for $minute and $second will be their now() values.
+ *
+ * If $hour is not null then the default values for $minute and $second
+ * will be 0.
+ *
+ * If one of the set values is not valid, an InvalidDateException
+ * will be thrown.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidDateException
+ *
+ * @return static|false
+ */
+ public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null)
+ {
+ $fields = static::getRangesByUnit();
+
+ foreach ($fields as $field => $range) {
+ if ($$field !== null && (!\is_int($$field) || $$field < $range[0] || $$field > $range[1])) {
+ if (static::isStrictModeEnabled()) {
+ throw new InvalidDateException($field, $$field);
+ }
+
+ return false;
+ }
+ }
+
+ $instance = static::create($year, $month, $day, $hour, $minute, $second, $tz);
+
+ foreach (array_reverse($fields) as $field => $range) {
+ if ($$field !== null && (!\is_int($$field) || $$field !== $instance->$field)) {
+ if (static::isStrictModeEnabled()) {
+ throw new InvalidDateException($field, $$field);
+ }
+
+ return false;
+ }
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Create a new Carbon instance from a specific date and time using strict validation.
+ *
+ * @see create()
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null): self
+ {
+ $initialStrictMode = static::isStrictModeEnabled();
+ static::useStrictMode(true);
+
+ try {
+ $date = static::create($year, $month, $day, $hour, $minute, $second, $tz);
+ } finally {
+ static::useStrictMode($initialStrictMode);
+ }
+
+ return $date;
+ }
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to now.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createFromDate($year = null, $month = null, $day = null, $tz = null)
+ {
+ return static::create($year, $month, $day, null, null, null, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from just a date. The time portion is set to midnight.
+ *
+ * @param int|null $year
+ * @param int|null $month
+ * @param int|null $day
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null)
+ {
+ return static::create($year, $month, $day, 0, 0, 0, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from just a time. The date portion is set to today.
+ *
+ * @param int|null $hour
+ * @param int|null $minute
+ * @param int|null $second
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null)
+ {
+ return static::create(null, null, null, $hour, $minute, $second, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from a time string. The date portion is set to today.
+ *
+ * @param string $time
+ * @param DateTimeZone|string|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function createFromTimeString($time, $tz = null)
+ {
+ return static::today($tz)->setTimeFromTimeString($time);
+ }
+
+ /**
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $originalTz
+ *
+ * @return DateTimeInterface|false
+ */
+ private static function createFromFormatAndTimezone($format, $time, $originalTz)
+ {
+ // Work-around for https://bugs.php.net/bug.php?id=75577
+ // @codeCoverageIgnoreStart
+ if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) {
+ $format = str_replace('.v', '.u', $format);
+ }
+ // @codeCoverageIgnoreEnd
+
+ if ($originalTz === null) {
+ return parent::createFromFormat($format, (string) $time);
+ }
+
+ $tz = \is_int($originalTz)
+ ? @timezone_name_from_abbr('', (int) ($originalTz * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE), 1)
+ : $originalTz;
+
+ $tz = static::safeCreateDateTimeZone($tz, $originalTz);
+
+ if ($tz === false) {
+ return false;
+ }
+
+ return parent::createFromFormat($format, (string) $time, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from a specific format.
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function rawCreateFromFormat($format, $time, $tz = null)
+ {
+ // Work-around for https://bugs.php.net/bug.php?id=80141
+ $format = preg_replace('/(?getTimezone();
+ }
+
+ $mock = $mock->copy();
+
+ // Prepend mock datetime only if the format does not contain non escaped unix epoch reset flag.
+ if (!preg_match("/{$nonEscaped}[!|]/", $format)) {
+ if (preg_match('/[HhGgisvuB]/', $format)) {
+ $mock = $mock->setTime(0, 0);
+ }
+
+ $format = static::MOCK_DATETIME_FORMAT.' '.$format;
+ $time = ($mock instanceof self ? $mock->rawFormat(static::MOCK_DATETIME_FORMAT) : $mock->format(static::MOCK_DATETIME_FORMAT)).' '.$time;
+ }
+
+ // Regenerate date from the modified format to base result on the mocked instance instead of now.
+ $date = self::createFromFormatAndTimezone($format, $time, $tz);
+ }
+
+ if ($date instanceof DateTimeInterface) {
+ $instance = static::instance($date);
+ $instance::setLastErrors($lastErrors);
+
+ return $instance;
+ }
+
+ if (static::isStrictModeEnabled()) {
+ throw new InvalidFormatException(implode(PHP_EOL, $lastErrors['errors']));
+ }
+
+ return false;
+ }
+
+ /**
+ * Create a Carbon instance from a specific format.
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ #[ReturnTypeWillChange]
+ public static function createFromFormat($format, $time, $tz = null)
+ {
+ $function = static::$createFromFormatFunction;
+
+ if (!$function) {
+ return static::rawCreateFromFormat($format, $time, $tz);
+ }
+
+ if (\is_string($function) && method_exists(static::class, $function)) {
+ $function = [static::class, $function];
+ }
+
+ return $function(...\func_get_args());
+ }
+
+ /**
+ * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()).
+ *
+ * @param string $format Datetime format
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz optional timezone
+ * @param string|null $locale locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use)
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null)
+ {
+ $format = preg_replace_callback('/(? static::getTranslationMessageWith($translator, 'formats.LT', $locale, 'h:mm A'),
+ 'LTS' => static::getTranslationMessageWith($translator, 'formats.LTS', $locale, 'h:mm:ss A'),
+ 'L' => static::getTranslationMessageWith($translator, 'formats.L', $locale, 'MM/DD/YYYY'),
+ 'LL' => static::getTranslationMessageWith($translator, 'formats.LL', $locale, 'MMMM D, YYYY'),
+ 'LLL' => static::getTranslationMessageWith($translator, 'formats.LLL', $locale, 'MMMM D, YYYY h:mm A'),
+ 'LLLL' => static::getTranslationMessageWith($translator, 'formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'),
+ ];
+ }
+
+ return $formats[$code] ?? preg_replace_callback(
+ '/MMMM|MM|DD|dddd/',
+ function ($code) {
+ return mb_substr($code[0], 1);
+ },
+ $formats[strtoupper($code)] ?? ''
+ );
+ }, $format);
+
+ $format = preg_replace_callback('/(? 'd',
+ 'OM' => 'M',
+ 'OY' => 'Y',
+ 'OH' => 'G',
+ 'Oh' => 'g',
+ 'Om' => 'i',
+ 'Os' => 's',
+ 'D' => 'd',
+ 'DD' => 'd',
+ 'Do' => 'd',
+ 'd' => '!',
+ 'dd' => '!',
+ 'ddd' => 'D',
+ 'dddd' => 'D',
+ 'DDD' => 'z',
+ 'DDDD' => 'z',
+ 'DDDo' => 'z',
+ 'e' => '!',
+ 'E' => '!',
+ 'H' => 'G',
+ 'HH' => 'H',
+ 'h' => 'g',
+ 'hh' => 'h',
+ 'k' => 'G',
+ 'kk' => 'G',
+ 'hmm' => 'gi',
+ 'hmmss' => 'gis',
+ 'Hmm' => 'Gi',
+ 'Hmmss' => 'Gis',
+ 'm' => 'i',
+ 'mm' => 'i',
+ 'a' => 'a',
+ 'A' => 'a',
+ 's' => 's',
+ 'ss' => 's',
+ 'S' => '*',
+ 'SS' => '*',
+ 'SSS' => '*',
+ 'SSSS' => '*',
+ 'SSSSS' => '*',
+ 'SSSSSS' => 'u',
+ 'SSSSSSS' => 'u*',
+ 'SSSSSSSS' => 'u*',
+ 'SSSSSSSSS' => 'u*',
+ 'M' => 'm',
+ 'MM' => 'm',
+ 'MMM' => 'M',
+ 'MMMM' => 'M',
+ 'Mo' => 'm',
+ 'Q' => '!',
+ 'Qo' => '!',
+ 'G' => '!',
+ 'GG' => '!',
+ 'GGG' => '!',
+ 'GGGG' => '!',
+ 'GGGGG' => '!',
+ 'g' => '!',
+ 'gg' => '!',
+ 'ggg' => '!',
+ 'gggg' => '!',
+ 'ggggg' => '!',
+ 'W' => '!',
+ 'WW' => '!',
+ 'Wo' => '!',
+ 'w' => '!',
+ 'ww' => '!',
+ 'wo' => '!',
+ 'x' => 'U???',
+ 'X' => 'U',
+ 'Y' => 'Y',
+ 'YY' => 'y',
+ 'YYYY' => 'Y',
+ 'YYYYY' => 'Y',
+ 'YYYYYY' => 'Y',
+ 'z' => 'e',
+ 'zz' => 'e',
+ 'Z' => 'e',
+ 'ZZ' => 'e',
+ ];
+ }
+
+ $format = $replacements[$code] ?? '?';
+
+ if ($format === '!') {
+ throw new InvalidFormatException("Format $code not supported for creation.");
+ }
+
+ return $format;
+ }, $format);
+
+ return static::rawCreateFromFormat($format, $time, $tz);
+ }
+
+ /**
+ * Create a Carbon instance from a specific format and a string in a given language.
+ *
+ * @param string $format Datetime format
+ * @param string $locale
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function createFromLocaleFormat($format, $locale, $time, $tz = null)
+ {
+ $format = preg_replace_callback(
+ '/(?:\\\\[a-zA-Z]|[bfkqCEJKQRV]){2,}/',
+ static function (array $match) use ($locale): string {
+ $word = str_replace('\\', '', $match[0]);
+ $translatedWord = static::translateTimeString($word, $locale, 'en');
+
+ return $word === $translatedWord
+ ? $match[0]
+ : preg_replace('/[a-zA-Z]/', '\\\\$0', $translatedWord);
+ },
+ $format
+ );
+
+ return static::rawCreateFromFormat($format, static::translateTimeString($time, $locale, 'en'), $tz);
+ }
+
+ /**
+ * Create a Carbon instance from a specific ISO format and a string in a given language.
+ *
+ * @param string $format Datetime ISO format
+ * @param string $locale
+ * @param string $time
+ * @param DateTimeZone|string|false|null $tz
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|false
+ */
+ public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null)
+ {
+ $time = static::translateTimeString($time, $locale, 'en', CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS | CarbonInterface::TRANSLATE_MERIDIEM);
+
+ return static::createFromIsoFormat($format, $time, $tz, $locale);
+ }
+
+ /**
+ * Make a Carbon instance from given variable if possible.
+ *
+ * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
+ * and recurrences). Throw an exception for invalid format, but otherwise return null.
+ *
+ * @param mixed $var
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static|null
+ */
+ public static function make($var)
+ {
+ if ($var instanceof DateTimeInterface) {
+ return static::instance($var);
+ }
+
+ $date = null;
+
+ if (\is_string($var)) {
+ $var = trim($var);
+
+ if (!preg_match('/^P[\dT]/', $var) &&
+ !preg_match('/^R\d/', $var) &&
+ preg_match('/[a-z\d]/i', $var)
+ ) {
+ $date = static::parse($var);
+ }
+ }
+
+ return $date;
+ }
+
+ /**
+ * Set last errors.
+ *
+ * @param array|bool $lastErrors
+ *
+ * @return void
+ */
+ private static function setLastErrors($lastErrors)
+ {
+ if (\is_array($lastErrors) || $lastErrors === false) {
+ static::$lastErrors = \is_array($lastErrors) ? $lastErrors : [
+ 'warning_count' => 0,
+ 'warnings' => [],
+ 'error_count' => 0,
+ 'errors' => [],
+ ];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return array
+ */
+ #[ReturnTypeWillChange]
+ public static function getLastErrors()
+ {
+ return static::$lastErrors;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Date.php b/vendor/nesbot/carbon/src/Carbon/Traits/Date.php
new file mode 100644
index 0000000..8ae5c17
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Date.php
@@ -0,0 +1,2747 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use BadMethodCallException;
+use Carbon\Carbon;
+use Carbon\CarbonInterface;
+use Carbon\CarbonPeriod;
+use Carbon\CarbonTimeZone;
+use Carbon\Exceptions\BadComparisonUnitException;
+use Carbon\Exceptions\ImmutableException;
+use Carbon\Exceptions\InvalidTimeZoneException;
+use Carbon\Exceptions\InvalidTypeException;
+use Carbon\Exceptions\UnknownGetterException;
+use Carbon\Exceptions\UnknownMethodException;
+use Carbon\Exceptions\UnknownSetterException;
+use Carbon\Exceptions\UnknownUnitException;
+use Closure;
+use DateInterval;
+use DatePeriod;
+use DateTime;
+use DateTimeImmutable;
+use DateTimeInterface;
+use DateTimeZone;
+use InvalidArgumentException;
+use ReflectionException;
+use ReturnTypeWillChange;
+use Throwable;
+
+/**
+ * A simple API extension for DateTime.
+ *
+ * @mixin DeprecatedProperties
+ *
+ *
+ *
+ * @property int $year
+ * @property int $yearIso
+ * @property int $month
+ * @property int $day
+ * @property int $hour
+ * @property int $minute
+ * @property int $second
+ * @property int $micro
+ * @property int $microsecond
+ * @property int|float|string $timestamp seconds since the Unix Epoch
+ * @property string $englishDayOfWeek the day of week in English
+ * @property string $shortEnglishDayOfWeek the abbreviated day of week in English
+ * @property string $englishMonth the month in English
+ * @property string $shortEnglishMonth the abbreviated month in English
+ * @property int $milliseconds
+ * @property int $millisecond
+ * @property int $milli
+ * @property int $week 1 through 53
+ * @property int $isoWeek 1 through 53
+ * @property int $weekYear year according to week format
+ * @property int $isoWeekYear year according to ISO week format
+ * @property int $dayOfYear 1 through 366
+ * @property int $age does a diffInYears() with default parameters
+ * @property int $offset the timezone offset in seconds from UTC
+ * @property int $offsetMinutes the timezone offset in minutes from UTC
+ * @property int $offsetHours the timezone offset in hours from UTC
+ * @property CarbonTimeZone $timezone the current timezone
+ * @property CarbonTimeZone $tz alias of $timezone
+ * @property-read int $dayOfWeek 0 (for Sunday) through 6 (for Saturday)
+ * @property-read int $dayOfWeekIso 1 (for Monday) through 7 (for Sunday)
+ * @property-read int $weekOfYear ISO-8601 week number of year, weeks starting on Monday
+ * @property-read int $daysInMonth number of days in the given month
+ * @property-read string $latinMeridiem "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark)
+ * @property-read string $latinUpperMeridiem "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark)
+ * @property-read string $timezoneAbbreviatedName the current timezone abbreviated name
+ * @property-read string $tzAbbrName alias of $timezoneAbbreviatedName
+ * @property-read string $dayName long name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortDayName short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $minDayName very short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $monthName long name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $shortMonthName short name of month translated according to Carbon locale, in english if no translation available for current language
+ * @property-read string $meridiem lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read string $upperMeridiem uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ * @property-read int $noZeroHour current hour from 1 to 24
+ * @property-read int $weeksInYear 51 through 53
+ * @property-read int $isoWeeksInYear 51 through 53
+ * @property-read int $weekOfMonth 1 through 5
+ * @property-read int $weekNumberInMonth 1 through 5
+ * @property-read int $firstWeekDay 0 through 6
+ * @property-read int $lastWeekDay 0 through 6
+ * @property-read int $daysInYear 365 or 366
+ * @property-read int $quarter the quarter of this instance, 1 - 4
+ * @property-read int $decade the decade of this instance
+ * @property-read int $century the century of this instance
+ * @property-read int $millennium the millennium of this instance
+ * @property-read bool $dst daylight savings time indicator, true if DST, false otherwise
+ * @property-read bool $local checks if the timezone is local, true if local, false otherwise
+ * @property-read bool $utc checks if the timezone is UTC, true if UTC, false otherwise
+ * @property-read string $timezoneName the current timezone name
+ * @property-read string $tzName alias of $timezoneName
+ * @property-read string $locale locale of the current instance
+ *
+ * @method bool isUtc() Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.)
+ * @method bool isLocal() Check if the current instance has non-UTC timezone.
+ * @method bool isValid() Check if the current instance is a valid date.
+ * @method bool isDST() Check if the current instance is in a daylight saving time.
+ * @method bool isSunday() Checks if the instance day is sunday.
+ * @method bool isMonday() Checks if the instance day is monday.
+ * @method bool isTuesday() Checks if the instance day is tuesday.
+ * @method bool isWednesday() Checks if the instance day is wednesday.
+ * @method bool isThursday() Checks if the instance day is thursday.
+ * @method bool isFriday() Checks if the instance day is friday.
+ * @method bool isSaturday() Checks if the instance day is saturday.
+ * @method bool isSameYear(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same year as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentYear() Checks if the instance is in the same year as the current moment.
+ * @method bool isNextYear() Checks if the instance is in the same year as the current moment next year.
+ * @method bool isLastYear() Checks if the instance is in the same year as the current moment last year.
+ * @method bool isSameWeek(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same week as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentWeek() Checks if the instance is in the same week as the current moment.
+ * @method bool isNextWeek() Checks if the instance is in the same week as the current moment next week.
+ * @method bool isLastWeek() Checks if the instance is in the same week as the current moment last week.
+ * @method bool isSameDay(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same day as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDay() Checks if the instance is in the same day as the current moment.
+ * @method bool isNextDay() Checks if the instance is in the same day as the current moment next day.
+ * @method bool isLastDay() Checks if the instance is in the same day as the current moment last day.
+ * @method bool isSameHour(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same hour as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentHour() Checks if the instance is in the same hour as the current moment.
+ * @method bool isNextHour() Checks if the instance is in the same hour as the current moment next hour.
+ * @method bool isLastHour() Checks if the instance is in the same hour as the current moment last hour.
+ * @method bool isSameMinute(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same minute as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMinute() Checks if the instance is in the same minute as the current moment.
+ * @method bool isNextMinute() Checks if the instance is in the same minute as the current moment next minute.
+ * @method bool isLastMinute() Checks if the instance is in the same minute as the current moment last minute.
+ * @method bool isSameSecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same second as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentSecond() Checks if the instance is in the same second as the current moment.
+ * @method bool isNextSecond() Checks if the instance is in the same second as the current moment next second.
+ * @method bool isLastSecond() Checks if the instance is in the same second as the current moment last second.
+ * @method bool isSameMicro(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicro() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicro() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicro() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isSameMicrosecond(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same microsecond as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMicrosecond() Checks if the instance is in the same microsecond as the current moment.
+ * @method bool isNextMicrosecond() Checks if the instance is in the same microsecond as the current moment next microsecond.
+ * @method bool isLastMicrosecond() Checks if the instance is in the same microsecond as the current moment last microsecond.
+ * @method bool isCurrentMonth() Checks if the instance is in the same month as the current moment.
+ * @method bool isNextMonth() Checks if the instance is in the same month as the current moment next month.
+ * @method bool isLastMonth() Checks if the instance is in the same month as the current moment last month.
+ * @method bool isCurrentQuarter() Checks if the instance is in the same quarter as the current moment.
+ * @method bool isNextQuarter() Checks if the instance is in the same quarter as the current moment next quarter.
+ * @method bool isLastQuarter() Checks if the instance is in the same quarter as the current moment last quarter.
+ * @method bool isSameDecade(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same decade as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentDecade() Checks if the instance is in the same decade as the current moment.
+ * @method bool isNextDecade() Checks if the instance is in the same decade as the current moment next decade.
+ * @method bool isLastDecade() Checks if the instance is in the same decade as the current moment last decade.
+ * @method bool isSameCentury(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same century as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentCentury() Checks if the instance is in the same century as the current moment.
+ * @method bool isNextCentury() Checks if the instance is in the same century as the current moment next century.
+ * @method bool isLastCentury() Checks if the instance is in the same century as the current moment last century.
+ * @method bool isSameMillennium(Carbon|DateTimeInterface|string|null $date = null) Checks if the given date is in the same millennium as the instance. If null passed, compare to now (with the same timezone).
+ * @method bool isCurrentMillennium() Checks if the instance is in the same millennium as the current moment.
+ * @method bool isNextMillennium() Checks if the instance is in the same millennium as the current moment next millennium.
+ * @method bool isLastMillennium() Checks if the instance is in the same millennium as the current moment last millennium.
+ * @method CarbonInterface years(int $value) Set current instance year to the given value.
+ * @method CarbonInterface year(int $value) Set current instance year to the given value.
+ * @method CarbonInterface setYears(int $value) Set current instance year to the given value.
+ * @method CarbonInterface setYear(int $value) Set current instance year to the given value.
+ * @method CarbonInterface months(int $value) Set current instance month to the given value.
+ * @method CarbonInterface month(int $value) Set current instance month to the given value.
+ * @method CarbonInterface setMonths(int $value) Set current instance month to the given value.
+ * @method CarbonInterface setMonth(int $value) Set current instance month to the given value.
+ * @method CarbonInterface days(int $value) Set current instance day to the given value.
+ * @method CarbonInterface day(int $value) Set current instance day to the given value.
+ * @method CarbonInterface setDays(int $value) Set current instance day to the given value.
+ * @method CarbonInterface setDay(int $value) Set current instance day to the given value.
+ * @method CarbonInterface hours(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface hour(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface setHours(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface setHour(int $value) Set current instance hour to the given value.
+ * @method CarbonInterface minutes(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface minute(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface setMinutes(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface setMinute(int $value) Set current instance minute to the given value.
+ * @method CarbonInterface seconds(int $value) Set current instance second to the given value.
+ * @method CarbonInterface second(int $value) Set current instance second to the given value.
+ * @method CarbonInterface setSeconds(int $value) Set current instance second to the given value.
+ * @method CarbonInterface setSecond(int $value) Set current instance second to the given value.
+ * @method CarbonInterface millis(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface milli(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMillis(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMilli(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface milliseconds(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface millisecond(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMilliseconds(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface setMillisecond(int $value) Set current instance millisecond to the given value.
+ * @method CarbonInterface micros(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface micro(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicros(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicro(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface microseconds(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface microsecond(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicroseconds(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface setMicrosecond(int $value) Set current instance microsecond to the given value.
+ * @method CarbonInterface addYears(int $value = 1) Add years (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addYear() Add one year to the instance (using date interval).
+ * @method CarbonInterface subYears(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subYear() Sub one year to the instance (using date interval).
+ * @method CarbonInterface addYearsWithOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addYearWithOverflow() Add one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subYearsWithOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subYearWithOverflow() Sub one year to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addYearsWithoutOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearWithoutOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearsWithoutOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearWithoutOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearsWithNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearWithNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearsWithNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearWithNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearsNoOverflow(int $value = 1) Add years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addYearNoOverflow() Add one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearsNoOverflow(int $value = 1) Sub years (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subYearNoOverflow() Sub one year to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonths(int $value = 1) Add months (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMonth() Add one month to the instance (using date interval).
+ * @method CarbonInterface subMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMonth() Sub one month to the instance (using date interval).
+ * @method CarbonInterface addMonthsWithOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMonthWithOverflow() Add one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMonthsWithOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMonthWithOverflow() Sub one month to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMonthsWithoutOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthWithoutOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthsWithoutOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthWithoutOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthsWithNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthWithNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthsWithNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthWithNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthsNoOverflow(int $value = 1) Add months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMonthNoOverflow() Add one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthsNoOverflow(int $value = 1) Sub months (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMonthNoOverflow() Sub one month to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDays(int $value = 1) Add days (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addDay() Add one day to the instance (using date interval).
+ * @method CarbonInterface subDays(int $value = 1) Sub days (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subDay() Sub one day to the instance (using date interval).
+ * @method CarbonInterface addHours(int $value = 1) Add hours (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addHour() Add one hour to the instance (using date interval).
+ * @method CarbonInterface subHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subHour() Sub one hour to the instance (using date interval).
+ * @method CarbonInterface addMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMinute() Add one minute to the instance (using date interval).
+ * @method CarbonInterface subMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMinute() Sub one minute to the instance (using date interval).
+ * @method CarbonInterface addSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addSecond() Add one second to the instance (using date interval).
+ * @method CarbonInterface subSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subSecond() Sub one second to the instance (using date interval).
+ * @method CarbonInterface addMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMilli() Add one millisecond to the instance (using date interval).
+ * @method CarbonInterface subMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMilli() Sub one millisecond to the instance (using date interval).
+ * @method CarbonInterface addMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMillisecond() Add one millisecond to the instance (using date interval).
+ * @method CarbonInterface subMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMillisecond() Sub one millisecond to the instance (using date interval).
+ * @method CarbonInterface addMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMicro() Add one microsecond to the instance (using date interval).
+ * @method CarbonInterface subMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMicro() Sub one microsecond to the instance (using date interval).
+ * @method CarbonInterface addMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMicrosecond() Add one microsecond to the instance (using date interval).
+ * @method CarbonInterface subMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMicrosecond() Sub one microsecond to the instance (using date interval).
+ * @method CarbonInterface addMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addMillennium() Add one millennium to the instance (using date interval).
+ * @method CarbonInterface subMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subMillennium() Sub one millennium to the instance (using date interval).
+ * @method CarbonInterface addMillenniaWithOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMillenniumWithOverflow() Add one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMillenniaWithOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subMillenniumWithOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addMillenniaWithoutOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniumWithoutOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniaWithoutOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniumWithoutOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniaWithNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniumWithNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniaWithNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniumWithNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniaNoOverflow(int $value = 1) Add millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addMillenniumNoOverflow() Add one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniaNoOverflow(int $value = 1) Sub millennia (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subMillenniumNoOverflow() Sub one millennium to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addCentury() Add one century to the instance (using date interval).
+ * @method CarbonInterface subCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subCentury() Sub one century to the instance (using date interval).
+ * @method CarbonInterface addCenturiesWithOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addCenturyWithOverflow() Add one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subCenturiesWithOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subCenturyWithOverflow() Sub one century to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addCenturiesWithoutOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturyWithoutOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturiesWithoutOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturyWithoutOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturiesWithNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturyWithNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturiesWithNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturyWithNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturiesNoOverflow(int $value = 1) Add centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addCenturyNoOverflow() Add one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturiesNoOverflow(int $value = 1) Sub centuries (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subCenturyNoOverflow() Sub one century to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addDecade() Add one decade to the instance (using date interval).
+ * @method CarbonInterface subDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subDecade() Sub one decade to the instance (using date interval).
+ * @method CarbonInterface addDecadesWithOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addDecadeWithOverflow() Add one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subDecadesWithOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subDecadeWithOverflow() Sub one decade to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addDecadesWithoutOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadeWithoutOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadesWithoutOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadeWithoutOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadesWithNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadeWithNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadesWithNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadeWithNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadesNoOverflow(int $value = 1) Add decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addDecadeNoOverflow() Add one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadesNoOverflow(int $value = 1) Sub decades (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subDecadeNoOverflow() Sub one decade to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addQuarter() Add one quarter to the instance (using date interval).
+ * @method CarbonInterface subQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subQuarter() Sub one quarter to the instance (using date interval).
+ * @method CarbonInterface addQuartersWithOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addQuarterWithOverflow() Add one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subQuartersWithOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface subQuarterWithOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly allowed.
+ * @method CarbonInterface addQuartersWithoutOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarterWithoutOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuartersWithoutOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuarterWithoutOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuartersWithNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarterWithNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuartersWithNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuarterWithNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuartersNoOverflow(int $value = 1) Add quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addQuarterNoOverflow() Add one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuartersNoOverflow(int $value = 1) Sub quarters (the $value count passed in) to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface subQuarterNoOverflow() Sub one quarter to the instance (using date interval) with overflow explicitly forbidden.
+ * @method CarbonInterface addWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addWeek() Add one week to the instance (using date interval).
+ * @method CarbonInterface subWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subWeek() Sub one week to the instance (using date interval).
+ * @method CarbonInterface addWeekdays(int $value = 1) Add weekdays (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface addWeekday() Add one weekday to the instance (using date interval).
+ * @method CarbonInterface subWeekdays(int $value = 1) Sub weekdays (the $value count passed in) to the instance (using date interval).
+ * @method CarbonInterface subWeekday() Sub one weekday to the instance (using date interval).
+ * @method CarbonInterface addRealMicros(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMicro() Add one microsecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMicros(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMicro() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method CarbonInterface addRealMicroseconds(int $value = 1) Add microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMicrosecond() Add one microsecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMicroseconds(int $value = 1) Sub microseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMicrosecond() Sub one microsecond to the instance (using timestamp).
+ * @method CarbonPeriod microsecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each microsecond or every X microseconds if a factor is given.
+ * @method CarbonInterface addRealMillis(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMilli() Add one millisecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMillis(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMilli() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method CarbonInterface addRealMilliseconds(int $value = 1) Add milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMillisecond() Add one millisecond to the instance (using timestamp).
+ * @method CarbonInterface subRealMilliseconds(int $value = 1) Sub milliseconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMillisecond() Sub one millisecond to the instance (using timestamp).
+ * @method CarbonPeriod millisecondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millisecond or every X milliseconds if a factor is given.
+ * @method CarbonInterface addRealSeconds(int $value = 1) Add seconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealSecond() Add one second to the instance (using timestamp).
+ * @method CarbonInterface subRealSeconds(int $value = 1) Sub seconds (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealSecond() Sub one second to the instance (using timestamp).
+ * @method CarbonPeriod secondsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each second or every X seconds if a factor is given.
+ * @method CarbonInterface addRealMinutes(int $value = 1) Add minutes (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMinute() Add one minute to the instance (using timestamp).
+ * @method CarbonInterface subRealMinutes(int $value = 1) Sub minutes (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMinute() Sub one minute to the instance (using timestamp).
+ * @method CarbonPeriod minutesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each minute or every X minutes if a factor is given.
+ * @method CarbonInterface addRealHours(int $value = 1) Add hours (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealHour() Add one hour to the instance (using timestamp).
+ * @method CarbonInterface subRealHours(int $value = 1) Sub hours (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealHour() Sub one hour to the instance (using timestamp).
+ * @method CarbonPeriod hoursUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each hour or every X hours if a factor is given.
+ * @method CarbonInterface addRealDays(int $value = 1) Add days (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealDay() Add one day to the instance (using timestamp).
+ * @method CarbonInterface subRealDays(int $value = 1) Sub days (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealDay() Sub one day to the instance (using timestamp).
+ * @method CarbonPeriod daysUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each day or every X days if a factor is given.
+ * @method CarbonInterface addRealWeeks(int $value = 1) Add weeks (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealWeek() Add one week to the instance (using timestamp).
+ * @method CarbonInterface subRealWeeks(int $value = 1) Sub weeks (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealWeek() Sub one week to the instance (using timestamp).
+ * @method CarbonPeriod weeksUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each week or every X weeks if a factor is given.
+ * @method CarbonInterface addRealMonths(int $value = 1) Add months (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMonth() Add one month to the instance (using timestamp).
+ * @method CarbonInterface subRealMonths(int $value = 1) Sub months (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMonth() Sub one month to the instance (using timestamp).
+ * @method CarbonPeriod monthsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each month or every X months if a factor is given.
+ * @method CarbonInterface addRealQuarters(int $value = 1) Add quarters (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealQuarter() Add one quarter to the instance (using timestamp).
+ * @method CarbonInterface subRealQuarters(int $value = 1) Sub quarters (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealQuarter() Sub one quarter to the instance (using timestamp).
+ * @method CarbonPeriod quartersUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each quarter or every X quarters if a factor is given.
+ * @method CarbonInterface addRealYears(int $value = 1) Add years (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealYear() Add one year to the instance (using timestamp).
+ * @method CarbonInterface subRealYears(int $value = 1) Sub years (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealYear() Sub one year to the instance (using timestamp).
+ * @method CarbonPeriod yearsUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each year or every X years if a factor is given.
+ * @method CarbonInterface addRealDecades(int $value = 1) Add decades (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealDecade() Add one decade to the instance (using timestamp).
+ * @method CarbonInterface subRealDecades(int $value = 1) Sub decades (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealDecade() Sub one decade to the instance (using timestamp).
+ * @method CarbonPeriod decadesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each decade or every X decades if a factor is given.
+ * @method CarbonInterface addRealCenturies(int $value = 1) Add centuries (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealCentury() Add one century to the instance (using timestamp).
+ * @method CarbonInterface subRealCenturies(int $value = 1) Sub centuries (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealCentury() Sub one century to the instance (using timestamp).
+ * @method CarbonPeriod centuriesUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each century or every X centuries if a factor is given.
+ * @method CarbonInterface addRealMillennia(int $value = 1) Add millennia (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface addRealMillennium() Add one millennium to the instance (using timestamp).
+ * @method CarbonInterface subRealMillennia(int $value = 1) Sub millennia (the $value count passed in) to the instance (using timestamp).
+ * @method CarbonInterface subRealMillennium() Sub one millennium to the instance (using timestamp).
+ * @method CarbonPeriod millenniaUntil($endDate = null, int $factor = 1) Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each millennium or every X millennia if a factor is given.
+ * @method CarbonInterface roundYear(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method CarbonInterface roundYears(float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
+ * @method CarbonInterface floorYear(float $precision = 1) Truncate the current instance year with given precision.
+ * @method CarbonInterface floorYears(float $precision = 1) Truncate the current instance year with given precision.
+ * @method CarbonInterface ceilYear(float $precision = 1) Ceil the current instance year with given precision.
+ * @method CarbonInterface ceilYears(float $precision = 1) Ceil the current instance year with given precision.
+ * @method CarbonInterface roundMonth(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method CarbonInterface roundMonths(float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
+ * @method CarbonInterface floorMonth(float $precision = 1) Truncate the current instance month with given precision.
+ * @method CarbonInterface floorMonths(float $precision = 1) Truncate the current instance month with given precision.
+ * @method CarbonInterface ceilMonth(float $precision = 1) Ceil the current instance month with given precision.
+ * @method CarbonInterface ceilMonths(float $precision = 1) Ceil the current instance month with given precision.
+ * @method CarbonInterface roundDay(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method CarbonInterface roundDays(float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
+ * @method CarbonInterface floorDay(float $precision = 1) Truncate the current instance day with given precision.
+ * @method CarbonInterface floorDays(float $precision = 1) Truncate the current instance day with given precision.
+ * @method CarbonInterface ceilDay(float $precision = 1) Ceil the current instance day with given precision.
+ * @method CarbonInterface ceilDays(float $precision = 1) Ceil the current instance day with given precision.
+ * @method CarbonInterface roundHour(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method CarbonInterface roundHours(float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
+ * @method CarbonInterface floorHour(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method CarbonInterface floorHours(float $precision = 1) Truncate the current instance hour with given precision.
+ * @method CarbonInterface ceilHour(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method CarbonInterface ceilHours(float $precision = 1) Ceil the current instance hour with given precision.
+ * @method CarbonInterface roundMinute(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method CarbonInterface roundMinutes(float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
+ * @method CarbonInterface floorMinute(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method CarbonInterface floorMinutes(float $precision = 1) Truncate the current instance minute with given precision.
+ * @method CarbonInterface ceilMinute(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method CarbonInterface ceilMinutes(float $precision = 1) Ceil the current instance minute with given precision.
+ * @method CarbonInterface roundSecond(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method CarbonInterface roundSeconds(float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
+ * @method CarbonInterface floorSecond(float $precision = 1) Truncate the current instance second with given precision.
+ * @method CarbonInterface floorSeconds(float $precision = 1) Truncate the current instance second with given precision.
+ * @method CarbonInterface ceilSecond(float $precision = 1) Ceil the current instance second with given precision.
+ * @method CarbonInterface ceilSeconds(float $precision = 1) Ceil the current instance second with given precision.
+ * @method CarbonInterface roundMillennium(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method CarbonInterface roundMillennia(float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
+ * @method CarbonInterface floorMillennium(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method CarbonInterface floorMillennia(float $precision = 1) Truncate the current instance millennium with given precision.
+ * @method CarbonInterface ceilMillennium(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method CarbonInterface ceilMillennia(float $precision = 1) Ceil the current instance millennium with given precision.
+ * @method CarbonInterface roundCentury(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method CarbonInterface roundCenturies(float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
+ * @method CarbonInterface floorCentury(float $precision = 1) Truncate the current instance century with given precision.
+ * @method CarbonInterface floorCenturies(float $precision = 1) Truncate the current instance century with given precision.
+ * @method CarbonInterface ceilCentury(float $precision = 1) Ceil the current instance century with given precision.
+ * @method CarbonInterface ceilCenturies(float $precision = 1) Ceil the current instance century with given precision.
+ * @method CarbonInterface roundDecade(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method CarbonInterface roundDecades(float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
+ * @method CarbonInterface floorDecade(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method CarbonInterface floorDecades(float $precision = 1) Truncate the current instance decade with given precision.
+ * @method CarbonInterface ceilDecade(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method CarbonInterface ceilDecades(float $precision = 1) Ceil the current instance decade with given precision.
+ * @method CarbonInterface roundQuarter(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method CarbonInterface roundQuarters(float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
+ * @method CarbonInterface floorQuarter(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method CarbonInterface floorQuarters(float $precision = 1) Truncate the current instance quarter with given precision.
+ * @method CarbonInterface ceilQuarter(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method CarbonInterface ceilQuarters(float $precision = 1) Ceil the current instance quarter with given precision.
+ * @method CarbonInterface roundMillisecond(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method CarbonInterface roundMilliseconds(float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
+ * @method CarbonInterface floorMillisecond(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method CarbonInterface floorMilliseconds(float $precision = 1) Truncate the current instance millisecond with given precision.
+ * @method CarbonInterface ceilMillisecond(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method CarbonInterface ceilMilliseconds(float $precision = 1) Ceil the current instance millisecond with given precision.
+ * @method CarbonInterface roundMicrosecond(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method CarbonInterface roundMicroseconds(float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
+ * @method CarbonInterface floorMicrosecond(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method CarbonInterface floorMicroseconds(float $precision = 1) Truncate the current instance microsecond with given precision.
+ * @method CarbonInterface ceilMicrosecond(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method CarbonInterface ceilMicroseconds(float $precision = 1) Ceil the current instance microsecond with given precision.
+ * @method string shortAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longAbsoluteDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Absolute' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'Relative' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToNowDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToNow' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string shortRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (short format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ * @method string longRelativeToOtherDiffForHumans(DateTimeInterface $other = null, int $parts = 1) Get the difference (long format, 'RelativeToOther' mode) in a human readable format in the current locale. ($other and $parts parameters can be swapped.)
+ *
+ *
+ */
+trait Date
+{
+ use Boundaries;
+ use Comparison;
+ use Converter;
+ use Creator;
+ use Difference;
+ use Macro;
+ use MagicParameter;
+ use Modifiers;
+ use Mutability;
+ use ObjectInitialisation;
+ use Options;
+ use Rounding;
+ use Serialization;
+ use Test;
+ use Timestamp;
+ use Units;
+ use Week;
+
+ /**
+ * Names of days of the week.
+ *
+ * @var array
+ */
+ protected static $days = [
+ // @call isDayOfWeek
+ CarbonInterface::SUNDAY => 'Sunday',
+ // @call isDayOfWeek
+ CarbonInterface::MONDAY => 'Monday',
+ // @call isDayOfWeek
+ CarbonInterface::TUESDAY => 'Tuesday',
+ // @call isDayOfWeek
+ CarbonInterface::WEDNESDAY => 'Wednesday',
+ // @call isDayOfWeek
+ CarbonInterface::THURSDAY => 'Thursday',
+ // @call isDayOfWeek
+ CarbonInterface::FRIDAY => 'Friday',
+ // @call isDayOfWeek
+ CarbonInterface::SATURDAY => 'Saturday',
+ ];
+
+ /**
+ * Will UTF8 encoding be used to print localized date/time ?
+ *
+ * @var bool
+ */
+ protected static $utf8 = false;
+
+ /**
+ * List of unit and magic methods associated as doc-comments.
+ *
+ * @var array
+ */
+ protected static $units = [
+ // @call setUnit
+ // @call addUnit
+ 'year',
+ // @call setUnit
+ // @call addUnit
+ 'month',
+ // @call setUnit
+ // @call addUnit
+ 'day',
+ // @call setUnit
+ // @call addUnit
+ 'hour',
+ // @call setUnit
+ // @call addUnit
+ 'minute',
+ // @call setUnit
+ // @call addUnit
+ 'second',
+ // @call setUnit
+ // @call addUnit
+ 'milli',
+ // @call setUnit
+ // @call addUnit
+ 'millisecond',
+ // @call setUnit
+ // @call addUnit
+ 'micro',
+ // @call setUnit
+ // @call addUnit
+ 'microsecond',
+ ];
+
+ /**
+ * Creates a DateTimeZone from a string, DateTimeZone or integer offset.
+ *
+ * @param DateTimeZone|string|int|null $object original value to get CarbonTimeZone from it.
+ * @param DateTimeZone|string|int|null $objectDump dump of the object for error messages.
+ *
+ * @throws InvalidTimeZoneException
+ *
+ * @return CarbonTimeZone|false
+ */
+ protected static function safeCreateDateTimeZone($object, $objectDump = null)
+ {
+ return CarbonTimeZone::instance($object, $objectDump);
+ }
+
+ /**
+ * Get the TimeZone associated with the Carbon instance (as CarbonTimeZone).
+ *
+ * @return CarbonTimeZone
+ *
+ * @link https://php.net/manual/en/datetime.gettimezone.php
+ */
+ #[ReturnTypeWillChange]
+ public function getTimezone()
+ {
+ return CarbonTimeZone::instance(parent::getTimezone());
+ }
+
+ /**
+ * List of minimum and maximums for each unit.
+ *
+ * @param int $daysInMonth
+ *
+ * @return array
+ */
+ protected static function getRangesByUnit(int $daysInMonth = 31): array
+ {
+ return [
+ // @call roundUnit
+ 'year' => [1, 9999],
+ // @call roundUnit
+ 'month' => [1, static::MONTHS_PER_YEAR],
+ // @call roundUnit
+ 'day' => [1, $daysInMonth],
+ // @call roundUnit
+ 'hour' => [0, static::HOURS_PER_DAY - 1],
+ // @call roundUnit
+ 'minute' => [0, static::MINUTES_PER_HOUR - 1],
+ // @call roundUnit
+ 'second' => [0, static::SECONDS_PER_MINUTE - 1],
+ ];
+ }
+
+ /**
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function copy()
+ {
+ return clone $this;
+ }
+
+ /**
+ * @alias copy
+ *
+ * Get a copy of the instance.
+ *
+ * @return static
+ */
+ public function clone()
+ {
+ return clone $this;
+ }
+
+ /**
+ * Clone the current instance if it's mutable.
+ *
+ * This method is convenient to ensure you don't mutate the initial object
+ * but avoid to make a useless copy of it if it's already immutable.
+ *
+ * @return static
+ */
+ public function avoidMutation(): self
+ {
+ if ($this instanceof DateTimeImmutable) {
+ return $this;
+ }
+
+ return clone $this;
+ }
+
+ /**
+ * Returns a present instance in the same timezone.
+ *
+ * @return static
+ */
+ public function nowWithSameTz()
+ {
+ return static::now($this->getTimezone());
+ }
+
+ /**
+ * Throws an exception if the given object is not a DateTime and does not implement DateTimeInterface.
+ *
+ * @param mixed $date
+ * @param string|array $other
+ *
+ * @throws InvalidTypeException
+ */
+ protected static function expectDateTime($date, $other = [])
+ {
+ $message = 'Expected ';
+ foreach ((array) $other as $expect) {
+ $message .= "$expect, ";
+ }
+
+ if (!$date instanceof DateTime && !$date instanceof DateTimeInterface) {
+ throw new InvalidTypeException(
+ $message.'DateTime or DateTimeInterface, '.
+ (\is_object($date) ? \get_class($date) : \gettype($date)).' given'
+ );
+ }
+ }
+
+ /**
+ * Return the Carbon instance passed through, a now instance in the same timezone
+ * if null given or parse the input if string given.
+ *
+ * @param Carbon|DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ protected function resolveCarbon($date = null)
+ {
+ if (!$date) {
+ return $this->nowWithSameTz();
+ }
+
+ if (\is_string($date)) {
+ return static::parse($date, $this->getTimezone());
+ }
+
+ static::expectDateTime($date, ['null', 'string']);
+
+ return $date instanceof self ? $date : static::instance($date);
+ }
+
+ /**
+ * Return the Carbon instance passed through, a now instance in UTC
+ * if null given or parse the input if string given (using current timezone
+ * then switching to UTC).
+ *
+ * @param Carbon|DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ protected function resolveUTC($date = null): self
+ {
+ if (!$date) {
+ return static::now('UTC');
+ }
+
+ if (\is_string($date)) {
+ return static::parse($date, $this->getTimezone())->utc();
+ }
+
+ static::expectDateTime($date, ['null', 'string']);
+
+ return $date instanceof self ? $date : static::instance($date)->utc();
+ }
+
+ /**
+ * Return the Carbon instance passed through, a now instance in the same timezone
+ * if null given or parse the input if string given.
+ *
+ * @param Carbon|\Carbon\CarbonPeriod|\Carbon\CarbonInterval|\DateInterval|\DatePeriod|DateTimeInterface|string|null $date
+ *
+ * @return static
+ */
+ public function carbonize($date = null)
+ {
+ if ($date instanceof DateInterval) {
+ return $this->avoidMutation()->add($date);
+ }
+
+ if ($date instanceof DatePeriod || $date instanceof CarbonPeriod) {
+ $date = $date->getStartDate();
+ }
+
+ return $this->resolveCarbon($date);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// GETTERS AND SETTERS /////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a part of the Carbon object
+ *
+ * @param string $name
+ *
+ * @throws UnknownGetterException
+ *
+ * @return string|int|bool|DateTimeZone|null
+ */
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * Get a part of the Carbon object
+ *
+ * @param string $name
+ *
+ * @throws UnknownGetterException
+ *
+ * @return string|int|bool|DateTimeZone|null
+ */
+ public function get($name)
+ {
+ static $formats = [
+ // @property int
+ 'year' => 'Y',
+ // @property int
+ 'yearIso' => 'o',
+ // @property int
+ // @call isSameUnit
+ 'month' => 'n',
+ // @property int
+ 'day' => 'j',
+ // @property int
+ 'hour' => 'G',
+ // @property int
+ 'minute' => 'i',
+ // @property int
+ 'second' => 's',
+ // @property int
+ 'micro' => 'u',
+ // @property int
+ 'microsecond' => 'u',
+ // @property-read int 0 (for Sunday) through 6 (for Saturday)
+ 'dayOfWeek' => 'w',
+ // @property-read int 1 (for Monday) through 7 (for Sunday)
+ 'dayOfWeekIso' => 'N',
+ // @property-read int ISO-8601 week number of year, weeks starting on Monday
+ 'weekOfYear' => 'W',
+ // @property-read int number of days in the given month
+ 'daysInMonth' => 't',
+ // @property int|float|string seconds since the Unix Epoch
+ 'timestamp' => 'U',
+ // @property-read string "am"/"pm" (Ante meridiem or Post meridiem latin lowercase mark)
+ 'latinMeridiem' => 'a',
+ // @property-read string "AM"/"PM" (Ante meridiem or Post meridiem latin uppercase mark)
+ 'latinUpperMeridiem' => 'A',
+ // @property string the day of week in English
+ 'englishDayOfWeek' => 'l',
+ // @property string the abbreviated day of week in English
+ 'shortEnglishDayOfWeek' => 'D',
+ // @property string the month in English
+ 'englishMonth' => 'F',
+ // @property string the abbreviated month in English
+ 'shortEnglishMonth' => 'M',
+ // @property string the day of week in current locale LC_TIME
+ // @deprecated
+ // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ // replacement: Use ->isoFormat('MMM') instead.
+ // since: 2.55.0
+ 'localeDayOfWeek' => '%A',
+ // @property string the abbreviated day of week in current locale LC_TIME
+ // @deprecated
+ // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ // replacement: Use ->isoFormat('dddd') instead.
+ // since: 2.55.0
+ 'shortLocaleDayOfWeek' => '%a',
+ // @property string the month in current locale LC_TIME
+ // @deprecated
+ // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ // replacement: Use ->isoFormat('ddd') instead.
+ // since: 2.55.0
+ 'localeMonth' => '%B',
+ // @property string the abbreviated month in current locale LC_TIME
+ // @deprecated
+ // reason: It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ // replacement: Use ->isoFormat('MMMM') instead.
+ // since: 2.55.0
+ 'shortLocaleMonth' => '%b',
+ // @property-read string $timezoneAbbreviatedName the current timezone abbreviated name
+ 'timezoneAbbreviatedName' => 'T',
+ // @property-read string $tzAbbrName alias of $timezoneAbbreviatedName
+ 'tzAbbrName' => 'T',
+ ];
+
+ switch (true) {
+ case isset($formats[$name]):
+ $format = $formats[$name];
+ $method = str_starts_with($format, '%') ? 'formatLocalized' : 'rawFormat';
+ $value = $this->$method($format);
+
+ return is_numeric($value) ? (int) $value : $value;
+
+ // @property-read string long name of weekday translated according to Carbon locale, in english if no translation available for current language
+ case $name === 'dayName':
+ return $this->getTranslatedDayName();
+ // @property-read string short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ case $name === 'shortDayName':
+ return $this->getTranslatedShortDayName();
+ // @property-read string very short name of weekday translated according to Carbon locale, in english if no translation available for current language
+ case $name === 'minDayName':
+ return $this->getTranslatedMinDayName();
+ // @property-read string long name of month translated according to Carbon locale, in english if no translation available for current language
+ case $name === 'monthName':
+ return $this->getTranslatedMonthName();
+ // @property-read string short name of month translated according to Carbon locale, in english if no translation available for current language
+ case $name === 'shortMonthName':
+ return $this->getTranslatedShortMonthName();
+ // @property-read string lowercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ case $name === 'meridiem':
+ return $this->meridiem(true);
+ // @property-read string uppercase meridiem mark translated according to Carbon locale, in latin if no translation available for current language
+ case $name === 'upperMeridiem':
+ return $this->meridiem();
+ // @property-read int current hour from 1 to 24
+ case $name === 'noZeroHour':
+ return $this->hour ?: 24;
+ // @property int
+ case $name === 'milliseconds':
+ // @property int
+ case $name === 'millisecond':
+ // @property int
+ case $name === 'milli':
+ return (int) floor(((int) $this->rawFormat('u')) / 1000);
+
+ // @property int 1 through 53
+ case $name === 'week':
+ return (int) $this->week();
+
+ // @property int 1 through 53
+ case $name === 'isoWeek':
+ return (int) $this->isoWeek();
+
+ // @property int year according to week format
+ case $name === 'weekYear':
+ return (int) $this->weekYear();
+
+ // @property int year according to ISO week format
+ case $name === 'isoWeekYear':
+ return (int) $this->isoWeekYear();
+
+ // @property-read int 51 through 53
+ case $name === 'weeksInYear':
+ return $this->weeksInYear();
+
+ // @property-read int 51 through 53
+ case $name === 'isoWeeksInYear':
+ return $this->isoWeeksInYear();
+
+ // @property-read int 1 through 5
+ case $name === 'weekOfMonth':
+ return (int) ceil($this->day / static::DAYS_PER_WEEK);
+
+ // @property-read int 1 through 5
+ case $name === 'weekNumberInMonth':
+ return (int) ceil(($this->day + $this->avoidMutation()->startOfMonth()->dayOfWeekIso - 1) / static::DAYS_PER_WEEK);
+
+ // @property-read int 0 through 6
+ case $name === 'firstWeekDay':
+ return $this->localTranslator ? ($this->getTranslationMessage('first_day_of_week') ?? 0) : static::getWeekStartsAt();
+
+ // @property-read int 0 through 6
+ case $name === 'lastWeekDay':
+ return $this->localTranslator ? (($this->getTranslationMessage('first_day_of_week') ?? 0) + static::DAYS_PER_WEEK - 1) % static::DAYS_PER_WEEK : static::getWeekEndsAt();
+
+ // @property int 1 through 366
+ case $name === 'dayOfYear':
+ return 1 + (int) ($this->rawFormat('z'));
+
+ // @property-read int 365 or 366
+ case $name === 'daysInYear':
+ return $this->isLeapYear() ? 366 : 365;
+
+ // @property int does a diffInYears() with default parameters
+ case $name === 'age':
+ return $this->diffInYears();
+
+ // @property-read int the quarter of this instance, 1 - 4
+ // @call isSameUnit
+ case $name === 'quarter':
+ return (int) ceil($this->month / static::MONTHS_PER_QUARTER);
+
+ // @property-read int the decade of this instance
+ // @call isSameUnit
+ case $name === 'decade':
+ return (int) ceil($this->year / static::YEARS_PER_DECADE);
+
+ // @property-read int the century of this instance
+ // @call isSameUnit
+ case $name === 'century':
+ $factor = 1;
+ $year = $this->year;
+ if ($year < 0) {
+ $year = -$year;
+ $factor = -1;
+ }
+
+ return (int) ($factor * ceil($year / static::YEARS_PER_CENTURY));
+
+ // @property-read int the millennium of this instance
+ // @call isSameUnit
+ case $name === 'millennium':
+ $factor = 1;
+ $year = $this->year;
+ if ($year < 0) {
+ $year = -$year;
+ $factor = -1;
+ }
+
+ return (int) ($factor * ceil($year / static::YEARS_PER_MILLENNIUM));
+
+ // @property int the timezone offset in seconds from UTC
+ case $name === 'offset':
+ return $this->getOffset();
+
+ // @property int the timezone offset in minutes from UTC
+ case $name === 'offsetMinutes':
+ return $this->getOffset() / static::SECONDS_PER_MINUTE;
+
+ // @property int the timezone offset in hours from UTC
+ case $name === 'offsetHours':
+ return $this->getOffset() / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR;
+
+ // @property-read bool daylight savings time indicator, true if DST, false otherwise
+ case $name === 'dst':
+ return $this->rawFormat('I') === '1';
+
+ // @property-read bool checks if the timezone is local, true if local, false otherwise
+ case $name === 'local':
+ return $this->getOffset() === $this->avoidMutation()->setTimezone(date_default_timezone_get())->getOffset();
+
+ // @property-read bool checks if the timezone is UTC, true if UTC, false otherwise
+ case $name === 'utc':
+ return $this->getOffset() === 0;
+
+ // @property CarbonTimeZone $timezone the current timezone
+ // @property CarbonTimeZone $tz alias of $timezone
+ case $name === 'timezone' || $name === 'tz':
+ return CarbonTimeZone::instance($this->getTimezone());
+
+ // @property-read string $timezoneName the current timezone name
+ // @property-read string $tzName alias of $timezoneName
+ case $name === 'timezoneName' || $name === 'tzName':
+ return $this->getTimezone()->getName();
+
+ // @property-read string locale of the current instance
+ case $name === 'locale':
+ return $this->getTranslatorLocale();
+
+ default:
+ $macro = $this->getLocalMacro('get'.ucfirst($name));
+
+ if ($macro) {
+ return $this->executeCallableWithContext($macro);
+ }
+
+ throw new UnknownGetterException($name);
+ }
+ }
+
+ /**
+ * Check if an attribute exists on the object
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ try {
+ $this->__get($name);
+ } catch (UnknownGetterException | ReflectionException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Set a part of the Carbon object
+ *
+ * @param string $name
+ * @param string|int|DateTimeZone $value
+ *
+ * @throws UnknownSetterException|ReflectionException
+ *
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ if ($this->constructedObjectId === spl_object_hash($this)) {
+ $this->set($name, $value);
+
+ return;
+ }
+
+ $this->$name = $value;
+ }
+
+ /**
+ * Set a part of the Carbon object
+ *
+ * @param string|array $name
+ * @param string|int|DateTimeZone $value
+ *
+ * @throws ImmutableException|UnknownSetterException
+ *
+ * @return $this
+ */
+ public function set($name, $value = null)
+ {
+ if ($this->isImmutable()) {
+ throw new ImmutableException(sprintf('%s class', static::class));
+ }
+
+ if (\is_array($name)) {
+ foreach ($name as $key => $value) {
+ $this->set($key, $value);
+ }
+
+ return $this;
+ }
+
+ switch ($name) {
+ case 'milliseconds':
+ case 'millisecond':
+ case 'milli':
+ case 'microseconds':
+ case 'microsecond':
+ case 'micro':
+ if (str_starts_with($name, 'milli')) {
+ $value *= 1000;
+ }
+
+ while ($value < 0) {
+ $this->subSecond();
+ $value += static::MICROSECONDS_PER_SECOND;
+ }
+
+ while ($value >= static::MICROSECONDS_PER_SECOND) {
+ $this->addSecond();
+ $value -= static::MICROSECONDS_PER_SECOND;
+ }
+
+ $this->modify($this->rawFormat('H:i:s.').str_pad((string) round($value), 6, '0', STR_PAD_LEFT));
+
+ break;
+
+ case 'year':
+ case 'month':
+ case 'day':
+ case 'hour':
+ case 'minute':
+ case 'second':
+ [$year, $month, $day, $hour, $minute, $second] = array_map('intval', explode('-', $this->rawFormat('Y-n-j-G-i-s')));
+ $$name = $value;
+ $this->setDateTime($year, $month, $day, $hour, $minute, $second);
+
+ break;
+
+ case 'week':
+ $this->week($value);
+
+ break;
+
+ case 'isoWeek':
+ $this->isoWeek($value);
+
+ break;
+
+ case 'weekYear':
+ $this->weekYear($value);
+
+ break;
+
+ case 'isoWeekYear':
+ $this->isoWeekYear($value);
+
+ break;
+
+ case 'dayOfYear':
+ $this->addDays($value - $this->dayOfYear);
+
+ break;
+
+ case 'timestamp':
+ $this->setTimestamp($value);
+
+ break;
+
+ case 'offset':
+ $this->setTimezone(static::safeCreateDateTimeZone($value / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR));
+
+ break;
+
+ case 'offsetMinutes':
+ $this->setTimezone(static::safeCreateDateTimeZone($value / static::MINUTES_PER_HOUR));
+
+ break;
+
+ case 'offsetHours':
+ $this->setTimezone(static::safeCreateDateTimeZone($value));
+
+ break;
+
+ case 'timezone':
+ case 'tz':
+ $this->setTimezone($value);
+
+ break;
+
+ default:
+ $macro = $this->getLocalMacro('set'.ucfirst($name));
+
+ if ($macro) {
+ $this->executeCallableWithContext($macro, $value);
+
+ break;
+ }
+
+ if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) {
+ throw new UnknownSetterException($name);
+ }
+
+ $this->$name = $value;
+ }
+
+ return $this;
+ }
+
+ protected function getTranslatedFormByRegExp($baseKey, $keySuffix, $context, $subKey, $defaultValue)
+ {
+ $key = $baseKey.$keySuffix;
+ $standaloneKey = "{$key}_standalone";
+ $baseTranslation = $this->getTranslationMessage($key);
+
+ if ($baseTranslation instanceof Closure) {
+ return $baseTranslation($this, $context, $subKey) ?: $defaultValue;
+ }
+
+ if (
+ $this->getTranslationMessage("$standaloneKey.$subKey") &&
+ (!$context || (($regExp = $this->getTranslationMessage("{$baseKey}_regexp")) && !preg_match($regExp, $context)))
+ ) {
+ $key = $standaloneKey;
+ }
+
+ return $this->getTranslationMessage("$key.$subKey", null, $defaultValue);
+ }
+
+ /**
+ * Get the translation of the current week day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ * @param string $keySuffix "", "_short" or "_min"
+ * @param string|null $defaultValue default value if translation missing
+ *
+ * @return string
+ */
+ public function getTranslatedDayName($context = null, $keySuffix = '', $defaultValue = null)
+ {
+ return $this->getTranslatedFormByRegExp('weekdays', $keySuffix, $context, $this->dayOfWeek, $defaultValue ?: $this->englishDayOfWeek);
+ }
+
+ /**
+ * Get the translation of the current short week day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ *
+ * @return string
+ */
+ public function getTranslatedShortDayName($context = null)
+ {
+ return $this->getTranslatedDayName($context, '_short', $this->shortEnglishDayOfWeek);
+ }
+
+ /**
+ * Get the translation of the current abbreviated week day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ *
+ * @return string
+ */
+ public function getTranslatedMinDayName($context = null)
+ {
+ return $this->getTranslatedDayName($context, '_min', $this->shortEnglishDayOfWeek);
+ }
+
+ /**
+ * Get the translation of the current month day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ * @param string $keySuffix "" or "_short"
+ * @param string|null $defaultValue default value if translation missing
+ *
+ * @return string
+ */
+ public function getTranslatedMonthName($context = null, $keySuffix = '', $defaultValue = null)
+ {
+ return $this->getTranslatedFormByRegExp('months', $keySuffix, $context, $this->month - 1, $defaultValue ?: $this->englishMonth);
+ }
+
+ /**
+ * Get the translation of the current short month day name (with context for languages with multiple forms).
+ *
+ * @param string|null $context whole format string
+ *
+ * @return string
+ */
+ public function getTranslatedShortMonthName($context = null)
+ {
+ return $this->getTranslatedMonthName($context, '_short', $this->shortEnglishMonth);
+ }
+
+ /**
+ * Get/set the day of year.
+ *
+ * @param int|null $value new value for day of year if using as setter.
+ *
+ * @return static|int
+ */
+ public function dayOfYear($value = null)
+ {
+ $dayOfYear = $this->dayOfYear;
+
+ return $value === null ? $dayOfYear : $this->addDays($value - $dayOfYear);
+ }
+
+ /**
+ * Get/set the weekday from 0 (Sunday) to 6 (Saturday).
+ *
+ * @param int|null $value new value for weekday if using as setter.
+ *
+ * @return static|int
+ */
+ public function weekday($value = null)
+ {
+ if ($value === null) {
+ return $this->dayOfWeek;
+ }
+
+ $firstDay = (int) ($this->getTranslationMessage('first_day_of_week') ?? 0);
+ $dayOfWeek = ($this->dayOfWeek + 7 - $firstDay) % 7;
+
+ return $this->addDays((($value + 7 - $firstDay) % 7) - $dayOfWeek);
+ }
+
+ /**
+ * Get/set the ISO weekday from 1 (Monday) to 7 (Sunday).
+ *
+ * @param int|null $value new value for weekday if using as setter.
+ *
+ * @return static|int
+ */
+ public function isoWeekday($value = null)
+ {
+ $dayOfWeekIso = $this->dayOfWeekIso;
+
+ return $value === null ? $dayOfWeekIso : $this->addDays($value - $dayOfWeekIso);
+ }
+
+ /**
+ * Return the number of days since the start of the week (using the current locale or the first parameter
+ * if explicitly given).
+ *
+ * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week,
+ * if not provided, start of week is inferred from the locale
+ * (Sunday for en_US, Monday for de_DE, etc.)
+ *
+ * @return int
+ */
+ public function getDaysFromStartOfWeek(int $weekStartsAt = null): int
+ {
+ $firstDay = (int) ($weekStartsAt ?? $this->getTranslationMessage('first_day_of_week') ?? 0);
+
+ return ($this->dayOfWeek + 7 - $firstDay) % 7;
+ }
+
+ /**
+ * Set the day (keeping the current time) to the start of the week + the number of days passed as the first
+ * parameter. First day of week is driven by the locale unless explicitly set with the second parameter.
+ *
+ * @param int $numberOfDays number of days to add after the start of the current week
+ * @param int|null $weekStartsAt optional start allow you to specify the day of week to use to start the week,
+ * if not provided, start of week is inferred from the locale
+ * (Sunday for en_US, Monday for de_DE, etc.)
+ *
+ * @return static
+ */
+ public function setDaysFromStartOfWeek(int $numberOfDays, int $weekStartsAt = null)
+ {
+ return $this->addDays($numberOfDays - $this->getDaysFromStartOfWeek($weekStartsAt));
+ }
+
+ /**
+ * Set any unit to a new value without overflowing current other unit given.
+ *
+ * @param string $valueUnit unit name to modify
+ * @param int $value new value for the input unit
+ * @param string $overflowUnit unit name to not overflow
+ *
+ * @return static
+ */
+ public function setUnitNoOverflow($valueUnit, $value, $overflowUnit)
+ {
+ try {
+ $original = $this->avoidMutation();
+ /** @var static $date */
+ $date = $this->$valueUnit($value);
+ $end = $original->avoidMutation()->endOf($overflowUnit);
+ $start = $original->avoidMutation()->startOf($overflowUnit);
+ if ($date < $start) {
+ $date = $date->setDateTimeFrom($start);
+ } elseif ($date > $end) {
+ $date = $date->setDateTimeFrom($end);
+ }
+
+ return $date;
+ } catch (BadMethodCallException | ReflectionException $exception) {
+ throw new UnknownUnitException($valueUnit, 0, $exception);
+ }
+ }
+
+ /**
+ * Add any unit to a new value without overflowing current other unit given.
+ *
+ * @param string $valueUnit unit name to modify
+ * @param int $value amount to add to the input unit
+ * @param string $overflowUnit unit name to not overflow
+ *
+ * @return static
+ */
+ public function addUnitNoOverflow($valueUnit, $value, $overflowUnit)
+ {
+ return $this->setUnitNoOverflow($valueUnit, $this->$valueUnit + $value, $overflowUnit);
+ }
+
+ /**
+ * Subtract any unit to a new value without overflowing current other unit given.
+ *
+ * @param string $valueUnit unit name to modify
+ * @param int $value amount to subtract to the input unit
+ * @param string $overflowUnit unit name to not overflow
+ *
+ * @return static
+ */
+ public function subUnitNoOverflow($valueUnit, $value, $overflowUnit)
+ {
+ return $this->setUnitNoOverflow($valueUnit, $this->$valueUnit - $value, $overflowUnit);
+ }
+
+ /**
+ * Returns the minutes offset to UTC if no arguments passed, else set the timezone with given minutes shift passed.
+ *
+ * @param int|null $minuteOffset
+ *
+ * @return int|static
+ */
+ public function utcOffset(int $minuteOffset = null)
+ {
+ if (\func_num_args() < 1) {
+ return $this->offsetMinutes;
+ }
+
+ return $this->setTimezone(CarbonTimeZone::createFromMinuteOffset($minuteOffset));
+ }
+
+ /**
+ * Set the date with gregorian year, month and day numbers.
+ *
+ * @see https://php.net/manual/en/datetime.setdate.php
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setDate($year, $month, $day)
+ {
+ return parent::setDate((int) $year, (int) $month, (int) $day);
+ }
+
+ /**
+ * Set a date according to the ISO 8601 standard - using weeks and day offsets rather than specific dates.
+ *
+ * @see https://php.net/manual/en/datetime.setisodate.php
+ *
+ * @param int $year
+ * @param int $week
+ * @param int $day
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setISODate($year, $week, $day = 1)
+ {
+ return parent::setISODate((int) $year, (int) $week, (int) $day);
+ }
+
+ /**
+ * Set the date and time all together.
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ * @param int $hour
+ * @param int $minute
+ * @param int $second
+ * @param int $microseconds
+ *
+ * @return static
+ */
+ public function setDateTime($year, $month, $day, $hour, $minute, $second = 0, $microseconds = 0)
+ {
+ return $this->setDate($year, $month, $day)->setTime((int) $hour, (int) $minute, (int) $second, (int) $microseconds);
+ }
+
+ /**
+ * Resets the current time of the DateTime object to a different time.
+ *
+ * @see https://php.net/manual/en/datetime.settime.php
+ *
+ * @param int $hour
+ * @param int $minute
+ * @param int $second
+ * @param int $microseconds
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setTime($hour, $minute, $second = 0, $microseconds = 0)
+ {
+ return parent::setTime((int) $hour, (int) $minute, (int) $second, (int) $microseconds);
+ }
+
+ /**
+ * Set the instance's timestamp.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $unixTimestamp
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setTimestamp($unixTimestamp)
+ {
+ [$timestamp, $microseconds] = self::getIntegerAndDecimalParts($unixTimestamp);
+
+ return parent::setTimestamp((int) $timestamp)->setMicroseconds((int) $microseconds);
+ }
+
+ /**
+ * Set the time by time string.
+ *
+ * @param string $time
+ *
+ * @return static
+ */
+ public function setTimeFromTimeString($time)
+ {
+ if (!str_contains($time, ':')) {
+ $time .= ':0';
+ }
+
+ return $this->modify($time);
+ }
+
+ /**
+ * @alias setTimezone
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function timezone($value)
+ {
+ return $this->setTimezone($value);
+ }
+
+ /**
+ * Set the timezone or returns the timezone name if no arguments passed.
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static|string
+ */
+ public function tz($value = null)
+ {
+ if (\func_num_args() < 1) {
+ return $this->tzName;
+ }
+
+ return $this->setTimezone($value);
+ }
+
+ /**
+ * Set the instance's timezone from a string or object.
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function setTimezone($value)
+ {
+ $tz = static::safeCreateDateTimeZone($value);
+
+ if ($tz === false && !self::isStrictModeEnabled()) {
+ $tz = new CarbonTimeZone();
+ }
+
+ return parent::setTimezone($tz);
+ }
+
+ /**
+ * Set the instance's timezone from a string or object and add/subtract the offset difference.
+ *
+ * @param DateTimeZone|string $value
+ *
+ * @return static
+ */
+ public function shiftTimezone($value)
+ {
+ $dateTimeString = $this->format('Y-m-d H:i:s.u');
+
+ return $this
+ ->setTimezone($value)
+ ->modify($dateTimeString);
+ }
+
+ /**
+ * Set the instance's timezone to UTC.
+ *
+ * @return static
+ */
+ public function utc()
+ {
+ return $this->setTimezone('UTC');
+ }
+
+ /**
+ * Set the year, month, and date for this instance to that of the passed instance.
+ *
+ * @param Carbon|DateTimeInterface $date now if null
+ *
+ * @return static
+ */
+ public function setDateFrom($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->setDate($date->year, $date->month, $date->day);
+ }
+
+ /**
+ * Set the hour, minute, second and microseconds for this instance to that of the passed instance.
+ *
+ * @param Carbon|DateTimeInterface $date now if null
+ *
+ * @return static
+ */
+ public function setTimeFrom($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->setTime($date->hour, $date->minute, $date->second, $date->microsecond);
+ }
+
+ /**
+ * Set the date and time for this instance to that of the passed instance.
+ *
+ * @param Carbon|DateTimeInterface $date
+ *
+ * @return static
+ */
+ public function setDateTimeFrom($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->modify($date->rawFormat('Y-m-d H:i:s.u'));
+ }
+
+ /**
+ * Get the days of the week
+ *
+ * @return array
+ */
+ public static function getDays()
+ {
+ return static::$days;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// WEEK SPECIAL DAYS /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ private static function getFirstDayOfWeek(): int
+ {
+ return (int) static::getTranslationMessageWith(
+ static::getTranslator(),
+ 'first_day_of_week'
+ );
+ }
+
+ /**
+ * Get the first day of week
+ *
+ * @return int
+ */
+ public static function getWeekStartsAt()
+ {
+ if (static::$weekStartsAt === static::WEEK_DAY_AUTO) {
+ return self::getFirstDayOfWeek();
+ }
+
+ return static::$weekStartsAt;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekEndsAt optional parameter instead when using endOfWeek method. You can also use the
+ * 'first_day_of_week' locale setting to change the start of week according to current locale
+ * selected and implicitly the end of week.
+ *
+ * Set the first day of week
+ *
+ * @param int|string $day week start day (or 'auto' to get the first day of week from Carbon::getLocale() culture).
+ *
+ * @return void
+ */
+ public static function setWeekStartsAt($day)
+ {
+ static::$weekStartsAt = $day === static::WEEK_DAY_AUTO ? $day : max(0, (7 + $day) % 7);
+ }
+
+ /**
+ * Get the last day of week
+ *
+ * @return int
+ */
+ public static function getWeekEndsAt()
+ {
+ if (static::$weekStartsAt === static::WEEK_DAY_AUTO) {
+ return (int) (static::DAYS_PER_WEEK - 1 + self::getFirstDayOfWeek()) % static::DAYS_PER_WEEK;
+ }
+
+ return static::$weekEndsAt;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * Use $weekStartsAt optional parameter instead when using startOfWeek, floorWeek, ceilWeek
+ * or roundWeek method. You can also use the 'first_day_of_week' locale setting to change the
+ * start of week according to current locale selected and implicitly the end of week.
+ *
+ * Set the last day of week
+ *
+ * @param int|string $day week end day (or 'auto' to get the day before the first day of week
+ * from Carbon::getLocale() culture).
+ *
+ * @return void
+ */
+ public static function setWeekEndsAt($day)
+ {
+ static::$weekEndsAt = $day === static::WEEK_DAY_AUTO ? $day : max(0, (7 + $day) % 7);
+ }
+
+ /**
+ * Get weekend days
+ *
+ * @return array
+ */
+ public static function getWeekendDays()
+ {
+ return static::$weekendDays;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider week-end is always saturday and sunday, and if you have some custom
+ * week-end days to handle, give to those days an other name and create a macro for them:
+ *
+ * ```
+ * Carbon::macro('isDayOff', function ($date) {
+ * return $date->isSunday() || $date->isMonday();
+ * });
+ * Carbon::macro('isNotDayOff', function ($date) {
+ * return !$date->isDayOff();
+ * });
+ * if ($someDate->isDayOff()) ...
+ * if ($someDate->isNotDayOff()) ...
+ * // Add 5 not-off days
+ * $count = 5;
+ * while ($someDate->isDayOff() || ($count-- > 0)) {
+ * $someDate->addDay();
+ * }
+ * ```
+ *
+ * Set weekend days
+ *
+ * @param array $days
+ *
+ * @return void
+ */
+ public static function setWeekendDays($days)
+ {
+ static::$weekendDays = $days;
+ }
+
+ /**
+ * Determine if a time string will produce a relative date.
+ *
+ * @param string $time
+ *
+ * @return bool true if time match a relative date, false if absolute or invalid time string
+ */
+ public static function hasRelativeKeywords($time)
+ {
+ if (!$time || strtotime($time) === false) {
+ return false;
+ }
+
+ $date1 = new DateTime('2000-01-01T00:00:00Z');
+ $date1->modify($time);
+ $date2 = new DateTime('2001-12-25T00:00:00Z');
+ $date2->modify($time);
+
+ return $date1 != $date2;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /////////////////////// STRING FORMATTING /////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use UTF-8 language packages on every machine.
+ *
+ * Set if UTF8 will be used for localized date/time.
+ *
+ * @param bool $utf8
+ */
+ public static function setUtf8($utf8)
+ {
+ static::$utf8 = $utf8;
+ }
+
+ /**
+ * Format the instance with the current locale. You can set the current
+ * locale using setlocale() https://php.net/setlocale.
+ *
+ * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ * Use ->isoFormat() instead.
+ * Deprecated since 2.55.0
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function formatLocalized($format)
+ {
+ // Check for Windows to find and replace the %e modifier correctly.
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ $format = preg_replace('#(?toDateTimeString());
+ $formatted = ($this->localStrictModeEnabled ?? static::isStrictModeEnabled())
+ ? strftime($format, $time)
+ : @strftime($format, $time);
+
+ return static::$utf8
+ ? (
+ \function_exists('mb_convert_encoding')
+ ? mb_convert_encoding($formatted, 'UTF-8', mb_list_encodings())
+ : utf8_encode($formatted)
+ )
+ : $formatted;
+ }
+
+ /**
+ * Returns list of locale formats for ISO formatting.
+ *
+ * @param string|null $locale current locale used if null
+ *
+ * @return array
+ */
+ public function getIsoFormats($locale = null)
+ {
+ return [
+ 'LT' => $this->getTranslationMessage('formats.LT', $locale, 'h:mm A'),
+ 'LTS' => $this->getTranslationMessage('formats.LTS', $locale, 'h:mm:ss A'),
+ 'L' => $this->getTranslationMessage('formats.L', $locale, 'MM/DD/YYYY'),
+ 'LL' => $this->getTranslationMessage('formats.LL', $locale, 'MMMM D, YYYY'),
+ 'LLL' => $this->getTranslationMessage('formats.LLL', $locale, 'MMMM D, YYYY h:mm A'),
+ 'LLLL' => $this->getTranslationMessage('formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'),
+ 'l' => $this->getTranslationMessage('formats.l', $locale),
+ 'll' => $this->getTranslationMessage('formats.ll', $locale),
+ 'lll' => $this->getTranslationMessage('formats.lll', $locale),
+ 'llll' => $this->getTranslationMessage('formats.llll', $locale),
+ ];
+ }
+
+ /**
+ * Returns list of calendar formats for ISO formatting.
+ *
+ * @param string|null $locale current locale used if null
+ *
+ * @return array
+ */
+ public function getCalendarFormats($locale = null)
+ {
+ return [
+ 'sameDay' => $this->getTranslationMessage('calendar.sameDay', $locale, '[Today at] LT'),
+ 'nextDay' => $this->getTranslationMessage('calendar.nextDay', $locale, '[Tomorrow at] LT'),
+ 'nextWeek' => $this->getTranslationMessage('calendar.nextWeek', $locale, 'dddd [at] LT'),
+ 'lastDay' => $this->getTranslationMessage('calendar.lastDay', $locale, '[Yesterday at] LT'),
+ 'lastWeek' => $this->getTranslationMessage('calendar.lastWeek', $locale, '[Last] dddd [at] LT'),
+ 'sameElse' => $this->getTranslationMessage('calendar.sameElse', $locale, 'L'),
+ ];
+ }
+
+ /**
+ * Returns list of locale units for ISO formatting.
+ *
+ * @return array
+ */
+ public static function getIsoUnits()
+ {
+ static $units = null;
+
+ if ($units === null) {
+ $units = [
+ 'OD' => ['getAltNumber', ['day']],
+ 'OM' => ['getAltNumber', ['month']],
+ 'OY' => ['getAltNumber', ['year']],
+ 'OH' => ['getAltNumber', ['hour']],
+ 'Oh' => ['getAltNumber', ['h']],
+ 'Om' => ['getAltNumber', ['minute']],
+ 'Os' => ['getAltNumber', ['second']],
+ 'D' => 'day',
+ 'DD' => ['rawFormat', ['d']],
+ 'Do' => ['ordinal', ['day', 'D']],
+ 'd' => 'dayOfWeek',
+ 'dd' => function (CarbonInterface $date, $originalFormat = null) {
+ return $date->getTranslatedMinDayName($originalFormat);
+ },
+ 'ddd' => function (CarbonInterface $date, $originalFormat = null) {
+ return $date->getTranslatedShortDayName($originalFormat);
+ },
+ 'dddd' => function (CarbonInterface $date, $originalFormat = null) {
+ return $date->getTranslatedDayName($originalFormat);
+ },
+ 'DDD' => 'dayOfYear',
+ 'DDDD' => ['getPaddedUnit', ['dayOfYear', 3]],
+ 'DDDo' => ['ordinal', ['dayOfYear', 'DDD']],
+ 'e' => ['weekday', []],
+ 'E' => 'dayOfWeekIso',
+ 'H' => ['rawFormat', ['G']],
+ 'HH' => ['rawFormat', ['H']],
+ 'h' => ['rawFormat', ['g']],
+ 'hh' => ['rawFormat', ['h']],
+ 'k' => 'noZeroHour',
+ 'kk' => ['getPaddedUnit', ['noZeroHour']],
+ 'hmm' => ['rawFormat', ['gi']],
+ 'hmmss' => ['rawFormat', ['gis']],
+ 'Hmm' => ['rawFormat', ['Gi']],
+ 'Hmmss' => ['rawFormat', ['Gis']],
+ 'm' => 'minute',
+ 'mm' => ['rawFormat', ['i']],
+ 'a' => 'meridiem',
+ 'A' => 'upperMeridiem',
+ 's' => 'second',
+ 'ss' => ['getPaddedUnit', ['second']],
+ 'S' => function (CarbonInterface $date) {
+ return (string) floor($date->micro / 100000);
+ },
+ 'SS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro / 10000), 2, '0', STR_PAD_LEFT);
+ },
+ 'SSS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro / 1000), 3, '0', STR_PAD_LEFT);
+ },
+ 'SSSS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro / 100), 4, '0', STR_PAD_LEFT);
+ },
+ 'SSSSS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro / 10), 5, '0', STR_PAD_LEFT);
+ },
+ 'SSSSSS' => ['getPaddedUnit', ['micro', 6]],
+ 'SSSSSSS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro * 10), 7, '0', STR_PAD_LEFT);
+ },
+ 'SSSSSSSS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro * 100), 8, '0', STR_PAD_LEFT);
+ },
+ 'SSSSSSSSS' => function (CarbonInterface $date) {
+ return str_pad((string) floor($date->micro * 1000), 9, '0', STR_PAD_LEFT);
+ },
+ 'M' => 'month',
+ 'MM' => ['rawFormat', ['m']],
+ 'MMM' => function (CarbonInterface $date, $originalFormat = null) {
+ $month = $date->getTranslatedShortMonthName($originalFormat);
+ $suffix = $date->getTranslationMessage('mmm_suffix');
+ if ($suffix && $month !== $date->monthName) {
+ $month .= $suffix;
+ }
+
+ return $month;
+ },
+ 'MMMM' => function (CarbonInterface $date, $originalFormat = null) {
+ return $date->getTranslatedMonthName($originalFormat);
+ },
+ 'Mo' => ['ordinal', ['month', 'M']],
+ 'Q' => 'quarter',
+ 'Qo' => ['ordinal', ['quarter', 'M']],
+ 'G' => 'isoWeekYear',
+ 'GG' => ['getPaddedUnit', ['isoWeekYear']],
+ 'GGG' => ['getPaddedUnit', ['isoWeekYear', 3]],
+ 'GGGG' => ['getPaddedUnit', ['isoWeekYear', 4]],
+ 'GGGGG' => ['getPaddedUnit', ['isoWeekYear', 5]],
+ 'g' => 'weekYear',
+ 'gg' => ['getPaddedUnit', ['weekYear']],
+ 'ggg' => ['getPaddedUnit', ['weekYear', 3]],
+ 'gggg' => ['getPaddedUnit', ['weekYear', 4]],
+ 'ggggg' => ['getPaddedUnit', ['weekYear', 5]],
+ 'W' => 'isoWeek',
+ 'WW' => ['getPaddedUnit', ['isoWeek']],
+ 'Wo' => ['ordinal', ['isoWeek', 'W']],
+ 'w' => 'week',
+ 'ww' => ['getPaddedUnit', ['week']],
+ 'wo' => ['ordinal', ['week', 'w']],
+ 'x' => ['valueOf', []],
+ 'X' => 'timestamp',
+ 'Y' => 'year',
+ 'YY' => ['rawFormat', ['y']],
+ 'YYYY' => ['getPaddedUnit', ['year', 4]],
+ 'YYYYY' => ['getPaddedUnit', ['year', 5]],
+ 'YYYYYY' => function (CarbonInterface $date) {
+ return ($date->year < 0 ? '' : '+').$date->getPaddedUnit('year', 6);
+ },
+ 'z' => ['rawFormat', ['T']],
+ 'zz' => 'tzName',
+ 'Z' => ['getOffsetString', []],
+ 'ZZ' => ['getOffsetString', ['']],
+ ];
+ }
+
+ return $units;
+ }
+
+ /**
+ * Returns a unit of the instance padded with 0 by default or any other string if specified.
+ *
+ * @param string $unit Carbon unit name
+ * @param int $length Length of the output (2 by default)
+ * @param string $padString String to use for padding ("0" by default)
+ * @param int $padType Side(s) to pad (STR_PAD_LEFT by default)
+ *
+ * @return string
+ */
+ public function getPaddedUnit($unit, $length = 2, $padString = '0', $padType = STR_PAD_LEFT)
+ {
+ return ($this->$unit < 0 ? '-' : '').str_pad((string) abs($this->$unit), $length, $padString, $padType);
+ }
+
+ /**
+ * Return a property with its ordinal.
+ *
+ * @param string $key
+ * @param string|null $period
+ *
+ * @return string
+ */
+ public function ordinal(string $key, ?string $period = null): string
+ {
+ $number = $this->$key;
+ $result = $this->translate('ordinal', [
+ ':number' => $number,
+ ':period' => (string) $period,
+ ]);
+
+ return (string) ($result === 'ordinal' ? $number : $result);
+ }
+
+ /**
+ * Return the meridiem of the current time in the current locale.
+ *
+ * @param bool $isLower if true, returns lowercase variant if available in the current locale.
+ *
+ * @return string
+ */
+ public function meridiem(bool $isLower = false): string
+ {
+ $hour = $this->hour;
+ $index = $hour < 12 ? 0 : 1;
+
+ if ($isLower) {
+ $key = 'meridiem.'.($index + 2);
+ $result = $this->translate($key);
+
+ if ($result !== $key) {
+ return $result;
+ }
+ }
+
+ $key = "meridiem.$index";
+ $result = $this->translate($key);
+ if ($result === $key) {
+ $result = $this->translate('meridiem', [
+ ':hour' => $this->hour,
+ ':minute' => $this->minute,
+ ':isLower' => $isLower,
+ ]);
+
+ if ($result === 'meridiem') {
+ return $isLower ? $this->latinMeridiem : $this->latinUpperMeridiem;
+ }
+ } elseif ($isLower) {
+ $result = mb_strtolower($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the alternative number for a given date property if available in the current locale.
+ *
+ * @param string $key date property
+ *
+ * @return string
+ */
+ public function getAltNumber(string $key): string
+ {
+ return $this->translateNumber(\strlen($key) > 1 ? $this->$key : $this->rawFormat('h'));
+ }
+
+ /**
+ * Format in the current language using ISO replacement patterns.
+ *
+ * @param string $format
+ * @param string|null $originalFormat provide context if a chunk has been passed alone
+ *
+ * @return string
+ */
+ public function isoFormat(string $format, ?string $originalFormat = null): string
+ {
+ $result = '';
+ $length = mb_strlen($format);
+ $originalFormat = $originalFormat ?: $format;
+ $inEscaped = false;
+ $formats = null;
+ $units = null;
+
+ for ($i = 0; $i < $length; $i++) {
+ $char = mb_substr($format, $i, 1);
+
+ if ($char === '\\') {
+ $result .= mb_substr($format, ++$i, 1);
+
+ continue;
+ }
+
+ if ($char === '[' && !$inEscaped) {
+ $inEscaped = true;
+
+ continue;
+ }
+
+ if ($char === ']' && $inEscaped) {
+ $inEscaped = false;
+
+ continue;
+ }
+
+ if ($inEscaped) {
+ $result .= $char;
+
+ continue;
+ }
+
+ $input = mb_substr($format, $i);
+
+ if (preg_match('/^(LTS|LT|l{1,4}|L{1,4})/', $input, $match)) {
+ if ($formats === null) {
+ $formats = $this->getIsoFormats();
+ }
+
+ $code = $match[0];
+ $sequence = $formats[$code] ?? preg_replace_callback(
+ '/MMMM|MM|DD|dddd/',
+ function ($code) {
+ return mb_substr($code[0], 1);
+ },
+ $formats[strtoupper($code)] ?? ''
+ );
+ $rest = mb_substr($format, $i + mb_strlen($code));
+ $format = mb_substr($format, 0, $i).$sequence.$rest;
+ $length = mb_strlen($format);
+ $input = $sequence.$rest;
+ }
+
+ if (preg_match('/^'.CarbonInterface::ISO_FORMAT_REGEXP.'/', $input, $match)) {
+ $code = $match[0];
+
+ if ($units === null) {
+ $units = static::getIsoUnits();
+ }
+
+ $sequence = $units[$code] ?? '';
+
+ if ($sequence instanceof Closure) {
+ $sequence = $sequence($this, $originalFormat);
+ } elseif (\is_array($sequence)) {
+ try {
+ $sequence = $this->{$sequence[0]}(...$sequence[1]);
+ } catch (ReflectionException | InvalidArgumentException | BadMethodCallException $e) {
+ $sequence = '';
+ }
+ } elseif (\is_string($sequence)) {
+ $sequence = $this->$sequence ?? $code;
+ }
+
+ $format = mb_substr($format, 0, $i).$sequence.mb_substr($format, $i + mb_strlen($code));
+ $i += mb_strlen((string) $sequence) - 1;
+ $length = mb_strlen($format);
+ $char = $sequence;
+ }
+
+ $result .= $char;
+ }
+
+ return $result;
+ }
+
+ /**
+ * List of replacements from date() format to isoFormat().
+ *
+ * @return array
+ */
+ public static function getFormatsToIsoReplacements()
+ {
+ static $replacements = null;
+
+ if ($replacements === null) {
+ $replacements = [
+ 'd' => true,
+ 'D' => 'ddd',
+ 'j' => true,
+ 'l' => 'dddd',
+ 'N' => true,
+ 'S' => function ($date) {
+ $day = $date->rawFormat('j');
+
+ return str_replace((string) $day, '', $date->isoFormat('Do'));
+ },
+ 'w' => true,
+ 'z' => true,
+ 'W' => true,
+ 'F' => 'MMMM',
+ 'm' => true,
+ 'M' => 'MMM',
+ 'n' => true,
+ 't' => true,
+ 'L' => true,
+ 'o' => true,
+ 'Y' => true,
+ 'y' => true,
+ 'a' => 'a',
+ 'A' => 'A',
+ 'B' => true,
+ 'g' => true,
+ 'G' => true,
+ 'h' => true,
+ 'H' => true,
+ 'i' => true,
+ 's' => true,
+ 'u' => true,
+ 'v' => true,
+ 'E' => true,
+ 'I' => true,
+ 'O' => true,
+ 'P' => true,
+ 'Z' => true,
+ 'c' => true,
+ 'r' => true,
+ 'U' => true,
+ 'T' => true,
+ ];
+ }
+
+ return $replacements;
+ }
+
+ /**
+ * Format as ->format() do (using date replacements patterns from https://php.net/manual/en/function.date.php)
+ * but translate words whenever possible (months, day names, etc.) using the current locale.
+ *
+ * @param string $format
+ *
+ * @return string
+ */
+ public function translatedFormat(string $format): string
+ {
+ $replacements = static::getFormatsToIsoReplacements();
+ $context = '';
+ $isoFormat = '';
+ $length = mb_strlen($format);
+
+ for ($i = 0; $i < $length; $i++) {
+ $char = mb_substr($format, $i, 1);
+
+ if ($char === '\\') {
+ $replacement = mb_substr($format, $i, 2);
+ $isoFormat .= $replacement;
+ $i++;
+
+ continue;
+ }
+
+ if (!isset($replacements[$char])) {
+ $replacement = preg_match('/^[A-Za-z]$/', $char) ? "\\$char" : $char;
+ $isoFormat .= $replacement;
+ $context .= $replacement;
+
+ continue;
+ }
+
+ $replacement = $replacements[$char];
+
+ if ($replacement === true) {
+ static $contextReplacements = null;
+
+ if ($contextReplacements === null) {
+ $contextReplacements = [
+ 'm' => 'MM',
+ 'd' => 'DD',
+ 't' => 'D',
+ 'j' => 'D',
+ 'N' => 'e',
+ 'w' => 'e',
+ 'n' => 'M',
+ 'o' => 'YYYY',
+ 'Y' => 'YYYY',
+ 'y' => 'YY',
+ 'g' => 'h',
+ 'G' => 'H',
+ 'h' => 'hh',
+ 'H' => 'HH',
+ 'i' => 'mm',
+ 's' => 'ss',
+ ];
+ }
+
+ $isoFormat .= '['.$this->rawFormat($char).']';
+ $context .= $contextReplacements[$char] ?? ' ';
+
+ continue;
+ }
+
+ if ($replacement instanceof Closure) {
+ $replacement = '['.$replacement($this).']';
+ $isoFormat .= $replacement;
+ $context .= $replacement;
+
+ continue;
+ }
+
+ $isoFormat .= $replacement;
+ $context .= $replacement;
+ }
+
+ return $this->isoFormat($isoFormat, $context);
+ }
+
+ /**
+ * Returns the offset hour and minute formatted with +/- and a given separator (":" by default).
+ * For example, if the time zone is 9 hours 30 minutes, you'll get "+09:30", with "@@" as first
+ * argument, "+09@@30", with "" as first argument, "+0930". Negative offset will return something
+ * like "-12:00".
+ *
+ * @param string $separator string to place between hours and minutes (":" by default)
+ *
+ * @return string
+ */
+ public function getOffsetString($separator = ':')
+ {
+ $second = $this->getOffset();
+ $symbol = $second < 0 ? '-' : '+';
+ $minute = abs($second) / static::SECONDS_PER_MINUTE;
+ $hour = str_pad((string) floor($minute / static::MINUTES_PER_HOUR), 2, '0', STR_PAD_LEFT);
+ $minute = str_pad((string) (((int) $minute) % static::MINUTES_PER_HOUR), 2, '0', STR_PAD_LEFT);
+
+ return "$symbol$hour$separator$minute";
+ }
+
+ protected static function executeStaticCallable($macro, ...$parameters)
+ {
+ return static::bindMacroContext(null, function () use (&$macro, &$parameters) {
+ if ($macro instanceof Closure) {
+ $boundMacro = @Closure::bind($macro, null, static::class);
+
+ return ($boundMacro ?: $macro)(...$parameters);
+ }
+
+ return $macro(...$parameters);
+ });
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method magic method name called
+ * @param array $parameters parameters list
+ *
+ * @throws BadMethodCallException
+ *
+ * @return mixed
+ */
+ public static function __callStatic($method, $parameters)
+ {
+ if (!static::hasMacro($method)) {
+ foreach (static::getGenericMacros() as $callback) {
+ try {
+ return static::executeStaticCallable($callback, $method, ...$parameters);
+ } catch (BadMethodCallException $exception) {
+ continue;
+ }
+ }
+ if (static::isStrictModeEnabled()) {
+ throw new UnknownMethodException(sprintf('%s::%s', static::class, $method));
+ }
+
+ return null;
+ }
+
+ return static::executeStaticCallable(static::$globalMacros[$method], ...$parameters);
+ }
+
+ /**
+ * Set specified unit to new given value.
+ *
+ * @param string $unit year, month, day, hour, minute, second or microsecond
+ * @param int $value new value for given unit
+ *
+ * @return static
+ */
+ public function setUnit($unit, $value = null)
+ {
+ $unit = static::singularUnit($unit);
+ $dateUnits = ['year', 'month', 'day'];
+ if (\in_array($unit, $dateUnits)) {
+ return $this->setDate(...array_map(function ($name) use ($unit, $value) {
+ return (int) ($name === $unit ? $value : $this->$name);
+ }, $dateUnits));
+ }
+
+ $units = ['hour', 'minute', 'second', 'micro'];
+ if ($unit === 'millisecond' || $unit === 'milli') {
+ $value *= 1000;
+ $unit = 'micro';
+ } elseif ($unit === 'microsecond') {
+ $unit = 'micro';
+ }
+
+ return $this->setTime(...array_map(function ($name) use ($unit, $value) {
+ return (int) ($name === $unit ? $value : $this->$name);
+ }, $units));
+ }
+
+ /**
+ * Returns standardized singular of a given singular/plural unit name (in English).
+ *
+ * @param string $unit
+ *
+ * @return string
+ */
+ public static function singularUnit(string $unit): string
+ {
+ $unit = rtrim(mb_strtolower($unit), 's');
+
+ if ($unit === 'centurie') {
+ return 'century';
+ }
+
+ if ($unit === 'millennia') {
+ return 'millennium';
+ }
+
+ return $unit;
+ }
+
+ /**
+ * Returns standardized plural of a given singular/plural unit name (in English).
+ *
+ * @param string $unit
+ *
+ * @return string
+ */
+ public static function pluralUnit(string $unit): string
+ {
+ $unit = rtrim(strtolower($unit), 's');
+
+ if ($unit === 'century') {
+ return 'centuries';
+ }
+
+ if ($unit === 'millennium' || $unit === 'millennia') {
+ return 'millennia';
+ }
+
+ return "{$unit}s";
+ }
+
+ protected function executeCallable($macro, ...$parameters)
+ {
+ if ($macro instanceof Closure) {
+ $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class);
+
+ return ($boundMacro ?: $macro)(...$parameters);
+ }
+
+ return $macro(...$parameters);
+ }
+
+ protected function executeCallableWithContext($macro, ...$parameters)
+ {
+ return static::bindMacroContext($this, function () use (&$macro, &$parameters) {
+ return $this->executeCallable($macro, ...$parameters);
+ });
+ }
+
+ protected static function getGenericMacros()
+ {
+ foreach (static::$globalGenericMacros as $list) {
+ foreach ($list as $macro) {
+ yield $macro;
+ }
+ }
+ }
+
+ /**
+ * Dynamically handle calls to the class.
+ *
+ * @param string $method magic method name called
+ * @param array $parameters parameters list
+ *
+ * @throws UnknownMethodException|BadMethodCallException|ReflectionException|Throwable
+ *
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ $diffSizes = [
+ // @mode diffForHumans
+ 'short' => true,
+ // @mode diffForHumans
+ 'long' => false,
+ ];
+ $diffSyntaxModes = [
+ // @call diffForHumans
+ 'Absolute' => CarbonInterface::DIFF_ABSOLUTE,
+ // @call diffForHumans
+ 'Relative' => CarbonInterface::DIFF_RELATIVE_AUTO,
+ // @call diffForHumans
+ 'RelativeToNow' => CarbonInterface::DIFF_RELATIVE_TO_NOW,
+ // @call diffForHumans
+ 'RelativeToOther' => CarbonInterface::DIFF_RELATIVE_TO_OTHER,
+ ];
+ $sizePattern = implode('|', array_keys($diffSizes));
+ $syntaxPattern = implode('|', array_keys($diffSyntaxModes));
+
+ if (preg_match("/^(?$sizePattern)(?$syntaxPattern)DiffForHumans$/", $method, $match)) {
+ $dates = array_filter($parameters, function ($parameter) {
+ return $parameter instanceof DateTimeInterface;
+ });
+ $other = null;
+
+ if (\count($dates)) {
+ $key = key($dates);
+ $other = current($dates);
+ array_splice($parameters, $key, 1);
+ }
+
+ return $this->diffForHumans($other, $diffSyntaxModes[$match['syntax']], $diffSizes[$match['size']], ...$parameters);
+ }
+
+ $roundedValue = $this->callRoundMethod($method, $parameters);
+
+ if ($roundedValue !== null) {
+ return $roundedValue;
+ }
+
+ $unit = rtrim($method, 's');
+
+ if (str_starts_with($unit, 'is')) {
+ $word = substr($unit, 2);
+
+ if (\in_array($word, static::$days, true)) {
+ return $this->isDayOfWeek($word);
+ }
+
+ switch ($word) {
+ // @call is Check if the current instance has UTC timezone. (Both isUtc and isUTC cases are valid.)
+ case 'Utc':
+ case 'UTC':
+ return $this->utc;
+ // @call is Check if the current instance has non-UTC timezone.
+ case 'Local':
+ return $this->local;
+ // @call is Check if the current instance is a valid date.
+ case 'Valid':
+ return $this->year !== 0;
+ // @call is Check if the current instance is in a daylight saving time.
+ case 'DST':
+ return $this->dst;
+ }
+ }
+
+ $action = substr($unit, 0, 3);
+ $overflow = null;
+
+ if ($action === 'set') {
+ $unit = strtolower(substr($unit, 3));
+ }
+
+ if (\in_array($unit, static::$units, true)) {
+ return $this->setUnit($unit, ...$parameters);
+ }
+
+ if ($action === 'add' || $action === 'sub') {
+ $unit = substr($unit, 3);
+
+ if (str_starts_with($unit, 'Real')) {
+ $unit = static::singularUnit(substr($unit, 4));
+
+ return $this->{"{$action}RealUnit"}($unit, ...$parameters);
+ }
+
+ if (preg_match('/^(Month|Quarter|Year|Decade|Century|Centurie|Millennium|Millennia)s?(No|With|Without|WithNo)Overflow$/', $unit, $match)) {
+ $unit = $match[1];
+ $overflow = $match[2] === 'With';
+ }
+
+ $unit = static::singularUnit($unit);
+ }
+
+ if (static::isModifiableUnit($unit)) {
+ return $this->{"{$action}Unit"}($unit, $this->getMagicParameter($parameters, 0, 'value', 1), $overflow);
+ }
+
+ $sixFirstLetters = substr($unit, 0, 6);
+ $factor = -1;
+
+ if ($sixFirstLetters === 'isLast') {
+ $sixFirstLetters = 'isNext';
+ $factor = 1;
+ }
+
+ if ($sixFirstLetters === 'isNext') {
+ $lowerUnit = strtolower(substr($unit, 6));
+
+ if (static::isModifiableUnit($lowerUnit)) {
+ return $this->copy()->addUnit($lowerUnit, $factor, false)->isSameUnit($lowerUnit, ...$parameters);
+ }
+ }
+
+ if ($sixFirstLetters === 'isSame') {
+ try {
+ return $this->isSameUnit(strtolower(substr($unit, 6)), ...$parameters);
+ } catch (BadComparisonUnitException $exception) {
+ // Try next
+ }
+ }
+
+ if (str_starts_with($unit, 'isCurrent')) {
+ try {
+ return $this->isCurrentUnit(strtolower(substr($unit, 9)));
+ } catch (BadComparisonUnitException | BadMethodCallException $exception) {
+ // Try next
+ }
+ }
+
+ if (str_ends_with($method, 'Until')) {
+ try {
+ $unit = static::singularUnit(substr($method, 0, -5));
+
+ return $this->range(
+ $this->getMagicParameter($parameters, 0, 'endDate', $this),
+ $this->getMagicParameter($parameters, 1, 'factor', 1),
+ $unit
+ );
+ } catch (InvalidArgumentException $exception) {
+ // Try macros
+ }
+ }
+
+ return static::bindMacroContext($this, function () use (&$method, &$parameters) {
+ $macro = $this->getLocalMacro($method);
+
+ if (!$macro) {
+ foreach ([$this->localGenericMacros ?: [], static::getGenericMacros()] as $list) {
+ foreach ($list as $callback) {
+ try {
+ return $this->executeCallable($callback, $method, ...$parameters);
+ } catch (BadMethodCallException $exception) {
+ continue;
+ }
+ }
+ }
+
+ if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) {
+ throw new UnknownMethodException($method);
+ }
+
+ return null;
+ }
+
+ return $this->executeCallable($macro, ...$parameters);
+ });
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php
new file mode 100644
index 0000000..5acc6f5
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/DeprecatedProperties.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+trait DeprecatedProperties
+{
+ /**
+ * the day of week in current locale LC_TIME
+ *
+ * @var string
+ *
+ * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ * Use ->isoFormat('MMM') instead.
+ * Deprecated since 2.55.0
+ */
+ public $localeDayOfWeek;
+
+ /**
+ * the abbreviated day of week in current locale LC_TIME
+ *
+ * @var string
+ *
+ * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ * Use ->isoFormat('dddd') instead.
+ * Deprecated since 2.55.0
+ */
+ public $shortLocaleDayOfWeek;
+
+ /**
+ * the month in current locale LC_TIME
+ *
+ * @var string
+ *
+ * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ * Use ->isoFormat('ddd') instead.
+ * Deprecated since 2.55.0
+ */
+ public $localeMonth;
+
+ /**
+ * the abbreviated month in current locale LC_TIME
+ *
+ * @var string
+ *
+ * @deprecated It uses OS language package and strftime() which is deprecated since PHP 8.1.
+ * Use ->isoFormat('MMMM') instead.
+ * Deprecated since 2.55.0
+ */
+ public $shortLocaleMonth;
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php b/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php
new file mode 100644
index 0000000..ab5b65d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Difference.php
@@ -0,0 +1,1182 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Carbon;
+use Carbon\CarbonImmutable;
+use Carbon\CarbonInterface;
+use Carbon\CarbonInterval;
+use Carbon\CarbonPeriod;
+use Carbon\Translator;
+use Closure;
+use DateInterval;
+use DateTimeInterface;
+use ReturnTypeWillChange;
+
+/**
+ * Trait Difference.
+ *
+ * Depends on the following methods:
+ *
+ * @method bool lessThan($date)
+ * @method static copy()
+ * @method static resolveCarbon($date = null)
+ * @method static Translator translator()
+ */
+trait Difference
+{
+ /**
+ * @codeCoverageIgnore
+ *
+ * @param CarbonInterval $diff
+ */
+ protected static function fixNegativeMicroseconds(CarbonInterval $diff)
+ {
+ if ($diff->s !== 0 || $diff->i !== 0 || $diff->h !== 0 || $diff->d !== 0 || $diff->m !== 0 || $diff->y !== 0) {
+ $diff->f = (round($diff->f * 1000000) + 1000000) / 1000000;
+ $diff->s--;
+
+ if ($diff->s < 0) {
+ $diff->s += 60;
+ $diff->i--;
+
+ if ($diff->i < 0) {
+ $diff->i += 60;
+ $diff->h--;
+
+ if ($diff->h < 0) {
+ $diff->h += 24;
+ $diff->d--;
+
+ if ($diff->d < 0) {
+ $diff->d += 30;
+ $diff->m--;
+
+ if ($diff->m < 0) {
+ $diff->m += 12;
+ $diff->y--;
+ }
+ }
+ }
+ }
+ }
+
+ return;
+ }
+
+ $diff->f *= -1;
+ $diff->invert();
+ }
+
+ /**
+ * @param DateInterval $diff
+ * @param bool $absolute
+ *
+ * @return CarbonInterval
+ */
+ protected static function fixDiffInterval(DateInterval $diff, $absolute, array $skip = [])
+ {
+ $diff = CarbonInterval::instance($diff, $skip);
+
+ // Work-around for https://bugs.php.net/bug.php?id=77145
+ // @codeCoverageIgnoreStart
+ if ($diff->f > 0 && $diff->y === -1 && $diff->m === 11 && $diff->d >= 27 && $diff->h === 23 && $diff->i === 59 && $diff->s === 59) {
+ $diff->y = 0;
+ $diff->m = 0;
+ $diff->d = 0;
+ $diff->h = 0;
+ $diff->i = 0;
+ $diff->s = 0;
+ $diff->f = (1000000 - round($diff->f * 1000000)) / 1000000;
+ $diff->invert();
+ } elseif ($diff->f < 0) {
+ static::fixNegativeMicroseconds($diff);
+ }
+ // @codeCoverageIgnoreEnd
+
+ if ($absolute && $diff->invert) {
+ $diff->invert();
+ }
+
+ return $diff;
+ }
+
+ /**
+ * Get the difference as a DateInterval instance.
+ * Return relative interval (negative if $absolute flag is not set to true and the given date is before
+ * current one).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return DateInterval
+ */
+ #[ReturnTypeWillChange]
+ public function diff($date = null, $absolute = false)
+ {
+ $other = $this->resolveCarbon($date);
+
+ // Work-around for https://bugs.php.net/bug.php?id=81458
+ // It was initially introduced for https://bugs.php.net/bug.php?id=80998
+ // The very specific case of 80998 was fixed in PHP 8.1beta3, but it introduced 81458
+ // So we still need to keep this for now
+ // @codeCoverageIgnoreStart
+ if (version_compare(PHP_VERSION, '8.1.0-dev', '>=') && $other->tz !== $this->tz) {
+ $other = $other->avoidMutation()->tz($this->tz);
+ }
+ // @codeCoverageIgnoreEnd
+
+ return parent::diff($other, (bool) $absolute);
+ }
+
+ /**
+ * Get the difference as a CarbonInterval instance.
+ * Return relative interval (negative if $absolute flag is not set to true and the given date is before
+ * current one).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return CarbonInterval
+ */
+ public function diffAsCarbonInterval($date = null, $absolute = true, array $skip = [])
+ {
+ return static::fixDiffInterval($this->diff($this->resolveCarbon($date), $absolute), $absolute, $skip);
+ }
+
+ /**
+ * Get the difference in years
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInYears($date = null, $absolute = true)
+ {
+ return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%y');
+ }
+
+ /**
+ * Get the difference in quarters rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInQuarters($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInMonths($date, $absolute) / static::MONTHS_PER_QUARTER);
+ }
+
+ /**
+ * Get the difference in months rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMonths($date = null, $absolute = true)
+ {
+ $date = $this->resolveCarbon($date)->avoidMutation()->tz($this->tz);
+
+ [$yearStart, $monthStart, $dayStart] = explode('-', $this->format('Y-m-dHisu'));
+ [$yearEnd, $monthEnd, $dayEnd] = explode('-', $date->format('Y-m-dHisu'));
+
+ $diff = (((int) $yearEnd) - ((int) $yearStart)) * static::MONTHS_PER_YEAR +
+ ((int) $monthEnd) - ((int) $monthStart);
+
+ if ($diff > 0) {
+ $diff -= ($dayStart > $dayEnd ? 1 : 0);
+ } elseif ($diff < 0) {
+ $diff += ($dayStart < $dayEnd ? 1 : 0);
+ }
+
+ return $absolute ? abs($diff) : $diff;
+ }
+
+ /**
+ * Get the difference in weeks rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeeks($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK);
+ }
+
+ /**
+ * Get the difference in days rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDays($date = null, $absolute = true)
+ {
+ return $this->getIntervalDayDiff($this->diff($this->resolveCarbon($date), $absolute));
+ }
+
+ /**
+ * Get the difference in days using a filter closure rounded down.
+ *
+ * @param Closure $callback
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true)
+ {
+ return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute);
+ }
+
+ /**
+ * Get the difference in hours using a filter closure rounded down.
+ *
+ * @param Closure $callback
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true)
+ {
+ return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute);
+ }
+
+ /**
+ * Get the difference by the given interval using a filter closure.
+ *
+ * @param CarbonInterval $ci An interval to traverse by
+ * @param Closure $callback
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true)
+ {
+ $start = $this;
+ $end = $this->resolveCarbon($date);
+ $inverse = false;
+
+ if ($end < $start) {
+ $start = $end;
+ $end = $this;
+ $inverse = true;
+ }
+
+ $options = CarbonPeriod::EXCLUDE_END_DATE | ($this->isMutable() ? 0 : CarbonPeriod::IMMUTABLE);
+ $diff = $ci->toPeriod($start, $end, $options)->filter($callback)->count();
+
+ return $inverse && !$absolute ? -$diff : $diff;
+ }
+
+ /**
+ * Get the difference in weekdays rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekdays($date = null, $absolute = true)
+ {
+ return $this->diffInDaysFiltered(static function (CarbonInterface $date) {
+ return $date->isWeekday();
+ }, $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), $absolute);
+ }
+
+ /**
+ * Get the difference in weekend days using a filter rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInWeekendDays($date = null, $absolute = true)
+ {
+ return $this->diffInDaysFiltered(static function (CarbonInterface $date) {
+ return $date->isWeekend();
+ }, $this->resolveCarbon($date)->avoidMutation()->modify($this->format('H:i:s.u')), $absolute);
+ }
+
+ /**
+ * Get the difference in hours rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInHours($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Get the difference in hours rounded down using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealHours($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
+ }
+
+ /**
+ * Get the difference in minutes rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMinutes($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE);
+ }
+
+ /**
+ * Get the difference in minutes rounded down using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMinutes($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE);
+ }
+
+ /**
+ * Get the difference in seconds rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInSeconds($date = null, $absolute = true)
+ {
+ $diff = $this->diff($date);
+
+ if ($diff->days === 0) {
+ $diff = static::fixDiffInterval($diff, $absolute);
+ }
+
+ $value = (((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) +
+ $diff->h) * static::MINUTES_PER_HOUR +
+ $diff->i) * static::SECONDS_PER_MINUTE +
+ $diff->s;
+
+ return $absolute || !$diff->invert ? $value : -$value;
+ }
+
+ /**
+ * Get the difference in microseconds.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMicroseconds($date = null, $absolute = true)
+ {
+ $diff = $this->diff($date);
+ $value = (int) round(((((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) +
+ $diff->h) * static::MINUTES_PER_HOUR +
+ $diff->i) * static::SECONDS_PER_MINUTE +
+ ($diff->f + $diff->s)) * static::MICROSECONDS_PER_SECOND);
+
+ return $absolute || !$diff->invert ? $value : -$value;
+ }
+
+ /**
+ * Get the difference in milliseconds rounded down.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInMilliseconds($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND);
+ }
+
+ /**
+ * Get the difference in seconds using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealSeconds($date = null, $absolute = true)
+ {
+ /** @var CarbonInterface $date */
+ $date = $this->resolveCarbon($date);
+ $value = $date->getTimestamp() - $this->getTimestamp();
+
+ return $absolute ? abs($value) : $value;
+ }
+
+ /**
+ * Get the difference in microseconds using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMicroseconds($date = null, $absolute = true)
+ {
+ /** @var CarbonInterface $date */
+ $date = $this->resolveCarbon($date);
+ $value = ($date->timestamp - $this->timestamp) * static::MICROSECONDS_PER_SECOND +
+ $date->micro - $this->micro;
+
+ return $absolute ? abs($value) : $value;
+ }
+
+ /**
+ * Get the difference in milliseconds rounded down using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return int
+ */
+ public function diffInRealMilliseconds($date = null, $absolute = true)
+ {
+ return (int) ($this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND);
+ }
+
+ /**
+ * Get the difference in seconds as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInSeconds($date = null, $absolute = true)
+ {
+ return (float) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND);
+ }
+
+ /**
+ * Get the difference in minutes as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInMinutes($date = null, $absolute = true)
+ {
+ return $this->floatDiffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE;
+ }
+
+ /**
+ * Get the difference in hours as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInHours($date = null, $absolute = true)
+ {
+ return $this->floatDiffInMinutes($date, $absolute) / static::MINUTES_PER_HOUR;
+ }
+
+ /**
+ * Get the difference in days as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInDays($date = null, $absolute = true)
+ {
+ $hoursDiff = $this->floatDiffInHours($date, $absolute);
+ $interval = $this->diff($date, $absolute);
+
+ if ($interval->y === 0 && $interval->m === 0 && $interval->d === 0) {
+ return $hoursDiff / static::HOURS_PER_DAY;
+ }
+
+ $daysDiff = $this->getIntervalDayDiff($interval);
+
+ return $daysDiff + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY;
+ }
+
+ /**
+ * Get the difference in weeks as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInWeeks($date = null, $absolute = true)
+ {
+ return $this->floatDiffInDays($date, $absolute) / static::DAYS_PER_WEEK;
+ }
+
+ /**
+ * Get the difference in months as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInMonths($date = null, $absolute = true)
+ {
+ $start = $this;
+ $end = $this->resolveCarbon($date);
+ $ascending = ($start <= $end);
+ $sign = $absolute || $ascending ? 1 : -1;
+ if (!$ascending) {
+ [$start, $end] = [$end, $start];
+ }
+ $monthsDiff = $start->diffInMonths($end);
+ /** @var Carbon|CarbonImmutable $floorEnd */
+ $floorEnd = $start->avoidMutation()->addMonths($monthsDiff);
+
+ if ($floorEnd >= $end) {
+ return $sign * $monthsDiff;
+ }
+
+ /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */
+ $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth();
+
+ if ($startOfMonthAfterFloorEnd > $end) {
+ return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInMonth);
+ }
+
+ return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInDays($end) / $end->daysInMonth);
+ }
+
+ /**
+ * Get the difference in year as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInYears($date = null, $absolute = true)
+ {
+ $start = $this;
+ $end = $this->resolveCarbon($date);
+ $ascending = ($start <= $end);
+ $sign = $absolute || $ascending ? 1 : -1;
+ if (!$ascending) {
+ [$start, $end] = [$end, $start];
+ }
+ $yearsDiff = $start->diffInYears($end);
+ /** @var Carbon|CarbonImmutable $floorEnd */
+ $floorEnd = $start->avoidMutation()->addYears($yearsDiff);
+
+ if ($floorEnd >= $end) {
+ return $sign * $yearsDiff;
+ }
+
+ /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */
+ $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear();
+
+ if ($startOfYearAfterFloorEnd > $end) {
+ return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInYear);
+ }
+
+ return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInDays($end) / $end->daysInYear);
+ }
+
+ /**
+ * Get the difference in seconds as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealSeconds($date = null, $absolute = true)
+ {
+ return $this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND;
+ }
+
+ /**
+ * Get the difference in minutes as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealMinutes($date = null, $absolute = true)
+ {
+ return $this->floatDiffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE;
+ }
+
+ /**
+ * Get the difference in hours as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealHours($date = null, $absolute = true)
+ {
+ return $this->floatDiffInRealMinutes($date, $absolute) / static::MINUTES_PER_HOUR;
+ }
+
+ /**
+ * Get the difference in days as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealDays($date = null, $absolute = true)
+ {
+ $date = $this->resolveUTC($date);
+ $utc = $this->avoidMutation()->utc();
+ $hoursDiff = $utc->floatDiffInRealHours($date, $absolute);
+
+ return ($hoursDiff < 0 ? -1 : 1) * $utc->diffInDays($date) + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY;
+ }
+
+ /**
+ * Get the difference in weeks as float (microsecond-precision).
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealWeeks($date = null, $absolute = true)
+ {
+ return $this->floatDiffInRealDays($date, $absolute) / static::DAYS_PER_WEEK;
+ }
+
+ /**
+ * Get the difference in months as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealMonths($date = null, $absolute = true)
+ {
+ $start = $this;
+ $end = $this->resolveCarbon($date);
+ $ascending = ($start <= $end);
+ $sign = $absolute || $ascending ? 1 : -1;
+ if (!$ascending) {
+ [$start, $end] = [$end, $start];
+ }
+ $monthsDiff = $start->diffInMonths($end);
+ /** @var Carbon|CarbonImmutable $floorEnd */
+ $floorEnd = $start->avoidMutation()->addMonths($monthsDiff);
+
+ if ($floorEnd >= $end) {
+ return $sign * $monthsDiff;
+ }
+
+ /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */
+ $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth();
+
+ if ($startOfMonthAfterFloorEnd > $end) {
+ return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInMonth);
+ }
+
+ return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInMonth);
+ }
+
+ /**
+ * Get the difference in year as float (microsecond-precision) using timestamps.
+ *
+ * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
+ * @param bool $absolute Get the absolute of the difference
+ *
+ * @return float
+ */
+ public function floatDiffInRealYears($date = null, $absolute = true)
+ {
+ $start = $this;
+ $end = $this->resolveCarbon($date);
+ $ascending = ($start <= $end);
+ $sign = $absolute || $ascending ? 1 : -1;
+ if (!$ascending) {
+ [$start, $end] = [$end, $start];
+ }
+ $yearsDiff = $start->diffInYears($end);
+ /** @var Carbon|CarbonImmutable $floorEnd */
+ $floorEnd = $start->avoidMutation()->addYears($yearsDiff);
+
+ if ($floorEnd >= $end) {
+ return $sign * $yearsDiff;
+ }
+
+ /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */
+ $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear();
+
+ if ($startOfYearAfterFloorEnd > $end) {
+ return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInYear);
+ }
+
+ return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInYear);
+ }
+
+ /**
+ * The number of seconds since midnight.
+ *
+ * @return int
+ */
+ public function secondsSinceMidnight()
+ {
+ return $this->diffInSeconds($this->avoidMutation()->startOfDay());
+ }
+
+ /**
+ * The number of seconds until 23:59:59.
+ *
+ * @return int
+ */
+ public function secondsUntilEndOfDay()
+ {
+ return $this->diffInSeconds($this->avoidMutation()->endOfDay());
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ *
+ * @example
+ * ```
+ * echo Carbon::tomorrow()->diffForHumans() . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(['parts' => 2]) . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(['parts' => 3, 'join' => true]) . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday()) . "\n";
+ * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n";
+ * ```
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'skip' entry, list of units to skip (array of strings or a single string,
+ * ` it can be the unit name (singular or plural) or its shortcut
+ * ` (y, m, w, d, h, min, s, ms, µs).
+ * - 'aUnit' entry, prefer "an hour" over "1 hour" if true
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * - 'minimumUnit' entry determines the smallest unit of time to display can be long or
+ * ` short form of the units, e.g. 'hour' or 'h' (default value: s)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ /* @var CarbonInterface $this */
+ if (\is_array($other)) {
+ $other['syntax'] = \array_key_exists('syntax', $other) ? $other['syntax'] : $syntax;
+ $syntax = $other;
+ $other = $syntax['other'] ?? null;
+ }
+
+ $intSyntax = &$syntax;
+ if (\is_array($syntax)) {
+ $syntax['syntax'] = $syntax['syntax'] ?? null;
+ $intSyntax = &$syntax['syntax'];
+ }
+ $intSyntax = (int) ($intSyntax ?? static::DIFF_RELATIVE_AUTO);
+ $intSyntax = $intSyntax === static::DIFF_RELATIVE_AUTO && $other === null ? static::DIFF_RELATIVE_TO_NOW : $intSyntax;
+
+ $parts = min(7, max(1, (int) $parts));
+ $skip = \is_array($syntax) ? ($syntax['skip'] ?? []) : [];
+
+ return $this->diffAsCarbonInterval($other, false, (array) $skip)
+ ->setLocalTranslator($this->getLocalTranslator())
+ ->forHumans($syntax, (bool) $short, $parts, $options ?? $this->localHumanDiffOptions ?? static::getHumanDiffOptions());
+ }
+
+ /**
+ * @alias diffForHumans
+ *
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function from($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ return $this->diffForHumans($other, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * @alias diffForHumans
+ *
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ */
+ public function since($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ return $this->diffForHumans($other, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given (or now if null given) to current instance.
+ *
+ * When comparing a value in the past to default now:
+ * 1 hour from now
+ * 5 months from now
+ *
+ * When comparing a value in the future to default now:
+ * 1 hour ago
+ * 5 months ago
+ *
+ * When comparing a value in the past to another value:
+ * 1 hour after
+ * 5 months after
+ *
+ * When comparing a value in the future to another value:
+ * 1 hour before
+ * 5 months before
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function to($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ if (!$syntax && !$other) {
+ $syntax = CarbonInterface::DIFF_RELATIVE_TO_NOW;
+ }
+
+ return $this->resolveCarbon($other)->diffForHumans($this, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * @alias to
+ *
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given (or now if null given) to current instance.
+ *
+ * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
+ * if null passed, now will be used as comparison reference;
+ * if any other type, it will be converted to date and used as reference.
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * - 'other' entry (see above)
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function until($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ return $this->to($other, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale from current
+ * instance to now.
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single unit)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function fromNow($syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ $other = null;
+
+ if ($syntax instanceof DateTimeInterface) {
+ [$other, $syntax, $short, $parts, $options] = array_pad(\func_get_args(), 5, null);
+ }
+
+ return $this->from($other, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given to now
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single part)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function toNow($syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ return $this->to(null, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale from an other
+ * instance given to now
+ *
+ * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
+ * - 'syntax' entry (see below)
+ * - 'short' entry (see below)
+ * - 'parts' entry (see below)
+ * - 'options' entry (see below)
+ * - 'join' entry determines how to join multiple parts of the string
+ * ` - if $join is a string, it's used as a joiner glue
+ * ` - if $join is a callable/closure, it get the list of string and should return a string
+ * ` - if $join is an array, the first item will be the default glue, and the second item
+ * ` will be used instead of the glue for the last item
+ * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
+ * ` - if $join is missing, a space will be used as glue
+ * if int passed, it add modifiers:
+ * Possible values:
+ * - CarbonInterface::DIFF_ABSOLUTE no modifiers
+ * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
+ * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
+ * Default value: CarbonInterface::DIFF_ABSOLUTE
+ * @param bool $short displays short format of time units
+ * @param int $parts maximum number of parts to display (default value: 1: single part)
+ * @param int $options human diff options
+ *
+ * @return string
+ */
+ public function ago($syntax = null, $short = false, $parts = 1, $options = null)
+ {
+ $other = null;
+
+ if ($syntax instanceof DateTimeInterface) {
+ [$other, $syntax, $short, $parts, $options] = array_pad(\func_get_args(), 5, null);
+ }
+
+ return $this->from($other, $syntax, $short, $parts, $options);
+ }
+
+ /**
+ * Get the difference in a human readable format in the current locale from current instance to an other
+ * instance given (or now if null given).
+ *
+ * @return string
+ */
+ public function timespan($other = null, $timezone = null)
+ {
+ if (!$other instanceof DateTimeInterface) {
+ $other = static::parse($other, $timezone);
+ }
+
+ return $this->diffForHumans($other, [
+ 'join' => ', ',
+ 'syntax' => CarbonInterface::DIFF_ABSOLUTE,
+ 'options' => CarbonInterface::NO_ZERO_DIFF,
+ 'parts' => -1,
+ ]);
+ }
+
+ /**
+ * Returns either day of week + time (e.g. "Last Friday at 3:30 PM") if reference time is within 7 days,
+ * or a calendar date (e.g. "10/29/2017") otherwise.
+ *
+ * Language, date and time formats will change according to the current locale.
+ *
+ * @param Carbon|\DateTimeInterface|string|null $referenceTime
+ * @param array $formats
+ *
+ * @return string
+ */
+ public function calendar($referenceTime = null, array $formats = [])
+ {
+ /** @var CarbonInterface $current */
+ $current = $this->avoidMutation()->startOfDay();
+ /** @var CarbonInterface $other */
+ $other = $this->resolveCarbon($referenceTime)->avoidMutation()->setTimezone($this->getTimezone())->startOfDay();
+ $diff = $other->diffInDays($current, false);
+ $format = $diff < -6 ? 'sameElse' : (
+ $diff < -1 ? 'lastWeek' : (
+ $diff < 0 ? 'lastDay' : (
+ $diff < 1 ? 'sameDay' : (
+ $diff < 2 ? 'nextDay' : (
+ $diff < 7 ? 'nextWeek' : 'sameElse'
+ )
+ )
+ )
+ )
+ );
+ $format = array_merge($this->getCalendarFormats(), $formats)[$format];
+ if ($format instanceof Closure) {
+ $format = $format($current, $other) ?? '';
+ }
+
+ return $this->isoFormat((string) $format);
+ }
+
+ private function getIntervalDayDiff(DateInterval $interval): int
+ {
+ $daysDiff = (int) $interval->format('%a');
+ $sign = $interval->format('%r') === '-' ? -1 : 1;
+
+ if (\is_int($interval->days) &&
+ $interval->y === 0 &&
+ $interval->m === 0 &&
+ version_compare(PHP_VERSION, '8.1.0-dev', '<') &&
+ abs($interval->d - $daysDiff) === 1
+ ) {
+ $daysDiff = abs($interval->d); // @codeCoverageIgnore
+ }
+
+ return $daysDiff * $sign;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php
new file mode 100644
index 0000000..f069c28
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterval;
+use Carbon\Exceptions\InvalidIntervalException;
+use DateInterval;
+
+/**
+ * Trait to call rounding methods to interval or the interval of a period.
+ */
+trait IntervalRounding
+{
+ protected function callRoundMethod(string $method, array $parameters)
+ {
+ $action = substr($method, 0, 4);
+
+ if ($action !== 'ceil') {
+ $action = substr($method, 0, 5);
+ }
+
+ if (\in_array($action, ['round', 'floor', 'ceil'])) {
+ return $this->{$action.'Unit'}(substr($method, \strlen($action)), ...$parameters);
+ }
+
+ return null;
+ }
+
+ protected function roundWith($precision, $function)
+ {
+ $unit = 'second';
+
+ if ($precision instanceof DateInterval) {
+ $precision = (string) CarbonInterval::instance($precision, [], true);
+ }
+
+ if (\is_string($precision) && preg_match('/^\s*(?\d+)?\s*(?\w+)(?\W.*)?$/', $precision, $match)) {
+ if (trim($match['other'] ?? '') !== '') {
+ throw new InvalidIntervalException('Rounding is only possible with single unit intervals.');
+ }
+
+ $precision = (int) ($match['precision'] ?: 1);
+ $unit = $match['unit'];
+ }
+
+ return $this->roundUnit($unit, $precision, $function);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php
new file mode 100644
index 0000000..82d7c32
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/IntervalStep.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Carbon;
+use Carbon\CarbonImmutable;
+use Carbon\CarbonInterface;
+use Closure;
+use DateTimeImmutable;
+use DateTimeInterface;
+
+trait IntervalStep
+{
+ /**
+ * Step to apply instead of a fixed interval to get the new date.
+ *
+ * @var Closure|null
+ */
+ protected $step;
+
+ /**
+ * Get the dynamic step in use.
+ *
+ * @return Closure
+ */
+ public function getStep(): ?Closure
+ {
+ return $this->step;
+ }
+
+ /**
+ * Set a step to apply instead of a fixed interval to get the new date.
+ *
+ * Or pass null to switch to fixed interval.
+ *
+ * @param Closure|null $step
+ */
+ public function setStep(?Closure $step): void
+ {
+ $this->step = $step;
+ }
+
+ /**
+ * Take a date and apply either the step if set, or the current interval else.
+ *
+ * The interval/step is applied negatively (typically subtraction instead of addition) if $negated is true.
+ *
+ * @param DateTimeInterface $dateTime
+ * @param bool $negated
+ *
+ * @return CarbonInterface
+ */
+ public function convertDate(DateTimeInterface $dateTime, bool $negated = false): CarbonInterface
+ {
+ /** @var CarbonInterface $carbonDate */
+ $carbonDate = $dateTime instanceof CarbonInterface ? $dateTime : $this->resolveCarbon($dateTime);
+
+ if ($this->step) {
+ return $carbonDate->setDateTimeFrom(($this->step)($carbonDate->avoidMutation(), $negated));
+ }
+
+ if ($negated) {
+ return $carbonDate->rawSub($this);
+ }
+
+ return $carbonDate->rawAdd($this);
+ }
+
+ /**
+ * Convert DateTimeImmutable instance to CarbonImmutable instance and DateTime instance to Carbon instance.
+ *
+ * @param DateTimeInterface $dateTime
+ *
+ * @return Carbon|CarbonImmutable
+ */
+ private function resolveCarbon(DateTimeInterface $dateTime)
+ {
+ if ($dateTime instanceof DateTimeImmutable) {
+ return CarbonImmutable::instance($dateTime);
+ }
+
+ return Carbon::instance($dateTime);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php b/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php
new file mode 100644
index 0000000..46aff11
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Localization.php
@@ -0,0 +1,840 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterface;
+use Carbon\Exceptions\InvalidTypeException;
+use Carbon\Exceptions\NotLocaleAwareException;
+use Carbon\Language;
+use Carbon\Translator;
+use Carbon\TranslatorStrongTypeInterface;
+use Closure;
+use Symfony\Component\Translation\TranslatorBagInterface;
+use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\LocaleAwareInterface;
+use Symfony\Contracts\Translation\TranslatorInterface as ContractsTranslatorInterface;
+
+// @codeCoverageIgnoreStart
+if (interface_exists('Symfony\\Contracts\\Translation\\TranslatorInterface') &&
+ !interface_exists('Symfony\\Component\\Translation\\TranslatorInterface')
+) {
+ class_alias(
+ 'Symfony\\Contracts\\Translation\\TranslatorInterface',
+ 'Symfony\\Component\\Translation\\TranslatorInterface'
+ );
+}
+// @codeCoverageIgnoreEnd
+
+/**
+ * Trait Localization.
+ *
+ * Embed default and locale translators and translation base methods.
+ */
+trait Localization
+{
+ /**
+ * Default translator.
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static $translator;
+
+ /**
+ * Specific translator of the current instance.
+ *
+ * @var \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected $localTranslator;
+
+ /**
+ * Options for diffForHumans().
+ *
+ * @var int
+ */
+ protected static $humanDiffOptions = CarbonInterface::NO_ZERO_DIFF;
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * @param int $humanDiffOptions
+ */
+ public static function setHumanDiffOptions($humanDiffOptions)
+ {
+ static::$humanDiffOptions = $humanDiffOptions;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * @param int $humanDiffOption
+ */
+ public static function enableHumanDiffOption($humanDiffOption)
+ {
+ static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * @param int $humanDiffOption
+ */
+ public static function disableHumanDiffOption($humanDiffOption)
+ {
+ static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption;
+ }
+
+ /**
+ * Return default humanDiff() options (merged flags as integer).
+ *
+ * @return int
+ */
+ public static function getHumanDiffOptions()
+ {
+ return static::$humanDiffOptions;
+ }
+
+ /**
+ * Get the default translator instance in use.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public static function getTranslator()
+ {
+ return static::translator();
+ }
+
+ /**
+ * Set the default translator instance to use.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ *
+ * @return void
+ */
+ public static function setTranslator(TranslatorInterface $translator)
+ {
+ static::$translator = $translator;
+ }
+
+ /**
+ * Return true if the current instance has its own translator.
+ *
+ * @return bool
+ */
+ public function hasLocalTranslator()
+ {
+ return isset($this->localTranslator);
+ }
+
+ /**
+ * Get the translator of the current instance or the default if none set.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ public function getLocalTranslator()
+ {
+ return $this->localTranslator ?: static::translator();
+ }
+
+ /**
+ * Set the translator for the current instance.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ *
+ * @return $this
+ */
+ public function setLocalTranslator(TranslatorInterface $translator)
+ {
+ $this->localTranslator = $translator;
+
+ return $this;
+ }
+
+ /**
+ * Returns raw translation message for a given key.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use
+ * @param string $key key to find
+ * @param string|null $locale current locale used if null
+ * @param string|null $default default value if translation returns the key
+ *
+ * @return string
+ */
+ public static function getTranslationMessageWith($translator, string $key, ?string $locale = null, ?string $default = null)
+ {
+ if (!($translator instanceof TranslatorBagInterface && $translator instanceof TranslatorInterface)) {
+ throw new InvalidTypeException(
+ 'Translator does not implement '.TranslatorInterface::class.' and '.TranslatorBagInterface::class.'. '.
+ (\is_object($translator) ? \get_class($translator) : \gettype($translator)).' has been given.'
+ );
+ }
+
+ if (!$locale && $translator instanceof LocaleAwareInterface) {
+ $locale = $translator->getLocale();
+ }
+
+ $result = self::getFromCatalogue($translator, $translator->getCatalogue($locale), $key);
+
+ return $result === $key ? $default : $result;
+ }
+
+ /**
+ * Returns raw translation message for a given key.
+ *
+ * @param string $key key to find
+ * @param string|null $locale current locale used if null
+ * @param string|null $default default value if translation returns the key
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use
+ *
+ * @return string
+ */
+ public function getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null)
+ {
+ return static::getTranslationMessageWith($translator ?: $this->getLocalTranslator(), $key, $locale, $default);
+ }
+
+ /**
+ * Translate using translation string or callback available.
+ *
+ * @param \Symfony\Component\Translation\TranslatorInterface $translator
+ * @param string $key
+ * @param array $parameters
+ * @param null $number
+ *
+ * @return string
+ */
+ public static function translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null): string
+ {
+ $message = static::getTranslationMessageWith($translator, $key, null, $key);
+ if ($message instanceof Closure) {
+ return (string) $message(...array_values($parameters));
+ }
+
+ if ($number !== null) {
+ $parameters['%count%'] = $number;
+ }
+ if (isset($parameters['%count%'])) {
+ $parameters[':count'] = $parameters['%count%'];
+ }
+
+ // @codeCoverageIgnoreStart
+ $choice = $translator instanceof ContractsTranslatorInterface
+ ? $translator->trans($key, $parameters)
+ : $translator->transChoice($key, $number, $parameters);
+ // @codeCoverageIgnoreEnd
+
+ return (string) $choice;
+ }
+
+ /**
+ * Translate using translation string or callback available.
+ *
+ * @param string $key
+ * @param array $parameters
+ * @param string|int|float|null $number
+ * @param \Symfony\Component\Translation\TranslatorInterface|null $translator
+ * @param bool $altNumbers
+ *
+ * @return string
+ */
+ public function translate(string $key, array $parameters = [], $number = null, ?TranslatorInterface $translator = null, bool $altNumbers = false): string
+ {
+ $translation = static::translateWith($translator ?: $this->getLocalTranslator(), $key, $parameters, $number);
+
+ if ($number !== null && $altNumbers) {
+ return str_replace($number, $this->translateNumber($number), $translation);
+ }
+
+ return $translation;
+ }
+
+ /**
+ * Returns the alternative number for a given integer if available in the current locale.
+ *
+ * @param int $number
+ *
+ * @return string
+ */
+ public function translateNumber(int $number): string
+ {
+ $translateKey = "alt_numbers.$number";
+ $symbol = $this->translate($translateKey);
+
+ if ($symbol !== $translateKey) {
+ return $symbol;
+ }
+
+ if ($number > 99 && $this->translate('alt_numbers.99') !== 'alt_numbers.99') {
+ $start = '';
+ foreach ([10000, 1000, 100] as $exp) {
+ $key = "alt_numbers_pow.$exp";
+ if ($number >= $exp && $number < $exp * 10 && ($pow = $this->translate($key)) !== $key) {
+ $unit = floor($number / $exp);
+ $number -= $unit * $exp;
+ $start .= ($unit > 1 ? $this->translate("alt_numbers.$unit") : '').$pow;
+ }
+ }
+ $result = '';
+ while ($number) {
+ $chunk = $number % 100;
+ $result = $this->translate("alt_numbers.$chunk").$result;
+ $number = floor($number / 100);
+ }
+
+ return "$start$result";
+ }
+
+ if ($number > 9 && $this->translate('alt_numbers.9') !== 'alt_numbers.9') {
+ $result = '';
+ while ($number) {
+ $chunk = $number % 10;
+ $result = $this->translate("alt_numbers.$chunk").$result;
+ $number = floor($number / 10);
+ }
+
+ return $result;
+ }
+
+ return (string) $number;
+ }
+
+ /**
+ * Translate a time string from a locale to an other.
+ *
+ * @param string $timeString date/time/duration string to translate (may also contain English)
+ * @param string|null $from input locale of the $timeString parameter (`Carbon::getLocale()` by default)
+ * @param string|null $to output locale of the result returned (`"en"` by default)
+ * @param int $mode specify what to translate with options:
+ * - CarbonInterface::TRANSLATE_ALL (default)
+ * - CarbonInterface::TRANSLATE_MONTHS
+ * - CarbonInterface::TRANSLATE_DAYS
+ * - CarbonInterface::TRANSLATE_UNITS
+ * - CarbonInterface::TRANSLATE_MERIDIEM
+ * You can use pipe to group: CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS
+ *
+ * @return string
+ */
+ public static function translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL)
+ {
+ // Fallback source and destination locales
+ $from = $from ?: static::getLocale();
+ $to = $to ?: 'en';
+
+ if ($from === $to) {
+ return $timeString;
+ }
+
+ // Standardize apostrophe
+ $timeString = strtr($timeString, ['’' => "'"]);
+
+ $fromTranslations = [];
+ $toTranslations = [];
+
+ foreach (['from', 'to'] as $key) {
+ $language = $$key;
+ $translator = Translator::get($language);
+ $translations = $translator->getMessages();
+
+ if (!isset($translations[$language])) {
+ return $timeString;
+ }
+
+ $translationKey = $key.'Translations';
+ $messages = $translations[$language];
+ $months = $messages['months'] ?? [];
+ $weekdays = $messages['weekdays'] ?? [];
+ $meridiem = $messages['meridiem'] ?? ['AM', 'PM'];
+
+ if (isset($messages['ordinal_words'])) {
+ $timeString = self::replaceOrdinalWords(
+ $timeString,
+ $key === 'from' ? array_flip($messages['ordinal_words']) : $messages['ordinal_words']
+ );
+ }
+
+ if ($key === 'from') {
+ foreach (['months', 'weekdays'] as $variable) {
+ $list = $messages[$variable.'_standalone'] ?? null;
+
+ if ($list) {
+ foreach ($$variable as $index => &$name) {
+ $name .= '|'.$messages[$variable.'_standalone'][$index];
+ }
+ }
+ }
+ }
+
+ $$translationKey = array_merge(
+ $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($months, 12, $timeString) : [],
+ $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($messages['months_short'] ?? [], 12, $timeString) : [],
+ $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($weekdays, 7, $timeString) : [],
+ $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($messages['weekdays_short'] ?? [], 7, $timeString) : [],
+ $mode & CarbonInterface::TRANSLATE_DIFF ? static::translateWordsByKeys([
+ 'diff_now',
+ 'diff_today',
+ 'diff_yesterday',
+ 'diff_tomorrow',
+ 'diff_before_yesterday',
+ 'diff_after_tomorrow',
+ ], $messages, $key) : [],
+ $mode & CarbonInterface::TRANSLATE_UNITS ? static::translateWordsByKeys([
+ 'year',
+ 'month',
+ 'week',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ ], $messages, $key) : [],
+ $mode & CarbonInterface::TRANSLATE_MERIDIEM ? array_map(function ($hour) use ($meridiem) {
+ if (\is_array($meridiem)) {
+ return $meridiem[$hour < 12 ? 0 : 1];
+ }
+
+ return $meridiem($hour, 0, false);
+ }, range(0, 23)) : []
+ );
+ }
+
+ return substr(preg_replace_callback('/(?<=[\d\s+.\/,_-])('.implode('|', $fromTranslations).')(?=[\d\s+.\/,_-])/iu', function ($match) use ($fromTranslations, $toTranslations) {
+ [$chunk] = $match;
+
+ foreach ($fromTranslations as $index => $word) {
+ if (preg_match("/^$word\$/iu", $chunk)) {
+ return $toTranslations[$index] ?? '';
+ }
+ }
+
+ return $chunk; // @codeCoverageIgnore
+ }, " $timeString "), 1, -1);
+ }
+
+ /**
+ * Translate a time string from the current locale (`$date->locale()`) to an other.
+ *
+ * @param string $timeString time string to translate
+ * @param string|null $to output locale of the result returned ("en" by default)
+ *
+ * @return string
+ */
+ public function translateTimeStringTo($timeString, $to = null)
+ {
+ return static::translateTimeString($timeString, $this->getTranslatorLocale(), $to);
+ }
+
+ /**
+ * Get/set the locale for the current instance.
+ *
+ * @param string|null $locale
+ * @param string ...$fallbackLocales
+ *
+ * @return $this|string
+ */
+ public function locale(string $locale = null, ...$fallbackLocales)
+ {
+ if ($locale === null) {
+ return $this->getTranslatorLocale();
+ }
+
+ if (!$this->localTranslator || $this->getTranslatorLocale($this->localTranslator) !== $locale) {
+ $translator = Translator::get($locale);
+
+ if (!empty($fallbackLocales)) {
+ $translator->setFallbackLocales($fallbackLocales);
+
+ foreach ($fallbackLocales as $fallbackLocale) {
+ $messages = Translator::get($fallbackLocale)->getMessages();
+
+ if (isset($messages[$fallbackLocale])) {
+ $translator->setMessages($fallbackLocale, $messages[$fallbackLocale]);
+ }
+ }
+ }
+
+ $this->localTranslator = $translator;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the current translator locale.
+ *
+ * @return string
+ */
+ public static function getLocale()
+ {
+ return static::getLocaleAwareTranslator()->getLocale();
+ }
+
+ /**
+ * Set the current translator locale and indicate if the source locale file exists.
+ * Pass 'auto' as locale to use closest language from the current LC_TIME locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function setLocale($locale)
+ {
+ return static::getLocaleAwareTranslator()->setLocale($locale) !== false;
+ }
+
+ /**
+ * Set the fallback locale.
+ *
+ * @see https://symfony.com/doc/current/components/translation.html#fallback-locales
+ *
+ * @param string $locale
+ */
+ public static function setFallbackLocale($locale)
+ {
+ $translator = static::getTranslator();
+
+ if (method_exists($translator, 'setFallbackLocales')) {
+ $translator->setFallbackLocales([$locale]);
+
+ if ($translator instanceof Translator) {
+ $preferredLocale = $translator->getLocale();
+ $translator->setMessages($preferredLocale, array_replace_recursive(
+ $translator->getMessages()[$locale] ?? [],
+ Translator::get($locale)->getMessages()[$locale] ?? [],
+ $translator->getMessages($preferredLocale)
+ ));
+ }
+ }
+ }
+
+ /**
+ * Get the fallback locale.
+ *
+ * @see https://symfony.com/doc/current/components/translation.html#fallback-locales
+ *
+ * @return string|null
+ */
+ public static function getFallbackLocale()
+ {
+ $translator = static::getTranslator();
+
+ if (method_exists($translator, 'getFallbackLocales')) {
+ return $translator->getFallbackLocales()[0] ?? null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the current locale to the given, execute the passed function, reset the locale to previous one,
+ * then return the result of the closure (or null if the closure was void).
+ *
+ * @param string $locale locale ex. en
+ * @param callable $func
+ *
+ * @return mixed
+ */
+ public static function executeWithLocale($locale, $func)
+ {
+ $currentLocale = static::getLocale();
+ $result = $func(static::setLocale($locale) ? static::getLocale() : false, static::translator());
+ static::setLocale($currentLocale);
+
+ return $result;
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has short-units support.
+ * Support is considered enabled if either year, day or hour has a short variant translated.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasShortUnits($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return ($newLocale && (($y = static::translateWith($translator, 'y')) !== 'y' && $y !== static::translateWith($translator, 'year'))) || (
+ ($y = static::translateWith($translator, 'd')) !== 'd' &&
+ $y !== static::translateWith($translator, 'day')
+ ) || (
+ ($y = static::translateWith($translator, 'h')) !== 'h' &&
+ $y !== static::translateWith($translator, 'hour')
+ );
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffSyntax($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ if (!$newLocale) {
+ return false;
+ }
+
+ foreach (['ago', 'from_now', 'before', 'after'] as $key) {
+ if ($translator instanceof TranslatorBagInterface &&
+ self::getFromCatalogue($translator, $translator->getCatalogue($newLocale), $key) instanceof Closure
+ ) {
+ continue;
+ }
+
+ if ($translator->trans($key) === $key) {
+ return false;
+ }
+ }
+
+ return true;
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow).
+ * Support is considered enabled if the 3 words are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffOneDayWords($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('diff_now') !== 'diff_now' &&
+ $translator->trans('diff_yesterday') !== 'diff_yesterday' &&
+ $translator->trans('diff_tomorrow') !== 'diff_tomorrow';
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow).
+ * Support is considered enabled if the 2 words are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasDiffTwoDayWords($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('diff_before_yesterday') !== 'diff_before_yesterday' &&
+ $translator->trans('diff_after_tomorrow') !== 'diff_after_tomorrow';
+ });
+ }
+
+ /**
+ * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X).
+ * Support is considered enabled if the 4 sentences are translated in the given locale.
+ *
+ * @param string $locale locale ex. en
+ *
+ * @return bool
+ */
+ public static function localeHasPeriodSyntax($locale)
+ {
+ return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
+ return $newLocale &&
+ $translator->trans('period_recurrences') !== 'period_recurrences' &&
+ $translator->trans('period_interval') !== 'period_interval' &&
+ $translator->trans('period_start_date') !== 'period_start_date' &&
+ $translator->trans('period_end_date') !== 'period_end_date';
+ });
+ }
+
+ /**
+ * Returns the list of internally available locales and already loaded custom locales.
+ * (It will ignore custom translator dynamic loading.)
+ *
+ * @return array
+ */
+ public static function getAvailableLocales()
+ {
+ $translator = static::getLocaleAwareTranslator();
+
+ return $translator instanceof Translator
+ ? $translator->getAvailableLocales()
+ : [$translator->getLocale()];
+ }
+
+ /**
+ * Returns list of Language object for each available locale. This object allow you to get the ISO name, native
+ * name, region and variant of the locale.
+ *
+ * @return Language[]
+ */
+ public static function getAvailableLocalesInfo()
+ {
+ $languages = [];
+ foreach (static::getAvailableLocales() as $id) {
+ $languages[$id] = new Language($id);
+ }
+
+ return $languages;
+ }
+
+ /**
+ * Initialize the default translator instance if necessary.
+ *
+ * @return \Symfony\Component\Translation\TranslatorInterface
+ */
+ protected static function translator()
+ {
+ if (static::$translator === null) {
+ static::$translator = Translator::get();
+ }
+
+ return static::$translator;
+ }
+
+ /**
+ * Get the locale of a given translator.
+ *
+ * If null or omitted, current local translator is used.
+ * If no local translator is in use, current global translator is used.
+ *
+ * @param null $translator
+ *
+ * @return string|null
+ */
+ protected function getTranslatorLocale($translator = null): ?string
+ {
+ if (\func_num_args() === 0) {
+ $translator = $this->getLocalTranslator();
+ }
+
+ $translator = static::getLocaleAwareTranslator($translator);
+
+ return $translator ? $translator->getLocale() : null;
+ }
+
+ /**
+ * Throw an error if passed object is not LocaleAwareInterface.
+ *
+ * @param LocaleAwareInterface|null $translator
+ *
+ * @return LocaleAwareInterface|null
+ */
+ protected static function getLocaleAwareTranslator($translator = null)
+ {
+ if (\func_num_args() === 0) {
+ $translator = static::translator();
+ }
+
+ if ($translator && !($translator instanceof LocaleAwareInterface || method_exists($translator, 'getLocale'))) {
+ throw new NotLocaleAwareException($translator); // @codeCoverageIgnore
+ }
+
+ return $translator;
+ }
+
+ /**
+ * @param mixed $translator
+ * @param \Symfony\Component\Translation\MessageCatalogueInterface $catalogue
+ *
+ * @return mixed
+ */
+ private static function getFromCatalogue($translator, $catalogue, string $id, string $domain = 'messages')
+ {
+ return $translator instanceof TranslatorStrongTypeInterface
+ ? $translator->getFromCatalogue($catalogue, $id, $domain) // @codeCoverageIgnore
+ : $catalogue->get($id, $domain);
+ }
+
+ /**
+ * Return the word cleaned from its translation codes.
+ *
+ * @param string $word
+ *
+ * @return string
+ */
+ private static function cleanWordFromTranslationString($word)
+ {
+ $word = str_replace([':count', '%count', ':time'], '', $word);
+ $word = strtr($word, ['’' => "'"]);
+ $word = preg_replace('/({\d+(,(\d+|Inf))?}|[\[\]]\d+(,(\d+|Inf))?[\[\]])/', '', $word);
+
+ return trim($word);
+ }
+
+ /**
+ * Translate a list of words.
+ *
+ * @param string[] $keys keys to translate.
+ * @param string[] $messages messages bag handling translations.
+ * @param string $key 'to' (to get the translation) or 'from' (to get the detection RegExp pattern).
+ *
+ * @return string[]
+ */
+ private static function translateWordsByKeys($keys, $messages, $key): array
+ {
+ return array_map(function ($wordKey) use ($messages, $key) {
+ $message = $key === 'from' && isset($messages[$wordKey.'_regexp'])
+ ? $messages[$wordKey.'_regexp']
+ : ($messages[$wordKey] ?? null);
+
+ if (!$message) {
+ return '>>DO NOT REPLACE<<';
+ }
+
+ $parts = explode('|', $message);
+
+ return $key === 'to'
+ ? self::cleanWordFromTranslationString(end($parts))
+ : '(?:'.implode('|', array_map([static::class, 'cleanWordFromTranslationString'], $parts)).')';
+ }, $keys);
+ }
+
+ /**
+ * Get an array of translations based on the current date.
+ *
+ * @param callable $translation
+ * @param int $length
+ * @param string $timeString
+ *
+ * @return string[]
+ */
+ private static function getTranslationArray($translation, $length, $timeString): array
+ {
+ $filler = '>>DO NOT REPLACE<<';
+
+ if (\is_array($translation)) {
+ return array_pad($translation, $length, $filler);
+ }
+
+ $list = [];
+ $date = static::now();
+
+ for ($i = 0; $i < $length; $i++) {
+ $list[] = $translation($date, $timeString, $i) ?? $filler;
+ }
+
+ return $list;
+ }
+
+ private static function replaceOrdinalWords(string $timeString, array $ordinalWords): string
+ {
+ return preg_replace_callback('/(?
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+/**
+ * Trait Macros.
+ *
+ * Allows users to register macros within the Carbon class.
+ */
+trait Macro
+{
+ use Mixin;
+
+ /**
+ * The registered macros.
+ *
+ * @var array
+ */
+ protected static $globalMacros = [];
+
+ /**
+ * The registered generic macros.
+ *
+ * @var array
+ */
+ protected static $globalGenericMacros = [];
+
+ /**
+ * Register a custom macro.
+ *
+ * @example
+ * ```
+ * $userSettings = [
+ * 'locale' => 'pt',
+ * 'timezone' => 'America/Sao_Paulo',
+ * ];
+ * Carbon::macro('userFormat', function () use ($userSettings) {
+ * return $this->copy()->locale($userSettings['locale'])->tz($userSettings['timezone'])->calendar();
+ * });
+ * echo Carbon::yesterday()->hours(11)->userFormat();
+ * ```
+ *
+ * @param string $name
+ * @param object|callable $macro
+ *
+ * @return void
+ */
+ public static function macro($name, $macro)
+ {
+ static::$globalMacros[$name] = $macro;
+ }
+
+ /**
+ * Remove all macros and generic macros.
+ */
+ public static function resetMacros()
+ {
+ static::$globalMacros = [];
+ static::$globalGenericMacros = [];
+ }
+
+ /**
+ * Register a custom macro.
+ *
+ * @param object|callable $macro
+ * @param int $priority marco with higher priority is tried first
+ *
+ * @return void
+ */
+ public static function genericMacro($macro, $priority = 0)
+ {
+ if (!isset(static::$globalGenericMacros[$priority])) {
+ static::$globalGenericMacros[$priority] = [];
+ krsort(static::$globalGenericMacros, SORT_NUMERIC);
+ }
+
+ static::$globalGenericMacros[$priority][] = $macro;
+ }
+
+ /**
+ * Checks if macro is registered globally.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public static function hasMacro($name)
+ {
+ return isset(static::$globalMacros[$name]);
+ }
+
+ /**
+ * Get the raw callable macro registered globally for a given name.
+ *
+ * @param string $name
+ *
+ * @return callable|null
+ */
+ public static function getMacro($name)
+ {
+ return static::$globalMacros[$name] ?? null;
+ }
+
+ /**
+ * Checks if macro is registered globally or locally.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasLocalMacro($name)
+ {
+ return ($this->localMacros && isset($this->localMacros[$name])) || static::hasMacro($name);
+ }
+
+ /**
+ * Get the raw callable macro registered globally or locally for a given name.
+ *
+ * @param string $name
+ *
+ * @return callable|null
+ */
+ public function getLocalMacro($name)
+ {
+ return ($this->localMacros ?? [])[$name] ?? static::getMacro($name);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php b/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php
new file mode 100644
index 0000000..310a44d
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/MagicParameter.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+/**
+ * Trait MagicParameter.
+ *
+ * Allows to retrieve parameter in magic calls by index or name.
+ */
+trait MagicParameter
+{
+ private function getMagicParameter(array $parameters, int $index, string $key, $default)
+ {
+ if (\array_key_exists($index, $parameters)) {
+ return $parameters[$index];
+ }
+
+ if (\array_key_exists($key, $parameters)) {
+ return $parameters[$key];
+ }
+
+ return $default;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php b/vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php
new file mode 100644
index 0000000..5822454
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Mixin.php
@@ -0,0 +1,226 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterface;
+use Carbon\CarbonInterval;
+use Carbon\CarbonPeriod;
+use Closure;
+use Generator;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionMethod;
+use Throwable;
+
+/**
+ * Trait Mixin.
+ *
+ * Allows mixing in entire classes with multiple macros.
+ */
+trait Mixin
+{
+ /**
+ * Stack of macro instance contexts.
+ *
+ * @var array
+ */
+ protected static $macroContextStack = [];
+
+ /**
+ * Mix another object into the class.
+ *
+ * @example
+ * ```
+ * Carbon::mixin(new class {
+ * public function addMoon() {
+ * return function () {
+ * return $this->addDays(30);
+ * };
+ * }
+ * public function subMoon() {
+ * return function () {
+ * return $this->subDays(30);
+ * };
+ * }
+ * });
+ * $fullMoon = Carbon::create('2018-12-22');
+ * $nextFullMoon = $fullMoon->addMoon();
+ * $blackMoon = Carbon::create('2019-01-06');
+ * $previousBlackMoon = $blackMoon->subMoon();
+ * echo "$nextFullMoon\n";
+ * echo "$previousBlackMoon\n";
+ * ```
+ *
+ * @param object|string $mixin
+ *
+ * @throws ReflectionException
+ *
+ * @return void
+ */
+ public static function mixin($mixin)
+ {
+ \is_string($mixin) && trait_exists($mixin)
+ ? self::loadMixinTrait($mixin)
+ : self::loadMixinClass($mixin);
+ }
+
+ /**
+ * @param object|string $mixin
+ *
+ * @throws ReflectionException
+ */
+ private static function loadMixinClass($mixin)
+ {
+ $methods = (new ReflectionClass($mixin))->getMethods(
+ ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
+ );
+
+ foreach ($methods as $method) {
+ if ($method->isConstructor() || $method->isDestructor()) {
+ continue;
+ }
+
+ $method->setAccessible(true);
+
+ static::macro($method->name, $method->invoke($mixin));
+ }
+ }
+
+ /**
+ * @param string $trait
+ */
+ private static function loadMixinTrait($trait)
+ {
+ $context = eval(self::getAnonymousClassCodeForTrait($trait));
+ $className = \get_class($context);
+ $baseClass = static::class;
+
+ foreach (self::getMixableMethods($context) as $name) {
+ $closureBase = Closure::fromCallable([$context, $name]);
+
+ static::macro($name, function (...$parameters) use ($closureBase, $className, $baseClass) {
+ $downContext = isset($this) ? ($this) : new $baseClass();
+ $context = isset($this) ? $this->cast($className) : new $className();
+
+ try {
+ // @ is required to handle error if not converted into exceptions
+ $closure = @$closureBase->bindTo($context);
+ } catch (Throwable $throwable) { // @codeCoverageIgnore
+ $closure = $closureBase; // @codeCoverageIgnore
+ }
+
+ // in case of errors not converted into exceptions
+ $closure = $closure ?: $closureBase;
+
+ $result = $closure(...$parameters);
+
+ if (!($result instanceof $className)) {
+ return $result;
+ }
+
+ if ($downContext instanceof CarbonInterface && $result instanceof CarbonInterface) {
+ if ($context !== $result) {
+ $downContext = $downContext->copy();
+ }
+
+ return $downContext
+ ->setTimezone($result->getTimezone())
+ ->modify($result->format('Y-m-d H:i:s.u'))
+ ->settings($result->getSettings());
+ }
+
+ if ($downContext instanceof CarbonInterval && $result instanceof CarbonInterval) {
+ if ($context !== $result) {
+ $downContext = $downContext->copy();
+ }
+
+ $downContext->copyProperties($result);
+ self::copyStep($downContext, $result);
+ self::copyNegativeUnits($downContext, $result);
+
+ return $downContext->settings($result->getSettings());
+ }
+
+ if ($downContext instanceof CarbonPeriod && $result instanceof CarbonPeriod) {
+ if ($context !== $result) {
+ $downContext = $downContext->copy();
+ }
+
+ return $downContext
+ ->setDates($result->getStartDate(), $result->getEndDate())
+ ->setRecurrences($result->getRecurrences())
+ ->setOptions($result->getOptions())
+ ->settings($result->getSettings());
+ }
+
+ return $result;
+ });
+ }
+ }
+
+ private static function getAnonymousClassCodeForTrait(string $trait)
+ {
+ return 'return new class() extends '.static::class.' {use '.$trait.';};';
+ }
+
+ private static function getMixableMethods(self $context): Generator
+ {
+ foreach (get_class_methods($context) as $name) {
+ if (method_exists(static::class, $name)) {
+ continue;
+ }
+
+ yield $name;
+ }
+ }
+
+ /**
+ * Stack a Carbon context from inside calls of self::this() and execute a given action.
+ *
+ * @param static|null $context
+ * @param callable $callable
+ *
+ * @throws Throwable
+ *
+ * @return mixed
+ */
+ protected static function bindMacroContext($context, callable $callable)
+ {
+ static::$macroContextStack[] = $context;
+
+ try {
+ return $callable();
+ } finally {
+ array_pop(static::$macroContextStack);
+ }
+ }
+
+ /**
+ * Return the current context from inside a macro callee or a null if static.
+ *
+ * @return static|null
+ */
+ protected static function context()
+ {
+ return end(static::$macroContextStack) ?: null;
+ }
+
+ /**
+ * Return the current context from inside a macro callee or a new one if static.
+ *
+ * @return static
+ */
+ protected static function this()
+ {
+ return end(static::$macroContextStack) ?: new static();
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php b/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php
new file mode 100644
index 0000000..39343d8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Modifiers.php
@@ -0,0 +1,472 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterface;
+use ReturnTypeWillChange;
+
+/**
+ * Trait Modifiers.
+ *
+ * Returns dates relative to current date using modifier short-hand.
+ */
+trait Modifiers
+{
+ /**
+ * Midday/noon hour.
+ *
+ * @var int
+ */
+ protected static $midDayAt = 12;
+
+ /**
+ * get midday/noon hour
+ *
+ * @return int
+ */
+ public static function getMidDayAt()
+ {
+ return static::$midDayAt;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather consider mid-day is always 12pm, then if you need to test if it's an other
+ * hour, test it explicitly:
+ * $date->format('G') == 13
+ * or to set explicitly to a given hour:
+ * $date->setTime(13, 0, 0, 0)
+ *
+ * Set midday/noon hour
+ *
+ * @param int $hour midday hour
+ *
+ * @return void
+ */
+ public static function setMidDayAt($hour)
+ {
+ static::$midDayAt = $hour;
+ }
+
+ /**
+ * Modify to midday, default to self::$midDayAt
+ *
+ * @return static
+ */
+ public function midDay()
+ {
+ return $this->setTime(static::$midDayAt, 0, 0, 0);
+ }
+
+ /**
+ * Modify to the next occurrence of a given modifier such as a day of
+ * the week. If no modifier is provided, modify to the next occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param string|int|null $modifier
+ *
+ * @return static|false
+ */
+ public function next($modifier = null)
+ {
+ if ($modifier === null) {
+ $modifier = $this->dayOfWeek;
+ }
+
+ return $this->change(
+ 'next '.(\is_string($modifier) ? $modifier : static::$days[$modifier])
+ );
+ }
+
+ /**
+ * Go forward or backward to the next week- or weekend-day.
+ *
+ * @param bool $weekday
+ * @param bool $forward
+ *
+ * @return static
+ */
+ private function nextOrPreviousDay($weekday = true, $forward = true)
+ {
+ /** @var CarbonInterface $date */
+ $date = $this;
+ $step = $forward ? 1 : -1;
+
+ do {
+ $date = $date->addDays($step);
+ } while ($weekday ? $date->isWeekend() : $date->isWeekday());
+
+ return $date;
+ }
+
+ /**
+ * Go forward to the next weekday.
+ *
+ * @return static
+ */
+ public function nextWeekday()
+ {
+ return $this->nextOrPreviousDay();
+ }
+
+ /**
+ * Go backward to the previous weekday.
+ *
+ * @return static
+ */
+ public function previousWeekday()
+ {
+ return $this->nextOrPreviousDay(true, false);
+ }
+
+ /**
+ * Go forward to the next weekend day.
+ *
+ * @return static
+ */
+ public function nextWeekendDay()
+ {
+ return $this->nextOrPreviousDay(false);
+ }
+
+ /**
+ * Go backward to the previous weekend day.
+ *
+ * @return static
+ */
+ public function previousWeekendDay()
+ {
+ return $this->nextOrPreviousDay(false, false);
+ }
+
+ /**
+ * Modify to the previous occurrence of a given modifier such as a day of
+ * the week. If no dayOfWeek is provided, modify to the previous occurrence
+ * of the current day of the week. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param string|int|null $modifier
+ *
+ * @return static|false
+ */
+ public function previous($modifier = null)
+ {
+ if ($modifier === null) {
+ $modifier = $this->dayOfWeek;
+ }
+
+ return $this->change(
+ 'last '.(\is_string($modifier) ? $modifier : static::$days[$modifier])
+ );
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * first day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function firstOfMonth($dayOfWeek = null)
+ {
+ $date = $this->startOfDay();
+
+ if ($dayOfWeek === null) {
+ return $date->day(1);
+ }
+
+ return $date->modify('first '.static::$days[$dayOfWeek].' of '.$date->rawFormat('F').' '.$date->year);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current month. If no dayOfWeek is provided, modify to the
+ * last day of the current month. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek
+ *
+ * @return static
+ */
+ public function lastOfMonth($dayOfWeek = null)
+ {
+ $date = $this->startOfDay();
+
+ if ($dayOfWeek === null) {
+ return $date->day($date->daysInMonth);
+ }
+
+ return $date->modify('last '.static::$days[$dayOfWeek].' of '.$date->rawFormat('F').' '.$date->year);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current month. If the calculated occurrence is outside the scope
+ * of the current month, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfMonth($nth, $dayOfWeek)
+ {
+ $date = $this->avoidMutation()->firstOfMonth();
+ $check = $date->rawFormat('Y-m');
+ $date = $date->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return $date->rawFormat('Y-m') === $check ? $this->modify((string) $date) : false;
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * first day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function firstOfQuarter($dayOfWeek = null)
+ {
+ return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER - 2, 1)->firstOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current quarter. If no dayOfWeek is provided, modify to the
+ * last day of the current quarter. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function lastOfQuarter($dayOfWeek = null)
+ {
+ return $this->setDate($this->year, $this->quarter * static::MONTHS_PER_QUARTER, 1)->lastOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current quarter. If the calculated occurrence is outside the scope
+ * of the current quarter, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfQuarter($nth, $dayOfWeek)
+ {
+ $date = $this->avoidMutation()->day(1)->month($this->quarter * static::MONTHS_PER_QUARTER);
+ $lastMonth = $date->month;
+ $year = $date->year;
+ $date = $date->firstOfQuarter()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return ($lastMonth < $date->month || $year !== $date->year) ? false : $this->modify((string) $date);
+ }
+
+ /**
+ * Modify to the first occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * first day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function firstOfYear($dayOfWeek = null)
+ {
+ return $this->month(1)->firstOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the last occurrence of a given day of the week
+ * in the current year. If no dayOfWeek is provided, modify to the
+ * last day of the current year. Use the supplied constants
+ * to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int|null $dayOfWeek day of the week default null
+ *
+ * @return static
+ */
+ public function lastOfYear($dayOfWeek = null)
+ {
+ return $this->month(static::MONTHS_PER_YEAR)->lastOfMonth($dayOfWeek);
+ }
+
+ /**
+ * Modify to the given occurrence of a given day of the week
+ * in the current year. If the calculated occurrence is outside the scope
+ * of the current year, then return false and no modifications are made.
+ * Use the supplied constants to indicate the desired dayOfWeek, ex. static::MONDAY.
+ *
+ * @param int $nth
+ * @param int $dayOfWeek
+ *
+ * @return mixed
+ */
+ public function nthOfYear($nth, $dayOfWeek)
+ {
+ $date = $this->avoidMutation()->firstOfYear()->modify('+'.$nth.' '.static::$days[$dayOfWeek]);
+
+ return $this->year === $date->year ? $this->modify((string) $date) : false;
+ }
+
+ /**
+ * Modify the current instance to the average of a given instance (default now) and the current instance
+ * (second-precision).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|null $date
+ *
+ * @return static
+ */
+ public function average($date = null)
+ {
+ return $this->addRealMicroseconds((int) ($this->diffInRealMicroseconds($this->resolveCarbon($date), false) / 2));
+ }
+
+ /**
+ * Get the closest date from the instance (second-precision).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return static
+ */
+ public function closest($date1, $date2)
+ {
+ return $this->diffInRealMicroseconds($date1) < $this->diffInRealMicroseconds($date2) ? $date1 : $date2;
+ }
+
+ /**
+ * Get the farthest date from the instance (second-precision).
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
+ *
+ * @return static
+ */
+ public function farthest($date1, $date2)
+ {
+ return $this->diffInRealMicroseconds($date1) > $this->diffInRealMicroseconds($date2) ? $date1 : $date2;
+ }
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return static
+ */
+ public function min($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->lt($date) ? $this : $date;
+ }
+
+ /**
+ * Get the minimum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see min()
+ *
+ * @return static
+ */
+ public function minimum($date = null)
+ {
+ return $this->min($date);
+ }
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @return static
+ */
+ public function max($date = null)
+ {
+ $date = $this->resolveCarbon($date);
+
+ return $this->gt($date) ? $this : $date;
+ }
+
+ /**
+ * Get the maximum instance between a given instance (default now) and the current instance.
+ *
+ * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
+ *
+ * @see max()
+ *
+ * @return static
+ */
+ public function maximum($date = null)
+ {
+ return $this->max($date);
+ }
+
+ /**
+ * Calls \DateTime::modify if mutable or \DateTimeImmutable::modify else.
+ *
+ * @see https://php.net/manual/en/datetime.modify.php
+ *
+ * @return static|false
+ */
+ #[ReturnTypeWillChange]
+ public function modify($modify)
+ {
+ return parent::modify((string) $modify);
+ }
+
+ /**
+ * Similar to native modify() method of DateTime but can handle more grammars.
+ *
+ * @example
+ * ```
+ * echo Carbon::now()->change('next 2pm');
+ * ```
+ *
+ * @link https://php.net/manual/en/datetime.modify.php
+ *
+ * @param string $modifier
+ *
+ * @return static|false
+ */
+ public function change($modifier)
+ {
+ return $this->modify(preg_replace_callback('/^(next|previous|last)\s+(\d{1,2}(h|am|pm|:\d{1,2}(:\d{1,2})?))$/i', function ($match) {
+ $match[2] = str_replace('h', ':00', $match[2]);
+ $test = $this->avoidMutation()->modify($match[2]);
+ $method = $match[1] === 'next' ? 'lt' : 'gt';
+ $match[1] = $test->$method($this) ? $match[1].' day' : 'today';
+
+ return $match[1].' '.$match[2];
+ }, strtr(trim($modifier), [
+ ' at ' => ' ',
+ 'just now' => 'now',
+ 'after tomorrow' => 'tomorrow +1 day',
+ 'before yesterday' => 'yesterday -1 day',
+ ])));
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php b/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php
new file mode 100644
index 0000000..561c867
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Mutability.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Carbon;
+use Carbon\CarbonImmutable;
+
+/**
+ * Trait Mutability.
+ *
+ * Utils to know if the current object is mutable or immutable and convert it.
+ */
+trait Mutability
+{
+ use Cast;
+
+ /**
+ * Returns true if the current class/instance is mutable.
+ *
+ * @return bool
+ */
+ public static function isMutable()
+ {
+ return false;
+ }
+
+ /**
+ * Returns true if the current class/instance is immutable.
+ *
+ * @return bool
+ */
+ public static function isImmutable()
+ {
+ return !static::isMutable();
+ }
+
+ /**
+ * Return a mutable copy of the instance.
+ *
+ * @return Carbon
+ */
+ public function toMutable()
+ {
+ /** @var Carbon $date */
+ $date = $this->cast(Carbon::class);
+
+ return $date;
+ }
+
+ /**
+ * Return a immutable copy of the instance.
+ *
+ * @return CarbonImmutable
+ */
+ public function toImmutable()
+ {
+ /** @var CarbonImmutable $date */
+ $date = $this->cast(CarbonImmutable::class);
+
+ return $date;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php b/vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php
new file mode 100644
index 0000000..c77a102
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+trait ObjectInitialisation
+{
+ /**
+ * True when parent::__construct has been called.
+ *
+ * @var string
+ */
+ protected $constructedObjectId;
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Options.php b/vendor/nesbot/carbon/src/Carbon/Traits/Options.php
new file mode 100644
index 0000000..48f9739
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Options.php
@@ -0,0 +1,471 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterface;
+use DateTimeInterface;
+use Throwable;
+
+/**
+ * Trait Options.
+ *
+ * Embed base methods to change settings of Carbon classes.
+ *
+ * Depends on the following methods:
+ *
+ * @method static shiftTimezone($timezone) Set the timezone
+ */
+trait Options
+{
+ use Localization;
+
+ /**
+ * Customizable PHP_INT_SIZE override.
+ *
+ * @var int
+ */
+ public static $PHPIntSize = PHP_INT_SIZE;
+
+ /**
+ * First day of week.
+ *
+ * @var int|string
+ */
+ protected static $weekStartsAt = CarbonInterface::MONDAY;
+
+ /**
+ * Last day of week.
+ *
+ * @var int|string
+ */
+ protected static $weekEndsAt = CarbonInterface::SUNDAY;
+
+ /**
+ * Days of weekend.
+ *
+ * @var array
+ */
+ protected static $weekendDays = [
+ CarbonInterface::SATURDAY,
+ CarbonInterface::SUNDAY,
+ ];
+
+ /**
+ * Format regex patterns.
+ *
+ * @var array
+ */
+ protected static $regexFormats = [
+ 'd' => '(3[01]|[12][0-9]|0[1-9])',
+ 'D' => '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)',
+ 'j' => '([123][0-9]|[1-9])',
+ 'l' => '([a-zA-Z]{2,})',
+ 'N' => '([1-7])',
+ 'S' => '(st|nd|rd|th)',
+ 'w' => '([0-6])',
+ 'z' => '(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9]?[0-9])',
+ 'W' => '(5[012]|[1-4][0-9]|0?[1-9])',
+ 'F' => '([a-zA-Z]{2,})',
+ 'm' => '(1[012]|0[1-9])',
+ 'M' => '([a-zA-Z]{3})',
+ 'n' => '(1[012]|[1-9])',
+ 't' => '(2[89]|3[01])',
+ 'L' => '(0|1)',
+ 'o' => '([1-9][0-9]{0,4})',
+ 'Y' => '([1-9]?[0-9]{4})',
+ 'y' => '([0-9]{2})',
+ 'a' => '(am|pm)',
+ 'A' => '(AM|PM)',
+ 'B' => '([0-9]{3})',
+ 'g' => '(1[012]|[1-9])',
+ 'G' => '(2[0-3]|1?[0-9])',
+ 'h' => '(1[012]|0[1-9])',
+ 'H' => '(2[0-3]|[01][0-9])',
+ 'i' => '([0-5][0-9])',
+ 's' => '([0-5][0-9])',
+ 'u' => '([0-9]{1,6})',
+ 'v' => '([0-9]{1,3})',
+ 'e' => '([a-zA-Z]{1,5})|([a-zA-Z]*\\/[a-zA-Z]*)',
+ 'I' => '(0|1)',
+ 'O' => '([+-](1[012]|0[0-9])[0134][05])',
+ 'P' => '([+-](1[012]|0[0-9]):[0134][05])',
+ 'p' => '(Z|[+-](1[012]|0[0-9]):[0134][05])',
+ 'T' => '([a-zA-Z]{1,5})',
+ 'Z' => '(-?[1-5]?[0-9]{1,4})',
+ 'U' => '([0-9]*)',
+
+ // The formats below are combinations of the above formats.
+ 'c' => '(([1-9]?[0-9]{4})-(1[012]|0[1-9])-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[+-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP
+ 'r' => '(([a-zA-Z]{3}), ([123][0-9]|0[1-9]) ([a-zA-Z]{3}) ([1-9]?[0-9]{4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [+-](1[012]|0[0-9])([0134][05]))', // D, d M Y H:i:s O
+ ];
+
+ /**
+ * Format modifiers (such as available in createFromFormat) regex patterns.
+ *
+ * @var array
+ */
+ protected static $regexFormatModifiers = [
+ '*' => '.+',
+ ' ' => '[ ]',
+ '#' => '[;:\\/.,()-]',
+ '?' => '([^a]|[a])',
+ '!' => '',
+ '|' => '',
+ '+' => '',
+ ];
+
+ /**
+ * Indicates if months should be calculated with overflow.
+ * Global setting.
+ *
+ * @var bool
+ */
+ protected static $monthsOverflow = true;
+
+ /**
+ * Indicates if years should be calculated with overflow.
+ * Global setting.
+ *
+ * @var bool
+ */
+ protected static $yearsOverflow = true;
+
+ /**
+ * Indicates if the strict mode is in use.
+ * Global setting.
+ *
+ * @var bool
+ */
+ protected static $strictModeEnabled = true;
+
+ /**
+ * Function to call instead of format.
+ *
+ * @var string|callable|null
+ */
+ protected static $formatFunction;
+
+ /**
+ * Function to call instead of createFromFormat.
+ *
+ * @var string|callable|null
+ */
+ protected static $createFromFormatFunction;
+
+ /**
+ * Function to call instead of parse.
+ *
+ * @var string|callable|null
+ */
+ protected static $parseFunction;
+
+ /**
+ * Indicates if months should be calculated with overflow.
+ * Specific setting.
+ *
+ * @var bool|null
+ */
+ protected $localMonthsOverflow;
+
+ /**
+ * Indicates if years should be calculated with overflow.
+ * Specific setting.
+ *
+ * @var bool|null
+ */
+ protected $localYearsOverflow;
+
+ /**
+ * Indicates if the strict mode is in use.
+ * Specific setting.
+ *
+ * @var bool|null
+ */
+ protected $localStrictModeEnabled;
+
+ /**
+ * Options for diffForHumans and forHumans methods.
+ *
+ * @var bool|null
+ */
+ protected $localHumanDiffOptions;
+
+ /**
+ * Format to use on string cast.
+ *
+ * @var string|null
+ */
+ protected $localToStringFormat;
+
+ /**
+ * Format to use on JSON serialization.
+ *
+ * @var string|null
+ */
+ protected $localSerializer;
+
+ /**
+ * Instance-specific macros.
+ *
+ * @var array|null
+ */
+ protected $localMacros;
+
+ /**
+ * Instance-specific generic macros.
+ *
+ * @var array|null
+ */
+ protected $localGenericMacros;
+
+ /**
+ * Function to call instead of format.
+ *
+ * @var string|callable|null
+ */
+ protected $localFormatFunction;
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * @see settings
+ *
+ * Enable the strict mode (or disable with passing false).
+ *
+ * @param bool $strictModeEnabled
+ */
+ public static function useStrictMode($strictModeEnabled = true)
+ {
+ static::$strictModeEnabled = $strictModeEnabled;
+ }
+
+ /**
+ * Returns true if the strict mode is globally in use, false else.
+ * (It can be overridden in specific instances.)
+ *
+ * @return bool
+ */
+ public static function isStrictModeEnabled()
+ {
+ return static::$strictModeEnabled;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Indicates if months should be calculated with overflow.
+ *
+ * @param bool $monthsOverflow
+ *
+ * @return void
+ */
+ public static function useMonthsOverflow($monthsOverflow = true)
+ {
+ static::$monthsOverflow = $monthsOverflow;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addMonthsWithOverflow/addMonthsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetMonthsOverflow()
+ {
+ static::$monthsOverflow = true;
+ }
+
+ /**
+ * Get the month overflow global behavior (can be overridden in specific instances).
+ *
+ * @return bool
+ */
+ public static function shouldOverflowMonths()
+ {
+ return static::$monthsOverflow;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Indicates if years should be calculated with overflow.
+ *
+ * @param bool $yearsOverflow
+ *
+ * @return void
+ */
+ public static function useYearsOverflow($yearsOverflow = true)
+ {
+ static::$yearsOverflow = $yearsOverflow;
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather use the ->settings() method.
+ * Or you can use method variants: addYearsWithOverflow/addYearsNoOverflow, same variants
+ * are available for quarters, years, decade, centuries, millennia (singular and plural forms).
+ * @see settings
+ *
+ * Reset the month overflow behavior.
+ *
+ * @return void
+ */
+ public static function resetYearsOverflow()
+ {
+ static::$yearsOverflow = true;
+ }
+
+ /**
+ * Get the month overflow global behavior (can be overridden in specific instances).
+ *
+ * @return bool
+ */
+ public static function shouldOverflowYears()
+ {
+ return static::$yearsOverflow;
+ }
+
+ /**
+ * Set specific options.
+ * - strictMode: true|false|null
+ * - monthOverflow: true|false|null
+ * - yearOverflow: true|false|null
+ * - humanDiffOptions: int|null
+ * - toStringFormat: string|Closure|null
+ * - toJsonFormat: string|Closure|null
+ * - locale: string|null
+ * - timezone: \DateTimeZone|string|int|null
+ * - macros: array|null
+ * - genericMacros: array|null
+ *
+ * @param array $settings
+ *
+ * @return $this|static
+ */
+ public function settings(array $settings)
+ {
+ $this->localStrictModeEnabled = $settings['strictMode'] ?? null;
+ $this->localMonthsOverflow = $settings['monthOverflow'] ?? null;
+ $this->localYearsOverflow = $settings['yearOverflow'] ?? null;
+ $this->localHumanDiffOptions = $settings['humanDiffOptions'] ?? null;
+ $this->localToStringFormat = $settings['toStringFormat'] ?? null;
+ $this->localSerializer = $settings['toJsonFormat'] ?? null;
+ $this->localMacros = $settings['macros'] ?? null;
+ $this->localGenericMacros = $settings['genericMacros'] ?? null;
+ $this->localFormatFunction = $settings['formatFunction'] ?? null;
+
+ if (isset($settings['locale'])) {
+ $locales = $settings['locale'];
+
+ if (!\is_array($locales)) {
+ $locales = [$locales];
+ }
+
+ $this->locale(...$locales);
+ }
+
+ if (isset($settings['innerTimezone'])) {
+ return $this->setTimezone($settings['innerTimezone']);
+ }
+
+ if (isset($settings['timezone'])) {
+ return $this->shiftTimezone($settings['timezone']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns current local settings.
+ *
+ * @return array
+ */
+ public function getSettings()
+ {
+ $settings = [];
+ $map = [
+ 'localStrictModeEnabled' => 'strictMode',
+ 'localMonthsOverflow' => 'monthOverflow',
+ 'localYearsOverflow' => 'yearOverflow',
+ 'localHumanDiffOptions' => 'humanDiffOptions',
+ 'localToStringFormat' => 'toStringFormat',
+ 'localSerializer' => 'toJsonFormat',
+ 'localMacros' => 'macros',
+ 'localGenericMacros' => 'genericMacros',
+ 'locale' => 'locale',
+ 'tzName' => 'timezone',
+ 'localFormatFunction' => 'formatFunction',
+ ];
+
+ foreach ($map as $property => $key) {
+ $value = $this->$property ?? null;
+
+ if ($value !== null && ($key !== 'locale' || $value !== 'en' || $this->localTranslator)) {
+ $settings[$key] = $value;
+ }
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Show truthy properties on var_dump().
+ *
+ * @return array
+ */
+ public function __debugInfo()
+ {
+ $infos = array_filter(get_object_vars($this), static function ($var) {
+ return $var;
+ });
+
+ foreach (['dumpProperties', 'constructedObjectId', 'constructed'] as $property) {
+ if (isset($infos[$property])) {
+ unset($infos[$property]);
+ }
+ }
+
+ $this->addExtraDebugInfos($infos);
+
+ return $infos;
+ }
+
+ protected function addExtraDebugInfos(&$infos): void
+ {
+ if ($this instanceof DateTimeInterface) {
+ try {
+ if (!isset($infos['date'])) {
+ $infos['date'] = $this->format(CarbonInterface::MOCK_DATETIME_FORMAT);
+ }
+
+ if (!isset($infos['timezone'])) {
+ $infos['timezone'] = $this->tzName;
+ }
+ } catch (Throwable $exception) {
+ // noop
+ }
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php b/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php
new file mode 100644
index 0000000..f98c2a3
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Rounding.php
@@ -0,0 +1,258 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterface;
+use Carbon\Exceptions\UnknownUnitException;
+
+/**
+ * Trait Rounding.
+ *
+ * Round, ceil, floor units.
+ *
+ * Depends on the following methods:
+ *
+ * @method static copy()
+ * @method static startOfWeek(int $weekStartsAt = null)
+ */
+trait Rounding
+{
+ use IntervalRounding;
+
+ /**
+ * Round the current instance at the given unit with given precision if specified and the given function.
+ *
+ * @param string $unit
+ * @param float|int $precision
+ * @param string $function
+ *
+ * @return CarbonInterface
+ */
+ public function roundUnit($unit, $precision = 1, $function = 'round')
+ {
+ $metaUnits = [
+ // @call roundUnit
+ 'millennium' => [static::YEARS_PER_MILLENNIUM, 'year'],
+ // @call roundUnit
+ 'century' => [static::YEARS_PER_CENTURY, 'year'],
+ // @call roundUnit
+ 'decade' => [static::YEARS_PER_DECADE, 'year'],
+ // @call roundUnit
+ 'quarter' => [static::MONTHS_PER_QUARTER, 'month'],
+ // @call roundUnit
+ 'millisecond' => [1000, 'microsecond'],
+ ];
+ $normalizedUnit = static::singularUnit($unit);
+ $ranges = array_merge(static::getRangesByUnit($this->daysInMonth), [
+ // @call roundUnit
+ 'microsecond' => [0, 999999],
+ ]);
+ $factor = 1;
+ $initialMonth = $this->month;
+
+ if ($normalizedUnit === 'week') {
+ $normalizedUnit = 'day';
+ $precision *= static::DAYS_PER_WEEK;
+ }
+
+ if (isset($metaUnits[$normalizedUnit])) {
+ [$factor, $normalizedUnit] = $metaUnits[$normalizedUnit];
+ }
+
+ $precision *= $factor;
+
+ if (!isset($ranges[$normalizedUnit])) {
+ throw new UnknownUnitException($unit);
+ }
+
+ $found = false;
+ $fraction = 0;
+ $arguments = null;
+ $initialValue = null;
+ $factor = $this->year < 0 ? -1 : 1;
+ $changes = [];
+ $minimumInc = null;
+
+ foreach ($ranges as $unit => [$minimum, $maximum]) {
+ if ($normalizedUnit === $unit) {
+ $arguments = [$this->$unit, $minimum];
+ $initialValue = $this->$unit;
+ $fraction = $precision - floor($precision);
+ $found = true;
+
+ continue;
+ }
+
+ if ($found) {
+ $delta = $maximum + 1 - $minimum;
+ $factor /= $delta;
+ $fraction *= $delta;
+ $inc = ($this->$unit - $minimum) * $factor;
+
+ if ($inc !== 0.0) {
+ $minimumInc = $minimumInc ?? ($arguments[0] / pow(2, 52));
+
+ // If value is still the same when adding a non-zero increment/decrement,
+ // it means precision got lost in the addition
+ if (abs($inc) < $minimumInc) {
+ $inc = $minimumInc * ($inc < 0 ? -1 : 1);
+ }
+
+ // If greater than $precision, assume precision loss caused an overflow
+ if ($function !== 'floor' || abs($arguments[0] + $inc - $initialValue) >= $precision) {
+ $arguments[0] += $inc;
+ }
+ }
+
+ $changes[$unit] = round(
+ $minimum + ($fraction ? $fraction * $function(($this->$unit - $minimum) / $fraction) : 0)
+ );
+
+ // Cannot use modulo as it lose double precision
+ while ($changes[$unit] >= $delta) {
+ $changes[$unit] -= $delta;
+ }
+
+ $fraction -= floor($fraction);
+ }
+ }
+
+ [$value, $minimum] = $arguments;
+ $normalizedValue = floor($function(($value - $minimum) / $precision) * $precision + $minimum);
+
+ /** @var CarbonInterface $result */
+ $result = $this->$normalizedUnit($normalizedValue);
+
+ foreach ($changes as $unit => $value) {
+ $result = $result->$unit($value);
+ }
+
+ return $normalizedUnit === 'month' && $precision <= 1 && abs($result->month - $initialMonth) === 2
+ // Re-run the change in case an overflow occurred
+ ? $result->$normalizedUnit($normalizedValue)
+ : $result;
+ }
+
+ /**
+ * Truncate the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int $precision
+ *
+ * @return CarbonInterface
+ */
+ public function floorUnit($unit, $precision = 1)
+ {
+ return $this->roundUnit($unit, $precision, 'floor');
+ }
+
+ /**
+ * Ceil the current instance at the given unit with given precision if specified.
+ *
+ * @param string $unit
+ * @param float|int $precision
+ *
+ * @return CarbonInterface
+ */
+ public function ceilUnit($unit, $precision = 1)
+ {
+ return $this->roundUnit($unit, $precision, 'ceil');
+ }
+
+ /**
+ * Round the current instance second with given precision if specified.
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ * @param string $function
+ *
+ * @return CarbonInterface
+ */
+ public function round($precision = 1, $function = 'round')
+ {
+ return $this->roundWith($precision, $function);
+ }
+
+ /**
+ * Round the current instance second with given precision if specified.
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return CarbonInterface
+ */
+ public function floor($precision = 1)
+ {
+ return $this->round($precision, 'floor');
+ }
+
+ /**
+ * Ceil the current instance second with given precision if specified.
+ *
+ * @param float|int|string|\DateInterval|null $precision
+ *
+ * @return CarbonInterface
+ */
+ public function ceil($precision = 1)
+ {
+ return $this->round($precision, 'ceil');
+ }
+
+ /**
+ * Round the current instance week.
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return CarbonInterface
+ */
+ public function roundWeek($weekStartsAt = null)
+ {
+ return $this->closest(
+ $this->avoidMutation()->floorWeek($weekStartsAt),
+ $this->avoidMutation()->ceilWeek($weekStartsAt)
+ );
+ }
+
+ /**
+ * Truncate the current instance week.
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return CarbonInterface
+ */
+ public function floorWeek($weekStartsAt = null)
+ {
+ return $this->startOfWeek($weekStartsAt);
+ }
+
+ /**
+ * Ceil the current instance week.
+ *
+ * @param int $weekStartsAt optional start allow you to specify the day of week to use to start the week
+ *
+ * @return CarbonInterface
+ */
+ public function ceilWeek($weekStartsAt = null)
+ {
+ if ($this->isMutable()) {
+ $startOfWeek = $this->avoidMutation()->startOfWeek($weekStartsAt);
+
+ return $startOfWeek != $this ?
+ $this->startOfWeek($weekStartsAt)->addWeek() :
+ $this;
+ }
+
+ $startOfWeek = $this->startOfWeek($weekStartsAt);
+
+ return $startOfWeek != $this ?
+ $startOfWeek->addWeek() :
+ $this->avoidMutation();
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php b/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php
new file mode 100644
index 0000000..c1d5c5e
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Serialization.php
@@ -0,0 +1,326 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\Exceptions\InvalidFormatException;
+use ReturnTypeWillChange;
+use Throwable;
+
+/**
+ * Trait Serialization.
+ *
+ * Serialization and JSON stuff.
+ *
+ * Depends on the following properties:
+ *
+ * @property int $year
+ * @property int $month
+ * @property int $daysInMonth
+ * @property int $quarter
+ *
+ * Depends on the following methods:
+ *
+ * @method string|static locale(string $locale = null, string ...$fallbackLocales)
+ * @method string toJSON()
+ */
+trait Serialization
+{
+ use ObjectInitialisation;
+
+ /**
+ * The custom Carbon JSON serializer.
+ *
+ * @var callable|null
+ */
+ protected static $serializer;
+
+ /**
+ * List of key to use for dump/serialization.
+ *
+ * @var string[]
+ */
+ protected $dumpProperties = ['date', 'timezone_type', 'timezone'];
+
+ /**
+ * Locale to dump comes here before serialization.
+ *
+ * @var string|null
+ */
+ protected $dumpLocale;
+
+ /**
+ * Embed date properties to dump in a dedicated variables so it won't overlap native
+ * DateTime ones.
+ *
+ * @var array|null
+ */
+ protected $dumpDateProperties;
+
+ /**
+ * Return a serialized string of the instance.
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ return serialize($this);
+ }
+
+ /**
+ * Create an instance from a serialized string.
+ *
+ * @param string $value
+ *
+ * @throws InvalidFormatException
+ *
+ * @return static
+ */
+ public static function fromSerialized($value)
+ {
+ $instance = @unserialize((string) $value);
+
+ if (!$instance instanceof static) {
+ throw new InvalidFormatException("Invalid serialized value: $value");
+ }
+
+ return $instance;
+ }
+
+ /**
+ * The __set_state handler.
+ *
+ * @param string|array $dump
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public static function __set_state($dump)
+ {
+ if (\is_string($dump)) {
+ return static::parse($dump);
+ }
+
+ /** @var \DateTimeInterface $date */
+ $date = get_parent_class(static::class) && method_exists(parent::class, '__set_state')
+ ? parent::__set_state((array) $dump)
+ : (object) $dump;
+
+ return static::instance($date);
+ }
+
+ /**
+ * Returns the list of properties to dump on serialize() called on.
+ *
+ * Only used by PHP < 7.4.
+ *
+ * @return array
+ */
+ public function __sleep()
+ {
+ $properties = $this->getSleepProperties();
+
+ if ($this->localTranslator ?? null) {
+ $properties[] = 'dumpLocale';
+ $this->dumpLocale = $this->locale ?? null;
+ }
+
+ return $properties;
+ }
+
+ /**
+ * Returns the values to dump on serialize() called on.
+ *
+ * Only used by PHP >= 7.4.
+ *
+ * @return array
+ */
+ public function __serialize(): array
+ {
+ // @codeCoverageIgnoreStart
+ if (isset($this->timezone_type, $this->timezone, $this->date)) {
+ return [
+ 'date' => $this->date ?? null,
+ 'timezone_type' => $this->timezone_type,
+ 'timezone' => $this->timezone ?? null,
+ ];
+ }
+ // @codeCoverageIgnoreEnd
+
+ $timezone = $this->getTimezone();
+ $export = [
+ 'date' => $this->format('Y-m-d H:i:s.u'),
+ 'timezone_type' => $timezone->getType(),
+ 'timezone' => $timezone->getName(),
+ ];
+
+ // @codeCoverageIgnoreStart
+ if (\extension_loaded('msgpack') && isset($this->constructedObjectId)) {
+ $export['dumpDateProperties'] = [
+ 'date' => $this->format('Y-m-d H:i:s.u'),
+ 'timezone' => serialize($this->timezone ?? null),
+ ];
+ }
+ // @codeCoverageIgnoreEnd
+
+ if ($this->localTranslator ?? null) {
+ $export['dumpLocale'] = $this->locale ?? null;
+ }
+
+ return $export;
+ }
+
+ /**
+ * Set locale if specified on unserialize() called.
+ *
+ * Only used by PHP < 7.4.
+ *
+ * @return void
+ */
+ #[ReturnTypeWillChange]
+ public function __wakeup()
+ {
+ if (parent::class && method_exists(parent::class, '__wakeup')) {
+ // @codeCoverageIgnoreStart
+ try {
+ parent::__wakeup();
+ } catch (Throwable $exception) {
+ try {
+ // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later.
+ ['date' => $date, 'timezone' => $timezone] = $this->dumpDateProperties;
+ parent::__construct($date, unserialize($timezone));
+ } catch (Throwable $ignoredException) {
+ throw $exception;
+ }
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ $this->constructedObjectId = spl_object_hash($this);
+
+ if (isset($this->dumpLocale)) {
+ $this->locale($this->dumpLocale);
+ $this->dumpLocale = null;
+ }
+
+ $this->cleanupDumpProperties();
+ }
+
+ /**
+ * Set locale if specified on unserialize() called.
+ *
+ * Only used by PHP >= 7.4.
+ *
+ * @return void
+ */
+ public function __unserialize(array $data): void
+ {
+ // @codeCoverageIgnoreStart
+ try {
+ $this->__construct($data['date'] ?? null, $data['timezone'] ?? null);
+ } catch (Throwable $exception) {
+ if (!isset($data['dumpDateProperties']['date'], $data['dumpDateProperties']['timezone'])) {
+ throw $exception;
+ }
+
+ try {
+ // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later.
+ ['date' => $date, 'timezone' => $timezone] = $data['dumpDateProperties'];
+ $this->__construct($date, unserialize($timezone));
+ } catch (Throwable $ignoredException) {
+ throw $exception;
+ }
+ }
+ // @codeCoverageIgnoreEnd
+
+ if (isset($data['dumpLocale'])) {
+ $this->locale($data['dumpLocale']);
+ }
+ }
+
+ /**
+ * Prepare the object for JSON serialization.
+ *
+ * @return array|string
+ */
+ #[ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ $serializer = $this->localSerializer ?? static::$serializer;
+
+ if ($serializer) {
+ return \is_string($serializer)
+ ? $this->rawFormat($serializer)
+ : $serializer($this);
+ }
+
+ return $this->toJSON();
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather transform Carbon object before the serialization.
+ *
+ * JSON serialize all Carbon instances using the given callback.
+ *
+ * @param callable $callback
+ *
+ * @return void
+ */
+ public static function serializeUsing($callback)
+ {
+ static::$serializer = $callback;
+ }
+
+ /**
+ * Cleanup properties attached to the public scope of DateTime when a dump of the date is requested.
+ * foreach ($date as $_) {}
+ * serializer($date)
+ * var_export($date)
+ * get_object_vars($date)
+ */
+ public function cleanupDumpProperties()
+ {
+ // @codeCoverageIgnoreStart
+ if (PHP_VERSION < 8.2) {
+ foreach ($this->dumpProperties as $property) {
+ if (isset($this->$property)) {
+ unset($this->$property);
+ }
+ }
+ }
+ // @codeCoverageIgnoreEnd
+
+ return $this;
+ }
+
+ private function getSleepProperties(): array
+ {
+ $properties = $this->dumpProperties;
+
+ // @codeCoverageIgnoreStart
+ if (!\extension_loaded('msgpack')) {
+ return $properties;
+ }
+
+ if (isset($this->constructedObjectId)) {
+ $this->dumpDateProperties = [
+ 'date' => $this->format('Y-m-d H:i:s.u'),
+ 'timezone' => serialize($this->timezone ?? null),
+ ];
+
+ $properties[] = 'dumpDateProperties';
+ }
+
+ return $properties;
+ // @codeCoverageIgnoreEnd
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Test.php b/vendor/nesbot/carbon/src/Carbon/Traits/Test.php
new file mode 100644
index 0000000..ffd01bd
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Test.php
@@ -0,0 +1,229 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonInterface;
+use Carbon\CarbonTimeZone;
+use Closure;
+use DateTimeImmutable;
+use DateTimeInterface;
+use InvalidArgumentException;
+use Throwable;
+
+trait Test
+{
+ ///////////////////////////////////////////////////////////////////
+ ///////////////////////// TESTING AIDS ////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * A test Carbon instance to be returned when now instances are created.
+ *
+ * @var Closure|static|null
+ */
+ protected static $testNow;
+
+ /**
+ * The timezone to resto to when clearing the time mock.
+ *
+ * @var string|null
+ */
+ protected static $testDefaultTimezone;
+
+ /**
+ * Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ *
+ * Note the timezone parameter was left out of the examples above and
+ * has no affect as the mock value will be returned regardless of its value.
+ *
+ * Only the moment is mocked with setTestNow(), the timezone will still be the one passed
+ * as parameter of date_default_timezone_get() as a fallback (see setTestNowAndTimezone()).
+ *
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ *
+ * /!\ Use this method for unit tests only.
+ *
+ * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance
+ */
+ public static function setTestNow($testNow = null)
+ {
+ static::$testNow = $testNow instanceof self || $testNow instanceof Closure
+ ? $testNow
+ : static::make($testNow);
+ }
+
+ /**
+ * Set a Carbon instance (real or mock) to be returned when a "now"
+ * instance is created. The provided instance will be returned
+ * specifically under the following conditions:
+ * - A call to the static now() method, ex. Carbon::now()
+ * - When a null (or blank string) is passed to the constructor or parse(), ex. new Carbon(null)
+ * - When the string "now" is passed to the constructor or parse(), ex. new Carbon('now')
+ * - When a string containing the desired time is passed to Carbon::parse().
+ *
+ * It will also align default timezone (e.g. call date_default_timezone_set()) with
+ * the second argument or if null, with the timezone of the given date object.
+ *
+ * To clear the test instance call this method using the default
+ * parameter of null.
+ *
+ * /!\ Use this method for unit tests only.
+ *
+ * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance
+ */
+ public static function setTestNowAndTimezone($testNow = null, $tz = null)
+ {
+ if ($testNow) {
+ self::$testDefaultTimezone = self::$testDefaultTimezone ?? date_default_timezone_get();
+ }
+
+ $useDateInstanceTimezone = $testNow instanceof DateTimeInterface;
+
+ if ($useDateInstanceTimezone) {
+ self::setDefaultTimezone($testNow->getTimezone()->getName(), $testNow);
+ }
+
+ static::setTestNow($testNow);
+
+ if (!$useDateInstanceTimezone) {
+ $now = static::getMockedTestNow(\func_num_args() === 1 ? null : $tz);
+ $tzName = $now ? $now->tzName : null;
+ self::setDefaultTimezone($tzName ?? self::$testDefaultTimezone ?? 'UTC', $now);
+ }
+
+ if (!$testNow) {
+ self::$testDefaultTimezone = null;
+ }
+ }
+
+ /**
+ * Temporarily sets a static date to be used within the callback.
+ * Using setTestNow to set the date, executing the callback, then
+ * clearing the test instance.
+ *
+ * /!\ Use this method for unit tests only.
+ *
+ * @template T
+ *
+ * @param DateTimeInterface|Closure|static|string|false|null $testNow real or mock Carbon instance
+ * @param Closure(): T $callback
+ *
+ * @return mixed
+ * @phpstan-return T
+ */
+ public static function withTestNow($testNow, $callback)
+ {
+ static::setTestNow($testNow);
+
+ try {
+ $result = $callback();
+ } finally {
+ static::setTestNow();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the Carbon instance (real or mock) to be returned when a "now"
+ * instance is created.
+ *
+ * @return Closure|static the current instance used for testing
+ */
+ public static function getTestNow()
+ {
+ return static::$testNow;
+ }
+
+ /**
+ * Determine if there is a valid test instance set. A valid test instance
+ * is anything that is not null.
+ *
+ * @return bool true if there is a test instance, otherwise false
+ */
+ public static function hasTestNow()
+ {
+ return static::getTestNow() !== null;
+ }
+
+ /**
+ * Get the mocked date passed in setTestNow() and if it's a Closure, execute it.
+ *
+ * @param string|\DateTimeZone $tz
+ *
+ * @return \Carbon\CarbonImmutable|\Carbon\Carbon|null
+ */
+ protected static function getMockedTestNow($tz)
+ {
+ $testNow = static::getTestNow();
+
+ if ($testNow instanceof Closure) {
+ $realNow = new DateTimeImmutable('now');
+ $testNow = $testNow(static::parse(
+ $realNow->format('Y-m-d H:i:s.u'),
+ $tz ?: $realNow->getTimezone()
+ ));
+ }
+ /* @var \Carbon\CarbonImmutable|\Carbon\Carbon|null $testNow */
+
+ return $testNow instanceof CarbonInterface
+ ? $testNow->avoidMutation()->tz($tz)
+ : $testNow;
+ }
+
+ protected static function mockConstructorParameters(&$time, $tz)
+ {
+ /** @var \Carbon\CarbonImmutable|\Carbon\Carbon $testInstance */
+ $testInstance = clone static::getMockedTestNow($tz);
+
+ if (static::hasRelativeKeywords($time)) {
+ $testInstance = $testInstance->modify($time);
+ }
+
+ $time = $testInstance instanceof self
+ ? $testInstance->rawFormat(static::MOCK_DATETIME_FORMAT)
+ : $testInstance->format(static::MOCK_DATETIME_FORMAT);
+ }
+
+ private static function setDefaultTimezone($timezone, DateTimeInterface $date = null)
+ {
+ $previous = null;
+ $success = false;
+
+ try {
+ $success = date_default_timezone_set($timezone);
+ } catch (Throwable $exception) {
+ $previous = $exception;
+ }
+
+ if (!$success) {
+ $suggestion = @CarbonTimeZone::create($timezone)->toRegionName($date);
+
+ throw new InvalidArgumentException(
+ "Timezone ID '$timezone' is invalid".
+ ($suggestion && $suggestion !== $timezone ? ", did you mean '$suggestion'?" : '.')."\n".
+ "It must be one of the IDs from DateTimeZone::listIdentifiers(),\n".
+ 'For the record, hours/minutes offset are relevant only for a particular moment, '.
+ 'but not as a default timezone.',
+ 0,
+ $previous
+ );
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php b/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php
new file mode 100644
index 0000000..88a465c
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Timestamp.php
@@ -0,0 +1,198 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+/**
+ * Trait Timestamp.
+ */
+trait Timestamp
+{
+ /**
+ * Create a Carbon instance from a timestamp and set the timezone (use default one if not specified).
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestamp($timestamp, $tz = null)
+ {
+ return static::createFromTimestampUTC($timestamp)->setTimezone($tz);
+ }
+
+ /**
+ * Create a Carbon instance from an timestamp keeping the timezone to UTC.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ *
+ * @return static
+ */
+ public static function createFromTimestampUTC($timestamp)
+ {
+ [$integer, $decimal] = self::getIntegerAndDecimalParts($timestamp);
+ $delta = floor($decimal / static::MICROSECONDS_PER_SECOND);
+ $integer += $delta;
+ $decimal -= $delta * static::MICROSECONDS_PER_SECOND;
+ $decimal = str_pad((string) $decimal, 6, '0', STR_PAD_LEFT);
+
+ return static::rawCreateFromFormat('U u', "$integer $decimal");
+ }
+
+ /**
+ * Create a Carbon instance from a timestamp in milliseconds.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ *
+ * @return static
+ */
+ public static function createFromTimestampMsUTC($timestamp)
+ {
+ [$milliseconds, $microseconds] = self::getIntegerAndDecimalParts($timestamp, 3);
+ $sign = $milliseconds < 0 || ($milliseconds === 0.0 && $microseconds < 0) ? -1 : 1;
+ $milliseconds = abs($milliseconds);
+ $microseconds = $sign * abs($microseconds) + static::MICROSECONDS_PER_MILLISECOND * ($milliseconds % static::MILLISECONDS_PER_SECOND);
+ $seconds = $sign * floor($milliseconds / static::MILLISECONDS_PER_SECOND);
+ $delta = floor($microseconds / static::MICROSECONDS_PER_SECOND);
+ $seconds += $delta;
+ $microseconds -= $delta * static::MICROSECONDS_PER_SECOND;
+ $microseconds = str_pad($microseconds, 6, '0', STR_PAD_LEFT);
+
+ return static::rawCreateFromFormat('U u', "$seconds $microseconds");
+ }
+
+ /**
+ * Create a Carbon instance from a timestamp in milliseconds.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $timestamp
+ * @param \DateTimeZone|string|null $tz
+ *
+ * @return static
+ */
+ public static function createFromTimestampMs($timestamp, $tz = null)
+ {
+ return static::createFromTimestampMsUTC($timestamp)
+ ->setTimezone($tz);
+ }
+
+ /**
+ * Set the instance's timestamp.
+ *
+ * Timestamp input can be given as int, float or a string containing one or more numbers.
+ *
+ * @param float|int|string $unixTimestamp
+ *
+ * @return static
+ */
+ public function timestamp($unixTimestamp)
+ {
+ return $this->setTimestamp($unixTimestamp);
+ }
+
+ /**
+ * Returns a timestamp rounded with the given precision (6 by default).
+ *
+ * @example getPreciseTimestamp() 1532087464437474 (microsecond maximum precision)
+ * @example getPreciseTimestamp(6) 1532087464437474
+ * @example getPreciseTimestamp(5) 153208746443747 (1/100000 second precision)
+ * @example getPreciseTimestamp(4) 15320874644375 (1/10000 second precision)
+ * @example getPreciseTimestamp(3) 1532087464437 (millisecond precision)
+ * @example getPreciseTimestamp(2) 153208746444 (1/100 second precision)
+ * @example getPreciseTimestamp(1) 15320874644 (1/10 second precision)
+ * @example getPreciseTimestamp(0) 1532087464 (second precision)
+ * @example getPreciseTimestamp(-1) 153208746 (10 second precision)
+ * @example getPreciseTimestamp(-2) 15320875 (100 second precision)
+ *
+ * @param int $precision
+ *
+ * @return float
+ */
+ public function getPreciseTimestamp($precision = 6)
+ {
+ return round(((float) $this->rawFormat('Uu')) / pow(10, 6 - $precision));
+ }
+
+ /**
+ * Returns the milliseconds timestamps used amongst other by Date javascript objects.
+ *
+ * @return float
+ */
+ public function valueOf()
+ {
+ return $this->getPreciseTimestamp(3);
+ }
+
+ /**
+ * Returns the timestamp with millisecond precision.
+ *
+ * @return int
+ */
+ public function getTimestampMs()
+ {
+ return (int) $this->getPreciseTimestamp(3);
+ }
+
+ /**
+ * @alias getTimestamp
+ *
+ * Returns the UNIX timestamp for the current date.
+ *
+ * @return int
+ */
+ public function unix()
+ {
+ return $this->getTimestamp();
+ }
+
+ /**
+ * Return an array with integer part digits and decimals digits split from one or more positive numbers
+ * (such as timestamps) as string with the given number of decimals (6 by default).
+ *
+ * By splitting integer and decimal, this method obtain a better precision than
+ * number_format when the input is a string.
+ *
+ * @param float|int|string $numbers one or more numbers
+ * @param int $decimals number of decimals precision (6 by default)
+ *
+ * @return array 0-index is integer part, 1-index is decimal part digits
+ */
+ private static function getIntegerAndDecimalParts($numbers, $decimals = 6)
+ {
+ if (\is_int($numbers) || \is_float($numbers)) {
+ $numbers = number_format($numbers, $decimals, '.', '');
+ }
+
+ $sign = str_starts_with($numbers, '-') ? -1 : 1;
+ $integer = 0;
+ $decimal = 0;
+
+ foreach (preg_split('`[^\d.]+`', $numbers) as $chunk) {
+ [$integerPart, $decimalPart] = explode('.', "$chunk.");
+
+ $integer += (int) $integerPart;
+ $decimal += (float) ("0.$decimalPart");
+ }
+
+ $overflow = floor($decimal);
+ $integer += $overflow;
+ $decimal -= $overflow;
+
+ return [$sign * $integer, $decimal === 0.0 ? 0.0 : $sign * round($decimal * pow(10, $decimals))];
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php b/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php
new file mode 100644
index 0000000..a81164f
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Closure;
+
+/**
+ * Trait ToStringFormat.
+ *
+ * Handle global format customization for string cast of the object.
+ */
+trait ToStringFormat
+{
+ /**
+ * Format to use for __toString method when type juggling occurs.
+ *
+ * @var string|Closure|null
+ */
+ protected static $toStringFormat;
+
+ /**
+ * Reset the format used to the default when type juggling a Carbon instance to a string
+ *
+ * @return void
+ */
+ public static function resetToStringFormat()
+ {
+ static::setToStringFormat(null);
+ }
+
+ /**
+ * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
+ * You should rather let Carbon object being cast to string with DEFAULT_TO_STRING_FORMAT, and
+ * use other method or custom format passed to format() method if you need to dump another string
+ * format.
+ *
+ * Set the default format used when type juggling a Carbon instance to a string.
+ *
+ * @param string|Closure|null $format
+ *
+ * @return void
+ */
+ public static function setToStringFormat($format)
+ {
+ static::$toStringFormat = $format;
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Units.php b/vendor/nesbot/carbon/src/Carbon/Traits/Units.php
new file mode 100644
index 0000000..5be14ec
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Units.php
@@ -0,0 +1,412 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+use Carbon\CarbonConverterInterface;
+use Carbon\CarbonInterface;
+use Carbon\CarbonInterval;
+use Carbon\Exceptions\UnitException;
+use Closure;
+use DateInterval;
+use DateMalformedStringException;
+use ReturnTypeWillChange;
+
+/**
+ * Trait Units.
+ *
+ * Add, subtract and set units.
+ */
+trait Units
+{
+ /**
+ * Add seconds to the instance using timestamp. Positive $value travels
+ * forward while negative $value travels into the past.
+ *
+ * @param string $unit
+ * @param int $value
+ *
+ * @return static
+ */
+ public function addRealUnit($unit, $value = 1)
+ {
+ switch ($unit) {
+ // @call addRealUnit
+ case 'micro':
+
+ // @call addRealUnit
+ case 'microsecond':
+ /* @var CarbonInterface $this */
+ $diff = $this->microsecond + $value;
+ $time = $this->getTimestamp();
+ $seconds = (int) floor($diff / static::MICROSECONDS_PER_SECOND);
+ $time += $seconds;
+ $diff -= $seconds * static::MICROSECONDS_PER_SECOND;
+ $microtime = str_pad((string) $diff, 6, '0', STR_PAD_LEFT);
+ $tz = $this->tz;
+
+ return $this->tz('UTC')->modify("@$time.$microtime")->tz($tz);
+
+ // @call addRealUnit
+ case 'milli':
+ // @call addRealUnit
+ case 'millisecond':
+ return $this->addRealUnit('microsecond', $value * static::MICROSECONDS_PER_MILLISECOND);
+
+ // @call addRealUnit
+ case 'second':
+ break;
+
+ // @call addRealUnit
+ case 'minute':
+ $value *= static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'hour':
+ $value *= static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'day':
+ $value *= static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'week':
+ $value *= static::DAYS_PER_WEEK * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'month':
+ $value *= 30 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'quarter':
+ $value *= static::MONTHS_PER_QUARTER * 30 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'year':
+ $value *= 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'decade':
+ $value *= static::YEARS_PER_DECADE * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'century':
+ $value *= static::YEARS_PER_CENTURY * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ // @call addRealUnit
+ case 'millennium':
+ $value *= static::YEARS_PER_MILLENNIUM * 365 * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE;
+
+ break;
+
+ default:
+ if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) {
+ throw new UnitException("Invalid unit for real timestamp add/sub: '$unit'");
+ }
+
+ return $this;
+ }
+
+ /* @var CarbonInterface $this */
+ return $this->setTimestamp((int) ($this->getTimestamp() + $value));
+ }
+
+ public function subRealUnit($unit, $value = 1)
+ {
+ return $this->addRealUnit($unit, -$value);
+ }
+
+ /**
+ * Returns true if a property can be changed via setter.
+ *
+ * @param string $unit
+ *
+ * @return bool
+ */
+ public static function isModifiableUnit($unit)
+ {
+ static $modifiableUnits = [
+ // @call addUnit
+ 'millennium',
+ // @call addUnit
+ 'century',
+ // @call addUnit
+ 'decade',
+ // @call addUnit
+ 'quarter',
+ // @call addUnit
+ 'week',
+ // @call addUnit
+ 'weekday',
+ ];
+
+ return \in_array($unit, $modifiableUnits, true) || \in_array($unit, static::$units, true);
+ }
+
+ /**
+ * Call native PHP DateTime/DateTimeImmutable add() method.
+ *
+ * @param DateInterval $interval
+ *
+ * @return static
+ */
+ public function rawAdd(DateInterval $interval)
+ {
+ return parent::add($interval);
+ }
+
+ /**
+ * Add given units or interval to the current instance.
+ *
+ * @example $date->add('hour', 3)
+ * @example $date->add(15, 'days')
+ * @example $date->add(CarbonInterval::days(4))
+ *
+ * @param string|DateInterval|Closure|CarbonConverterInterface $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function add($unit, $value = 1, $overflow = null)
+ {
+ if (\is_string($unit) && \func_num_args() === 1) {
+ $unit = CarbonInterval::make($unit, [], true);
+ }
+
+ if ($unit instanceof CarbonConverterInterface) {
+ return $this->resolveCarbon($unit->convertDate($this, false));
+ }
+
+ if ($unit instanceof Closure) {
+ return $this->resolveCarbon($unit($this, false));
+ }
+
+ if ($unit instanceof DateInterval) {
+ return parent::add($unit);
+ }
+
+ if (is_numeric($unit)) {
+ [$value, $unit] = [$unit, $value];
+ }
+
+ return $this->addUnit($unit, $value, $overflow);
+ }
+
+ /**
+ * Add given units to the current instance.
+ *
+ * @param string $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ public function addUnit($unit, $value = 1, $overflow = null)
+ {
+ $originalArgs = \func_get_args();
+
+ $date = $this;
+
+ if (!is_numeric($value) || !(float) $value) {
+ return $date->isMutable() ? $date : $date->avoidMutation();
+ }
+
+ $unit = self::singularUnit($unit);
+ $metaUnits = [
+ 'millennium' => [static::YEARS_PER_MILLENNIUM, 'year'],
+ 'century' => [static::YEARS_PER_CENTURY, 'year'],
+ 'decade' => [static::YEARS_PER_DECADE, 'year'],
+ 'quarter' => [static::MONTHS_PER_QUARTER, 'month'],
+ ];
+
+ if (isset($metaUnits[$unit])) {
+ [$factor, $unit] = $metaUnits[$unit];
+ $value *= $factor;
+ }
+
+ if ($unit === 'weekday') {
+ $weekendDays = static::getWeekendDays();
+
+ if ($weekendDays !== [static::SATURDAY, static::SUNDAY]) {
+ $absoluteValue = abs($value);
+ $sign = $value / max(1, $absoluteValue);
+ $weekDaysCount = 7 - min(6, \count(array_unique($weekendDays)));
+ $weeks = floor($absoluteValue / $weekDaysCount);
+
+ for ($diff = $absoluteValue % $weekDaysCount; $diff; $diff--) {
+ /** @var static $date */
+ $date = $date->addDays($sign);
+
+ while (\in_array($date->dayOfWeek, $weekendDays, true)) {
+ $date = $date->addDays($sign);
+ }
+ }
+
+ $value = $weeks * $sign;
+ $unit = 'week';
+ }
+
+ $timeString = $date->toTimeString();
+ } elseif ($canOverflow = (\in_array($unit, [
+ 'month',
+ 'year',
+ ]) && ($overflow === false || (
+ $overflow === null &&
+ ($ucUnit = ucfirst($unit).'s') &&
+ !($this->{'local'.$ucUnit.'Overflow'} ?? static::{'shouldOverflow'.$ucUnit}())
+ )))) {
+ $day = $date->day;
+ }
+
+ $value = (int) $value;
+
+ if ($unit === 'milli' || $unit === 'millisecond') {
+ $unit = 'microsecond';
+ $value *= static::MICROSECONDS_PER_MILLISECOND;
+ }
+
+ // Work-around for bug https://bugs.php.net/bug.php?id=75642
+ if ($unit === 'micro' || $unit === 'microsecond') {
+ $microseconds = $this->micro + $value;
+ $second = (int) floor($microseconds / static::MICROSECONDS_PER_SECOND);
+ $microseconds %= static::MICROSECONDS_PER_SECOND;
+ if ($microseconds < 0) {
+ $microseconds += static::MICROSECONDS_PER_SECOND;
+ }
+ $date = $date->microseconds($microseconds);
+ $unit = 'second';
+ $value = $second;
+ }
+
+ try {
+ $date = $date->modify("$value $unit");
+
+ if (isset($timeString)) {
+ $date = $date->setTimeFromTimeString($timeString);
+ } elseif (isset($canOverflow, $day) && $canOverflow && $day !== $date->day) {
+ $date = $date->modify('last day of previous month');
+ }
+ } catch (DateMalformedStringException $ignoredException) { // @codeCoverageIgnore
+ $date = null; // @codeCoverageIgnore
+ }
+
+ if (!$date) {
+ throw new UnitException('Unable to add unit '.var_export($originalArgs, true));
+ }
+
+ return $date;
+ }
+
+ /**
+ * Subtract given units to the current instance.
+ *
+ * @param string $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ public function subUnit($unit, $value = 1, $overflow = null)
+ {
+ return $this->addUnit($unit, -$value, $overflow);
+ }
+
+ /**
+ * Call native PHP DateTime/DateTimeImmutable sub() method.
+ *
+ * @param DateInterval $interval
+ *
+ * @return static
+ */
+ public function rawSub(DateInterval $interval)
+ {
+ return parent::sub($interval);
+ }
+
+ /**
+ * Subtract given units or interval to the current instance.
+ *
+ * @example $date->sub('hour', 3)
+ * @example $date->sub(15, 'days')
+ * @example $date->sub(CarbonInterval::days(4))
+ *
+ * @param string|DateInterval|Closure|CarbonConverterInterface $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ #[ReturnTypeWillChange]
+ public function sub($unit, $value = 1, $overflow = null)
+ {
+ if (\is_string($unit) && \func_num_args() === 1) {
+ $unit = CarbonInterval::make($unit, [], true);
+ }
+
+ if ($unit instanceof CarbonConverterInterface) {
+ return $this->resolveCarbon($unit->convertDate($this, true));
+ }
+
+ if ($unit instanceof Closure) {
+ return $this->resolveCarbon($unit($this, true));
+ }
+
+ if ($unit instanceof DateInterval) {
+ return parent::sub($unit);
+ }
+
+ if (is_numeric($unit)) {
+ [$value, $unit] = [$unit, $value];
+ }
+
+ return $this->addUnit($unit, -(float) $value, $overflow);
+ }
+
+ /**
+ * Subtract given units or interval to the current instance.
+ *
+ * @see sub()
+ *
+ * @param string|DateInterval $unit
+ * @param int $value
+ * @param bool|null $overflow
+ *
+ * @return static
+ */
+ public function subtract($unit, $value = 1, $overflow = null)
+ {
+ if (\is_string($unit) && \func_num_args() === 1) {
+ $unit = CarbonInterval::make($unit, [], true);
+ }
+
+ return $this->sub($unit, $value, $overflow);
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Traits/Week.php b/vendor/nesbot/carbon/src/Carbon/Traits/Week.php
new file mode 100644
index 0000000..6f14814
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Traits/Week.php
@@ -0,0 +1,219 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon\Traits;
+
+/**
+ * Trait Week.
+ *
+ * week and ISO week number, year and count in year.
+ *
+ * Depends on the following properties:
+ *
+ * @property int $daysInYear
+ * @property int $dayOfWeek
+ * @property int $dayOfYear
+ * @property int $year
+ *
+ * Depends on the following methods:
+ *
+ * @method static addWeeks(int $weeks = 1)
+ * @method static copy()
+ * @method static dayOfYear(int $dayOfYear)
+ * @method string getTranslationMessage(string $key, ?string $locale = null, ?string $default = null, $translator = null)
+ * @method static next(int|string $day = null)
+ * @method static startOfWeek(int $day = 1)
+ * @method static subWeeks(int $weeks = 1)
+ * @method static year(int $year = null)
+ */
+trait Week
+{
+ /**
+ * Set/get the week number of year using given first day of week and first
+ * day of year included in the first week. Or use ISO format if no settings
+ * given.
+ *
+ * @param int|null $year if null, act as a getter, if not null, set the year and return current instance.
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int|static
+ */
+ public function isoWeekYear($year = null, $dayOfWeek = null, $dayOfYear = null)
+ {
+ return $this->weekYear(
+ $year,
+ $dayOfWeek ?? 1,
+ $dayOfYear ?? 4
+ );
+ }
+
+ /**
+ * Set/get the week number of year using given first day of week and first
+ * day of year included in the first week. Or use US format if no settings
+ * given (Sunday / Jan 6).
+ *
+ * @param int|null $year if null, act as a getter, if not null, set the year and return current instance.
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int|static
+ */
+ public function weekYear($year = null, $dayOfWeek = null, $dayOfYear = null)
+ {
+ $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0;
+ $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1;
+
+ if ($year !== null) {
+ $year = (int) round($year);
+
+ if ($this->weekYear(null, $dayOfWeek, $dayOfYear) === $year) {
+ return $this->avoidMutation();
+ }
+
+ $week = $this->week(null, $dayOfWeek, $dayOfYear);
+ $day = $this->dayOfWeek;
+ $date = $this->year($year);
+ switch ($date->weekYear(null, $dayOfWeek, $dayOfYear) - $year) {
+ case 1:
+ $date = $date->subWeeks(26);
+
+ break;
+ case -1:
+ $date = $date->addWeeks(26);
+
+ break;
+ }
+
+ $date = $date->addWeeks($week - $date->week(null, $dayOfWeek, $dayOfYear))->startOfWeek($dayOfWeek);
+
+ if ($date->dayOfWeek === $day) {
+ return $date;
+ }
+
+ return $date->next($day);
+ }
+
+ $year = $this->year;
+ $day = $this->dayOfYear;
+ $date = $this->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek);
+
+ if ($date->year === $year && $day < $date->dayOfYear) {
+ return $year - 1;
+ }
+
+ $date = $this->avoidMutation()->addYear()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek);
+
+ if ($date->year === $year && $day >= $date->dayOfYear) {
+ return $year + 1;
+ }
+
+ return $year;
+ }
+
+ /**
+ * Get the number of weeks of the current week-year using given first day of week and first
+ * day of year included in the first week. Or use ISO format if no settings
+ * given.
+ *
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int
+ */
+ public function isoWeeksInYear($dayOfWeek = null, $dayOfYear = null)
+ {
+ return $this->weeksInYear(
+ $dayOfWeek ?? 1,
+ $dayOfYear ?? 4
+ );
+ }
+
+ /**
+ * Get the number of weeks of the current week-year using given first day of week and first
+ * day of year included in the first week. Or use US format if no settings
+ * given (Sunday / Jan 6).
+ *
+ * @param int|null $dayOfWeek first date of week from 0 (Sunday) to 6 (Saturday)
+ * @param int|null $dayOfYear first day of year included in the week #1
+ *
+ * @return int
+ */
+ public function weeksInYear($dayOfWeek = null, $dayOfYear = null)
+ {
+ $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0;
+ $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1;
+ $year = $this->year;
+ $start = $this->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek);
+ $startDay = $start->dayOfYear;
+ if ($start->year !== $year) {
+ $startDay -= $start->daysInYear;
+ }
+ $end = $this->avoidMutation()->addYear()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek);
+ $endDay = $end->dayOfYear;
+ if ($end->year !== $year) {
+ $endDay += $this->daysInYear;
+ }
+
+ return (int) round(($endDay - $startDay) / 7);
+ }
+
+ /**
+ * Get/set the week number using given first day of week and first
+ * day of year included in the first week. Or use US format if no settings
+ * given (Sunday / Jan 6).
+ *
+ * @param int|null $week
+ * @param int|null $dayOfWeek
+ * @param int|null $dayOfYear
+ *
+ * @return int|static
+ */
+ public function week($week = null, $dayOfWeek = null, $dayOfYear = null)
+ {
+ $date = $this;
+ $dayOfWeek = $dayOfWeek ?? $this->getTranslationMessage('first_day_of_week') ?? 0;
+ $dayOfYear = $dayOfYear ?? $this->getTranslationMessage('day_of_first_week_of_year') ?? 1;
+
+ if ($week !== null) {
+ return $date->addWeeks(round($week) - $this->week(null, $dayOfWeek, $dayOfYear));
+ }
+
+ $start = $date->avoidMutation()->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek);
+ $end = $date->avoidMutation()->startOfWeek($dayOfWeek);
+ if ($start > $end) {
+ $start = $start->subWeeks(26)->dayOfYear($dayOfYear)->startOfWeek($dayOfWeek);
+ }
+ $week = (int) ($start->diffInDays($end) / 7 + 1);
+
+ return $week > $end->weeksInYear($dayOfWeek, $dayOfYear) ? 1 : $week;
+ }
+
+ /**
+ * Get/set the week number using given first day of week and first
+ * day of year included in the first week. Or use ISO format if no settings
+ * given.
+ *
+ * @param int|null $week
+ * @param int|null $dayOfWeek
+ * @param int|null $dayOfYear
+ *
+ * @return int|static
+ */
+ public function isoWeek($week = null, $dayOfWeek = null, $dayOfYear = null)
+ {
+ return $this->week(
+ $week,
+ $dayOfWeek ?? 1,
+ $dayOfYear ?? 4
+ );
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/Translator.php b/vendor/nesbot/carbon/src/Carbon/Translator.php
new file mode 100644
index 0000000..491c9e7
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/Translator.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use ReflectionMethod;
+use Symfony\Component\Translation;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+$transMethod = new ReflectionMethod(
+ class_exists(TranslatorInterface::class)
+ ? TranslatorInterface::class
+ : Translation\Translator::class,
+ 'trans'
+);
+
+require $transMethod->hasReturnType()
+ ? __DIR__.'/../../lazy/Carbon/TranslatorStrongType.php'
+ : __DIR__.'/../../lazy/Carbon/TranslatorWeakType.php';
+
+class Translator extends LazyTranslator
+{
+ // Proxy dynamically loaded LazyTranslator in a static way
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php b/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php
new file mode 100644
index 0000000..ad36c67
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/TranslatorImmutable.php
@@ -0,0 +1,99 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Carbon\Exceptions\ImmutableException;
+use Symfony\Component\Config\ConfigCacheFactoryInterface;
+use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
+
+class TranslatorImmutable extends Translator
+{
+ /** @var bool */
+ private $constructed = false;
+
+ public function __construct($locale, MessageFormatterInterface $formatter = null, $cacheDir = null, $debug = false)
+ {
+ parent::__construct($locale, $formatter, $cacheDir, $debug);
+ $this->constructed = true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function setDirectories(array $directories)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ return parent::setDirectories($directories);
+ }
+
+ public function setLocale($locale)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ return parent::setLocale($locale);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function setMessages($locale, $messages)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ return parent::setMessages($locale, $messages);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function setTranslations($messages)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ return parent::setTranslations($messages);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ parent::setConfigCacheFactory($configCacheFactory);
+ }
+
+ public function resetMessages($locale = null)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ return parent::resetMessages($locale);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function setFallbackLocales(array $locales)
+ {
+ $this->disallowMutation(__METHOD__);
+
+ parent::setFallbackLocales($locales);
+ }
+
+ private function disallowMutation($method)
+ {
+ if ($this->constructed) {
+ throw new ImmutableException($method.' not allowed on '.static::class);
+ }
+ }
+}
diff --git a/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php b/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php
new file mode 100644
index 0000000..ef4dee8
--- /dev/null
+++ b/vendor/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Carbon;
+
+use Symfony\Component\Translation\MessageCatalogueInterface;
+
+/**
+ * Mark translator using strong type from symfony/translation >= 6.
+ */
+interface TranslatorStrongTypeInterface
+{
+ public function getFromCatalogue(MessageCatalogueInterface $catalogue, string $id, string $domain = 'messages');
+}
diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-si.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-si.php
new file mode 100644
index 0000000..dce502a
--- /dev/null
+++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-si.php
@@ -0,0 +1,34 @@
+
+ */
+
+$PHPMAILER_LANG['authenticate'] = 'SMTP දෝෂය: සත්යාපනය අසාර්ථක විය.';
+$PHPMAILER_LANG['buggy_php'] = 'ඔබගේ PHP version එකෙහි පවතින දෝෂයක් නිසා email පණිවිඩ දෝෂ සහගත වීමේ හැකියාවක් ඇත. මෙය විසදීම සදහා SMTP භාවිතා කිරීම, mail.add_x_header INI setting එක අක්රීය කිරීම, MacOS හෝ Linux වලට මාරු වීම, හෝ ඔබගේ PHP version එක 7.0.17+ හෝ 7.1.3+ වලට අලුත් කිරීම කරගන්න.';
+$PHPMAILER_LANG['connect_host'] = 'SMTP දෝෂය: සම්බන්ධ වීමට නොහැකි විය.';
+$PHPMAILER_LANG['data_not_accepted'] = 'SMTP දෝෂය: දත්ත පිළිගනු නොලැබේ.';
+$PHPMAILER_LANG['empty_message'] = 'පණිවිඩ අන්තර්ගතය හිස්';
+$PHPMAILER_LANG['encoding'] = 'නොදන්නා කේතනය: ';
+$PHPMAILER_LANG['execute'] = 'ක්රියාත්මක කළ නොහැකි විය: ';
+$PHPMAILER_LANG['extension_missing'] = 'Extension එක නොමැත: ';
+$PHPMAILER_LANG['file_access'] = 'File එකට ප්රවේශ විය නොහැකි විය: ';
+$PHPMAILER_LANG['file_open'] = 'File දෝෂය: File එක විවෘත කළ නොහැක: ';
+$PHPMAILER_LANG['from_failed'] = 'පහත From ලිපිනයන් අසාර්ථක විය: ';
+$PHPMAILER_LANG['instantiate'] = 'mail function එක ක්රියාත්මක කළ නොහැක.';
+$PHPMAILER_LANG['invalid_address'] = 'වලංගු නොවන ලිපිනය: ';
+$PHPMAILER_LANG['invalid_header'] = 'වලංගු නොවන header නාමයක් හෝ අගයක්';
+$PHPMAILER_LANG['invalid_hostentry'] = 'වලංගු නොවන hostentry එකක්: ';
+$PHPMAILER_LANG['invalid_host'] = 'වලංගු නොවන host එකක්: ';
+$PHPMAILER_LANG['mailer_not_supported'] = ' mailer සහාය නොදක්වයි.';
+$PHPMAILER_LANG['provide_address'] = 'ඔබ අවම වශයෙන් එක් ලබන්නෙකුගේ ඊමේල් ලිපිනයක් සැපයිය යුතුය.';
+$PHPMAILER_LANG['recipients_failed'] = 'SMTP දෝෂය: පහත ලබන්නන් අසමත් විය: ';
+$PHPMAILER_LANG['signing'] = 'Sign කිරීමේ දෝෂය: ';
+$PHPMAILER_LANG['smtp_code'] = 'SMTP කේතය: ';
+$PHPMAILER_LANG['smtp_code_ex'] = 'අමතර SMTP තොරතුරු: ';
+$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP සම්බන්ධය අසාර්ථක විය.';
+$PHPMAILER_LANG['smtp_detail'] = 'තොරතුරු: ';
+$PHPMAILER_LANG['smtp_error'] = 'SMTP දෝෂය: ';
+$PHPMAILER_LANG['variable_set'] = 'Variable එක සැකසීමට හෝ නැවත සැකසීමට නොහැක: ';
diff --git a/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php b/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..fb50aea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php
@@ -0,0 +1,251 @@
+exclude('vendor')
+ ->notPath('src/PhpSpreadsheet/Writer/ZipStream3.php')
+ ->name('/(\.php|^generate-document|^generate-locales)$/')
+ ->in(__DIR__);
+
+$config = new PhpCsFixer\Config();
+$config
+ ->setRiskyAllowed(true)
+ ->setFinder($finder)
+ ->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__))
+ ->setRules([
+ 'align_multiline_comment' => true,
+ 'array_indentation' => 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' => 'one', 'property' => 'one']], // const are often grouped with other related const
+ 'class_definition' => false, // phpcs disagree
+ 'class_keyword_remove' => false, // Deprecated, and ::class keyword gives us better support in IDE
+ 'combine_consecutive_issets' => true,
+ 'combine_consecutive_unsets' => true,
+ 'combine_nested_dirname' => true,
+ 'comment_to_phpdoc' => false, // interferes with annotations
+ 'compact_nullable_typehint' => true,
+ 'concat_space' => ['spacing' => 'one'],
+ 'constant_case' => true,
+ 'date_time_immutable' => false, // Break our unit tests
+ '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,
+ 'empty_loop_body' => true,
+ 'empty_loop_condition' => true,
+ 'encoding' => true,
+ 'ereg_to_preg' => true,
+ 'error_suppression' => false, // it breaks \PhpOffice\PhpSpreadsheet\Helper\Handler
+ 'escape_implicit_backslashes' => true,
+ 'explicit_indirect_variable' => false, // I feel it makes the code actually harder to read
+ 'explicit_string_variable' => false, // I feel it makes the code actually harder to read
+ 'final_class' => false, // We need non-final classes
+ 'final_internal_class' => true,
+ 'final_public_method_for_abstract_class' => false, // We need non-final methods
+ 'fopen_flag_order' => true,
+ 'fopen_flags' => true,
+ 'full_opening_tag' => true,
+ 'fully_qualified_strict_types' => true,
+ 'function_declaration' => true,
+ 'function_to_constant' => true,
+ 'function_typehint_space' => true,
+ 'general_phpdoc_annotation_remove' => ['annotations' => ['access', 'category', 'copyright']],
+ 'general_phpdoc_tag_rename' => true,
+ 'global_namespace_import' => true,
+ 'group_import' => false, // I feel it makes the code actually harder to read
+ 'header_comment' => false, // We don't use common header in all our files
+ 'heredoc_indentation' => true,
+ 'heredoc_to_nowdoc' => false, // Not sure about this one
+ 'implode_call' => true,
+ 'include' => true,
+ 'increment_style' => true,
+ 'indentation_type' => true,
+ 'integer_literal_case' => true,
+ 'is_null' => true,
+ 'lambda_not_used_import' => true,
+ 'line_ending' => true,
+ 'linebreak_after_opening_tag' => true,
+ 'list_syntax' => ['syntax' => 'short'],
+ 'logical_operators' => true,
+ 'lowercase_cast' => true,
+ 'lowercase_keywords' => true,
+ 'lowercase_static_reference' => true,
+ 'magic_constant_casing' => true,
+ 'magic_method_casing' => true,
+ 'mb_str_functions' => false, // No, too dangerous to change that
+ 'method_argument_space' => true,
+ 'method_chaining_indentation' => true,
+ 'modernize_strpos' => true,
+ 'modernize_types_casting' => true,
+ 'multiline_comment_opening_closing' => true,
+ 'multiline_whitespace_before_semicolons' => true,
+ 'native_constant_invocation' => false, // Micro optimization that look messy
+ 'native_function_casing' => true,
+ 'native_function_invocation' => false, // I suppose this would be best, but I am still unconvinced about the visual aspect of it
+ 'native_function_type_declaration_casing' => true,
+ 'new_with_braces' => true,
+ 'no_alias_functions' => true,
+ 'no_alias_language_construct_call' => true,
+ 'no_alternative_syntax' => true,
+ 'no_binary_string' => true,
+ 'no_blank_lines_after_class_opening' => true,
+ '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_null_property_initialization' => true,
+ 'no_php4_constructor' => true,
+ 'no_short_bool_cast' => true,
+ 'echo_tag_syntax' => ['format' => 'long'],
+ 'no_singleline_whitespace_before_semicolons' => true,
+ 'no_space_around_double_colon' => true,
+ 'no_spaces_after_function_name' => true,
+ 'no_spaces_around_offset' => true,
+ 'no_spaces_inside_parenthesis' => true,
+ 'no_superfluous_elseif' => false, // Might be risky on a huge code base
+ 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true],
+ 'no_trailing_comma_in_list_call' => true,
+ 'no_trailing_comma_in_singleline_array' => true,
+ 'no_trailing_whitespace' => true,
+ 'no_trailing_whitespace_in_comment' => true,
+ 'no_trailing_whitespace_in_string' => false, // Too dangerous
+ 'no_unneeded_control_parentheses' => true,
+ 'no_unneeded_curly_braces' => true,
+ 'no_unneeded_final_method' => true,
+ 'no_unreachable_default_argument_value' => true,
+ 'no_unset_cast' => true,
+ 'no_unset_on_property' => false,
+ 'no_unused_imports' => true,
+ 'no_useless_else' => true,
+ 'no_useless_return' => true,
+ 'no_useless_sprintf' => true,
+ 'no_whitespace_before_comma_in_array' => true,
+ 'no_whitespace_in_blank_line' => true,
+ 'non_printable_character' => true,
+ 'normalize_index_brace' => true,
+ 'not_operator_with_space' => false, // No we prefer to keep '!' without spaces
+ 'not_operator_with_successor_space' => false, // idem
+ 'nullable_type_declaration_for_default_null_value' => true,
+ 'object_operator_without_whitespace' => true,
+ 'octal_notation' => true,
+ 'operator_linebreak' => true,
+ 'ordered_class_elements' => false, // We prefer to keep some freedom
+ 'ordered_imports' => true,
+ 'ordered_interfaces' => true,
+ 'ordered_traits' => true,
+ 'php_unit_construct' => true,
+ 'php_unit_dedicate_assert' => true,
+ 'php_unit_dedicate_assert_internal_type' => true,
+ 'php_unit_expectation' => true,
+ 'php_unit_fqcn_annotation' => true,
+ 'php_unit_internal_class' => false, // Because tests are excluded from package
+ 'php_unit_method_casing' => true,
+ 'php_unit_mock' => true,
+ 'php_unit_mock_short_will_return' => true,
+ 'php_unit_namespaced' => true,
+ 'php_unit_no_expectation_annotation' => true,
+ 'phpdoc_order_by_value' => ['annotations' => ['covers']],
+ 'php_unit_set_up_tear_down_visibility' => true,
+ 'php_unit_size_class' => false, // That seems extra work to maintain for little benefits
+ 'php_unit_strict' => false, // We sometime actually need assertEquals
+ 'php_unit_test_annotation' => true,
+ 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'],
+ 'php_unit_test_class_requires_covers' => false, // We don't care as much as we should about coverage
+ 'phpdoc_add_missing_param_annotation' => false, // Don't add things that bring no value
+ 'phpdoc_align' => false, // Waste of time
+ 'phpdoc_annotation_without_dot' => true,
+ 'phpdoc_indent' => true,
+ //'phpdoc_inline_tag' => true,
+ 'phpdoc_line_span' => false, // Unfortunately our old comments turn even uglier with this
+ '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_tag_casing' => true,
+ 'phpdoc_tag_type' => true,
+ 'phpdoc_to_comment' => false, // interferes with annotations
+ 'phpdoc_to_param_type' => false, // Because experimental, but interesting for one shot use
+ 'phpdoc_to_property_type' => false, // Because experimental, but interesting for one shot use
+ 'phpdoc_to_return_type' => false, // Because experimental, but interesting for one shot use
+ 'phpdoc_trim' => true,
+ 'phpdoc_trim_consecutive_blank_line_separation' => true,
+ 'phpdoc_types' => true,
+ 'phpdoc_types_order' => true,
+ 'phpdoc_var_annotation_correct_order' => true,
+ 'phpdoc_var_without_name' => true,
+ 'pow_to_exponentiation' => true,
+ 'protected_to_private' => true,
+ 'psr_autoloading' => true,
+ 'random_api_migration' => true,
+ 'return_assignment' => false, // Sometimes useful for clarity or debug
+ 'return_type_declaration' => true,
+ 'self_accessor' => true,
+ 'self_static_accessor' => true,
+ 'semicolon_after_instruction' => false, // Buggy in `samples/index.php`
+ 'set_type_to_cast' => true,
+ 'short_scalar_cast' => true,
+ 'simple_to_complex_string_variable' => false, // Would differ from TypeScript without obvious advantages
+ 'simplified_if_return' => false, // Even if technically correct we prefer to be explicit
+ 'simplified_null_return' => false, // Even if technically correct we prefer to be explicit
+ 'single_blank_line_at_eof' => true,
+ 'single_blank_line_before_namespace' => true,
+ 'single_class_element_per_statement' => true,
+ 'single_import_per_statement' => true,
+ 'single_line_after_imports' => true,
+ 'single_line_comment_style' => true,
+ 'single_line_throw' => false, // I don't see any reason for having a special case for Exception
+ 'single_quote' => true,
+ 'single_space_after_construct' => true,
+ 'single_trait_insert_per_statement' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_increment' => true,
+ '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
+ 'string_length_to_empty' => true,
+ 'string_line_ending' => true,
+ 'switch_case_semicolon_to_colon' => true,
+ 'switch_case_space' => true,
+ 'switch_continue_to_break' => true,
+ 'ternary_operator_spaces' => true,
+ 'ternary_to_elvis_operator' => true,
+ 'ternary_to_null_coalescing' => true,
+ 'trailing_comma_in_multiline' => true,
+ 'trim_array_spaces' => true,
+ 'types_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'use_arrow_functions' => true,
+ 'visibility_required' => ['elements' => ['property', 'method']], // not const
+ 'void_return' => true,
+ 'whitespace_after_comma_in_array' => true,
+ 'yoda_style' => false,
+ ]);
+
+return $config;
diff --git a/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist b/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist
new file mode 100644
index 0000000..d902156
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+ samples
+ src
+ tests
+ infra
+ bin/generate-document
+ bin/generate-locales
+
+ samples/Header.php
+ */tests/Core/*/*Test\.(inc|css|js)$
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vendor/phpoffice/phpspreadsheet/.readthedocs.yaml b/vendor/phpoffice/phpspreadsheet/.readthedocs.yaml
new file mode 100644
index 0000000..c671015
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/.readthedocs.yaml
@@ -0,0 +1,12 @@
+# Read the Docs configuration file for MkDocs projects
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html
+
+version: 2
+
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3"
+
+mkdocs:
+ configuration: mkdocs.yml
diff --git a/vendor/phpoffice/phpspreadsheet/CHANGELOG.md b/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
new file mode 100644
index 0000000..f68cd66
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
@@ -0,0 +1,1481 @@
+# 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).
+
+## Unreleased - TBD
+
+### MINOR BREAKING CHANGE
+
+- Typing was strengthened by leveraging native typing. While this should not change any behavior, it might need minor
+ adaption of your code if you use static analysis tools such as PHPStan or
+ Psalm. [PR #3718](https://github.com/PHPOffice/PhpSpreadsheet/pull/3718)
+
+### Added
+
+- Split screens (Xlsx and Xml only, not 100% complete). [Issue #3601](https://github.com/PHPOffice/PhpSpreadsheet/issues/3601) [PR #3622](https://github.com/PHPOffice/PhpSpreadsheet/pull/3622)
+- Permit Meta Viewport in Html. [Issue #3565](https://github.com/PHPOffice/PhpSpreadsheet/issues/3565) [PR #3623](https://github.com/PHPOffice/PhpSpreadsheet/pull/3623)
+- Hyperlink support for Ods. [Issue #3660](https://github.com/PHPOffice/PhpSpreadsheet/issues/3660) [PR #3669](https://github.com/PHPOffice/PhpSpreadsheet/pull/3669)
+- ListWorksheetInfo/Names for Html/Csv/Slk. [Issue #3706](https://github.com/PHPOffice/PhpSpreadsheet/issues/3706) [PR #3709](https://github.com/PHPOffice/PhpSpreadsheet/pull/3709)
+- Methods to determine if cell is actually locked, or hidden on formula bar. [PR #3722](https://github.com/PHPOffice/PhpSpreadsheet/pull/3722)
+- Add iterateOnlyExistingCells to Constructors. [Issue #3721](https://github.com/PHPOffice/PhpSpreadsheet/issues/3721) [PR #3727](https://github.com/PHPOffice/PhpSpreadsheet/pull/3727)
+- Support for Conditional Formatting Color Scale. [PR #3738](https://github.com/PHPOffice/PhpSpreadsheet/pull/3738)
+- Support Additional Tags in Helper/Html. [Issue #3751](https://github.com/PHPOffice/PhpSpreadsheet/issues/3751) [PR #3752](https://github.com/PHPOffice/PhpSpreadsheet/pull/3752)
+- Writer ODS : Write Border Style for cells [Issue #3690](https://github.com/PHPOffice/PhpSpreadsheet/issues/3690) [PR #3693](https://github.com/PHPOffice/PhpSpreadsheet/pull/3693)
+- Sheet Background Images [Issue #1649](https://github.com/PHPOffice/PhpSpreadsheet/issues/1649) [PR #3795](https://github.com/PHPOffice/PhpSpreadsheet/pull/3795)
+- Check if Coordinate is Inside Range [PR #3779](https://github.com/PHPOffice/PhpSpreadsheet/pull/3779)
+- Flipping Images [Issue #731](https://github.com/PHPOffice/PhpSpreadsheet/issues/731) [PR #3801](https://github.com/PHPOffice/PhpSpreadsheet/pull/3801)
+- Chart Dynamic Title and Font Properties [Issue #3797](https://github.com/PHPOffice/PhpSpreadsheet/issues/3797) [PR #3800](https://github.com/PHPOffice/PhpSpreadsheet/pull/3800)
+- Chart Axis Display Units and Logarithmic Scale. [Issue #3833](https://github.com/PHPOffice/PhpSpreadsheet/pull/3833) [PR #3836](https://github.com/PHPOffice/PhpSpreadsheet/pull/3836)
+
+### Changed
+
+- Drop support for PHP 7.4, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support [PR #3713](https://github.com/PHPOffice/PhpSpreadsheet/pull/3713)
+- RLM Added to NumberFormatter Currency. This happens depending on release of ICU which Php is using (it does not yet happen with any official release). PhpSpreadsheet will continue to use the value returned by Php, but a method is added to keep the result unchanged from release to release. [Issue #3571](https://github.com/PHPOffice/PhpSpreadsheet/issues/3571) [PR #3640](https://github.com/PHPOffice/PhpSpreadsheet/pull/3640)
+- `toFormattedString` will now always return a string. This was introduced with 1.28.0, but was not properly documented at the time. This can affect the results of `toArray`, `namedRangeToArray`, and `rangeToArray`. [PR #3304](https://github.com/PHPOffice/PhpSpreadsheet/pull/3304)
+- Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD was changed in 1.28.0, but was not properly documented at the time. [Issue #3577](https://github.com/PHPOffice/PhpSpreadsheet/issues/3577)
+- Html Writer will attempt to use Chart coordinates to determine image size. [Issue #3783](https://github.com/PHPOffice/PhpSpreadsheet/issues/3783) [PR #3787](https://github.com/PHPOffice/PhpSpreadsheet/pull/3787)
+
+### Deprecated
+
+- Functions `_translateFormulaToLocale` and `_translateFormulaEnglish` are replaced by versions without leading underscore. [PR #3828](https://github.com/PHPOffice/PhpSpreadsheet/pull/3828)
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Take advantage of mitoteam/jpgraph Extended mode to enable rendering of more graphs. [PR #3603](https://github.com/PHPOffice/PhpSpreadsheet/pull/3603)
+- Column widths, especially for ODS. [Issue #3609](https://github.com/PHPOffice/PhpSpreadsheet/issues/3609) [PR #3610](https://github.com/PHPOffice/PhpSpreadsheet/pull/3610)
+- Avoid NULL in String Function call (partial solution). [Issue #3613](https://github.com/PHPOffice/PhpSpreadsheet/issues/3613) [PR #3617](https://github.com/PHPOffice/PhpSpreadsheet/pull/3617)
+- Preserve transparency in Memory Drawing. [Issue #3624](https://github.com/PHPOffice/PhpSpreadsheet/issues/3624) [PR #3627](https://github.com/PHPOffice/PhpSpreadsheet/pull/3627)
+- Customizable padding for Exact Column Width. [Issue #3626](https://github.com/PHPOffice/PhpSpreadsheet/issues/3626) [PR #3628](https://github.com/PHPOffice/PhpSpreadsheet/pull/3628)
+- Ensure ROW function returns int (problem exposed in unreleased Php). [PR #3641](https://github.com/PHPOffice/PhpSpreadsheet/pull/3641)
+- Minor changes to Mpdf and Html Writers. [PR #3645](https://github.com/PHPOffice/PhpSpreadsheet/pull/3645)
+- Xlsx Reader Namespacing for Tables, Autofilters. [Issue #3665](https://github.com/PHPOffice/PhpSpreadsheet/issues/3665) [PR #3668](https://github.com/PHPOffice/PhpSpreadsheet/pull/3668)
+- Read Code Page for Xls ListWorksheetInfo/Names BIFF5. [Issue #3671](https://github.com/PHPOffice/PhpSpreadsheet/issues/3671) [PR #3672](https://github.com/PHPOffice/PhpSpreadsheet/pull/3672)
+- Read Data from Table on Different Sheet. [Issue #3635](https://github.com/PHPOffice/PhpSpreadsheet/issues/3635) [PR #3659](https://github.com/PHPOffice/PhpSpreadsheet/pull/3659)
+- Html Writer Styles Using Inline Css. [Issue #3678](https://github.com/PHPOffice/PhpSpreadsheet/issues/3678) [PR #3680](https://github.com/PHPOffice/PhpSpreadsheet/pull/3680)
+- Xlsx Read Ignoring Some Comments. [Issue #3654](https://github.com/PHPOffice/PhpSpreadsheet/issues/3654) [PR #3655](https://github.com/PHPOffice/PhpSpreadsheet/pull/3655)
+- Fractional Seconds in Date/Time Values. [PR #3677](https://github.com/PHPOffice/PhpSpreadsheet/pull/3677)
+- SetCalculatedValue Avoid Casting String to Numeric. [Issue #3658](https://github.com/PHPOffice/PhpSpreadsheet/issues/3658) [PR #3685](https://github.com/PHPOffice/PhpSpreadsheet/pull/3685)
+- Several Problems in a Very Complicated Spreadsheet. [Issue #3679](https://github.com/PHPOffice/PhpSpreadsheet/issues/3679) [PR #3681](https://github.com/PHPOffice/PhpSpreadsheet/pull/3681)
+- Inconsistent String Handling for Sum Functions. [Issue #3652](https://github.com/PHPOffice/PhpSpreadsheet/issues/3652) [PR #3653](https://github.com/PHPOffice/PhpSpreadsheet/pull/3653)
+- Recomputation of Relative Addresses in Defined Names. [Issue #3661](https://github.com/PHPOffice/PhpSpreadsheet/issues/3661) [PR #3673](https://github.com/PHPOffice/PhpSpreadsheet/pull/3673)
+- Writer Xls Characters Outside BMP (emojis). [Issue #642](https://github.com/PHPOffice/PhpSpreadsheet/issues/642) [PR #3696](https://github.com/PHPOffice/PhpSpreadsheet/pull/3696)
+- Xlsx Reader Improve Handling of Row and Column Styles. [Issue #3533](https://github.com/PHPOffice/PhpSpreadsheet/issues/3533) [Issue #3534](https://github.com/PHPOffice/PhpSpreadsheet/issues/3534) [PR #3688](https://github.com/PHPOffice/PhpSpreadsheet/pull/3688)
+- Avoid Allocating RowDimension Unneccesarily. [PR #3686](https://github.com/PHPOffice/PhpSpreadsheet/pull/3686)
+- Use Column Style when Row Dimension Exists Without Style. [Issue #3534](https://github.com/PHPOffice/PhpSpreadsheet/issues/3534) [PR #3688](https://github.com/PHPOffice/PhpSpreadsheet/pull/3688)
+- Inconsistency Between Cell Data and Explicitly Declared Type. [Issue #3711](https://github.com/PHPOffice/PhpSpreadsheet/issues/3711) [PR #3715](https://github.com/PHPOffice/PhpSpreadsheet/pull/3715)
+- Unexpected Namespacing in rels File. [Issue #3720](https://github.com/PHPOffice/PhpSpreadsheet/issues/3720) [PR #3722](https://github.com/PHPOffice/PhpSpreadsheet/pull/3722)
+- Break Some Circular References. [PR #3716](https://github.com/PHPOffice/PhpSpreadsheet/pull/3716) [PR #3707](https://github.com/PHPOffice/PhpSpreadsheet/pull/3707)
+- Missing Font Index in Some Xls. [PR #3734](https://github.com/PHPOffice/PhpSpreadsheet/pull/3734)
+- Load Tables even with READ_DATA_ONLY. [PR #3726](https://github.com/PHPOffice/PhpSpreadsheet/pull/3726)
+- Theme File Missing but Referenced in Spreadsheet. [Issue #3770](https://github.com/PHPOffice/PhpSpreadsheet/issues/3770) [PR #3772](https://github.com/PHPOffice/PhpSpreadsheet/pull/3772)
+- Slk Shared Formulas. [Issue #2267](https://github.com/PHPOffice/PhpSpreadsheet/issues/2267) [PR #3776](https://github.com/PHPOffice/PhpSpreadsheet/pull/3776)
+- Html omitting some charts. [Issue #3767](https://github.com/PHPOffice/PhpSpreadsheet/issues/3767) [PR #3771](https://github.com/PHPOffice/PhpSpreadsheet/pull/3771)
+- Case Insensitive Comparison for Sheet Names [PR #3791](https://github.com/PHPOffice/PhpSpreadsheet/pull/3791)
+- Performance improvement for Xlsx Reader. [Issue #3683](https://github.com/PHPOffice/PhpSpreadsheet/issues/3683) [PR #3810](https://github.com/PHPOffice/PhpSpreadsheet/pull/3810)
+- Prevent loop in Shared/File. [Issue #3807](https://github.com/PHPOffice/PhpSpreadsheet/issues/3807) [PR #3809](https://github.com/PHPOffice/PhpSpreadsheet/pull/3809)
+- Consistent handling of decimal/thousands separators between StringHelper and Php setlocale. [Issue #3811](https://github.com/PHPOffice/PhpSpreadsheet/issues/3811) [PR #3815](https://github.com/PHPOffice/PhpSpreadsheet/pull/3815)
+- Clone worksheet with tables or charts. [Issue #3820](https://github.com/PHPOffice/PhpSpreadsheet/issues/3820) [PR #3821](https://github.com/PHPOffice/PhpSpreadsheet/pull/3821)
+- COUNTIFS Does Not Require xlfn. [Issue #3819](https://github.com/PHPOffice/PhpSpreadsheet/issues/3819) [PR #3827](https://github.com/PHPOffice/PhpSpreadsheet/pull/3827)
+- Strip `xlfn.` and `xlws.` from Formula Translations. [Issue #3819](https://github.com/PHPOffice/PhpSpreadsheet/issues/3819) [PR #3828](https://github.com/PHPOffice/PhpSpreadsheet/pull/3828)
+- Recurse directories searching for font file. [Issue #2809](https://github.com/PHPOffice/PhpSpreadsheet/issues/2809) [PR #3830](https://github.com/PHPOffice/PhpSpreadsheet/pull/3830)
+- Reduce memory consumption of Worksheet::rangeToArray() when many empty rows are read. [Issue #3814](https://github.com/PHPOffice/PhpSpreadsheet/pull/3814) [PR #3834](https://github.com/PHPOffice/PhpSpreadsheet/pull/3834)
+- Reduce time used by Worksheet::rangeToArray() when many empty rows are read. [PR #3839](https://github.com/PHPOffice/PhpSpreadsheet/pull/3839)
+
+## 1.29.0 - 2023-06-15
+
+### Added
+
+- Wizards for defining Number Format masks for Dates and Times, including Durations/Intervals. [PR #3458](https://github.com/PHPOffice/PhpSpreadsheet/pull/3458)
+- Specify data type in html tags. [Issue #3444](https://github.com/PHPOffice/PhpSpreadsheet/issues/3444) [PR #3445](https://github.com/PHPOffice/PhpSpreadsheet/pull/3445)
+- Provide option to ignore hidden rows/columns in `toArray()` methods. [PR #3494](https://github.com/PHPOffice/PhpSpreadsheet/pull/3494)
+- Font/Effects/Theme support for Chart Data Labels and Axis. [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476)
+- Font Themes support. [PR #3486](https://github.com/PHPOffice/PhpSpreadsheet/pull/3486)
+- Ability to Ignore Cell Errors in Excel. [Issue #1141](https://github.com/PHPOffice/PhpSpreadsheet/issues/1141) [PR #3508](https://github.com/PHPOffice/PhpSpreadsheet/pull/3508)
+- Unzipped Gnumeric file [PR #3591](https://github.com/PHPOffice/PhpSpreadsheet/pull/3591)
+
+### Changed
+
+- Xlsx Color schemes read in will be written out (previously Excel 2007-2010 Color scheme was always written); manipulation of those schemes before write, including restoring prior behavior, is provided [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476)
+- Memory and speed optimisations for Read Filters with Xlsx Files and Shared Formulae. [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474)
+- Allow `CellRange` and `CellAddress` objects for the `range` argument in the `rangeToArray()` method. [PR #3494](https://github.com/PHPOffice/PhpSpreadsheet/pull/3494)
+- Stock charts will now read and reproduce `upDownBars` and subsidiary tags; these were previously ignored on read and hard-coded on write. [PR #3515](https://github.com/PHPOffice/PhpSpreadsheet/pull/3515)
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Updates Cell formula absolute ranges/references, and Defined Name absolute ranges/references when inserting/deleting rows/columns. [Issue #3368](https://github.com/PHPOffice/PhpSpreadsheet/issues/3368) [PR #3402](https://github.com/PHPOffice/PhpSpreadsheet/pull/3402)
+- EOMONTH() and EDATE() Functions should round date value before evaluation. [Issue #3436](https://github.com/PHPOffice/PhpSpreadsheet/issues/3436) [PR #3437](https://github.com/PHPOffice/PhpSpreadsheet/pull/3437)
+- NETWORKDAYS function erroneously being converted to NETWORK_xlfn.DAYS in Xlsx Writer. [Issue #3461](https://github.com/PHPOffice/PhpSpreadsheet/issues/3461) [PR #3463](https://github.com/PHPOffice/PhpSpreadsheet/pull/3463)
+- Getting a style for a CellAddress instance fails if the worksheet is set in the CellAddress instance. [Issue #3439](https://github.com/PHPOffice/PhpSpreadsheet/issues/3439) [PR #3469](https://github.com/PHPOffice/PhpSpreadsheet/pull/3469)
+- Shared Formulae outside the filter range when reading with a filter are not always being identified. [Issue #3473](https://github.com/PHPOffice/PhpSpreadsheet/issues/3473) [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474)
+- Xls Reader Conditional Styles. [PR #3400](https://github.com/PHPOffice/PhpSpreadsheet/pull/3400)
+- Allow use of # and 0 digit placeholders in fraction masks. [PR #3401](https://github.com/PHPOffice/PhpSpreadsheet/pull/3401)
+- Modify Date/Time check in the NumberFormatter for decimal/fractional times. [PR #3413](https://github.com/PHPOffice/PhpSpreadsheet/pull/3413)
+- Misplaced Xml Writing Chart Label FillColor. [Issue #3397](https://github.com/PHPOffice/PhpSpreadsheet/issues/3397) [PR #3404](https://github.com/PHPOffice/PhpSpreadsheet/pull/3404)
+- TEXT function ignores Time in DateTimeStamp. [Issue #3409](https://github.com/PHPOffice/PhpSpreadsheet/issues/3409) [PR #3411](https://github.com/PHPOffice/PhpSpreadsheet/pull/3411)
+- Xlsx Column Autosize Approximate for CJK. [Issue #3405](https://github.com/PHPOffice/PhpSpreadsheet/issues/3405) [PR #3416](https://github.com/PHPOffice/PhpSpreadsheet/pull/3416)
+- Correct Xlsx Parsing of quotePrefix="0". [Issue #3435](https://github.com/PHPOffice/PhpSpreadsheet/issues/3435) [PR #3438](https://github.com/PHPOffice/PhpSpreadsheet/pull/3438)
+- More Display Options for Chart Axis and Legend. [Issue #3414](https://github.com/PHPOffice/PhpSpreadsheet/issues/3414) [PR #3434](https://github.com/PHPOffice/PhpSpreadsheet/pull/3434)
+- Apply strict type checking to Complex suffix. [PR #3452](https://github.com/PHPOffice/PhpSpreadsheet/pull/3452)
+- Incorrect Font Color Read Xlsx Rich Text Indexed Color Custom Palette. [Issue #3464](https://github.com/PHPOffice/PhpSpreadsheet/issues/3464) [PR #3465](https://github.com/PHPOffice/PhpSpreadsheet/pull/3465)
+- Xlsx Writer Honor Alignment in Default Font. [Issue #3443](https://github.com/PHPOffice/PhpSpreadsheet/issues/3443) [PR #3459](https://github.com/PHPOffice/PhpSpreadsheet/pull/3459)
+- Support Border for Charts. [PR #3462](https://github.com/PHPOffice/PhpSpreadsheet/pull/3462)
+- Error in "this row" structured reference calculation (cached result from first row when using a range) [Issue #3504](https://github.com/PHPOffice/PhpSpreadsheet/issues/3504) [PR #3505](https://github.com/PHPOffice/PhpSpreadsheet/pull/3505)
+- Allow colour palette index references in Number Format masks [Issue #3511](https://github.com/PHPOffice/PhpSpreadsheet/issues/3511) [PR #3512](https://github.com/PHPOffice/PhpSpreadsheet/pull/3512)
+- Xlsx Reader formula with quotePrefix [Issue #3495](https://github.com/PHPOffice/PhpSpreadsheet/issues/3495) [PR #3497](https://github.com/PHPOffice/PhpSpreadsheet/pull/3497)
+- Handle REF error as part of range [Issue #3453](https://github.com/PHPOffice/PhpSpreadsheet/issues/3453) [PR #3467](https://github.com/PHPOffice/PhpSpreadsheet/pull/3467)
+- Handle Absolute Pathnames in Rels File [Issue #3553](https://github.com/PHPOffice/PhpSpreadsheet/issues/3553) [PR #3554](https://github.com/PHPOffice/PhpSpreadsheet/pull/3554)
+- Return Page Breaks in Order [Issue #3552](https://github.com/PHPOffice/PhpSpreadsheet/issues/3552) [PR #3555](https://github.com/PHPOffice/PhpSpreadsheet/pull/3555)
+- Add position attribute for MemoryDrawing in Html [Issue #3529](https://github.com/PHPOffice/PhpSpreadsheet/issues/3529 [PR #3535](https://github.com/PHPOffice/PhpSpreadsheet/pull/3535)
+- Allow Index_number as Array for VLOOKUP/HLOOKUP [Issue #3561](https://github.com/PHPOffice/PhpSpreadsheet/issues/3561 [PR #3570](https://github.com/PHPOffice/PhpSpreadsheet/pull/3570)
+- Add Unsupported Options in Xml Spreadsheet [Issue #3566](https://github.com/PHPOffice/PhpSpreadsheet/issues/3566 [Issue #3568](https://github.com/PHPOffice/PhpSpreadsheet/issues/3568 [Issue #3569](https://github.com/PHPOffice/PhpSpreadsheet/issues/3569 [PR #3567](https://github.com/PHPOffice/PhpSpreadsheet/pull/3567)
+- Changes to NUMBERVALUE, VALUE, DATEVALUE, TIMEVALUE [Issue #3574](https://github.com/PHPOffice/PhpSpreadsheet/issues/3574 [PR #3575](https://github.com/PHPOffice/PhpSpreadsheet/pull/3575)
+- Redo calculation of color tinting [Issue #3550](https://github.com/PHPOffice/PhpSpreadsheet/issues/3550) [PR #3580](https://github.com/PHPOffice/PhpSpreadsheet/pull/3580)
+- Accommodate Slash with preg_quote [PR #3582](https://github.com/PHPOffice/PhpSpreadsheet/pull/3582) [PR #3583](https://github.com/PHPOffice/PhpSpreadsheet/pull/3583) [PR #3584](https://github.com/PHPOffice/PhpSpreadsheet/pull/3584)
+- HyperlinkBase Property and Html Handling of Properties [Issue #3573](https://github.com/PHPOffice/PhpSpreadsheet/issues/3573) [PR #3589](https://github.com/PHPOffice/PhpSpreadsheet/pull/3589)
+- Improvements for Data Validation [Issue #3592](https://github.com/PHPOffice/PhpSpreadsheet/issues/3592) [Issue #3594](https://github.com/PHPOffice/PhpSpreadsheet/issues/3594) [PR #3605](https://github.com/PHPOffice/PhpSpreadsheet/pull/3605)
+
+## 1.28.0 - 2023-02-25
+
+### Added
+
+- Support for configuring a Chart Title's overlay [PR #3325](https://github.com/PHPOffice/PhpSpreadsheet/pull/3325)
+- Wizards for defining Number Format masks for Numbers, Percentages, Scientific, Currency and Accounting [PR #3334](https://github.com/PHPOffice/PhpSpreadsheet/pull/3334)
+- Support for fixed value divisor in fractional Number Format Masks [PR #3339](https://github.com/PHPOffice/PhpSpreadsheet/pull/3339)
+- Allow More Fonts/Fontnames for Exact Width Calculation [PR #3326](https://github.com/PHPOffice/PhpSpreadsheet/pull/3326) [Issue #3190](https://github.com/PHPOffice/PhpSpreadsheet/issues/3190)
+- Allow override of the Value Binder when setting a Cell value [PR #3361](https://github.com/PHPOffice/PhpSpreadsheet/pull/3361)
+
+### Changed
+
+- Improved handling for @ placeholder in Number Format Masks [PR #3344](https://github.com/PHPOffice/PhpSpreadsheet/pull/3344)
+- Improved handling for ? placeholder in Number Format Masks [PR #3394](https://github.com/PHPOffice/PhpSpreadsheet/pull/3394)
+- Improved support for locale settings and currency codes when matching formatted strings to numerics in the Calculation Engine [PR #3373](https://github.com/PHPOffice/PhpSpreadsheet/pull/3373) and [PR #3374](https://github.com/PHPOffice/PhpSpreadsheet/pull/3374)
+- Improved support for locale settings and matching in the Advanced Value Binder [PR #3376](https://github.com/PHPOffice/PhpSpreadsheet/pull/3376)
+- `toFormattedString` will now always return a string. This can affect the results of `toArray`, `namedRangeToArray`, and `rangeToArray`. [PR #3304](https://github.com/PHPOffice/PhpSpreadsheet/pull/3304)
+- Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD is changed. [Issue #3577](https://github.com/PHPOffice/PhpSpreadsheet/issues/3577) [PR #3377](https://github.com/PHPOffice/PhpSpreadsheet/pull/3377)
+
+### Deprecated
+
+- Rationalisation of Pre-defined Currency Format Masks [PR #3377](https://github.com/PHPOffice/PhpSpreadsheet/pull/3377)
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Calculation Engine doesn't evaluate Defined Name when default cell A1 is quote-prefixed [Issue #3335](https://github.com/PHPOffice/PhpSpreadsheet/issues/3335) [PR #3336](https://github.com/PHPOffice/PhpSpreadsheet/pull/3336)
+- XLSX Writer - Array Formulas do not include function prefix [Issue #3337](https://github.com/PHPOffice/PhpSpreadsheet/issues/3337) [PR #3338](https://github.com/PHPOffice/PhpSpreadsheet/pull/3338)
+- Permit Max Column for Row Breaks [Issue #3143](https://github.com/PHPOffice/PhpSpreadsheet/issues/3143) [PR #3345](https://github.com/PHPOffice/PhpSpreadsheet/pull/3345)
+- AutoSize Columns should allow for dropdown icon when AutoFilter is for a Table [Issue #3356](https://github.com/PHPOffice/PhpSpreadsheet/issues/3356) [PR #3358](https://github.com/PHPOffice/PhpSpreadsheet/pull/3358) and for Center Alignment of Headers [Issue #3395](https://github.com/PHPOffice/PhpSpreadsheet/issues/3395) [PR #3399](https://github.com/PHPOffice/PhpSpreadsheet/pull/3399)
+- Decimal Precision for Scientific Number Format Mask [Issue #3381](https://github.com/PHPOffice/PhpSpreadsheet/issues/3381) [PR #3382](https://github.com/PHPOffice/PhpSpreadsheet/pull/3382)
+- Xls Writer Parser Handle Boolean Literals as Function Arguments [Issue #3369](https://github.com/PHPOffice/PhpSpreadsheet/issues/3369) [PR #3391](https://github.com/PHPOffice/PhpSpreadsheet/pull/3391)
+- Conditional Formatting Improvements for Xlsx [Issue #3370](https://github.com/PHPOffice/PhpSpreadsheet/issues/3370) [Issue #3202](https://github.com/PHPOffice/PhpSpreadsheet/issues/3302) [PR #3372](https://github.com/PHPOffice/PhpSpreadsheet/pull/3372)
+- Coerce Bool to Int for Mathematical Operations on Arrays [Issue #3389](https://github.com/PHPOffice/PhpSpreadsheet/issues/3389) [Issue #3396](https://github.com/PHPOffice/PhpSpreadsheet/issues/3396) [PR #3392](https://github.com/PHPOffice/PhpSpreadsheet/pull/3392)
+
+## 1.27.1 - 2023-02-08
+
+### Added
+
+- Nothing
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fix Composer --dev dependency issue with dealerdirect/phpcodesniffer-composer-installer renaming their `master` branch to `main`
+
+
+## 1.27.0 - 2023-01-24
+
+### Added
+
+- Option to specify a range of columns/rows for the Row/Column `isEmpty()` methods [PR #3315](https://github.com/PHPOffice/PhpSpreadsheet/pull/3315)
+- Option for Cell Iterator to return a null value or create and return a new cell when accessing a cell that doesn't exist [PR #3314](https://github.com/PHPOffice/PhpSpreadsheet/pull/3314)
+- Support for Structured References in the Calculation Engine [PR #3261](https://github.com/PHPOffice/PhpSpreadsheet/pull/3261)
+- Limited Support for Form Controls [PR #3130](https://github.com/PHPOffice/PhpSpreadsheet/pull/3130) [Issue #2396](https://github.com/PHPOffice/PhpSpreadsheet/issues/2396) [Issue #1770](https://github.com/PHPOffice/PhpSpreadsheet/issues/1770) [Issue #2388](https://github.com/PHPOffice/PhpSpreadsheet/issues/2388) [Issue #2904](https://github.com/PHPOffice/PhpSpreadsheet/issues/2904) [Issue #2661](https://github.com/PHPOffice/PhpSpreadsheet/issues/2661)
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Shared/JAMA is removed. [PR #3260](https://github.com/PHPOffice/PhpSpreadsheet/pull/3260)
+
+### Fixed
+
+- Namespace-Aware Code for SheetViewOptions, SheetProtection [PR #3230](https://github.com/PHPOffice/PhpSpreadsheet/pull/3230)
+- Additional Method for XIRR if Newton-Raphson Doesn't Converge [Issue #689](https://github.com/PHPOffice/PhpSpreadsheet/issues/689) [PR #3262](https://github.com/PHPOffice/PhpSpreadsheet/pull/3262)
+- Better Handling of Composite Charts [Issue #2333](https://github.com/PHPOffice/PhpSpreadsheet/issues/2333) [PR #3265](https://github.com/PHPOffice/PhpSpreadsheet/pull/3265)
+- Update Column Reference for Columns Beginning with Y and Z [Issue #3263](https://github.com/PHPOffice/PhpSpreadsheet/issues/3263) [PR #3264](https://github.com/PHPOffice/PhpSpreadsheet/pull/3264)
+- Honor Fit to 1-Page Height Html/Pdf [Issue #3266](https://github.com/PHPOffice/PhpSpreadsheet/issues/3266) [PR #3279](https://github.com/PHPOffice/PhpSpreadsheet/pull/3279)
+- AND/OR/XOR Handling of Literal Strings [PR #3287](https://github.com/PHPOffice/PhpSpreadsheet/pull/3287)
+- Xls Reader Vertical Break and Writer Page Order [Issue #3305](https://github.com/PHPOffice/PhpSpreadsheet/issues/3305) [PR #3306](https://github.com/PHPOffice/PhpSpreadsheet/pull/3306)
+
+
+## 1.26.0 - 2022-12-21
+
+### Added
+
+- Extended flag options for the Reader `load()` and Writer `save()` methods
+- Apply Row/Column limits (1048576 and XFD) in ReferenceHelper [PR #3213](https://github.com/PHPOffice/PhpSpreadsheet/pull/3213)
+- Allow the creation of In-Memory Drawings from a string of binary image data, or from a stream. [PR #3157](https://github.com/PHPOffice/PhpSpreadsheet/pull/3157)
+- Xlsx Reader support for Pivot Tables [PR #2829](https://github.com/PHPOffice/PhpSpreadsheet/pull/2829)
+- Permit Date/Time Entered on Spreadsheet to be calculated as Float [Issue #1416](https://github.com/PHPOffice/PhpSpreadsheet/issues/1416) [PR #3121](https://github.com/PHPOffice/PhpSpreadsheet/pull/3121)
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Direct update of Calculation::suppressFormulaErrors is replaced with setter.
+- Font public static variable defaultColumnWidths replaced with constant DEFAULT_COLUMN_WIDTHS.
+- ExcelError public static variable errorCodes replaced with constant ERROR_CODES.
+- NumberFormat constant FORMAT_DATE_YYYYMMDD2 replaced with existing identical FORMAT_DATE_YYYYMMDD.
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fixed handling for `_xlws` prefixed functions from Office365 [Issue #3245](https://github.com/PHPOffice/PhpSpreadsheet/issues/3245) [PR #3247](https://github.com/PHPOffice/PhpSpreadsheet/pull/3247)
+- Conditionals formatting rules applied to rows/columns are removed [Issue #3184](https://github.com/PHPOffice/PhpSpreadsheet/issues/3184) [PR #3213](https://github.com/PHPOffice/PhpSpreadsheet/pull/3213)
+- Treat strings containing currency or accounting values as floats in Calculation Engine operations [Issue #3165](https://github.com/PHPOffice/PhpSpreadsheet/issues/3165) [PR #3189](https://github.com/PHPOffice/PhpSpreadsheet/pull/3189)
+- Treat strings containing percentage values as floats in Calculation Engine operations [Issue #3155](https://github.com/PHPOffice/PhpSpreadsheet/issues/3155) [PR #3156](https://github.com/PHPOffice/PhpSpreadsheet/pull/3156) and [PR #3164](https://github.com/PHPOffice/PhpSpreadsheet/pull/3164)
+- Xlsx Reader Accept Palette of Fewer than 64 Colors [Issue #3093](https://github.com/PHPOffice/PhpSpreadsheet/issues/3093) [PR #3096](https://github.com/PHPOffice/PhpSpreadsheet/pull/3096)
+- Use Locale-Independent Float Conversion for Xlsx Writer Custom Property [Issue #3095](https://github.com/PHPOffice/PhpSpreadsheet/issues/3095) [PR #3099](https://github.com/PHPOffice/PhpSpreadsheet/pull/3099)
+- Allow setting AutoFilter range on a single cell or row [Issue #3102](https://github.com/PHPOffice/PhpSpreadsheet/issues/3102) [PR #3111](https://github.com/PHPOffice/PhpSpreadsheet/pull/3111)
+- Xlsx Reader External Data Validations Flag Missing [Issue #2677](https://github.com/PHPOffice/PhpSpreadsheet/issues/2677) [PR #3078](https://github.com/PHPOffice/PhpSpreadsheet/pull/3078)
+- Reduces extra memory usage on `__destruct()` calls [PR #3092](https://github.com/PHPOffice/PhpSpreadsheet/pull/3092)
+- Additional properties for Trendlines [Issue #3011](https://github.com/PHPOffice/PhpSpreadsheet/issues/3011) [PR #3028](https://github.com/PHPOffice/PhpSpreadsheet/pull/3028)
+- Calculation suppressFormulaErrors fix [Issue #1531](https://github.com/PHPOffice/PhpSpreadsheet/issues/1531) [PR #3092](https://github.com/PHPOffice/PhpSpreadsheet/pull/3092)
+- Permit Date/Time Entered on Spreadsheet to be Calculated as Float [Issue #1416](https://github.com/PHPOffice/PhpSpreadsheet/issues/1416) [PR #3121](https://github.com/PHPOffice/PhpSpreadsheet/pull/3121)
+- Incorrect Handling of Data Validation Formula Containing Ampersand [Issue #3145](https://github.com/PHPOffice/PhpSpreadsheet/issues/3145) [PR #3146](https://github.com/PHPOffice/PhpSpreadsheet/pull/3146)
+- Xlsx Namespace Handling of Drawings, RowAndColumnAttributes, MergeCells [Issue #3138](https://github.com/PHPOffice/PhpSpreadsheet/issues/3138) [PR #3136](https://github.com/PHPOffice/PhpSpreadsheet/pull/3137)
+- Generation3 Copy With Image in Footer [Issue #3126](https://github.com/PHPOffice/PhpSpreadsheet/issues/3126) [PR #3140](https://github.com/PHPOffice/PhpSpreadsheet/pull/3140)
+- MATCH Function Problems with Int/Float Compare and Wildcards [Issue #3141](https://github.com/PHPOffice/PhpSpreadsheet/issues/3141) [PR #3142](https://github.com/PHPOffice/PhpSpreadsheet/pull/3142)
+- Fix ODS Read Filter on number-columns-repeated cell [Issue #3148](https://github.com/PHPOffice/PhpSpreadsheet/issues/3148) [PR #3149](https://github.com/PHPOffice/PhpSpreadsheet/pull/3149)
+- Problems Formatting Very Small and Very Large Numbers [Issue #3128](https://github.com/PHPOffice/PhpSpreadsheet/issues/3128) [PR #3152](https://github.com/PHPOffice/PhpSpreadsheet/pull/3152)
+- XlsxWrite preserve line styles for y-axis, not just x-axis [PR #3163](https://github.com/PHPOffice/PhpSpreadsheet/pull/3163)
+- Xlsx Namespace Handling of Drawings, RowAndColumnAttributes, MergeCells [Issue #3138](https://github.com/PHPOffice/PhpSpreadsheet/issues/3138) [PR #3137](https://github.com/PHPOffice/PhpSpreadsheet/pull/3137)
+- More Detail for Cyclic Error Messages [Issue #3169](https://github.com/PHPOffice/PhpSpreadsheet/issues/3169) [PR #3170](https://github.com/PHPOffice/PhpSpreadsheet/pull/3170)
+- Improved Documentation for Deprecations - many PRs [Issue #3162](https://github.com/PHPOffice/PhpSpreadsheet/issues/3162)
+
+
+## 1.25.2 - 2022-09-25
+
+### Added
+
+- Nothing
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Composer dependency clash with ezyang/htmlpurifier
+
+
+## 1.25.0 - 2022-09-25
+
+### Added
+
+- Implementation of the new `TEXTBEFORE()`, `TEXTAFTER()` and `TEXTSPLIT()` Excel Functions
+- Implementation of the `ARRAYTOTEXT()` and `VALUETOTEXT()` Excel Functions
+- Support for [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) implementation of
+ JpGraph library to render charts added.
+- Charts: Add Gradients, Transparency, Hidden Axes, Rounded Corners, Trendlines, Date Axes.
+
+### Changed
+
+- Allow variant behaviour when merging cells [Issue #3065](https://github.com/PHPOffice/PhpSpreadsheet/issues/3065)
+ - Merge methods now allow an additional `$behaviour` argument. Permitted values are:
+ - Worksheet::MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells (the default behaviour)
+ - Worksheet::MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
+ - Worksheet::MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell
+
+### Deprecated
+
+- Axis getLineProperty deprecated in favor of getLineColorProperty.
+- Moved majorGridlines and minorGridlines from Chart to Axis. Setting either in Chart constructor or through Chart methods, or getting either using Chart methods is deprecated.
+- Chart::EXCEL_COLOR_TYPE_* copied from Properties to ChartColor; use in Properties is deprecated.
+- ChartColor::EXCEL_COLOR_TYPE_ARGB deprecated in favor of EXCEL_COLOR_TYPE_RGB ("A" component was never allowed).
+- Misspelled Properties::LINE_STYLE_DASH_SQUERE_DOT deprecated in favor of LINE_STYLE_DASH_SQUARE_DOT.
+- Clone not permitted for Spreadsheet. Spreadsheet->copy() can be used instead.
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](https://github.com/PHPOffice/PhpSpreadsheet/issues/3076) [PR #3077](https://github.com/PHPOffice/PhpSpreadsheet/pull/3077)
+- Fix DataValidation sqRef when inserting/deleting rows/columns [Issue #3056](https://github.com/PHPOffice/PhpSpreadsheet/issues/3056) [PR #3074](https://github.com/PHPOffice/PhpSpreadsheet/pull/3074)
+- Named ranges not usable as anchors in OFFSET function [Issue #3013](https://github.com/PHPOffice/PhpSpreadsheet/issues/3013)
+- Fully flatten an array [Issue #2955](https://github.com/PHPOffice/PhpSpreadsheet/issues/2955) [PR #2956](https://github.com/PHPOffice/PhpSpreadsheet/pull/2956)
+- cellExists() and getCell() methods should support UTF-8 named cells [Issue #2987](https://github.com/PHPOffice/PhpSpreadsheet/issues/2987) [PR #2988](https://github.com/PHPOffice/PhpSpreadsheet/pull/2988)
+- Spreadsheet copy fixed, clone disabled. [PR #2951](https://github.com/PHPOffice/PhpSpreadsheet/pull/2951)
+- Fix PDF problems with text rotation and paper size. [Issue #1747](https://github.com/PHPOffice/PhpSpreadsheet/issues/1747) [Issue #1713](https://github.com/PHPOffice/PhpSpreadsheet/issues/1713) [PR #2960](https://github.com/PHPOffice/PhpSpreadsheet/pull/2960)
+- Limited support for chart titles as formulas [Issue #2965](https://github.com/PHPOffice/PhpSpreadsheet/issues/2965) [Issue #749](https://github.com/PHPOffice/PhpSpreadsheet/issues/749) [PR #2971](https://github.com/PHPOffice/PhpSpreadsheet/pull/2971)
+- Add Gradients, Transparency, and Hidden Axes to Chart [Issue #2257](https://github.com/PHPOffice/PhpSpreadsheet/issues/2257) [Issue #2229](https://github.com/PHPOffice/PhpSpreadsheet/issues/2929) [Issue #2935](https://github.com/PHPOffice/PhpSpreadsheet/issues/2935) [PR #2950](https://github.com/PHPOffice/PhpSpreadsheet/pull/2950)
+- Chart Support for Rounded Corners and Trendlines [Issue #2968](https://github.com/PHPOffice/PhpSpreadsheet/issues/2968) [Issue #2815](https://github.com/PHPOffice/PhpSpreadsheet/issues/2815) [PR #2976](https://github.com/PHPOffice/PhpSpreadsheet/pull/2976)
+- Add setName Method for Chart [Issue #2991](https://github.com/PHPOffice/PhpSpreadsheet/issues/2991) [PR #3001](https://github.com/PHPOffice/PhpSpreadsheet/pull/3001)
+- Eliminate partial dependency on php-intl in StringHelper [Issue #2982](https://github.com/PHPOffice/PhpSpreadsheet/issues/2982) [PR #2994](https://github.com/PHPOffice/PhpSpreadsheet/pull/2994)
+- Minor changes for Pdf [Issue #2999](https://github.com/PHPOffice/PhpSpreadsheet/issues/2999) [PR #3002](https://github.com/PHPOffice/PhpSpreadsheet/pull/3002) [PR #3006](https://github.com/PHPOffice/PhpSpreadsheet/pull/3006)
+- Html/Pdf Do net set background color for cells using (default) nofill [PR #3016](https://github.com/PHPOffice/PhpSpreadsheet/pull/3016)
+- Add support for Date Axis to Chart [Issue #2967](https://github.com/PHPOffice/PhpSpreadsheet/issues/2967) [PR #3018](https://github.com/PHPOffice/PhpSpreadsheet/pull/3018)
+- Reconcile Differences Between Css and Excel for Cell Alignment [PR #3048](https://github.com/PHPOffice/PhpSpreadsheet/pull/3048)
+- R1C1 Format Internationalization and Better Support for Relative Offsets [Issue #1704](https://github.com/PHPOffice/PhpSpreadsheet/issues/1704) [PR #3052](https://github.com/PHPOffice/PhpSpreadsheet/pull/3052)
+- Minor Fix for Percentage Formatting [Issue #1929](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #3053](https://github.com/PHPOffice/PhpSpreadsheet/pull/3053)
+
+## 1.24.1 - 2022-07-18
+
+### Added
+
+- Support for SimpleCache Interface versions 1.0, 2.0 and 3.0
+- Add Chart Axis Option textRotation [Issue #2705](https://github.com/PHPOffice/PhpSpreadsheet/issues/2705) [PR #2940](https://github.com/PHPOffice/PhpSpreadsheet/pull/2940)
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fix Encoding issue with Html reader (PHP 8.2 deprecation for mb_convert_encoding) [Issue #2942](https://github.com/PHPOffice/PhpSpreadsheet/issues/2942) [PR #2943](https://github.com/PHPOffice/PhpSpreadsheet/pull/2943)
+- Additional Chart fixes
+ - Pie chart with part separated unwantedly [Issue #2506](https://github.com/PHPOffice/PhpSpreadsheet/issues/2506) [PR #2928](https://github.com/PHPOffice/PhpSpreadsheet/pull/2928)
+ - Chart styling is lost on simple load / save process [Issue #1797](https://github.com/PHPOffice/PhpSpreadsheet/issues/1797) [Issue #2077](https://github.com/PHPOffice/PhpSpreadsheet/issues/2077) [PR #2930](https://github.com/PHPOffice/PhpSpreadsheet/pull/2930)
+ - Can't create contour chart (surface 2d) [Issue #2931](https://github.com/PHPOffice/PhpSpreadsheet/issues/2931) [PR #2933](https://github.com/PHPOffice/PhpSpreadsheet/pull/2933)
+- VLOOKUP Breaks When Array Contains Null Cells [Issue #2934](https://github.com/PHPOffice/PhpSpreadsheet/issues/2934) [PR #2939](https://github.com/PHPOffice/PhpSpreadsheet/pull/2939)
+
+## 1.24.0 - 2022-07-09
+
+Note that this will be the last 1.x branch release before the 2.x release. We will maintain both branches in parallel for a time; but users are requested to update to version 2.0 once that is fully available.
+
+### Added
+
+- Added `removeComment()` method for Worksheet [PR #2875](https://github.com/PHPOffice/PhpSpreadsheet/pull/2875/files)
+- Add point size option for scatter charts [Issue #2298](https://github.com/PHPOffice/PhpSpreadsheet/issues/2298) [PR #2801](https://github.com/PHPOffice/PhpSpreadsheet/pull/2801)
+- Basic support for Xlsx reading/writing Chart Sheets [PR #2830](https://github.com/PHPOffice/PhpSpreadsheet/pull/2830)
+
+ Note that a ChartSheet is still only written as a normal Worksheet containing a single chart, not as an actual ChartSheet.
+
+- Added Worksheet visibility in Ods Reader [PR #2851](https://github.com/PHPOffice/PhpSpreadsheet/pull/2851) and Gnumeric Reader [PR #2853](https://github.com/PHPOffice/PhpSpreadsheet/pull/2853)
+- Added Worksheet visibility in Ods Writer [PR #2850](https://github.com/PHPOffice/PhpSpreadsheet/pull/2850)
+- Allow Csv Reader to treat string as contents of file [Issue #1285](https://github.com/PHPOffice/PhpSpreadsheet/issues/1285) [PR #2792](https://github.com/PHPOffice/PhpSpreadsheet/pull/2792)
+- Allow Csv Reader to store null string rather than leave cell empty [Issue #2840](https://github.com/PHPOffice/PhpSpreadsheet/issues/2840) [PR #2842](https://github.com/PHPOffice/PhpSpreadsheet/pull/2842)
+- Provide new Worksheet methods to identify if a row or column is "empty", making allowance for different definitions of "empty":
+ - Treat rows/columns containing no cell records as empty (default)
+ - Treat cells containing a null value as empty
+ - Treat cells containing an empty string as empty
+
+### Changed
+
+- Modify `rangeBoundaries()`, `rangeDimension()` and `getRangeBoundaries()` Coordinate methods to work with row/column ranges as well as with cell ranges and cells [PR #2926](https://github.com/PHPOffice/PhpSpreadsheet/pull/2926)
+- Better enforcement of value modification to match specified datatype when using `setValueExplicit()`
+- Relax validation of merge cells to allow merge for a single cell reference [Issue #2776](https://github.com/PHPOffice/PhpSpreadsheet/issues/2776)
+- Memory and speed improvements, particularly for the Cell Collection, and the Writers.
+
+ See [the Discussion section on github](https://github.com/PHPOffice/PhpSpreadsheet/discussions/2821) for details of performance across versions
+- Improved performance for removing rows/columns from a worksheet
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Xls Reader resolving absolute named ranges to relative ranges [Issue #2826](https://github.com/PHPOffice/PhpSpreadsheet/issues/2826) [PR #2827](https://github.com/PHPOffice/PhpSpreadsheet/pull/2827)
+- Null value handling in the Excel Math/Trig PRODUCT() function [Issue #2833](https://github.com/PHPOffice/PhpSpreadsheet/issues/2833) [PR #2834](https://github.com/PHPOffice/PhpSpreadsheet/pull/2834)
+- Invalid Print Area defined in Xlsx corrupts internal storage of print area [Issue #2848](https://github.com/PHPOffice/PhpSpreadsheet/issues/2848) [PR #2849](https://github.com/PHPOffice/PhpSpreadsheet/pull/2849)
+- Time interval formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
+- Copy from Xls(x) to Html/Pdf loses drawings [PR #2788](https://github.com/PHPOffice/PhpSpreadsheet/pull/2788)
+- Html Reader converting cell containing 0 to null string [Issue #2810](https://github.com/PHPOffice/PhpSpreadsheet/issues/2810) [PR #2813](https://github.com/PHPOffice/PhpSpreadsheet/pull/2813)
+- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [Issue #2219](https://github.com/PHPOffice/PhpSpreadsheet/issues/2219) [Issue #2863](https://github.com/PHPOffice/PhpSpreadsheet/issues/2863) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852) [PR #2856](https://github.com/PHPOffice/PhpSpreadsheet/pull/2856) [PR #2865](https://github.com/PHPOffice/PhpSpreadsheet/pull/2865) [PR #2872](https://github.com/PHPOffice/PhpSpreadsheet/pull/2872) [PR #2879](https://github.com/PHPOffice/PhpSpreadsheet/pull/2879) [PR #2898](https://github.com/PHPOffice/PhpSpreadsheet/pull/2898) [PR #2906](https://github.com/PHPOffice/PhpSpreadsheet/pull/2906) [PR #2922](https://github.com/PHPOffice/PhpSpreadsheet/pull/2922) [PR #2923](https://github.com/PHPOffice/PhpSpreadsheet/pull/2923)
+- Adjust both coordinates for two-cell anchors when rows/columns are added/deleted. [Issue #2908](https://github.com/PHPOffice/PhpSpreadsheet/issues/2908) [PR #2909](https://github.com/PHPOffice/PhpSpreadsheet/pull/2909)
+- Keep calculated string results below 32K. [PR #2921](https://github.com/PHPOffice/PhpSpreadsheet/pull/2921)
+- Filter out illegal Unicode char values FFFE/FFFF. [Issue #2897](https://github.com/PHPOffice/PhpSpreadsheet/issues/2897) [PR #2910](https://github.com/PHPOffice/PhpSpreadsheet/pull/2910)
+- Better handling of REF errors and propagation of all errors in Calculation engine. [PR #2902](https://github.com/PHPOffice/PhpSpreadsheet/pull/2902)
+- Calculating Engine regexp for Column/Row references when there are multiple quoted worksheet references in the formula [Issue #2874](https://github.com/PHPOffice/PhpSpreadsheet/issues/2874) [PR #2899](https://github.com/PHPOffice/PhpSpreadsheet/pull/2899)
+
+## 1.23.0 - 2022-04-24
+
+### Added
+
+- Ods Writer support for Freeze Pane [Issue #2013](https://github.com/PHPOffice/PhpSpreadsheet/issues/2013) [PR #2755](https://github.com/PHPOffice/PhpSpreadsheet/pull/2755)
+- Ods Writer support for setting column width/row height (including the use of AutoSize) [Issue #2346](https://github.com/PHPOffice/PhpSpreadsheet/issues/2346) [PR #2753](https://github.com/PHPOffice/PhpSpreadsheet/pull/2753)
+- Introduced CellAddress, CellRange, RowRange and ColumnRange value objects that can be used as an alternative to a string value (e.g. `'C5'`, `'B2:D4'`, `'2:2'` or `'B:C'`) in appropriate contexts.
+- Implementation of the FILTER(), SORT(), SORTBY() and UNIQUE() Lookup/Reference (array) functions.
+- Implementation of the ISREF() Information function.
+- Added support for reading "formatted" numeric values from Csv files; although default behaviour of reading these values as strings is preserved.
+
+ (i.e a value of "12,345.67" can be read as numeric `12345.67`, not simply as a string `"12,345.67"`, if the `castFormattedNumberToNumeric()` setting is enabled.
+
+ This functionality is locale-aware, using the server's locale settings to identify the thousands and decimal separators.
+
+- Support for two cell anchor drawing of images. [#2532](https://github.com/PHPOffice/PhpSpreadsheet/pull/2532) [#2674](https://github.com/PHPOffice/PhpSpreadsheet/pull/2674)
+- Limited support for Xls Reader to handle Conditional Formatting:
+
+ Ranges and Rules are read, but style is currently limited to font size, weight and color; and to fill style and color.
+
+- Add ability to suppress Mac line ending check for CSV [#2623](https://github.com/PHPOffice/PhpSpreadsheet/pull/2623)
+- Initial support for creating and writing Tables (Xlsx Writer only) [PR #2671](https://github.com/PHPOffice/PhpSpreadsheet/pull/2671)
+
+ See `/samples/Table` for examples of use.
+
+ Note that PreCalculateFormulas needs to be disabled when saving spreadsheets containing tables with formulae (totals or column formulae).
+
+### Changed
+
+- Gnumeric Reader now loads number formatting for cells.
+- Gnumeric Reader now correctly identifies selected worksheet and selected cells in a worksheet.
+- Some Refactoring of the Ods Reader, moving all formula and address translation from Ods to Excel into a separate class to eliminate code duplication and ensure consistency.
+- Make Boolean Conversion in Csv Reader locale-aware when using the String Value Binder.
+
+ This is determined by the Calculation Engine locale setting.
+
+ (i.e. `"Vrai"` wil be converted to a boolean `true` if the Locale is set to `fr`.)
+- Allow `psr/simple-cache` 2.x
+
+### Deprecated
+
+- All Excel Function implementations in `Calculation\Functions` (including the Error functions) have been moved to dedicated classes for groups of related functions. See the docblocks against all the deprecated methods for details of the new methods to call instead. At some point, these old classes will be deleted.
+- Worksheet methods that reference cells "byColumnandRow". All such methods have an equivalent that references the cell by its address (e.g. '`E3'` rather than `5, 3`).
+
+ These functions now accept either a cell address string (`'E3')` or an array with columnId and rowId (`[5, 3]`) or a new `CellAddress` object as their `cellAddress`/`coordinate` argument.
+ This includes the methods:
+ - `setCellValueByColumnAndRow()` use the equivalent `setCellValue()`
+ - `setCellValueExplicitByColumnAndRow()` use the equivalent `setCellValueExplicit()`
+ - `getCellByColumnAndRow()` use the equivalent `getCell()`
+ - `cellExistsByColumnAndRow()` use the equivalent `cellExists()`
+ - `getStyleByColumnAndRow()` use the equivalent `getStyle()`
+ - `setBreakByColumnAndRow()` use the equivalent `setBreak()`
+ - `mergeCellsByColumnAndRow()` use the equivalent `mergeCells()`
+ - `unmergeCellsByColumnAndRow()` use the equivalent `unmergeCells()`
+ - `protectCellsByColumnAndRow()` use the equivalent `protectCells()`
+ - `unprotectCellsByColumnAndRow()` use the equivalent `unprotectCells()`
+ - `setAutoFilterByColumnAndRow()` use the equivalent `setAutoFilter()`
+ - `freezePaneByColumnAndRow()` use the equivalent `freezePane()`
+ - `getCommentByColumnAndRow()` use the equivalent `getComment()`
+ - `setSelectedCellByColumnAndRow()` use the equivalent `setSelectedCells()`
+
+ This change provides more consistency in the methods (not every "by cell address" method has an equivalent "byColumnAndRow" method);
+ and the "by cell address" methods often provide more flexibility, such as allowing a range of cells, or referencing them by passing the defined name of a named range as the argument.
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Make allowance for the AutoFilter dropdown icon in the first row of an Autofilter range when using Autosize columns. [Issue #2413](https://github.com/PHPOffice/PhpSpreadsheet/issues/2413) [PR #2754](https://github.com/PHPOffice/PhpSpreadsheet/pull/2754)
+- Support for "chained" ranges (e.g. `A5:C10:C20:F1`) in the Calculation Engine; and also support for using named ranges with the Range operator (e.g. `NamedRange1:NamedRange2`) [Issue #2730](https://github.com/PHPOffice/PhpSpreadsheet/issues/2730) [PR #2746](https://github.com/PHPOffice/PhpSpreadsheet/pull/2746)
+- Update Conditional Formatting ranges and rule conditions when inserting/deleting rows/columns [Issue #2678](https://github.com/PHPOffice/PhpSpreadsheet/issues/2678) [PR #2689](https://github.com/PHPOffice/PhpSpreadsheet/pull/2689)
+- Allow `INDIRECT()` to accept row/column ranges as well as cell ranges [PR #2687](https://github.com/PHPOffice/PhpSpreadsheet/pull/2687)
+- Fix bug when deleting cells with hyperlinks, where the hyperlink was then being "inherited" by whatever cell moved to that cell address.
+- Fix bug in Conditional Formatting in the Xls Writer that resulted in a broken file when there were multiple conditional ranges in a worksheet.
+- Fix Conditional Formatting in the Xls Writer to work with rules that contain string literals, cell references and formulae.
+- Fix for setting Active Sheet to the first loaded worksheet when bookViews element isn't defined [Issue #2666](https://github.com/PHPOffice/PhpSpreadsheet/issues/2666) [PR #2669](https://github.com/PHPOffice/PhpSpreadsheet/pull/2669)
+- Fixed behaviour of XLSX font style vertical align settings [PR #2619](https://github.com/PHPOffice/PhpSpreadsheet/pull/2619)
+- Resolved formula translations to handle separators (row and column) for array functions as well as for function argument separators; and cleanly handle nesting levels.
+
+ Note that this method is used when translating Excel functions between `en_us` and other locale languages, as well as when converting formulae between different spreadsheet formats (e.g. Ods to Excel).
+
+ Nor is this a perfect solution, as there may still be issues when function calls have array arguments that themselves contain function calls; but it's still better than the current logic.
+- Fix for escaping double quotes within a formula [Issue #1971](https://github.com/PHPOffice/PhpSpreadsheet/issues/1971) [PR #2651](https://github.com/PHPOffice/PhpSpreadsheet/pull/2651)
+- Change open mode for output from `wb+` to `wb` [Issue #2372](https://github.com/PHPOffice/PhpSpreadsheet/issues/2372) [PR #2657](https://github.com/PHPOffice/PhpSpreadsheet/pull/2657)
+- Use color palette if supplied [Issue #2499](https://github.com/PHPOffice/PhpSpreadsheet/issues/2499) [PR #2595](https://github.com/PHPOffice/PhpSpreadsheet/pull/2595)
+- Xls reader treat drawing offsets as int rather than float [PR #2648](https://github.com/PHPOffice/PhpSpreadsheet/pull/2648)
+- Handle booleans in conditional styles properly [PR #2654](https://github.com/PHPOffice/PhpSpreadsheet/pull/2654)
+- Fix for reading files in the root directory of a ZipFile, which should not be prefixed by relative paths ("./") as dirname($filename) does by default.
+- Fix invalid style of cells in empty columns with columnDimensions and rows with rowDimensions in added external sheet. [PR #2739](https://github.com/PHPOffice/PhpSpreadsheet/pull/2739)
+- Time Interval Formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
+
+## 1.22.0 - 2022-02-18
+
+### Added
+
+- Namespacing phase 2 - styles.
+[PR #2471](https://github.com/PHPOffice/PhpSpreadsheet/pull/2471)
+
+- Improved support for passing of array arguments to Excel function implementations to return array results (where appropriate). [Issue #2551](https://github.com/PHPOffice/PhpSpreadsheet/issues/2551)
+
+ This is the first stage in an ongoing process of adding array support to all appropriate function implementations,
+- Support for the Excel365 Math/Trig SEQUENCE() function [PR #2536](https://github.com/PHPOffice/PhpSpreadsheet/pull/2536)
+- Support for the Excel365 Math/Trig RANDARRAY() function [PR #2540](https://github.com/PHPOffice/PhpSpreadsheet/pull/2540)
+
+ Note that the Spill Operator is not yet supported in the Calculation Engine; but this can still be useful for defining array constants.
+- Improved support for Conditional Formatting Rules [PR #2491](https://github.com/PHPOffice/PhpSpreadsheet/pull/2491)
+ - Provide support for a wider range of Conditional Formatting Rules for Xlsx Reader/Writer:
+ - Cells Containing (cellIs)
+ - Specific Text (containing, notContaining, beginsWith, endsWith)
+ - Dates Occurring (all supported timePeriods)
+ - Blanks/NoBlanks
+ - Errors/NoErrors
+ - Duplicates/Unique
+ - Expression
+ - Provision of CF Wizards (for all the above listed rule types) to help create/modify CF Rules without having to manage all the combinations of types/operators, and the complexities of formula expressions, or the text/timePeriod attributes.
+
+ See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/conditional-formatting/) for details
+
+ - Full support of the above CF Rules for the Xlsx Reader and Writer; even when the file being loaded has CF rules listed in the `` element for the worksheet rather than the `` element.
+ - Provision of a CellMatcher to identify if rules are matched for a cell, and which matching style will be applied.
+ - Improved documentation and examples, covering all supported CF rule types.
+ - Add support for one digit decimals (FORMAT_NUMBER_0, FORMAT_PERCENTAGE_0). [PR #2525](https://github.com/PHPOffice/PhpSpreadsheet/pull/2525)
+ - Initial work enabling Excel function implementations for handling arrays as arguments when used in "array formulae" [#2562](https://github.com/PHPOffice/PhpSpreadsheet/issues/2562)
+ - Enable most of the Date/Time functions to accept array arguments [#2573](https://github.com/PHPOffice/PhpSpreadsheet/issues/2573)
+ - Array ready functions - Text, Math/Trig, Statistical, Engineering and Logical [#2580](https://github.com/PHPOffice/PhpSpreadsheet/issues/2580)
+
+### Changed
+
+- Additional Russian translations for Excel Functions (courtesy of aleks-samurai).
+- Improved code coverage for NumberFormat. [PR #2556](https://github.com/PHPOffice/PhpSpreadsheet/pull/2556)
+- Extract some methods from the Calculation Engine into dedicated classes [#2537](https://github.com/PHPOffice/PhpSpreadsheet/issues/2537)
+- Eliminate calls to `flattenSingleValue()` that are no longer required when we're checking for array values as arguments [#2590](https://github.com/PHPOffice/PhpSpreadsheet/issues/2590)
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fixed `ReferenceHelper@insertNewBefore` behavior when removing column before last column with null value [PR #2541](https://github.com/PHPOffice/PhpSpreadsheet/pull/2541)
+- Fix bug with `DOLLARDE()` and `DOLLARFR()` functions when the dollar value is negative [Issue #2578](https://github.com/PHPOffice/PhpSpreadsheet/issues/2578) [PR #2579](https://github.com/PHPOffice/PhpSpreadsheet/pull/2579)
+- Fix partial function name matching when translating formulae from Russian to English [Issue #2533](https://github.com/PHPOffice/PhpSpreadsheet/issues/2533) [PR #2534](https://github.com/PHPOffice/PhpSpreadsheet/pull/2534)
+- Various bugs related to Conditional Formatting Rules, and errors in the Xlsx Writer for Conditional Formatting [PR #2491](https://github.com/PHPOffice/PhpSpreadsheet/pull/2491)
+- Xlsx Reader merge range fixes. [Issue #2501](https://github.com/PHPOffice/PhpSpreadsheet/issues/2501) [PR #2504](https://github.com/PHPOffice/PhpSpreadsheet/pull/2504)
+- Handle explicit "date" type for Cell in Xlsx Reader. [Issue #2373](https://github.com/PHPOffice/PhpSpreadsheet/issues/2373) [PR #2485](https://github.com/PHPOffice/PhpSpreadsheet/pull/2485)
+- Recalibrate Row/Column Dimensions after removeRow/Column. [Issue #2442](https://github.com/PHPOffice/PhpSpreadsheet/issues/2442) [PR #2486](https://github.com/PHPOffice/PhpSpreadsheet/pull/2486)
+- Refinement for XIRR. [Issue #2469](https://github.com/PHPOffice/PhpSpreadsheet/issues/2469) [PR #2487](https://github.com/PHPOffice/PhpSpreadsheet/pull/2487)
+- Xlsx Reader handle cell with non-null explicit type but null value. [Issue #2488](https://github.com/PHPOffice/PhpSpreadsheet/issues/2488) [PR #2489](https://github.com/PHPOffice/PhpSpreadsheet/pull/2489)
+- Xlsx Reader fix height and width for oneCellAnchorDrawings. [PR #2492](https://github.com/PHPOffice/PhpSpreadsheet/pull/2492)
+- Fix rounding error in NumberFormat::NUMBER_PERCENTAGE, NumberFormat::NUMBER_PERCENTAGE_00. [PR #2555](https://github.com/PHPOffice/PhpSpreadsheet/pull/2555)
+- Don't treat thumbnail file as xml. [Issue #2516](https://github.com/PHPOffice/PhpSpreadsheet/issues/2516) [PR #2517](https://github.com/PHPOffice/PhpSpreadsheet/pull/2517)
+- Eliminating Xlsx Reader warning when no sz tag for RichText. [Issue #2542](https://github.com/PHPOffice/PhpSpreadsheet/issues/2542) [PR #2550](https://github.com/PHPOffice/PhpSpreadsheet/pull/2550)
+- Fix Xlsx/Xls Writer handling of inline strings. [Issue #353](https://github.com/PHPOffice/PhpSpreadsheet/issues/353) [PR #2569](https://github.com/PHPOffice/PhpSpreadsheet/pull/2569)
+- Richtext colors were not being read correctly after namespace change [#2458](https://github.com/PHPOffice/PhpSpreadsheet/issues/2458)
+- Fix discrepancy between the way markdown tables are rendered in ReadTheDocs and in PHPStorm [#2520](https://github.com/PHPOffice/PhpSpreadsheet/issues/2520)
+- Update Russian Functions Text File [#2557](https://github.com/PHPOffice/PhpSpreadsheet/issues/2557)
+- Fix documentation, instantiation example [#2564](https://github.com/PHPOffice/PhpSpreadsheet/issues/2564)
+
+
+## 1.21.0 - 2022-01-06
+
+### Added
+
+- Ability to add a picture to the background of the comment. Supports four image formats: png, jpeg, gif, bmp. New `Comment::setSizeAsBackgroundImage()` to change the size of a comment to the size of a background image. [Issue #1547](https://github.com/PHPOffice/PhpSpreadsheet/issues/1547) [PR #2422](https://github.com/PHPOffice/PhpSpreadsheet/pull/2422)
+- Ability to set default paper size and orientation [PR #2410](https://github.com/PHPOffice/PhpSpreadsheet/pull/2410)
+- Ability to extend AutoFilter to Maximum Row [PR #2414](https://github.com/PHPOffice/PhpSpreadsheet/pull/2414)
+
+### Changed
+
+- Xlsx Writer will evaluate AutoFilter only if it is as yet unevaluated, or has changed since it was last evaluated [PR #2414](https://github.com/PHPOffice/PhpSpreadsheet/pull/2414)
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Rounding in `NumberFormatter` [Issue #2385](https://github.com/PHPOffice/PhpSpreadsheet/issues/2385) [PR #2399](https://github.com/PHPOffice/PhpSpreadsheet/pull/2399)
+- Support for themes [Issue #2075](https://github.com/PHPOffice/PhpSpreadsheet/issues/2075) [Issue #2387](https://github.com/PHPOffice/PhpSpreadsheet/issues/2387) [PR #2403](https://github.com/PHPOffice/PhpSpreadsheet/pull/2403)
+- Read spreadsheet with `#` in name [Issue #2405](https://github.com/PHPOffice/PhpSpreadsheet/issues/2405) [PR #2409](https://github.com/PHPOffice/PhpSpreadsheet/pull/2409)
+- Improve PDF support for page size and orientation [Issue #1691](https://github.com/PHPOffice/PhpSpreadsheet/issues/1691) [PR #2410](https://github.com/PHPOffice/PhpSpreadsheet/pull/2410)
+- Wildcard handling issues in text match [Issue #2430](https://github.com/PHPOffice/PhpSpreadsheet/issues/2430) [PR #2431](https://github.com/PHPOffice/PhpSpreadsheet/pull/2431)
+- Respect DataType in `insertNewBefore` [PR #2433](https://github.com/PHPOffice/PhpSpreadsheet/pull/2433)
+- Handle rows explicitly hidden after AutoFilter [Issue #1641](https://github.com/PHPOffice/PhpSpreadsheet/issues/1641) [PR #2414](https://github.com/PHPOffice/PhpSpreadsheet/pull/2414)
+- Special characters in image file name [Issue #1470](https://github.com/PHPOffice/PhpSpreadsheet/issues/1470) [Issue #2415](https://github.com/PHPOffice/PhpSpreadsheet/issues/2415) [PR #2416](https://github.com/PHPOffice/PhpSpreadsheet/pull/2416)
+- Mpdf with very many styles [Issue #2432](https://github.com/PHPOffice/PhpSpreadsheet/issues/2432) [PR #2434](https://github.com/PHPOffice/PhpSpreadsheet/pull/2434)
+- Name clashes between parsed and unparsed drawings [Issue #1767](https://github.com/PHPOffice/PhpSpreadsheet/issues/1767) [Issue #2396](https://github.com/PHPOffice/PhpSpreadsheet/issues/2396) [PR #2423](https://github.com/PHPOffice/PhpSpreadsheet/pull/2423)
+- Fill pattern start and end colors [Issue #2441](https://github.com/PHPOffice/PhpSpreadsheet/issues/2441) [PR #2444](https://github.com/PHPOffice/PhpSpreadsheet/pull/2444)
+- General style specified in wrong case [Issue #2450](https://github.com/PHPOffice/PhpSpreadsheet/issues/2450) [PR #2451](https://github.com/PHPOffice/PhpSpreadsheet/pull/2451)
+- Null passed to `AutoFilter::setRange()` [Issue #2281](https://github.com/PHPOffice/PhpSpreadsheet/issues/2281) [PR #2454](https://github.com/PHPOffice/PhpSpreadsheet/pull/2454)
+- Another undefined index in Xls reader (#2470) [Issue #2463](https://github.com/PHPOffice/PhpSpreadsheet/issues/2463) [PR #2470](https://github.com/PHPOffice/PhpSpreadsheet/pull/2470)
+- Allow single-cell checks on conditional styles, even when the style is configured for a range of cells (#) [PR #2483](https://github.com/PHPOffice/PhpSpreadsheet/pull/2483)
+
+## 1.20.0 - 2021-11-23
+
+### Added
+
+- Xlsx Writer Support for WMF Files [#2339](https://github.com/PHPOffice/PhpSpreadsheet/issues/2339)
+- Use standard temporary file for internal use of HTMLPurifier [#2383](https://github.com/PHPOffice/PhpSpreadsheet/issues/2383)
+
+### Changed
+
+- Drop support for PHP 7.2, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support
+- Use native typing for objects that were already documented as such
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fixed null conversation for strToUpper [#2292](https://github.com/PHPOffice/PhpSpreadsheet/issues/2292)
+- Fixed Trying to access array offset on value of type null (Xls Reader) [#2315](https://github.com/PHPOffice/PhpSpreadsheet/issues/2315)
+- Don't corrupt XLSX files containing data validation [#2377](https://github.com/PHPOffice/PhpSpreadsheet/issues/2377)
+- Non-fixed cells were not updated if shared formula has a fixed cell [#2354](https://github.com/PHPOffice/PhpSpreadsheet/issues/2354)
+- Declare key of generic ArrayObject
+- CSV reader better support for boolean values [#2374](https://github.com/PHPOffice/PhpSpreadsheet/pull/2374)
+- Some ZIP file could not be read [#2376](https://github.com/PHPOffice/PhpSpreadsheet/pull/2376)
+- Fix regression were hyperlinks could not be read [#2391](https://github.com/PHPOffice/PhpSpreadsheet/pull/2391)
+- AutoFilter Improvements [#2393](https://github.com/PHPOffice/PhpSpreadsheet/pull/2393)
+- Don't corrupt file when using chart with fill color [#589](https://github.com/PHPOffice/PhpSpreadsheet/pull/589)
+- Restore imperfect array formula values in xlsx writer [#2343](https://github.com/PHPOffice/PhpSpreadsheet/pull/2343)
+- Restore explicit list of changes to PHPExcel migration document [#1546](https://github.com/PHPOffice/PhpSpreadsheet/issues/1546)
+
+## 1.19.0 - 2021-10-31
+
+### Added
+
+- Ability to set style on named range, and validate input to setSelectedCells [Issue #2279](https://github.com/PHPOffice/PhpSpreadsheet/issues/2279) [PR #2280](https://github.com/PHPOffice/PhpSpreadsheet/pull/2280)
+- Process comments in Sylk file [Issue #2276](https://github.com/PHPOffice/PhpSpreadsheet/issues/2276) [PR #2277](https://github.com/PHPOffice/PhpSpreadsheet/pull/2277)
+- Addition of Custom Properties to Ods Writer, and 32-bit-safe timestamps for Document Properties [PR #2113](https://github.com/PHPOffice/PhpSpreadsheet/pull/2113)
+- Added callback to CSV reader to set user-specified defaults for various properties (especially for escape which has a poor PHP-inherited default of backslash which does not correspond with Excel) [PR #2103](https://github.com/PHPOffice/PhpSpreadsheet/pull/2103)
+- Phase 1 of better namespace handling for Xlsx, resolving many open issues [PR #2173](https://github.com/PHPOffice/PhpSpreadsheet/pull/2173) [PR #2204](https://github.com/PHPOffice/PhpSpreadsheet/pull/2204) [PR #2303](https://github.com/PHPOffice/PhpSpreadsheet/pull/2303)
+- Add ability to extract images if source is a URL [Issue #1997](https://github.com/PHPOffice/PhpSpreadsheet/issues/1997) [PR #2072](https://github.com/PHPOffice/PhpSpreadsheet/pull/2072)
+- Support for passing flags in the Reader `load()` and Writer `save()`methods, and through the IOFactory, to set behaviours [PR #2136](https://github.com/PHPOffice/PhpSpreadsheet/pull/2136)
+ - See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/reading-and-writing-to-file/#readerwriter-flags) for details
+- More flexibility in the StringValueBinder to determine what datatypes should be treated as strings [PR #2138](https://github.com/PHPOffice/PhpSpreadsheet/pull/2138)
+- Helper class for conversion between css size Units of measure (`px`, `pt`, `pc`, `in`, `cm`, `mm`) [PR #2152](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145)
+- Allow Row height and Column Width to be set using different units of measure (`px`, `pt`, `pc`, `in`, `cm`, `mm`), rather than only in points or MS Excel column width units [PR #2152](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145)
+- Ability to stream to an Amazon S3 bucket [Issue #2249](https://github.com/PHPOffice/PhpSpreadsheet/issues/2249)
+- Provided a Size Helper class to validate size values (pt, px, em) [PR #1694](https://github.com/PHPOffice/PhpSpreadsheet/pull/1694)
+
+### Changed
+
+- Nothing.
+
+### Deprecated
+
+- PHP 8.1 will deprecate auto_detect_line_endings. As a result of this change, Csv Reader using some release after PHP8.1 will no longer be able to handle a Csv with Mac line endings.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- Unexpected format in Xlsx Timestamp [Issue #2331](https://github.com/PHPOffice/PhpSpreadsheet/issues/2331) [PR #2332](https://github.com/PHPOffice/PhpSpreadsheet/pull/2332)
+- Corrections for HLOOKUP [Issue #2123](https://github.com/PHPOffice/PhpSpreadsheet/issues/2123) [PR #2330](https://github.com/PHPOffice/PhpSpreadsheet/pull/2330)
+- Corrections for Xlsx Read Comments [Issue #2316](https://github.com/PHPOffice/PhpSpreadsheet/issues/2316) [PR #2329](https://github.com/PHPOffice/PhpSpreadsheet/pull/2329)
+- Lowercase Calibri font names [Issue #2273](https://github.com/PHPOffice/PhpSpreadsheet/issues/2273) [PR #2325](https://github.com/PHPOffice/PhpSpreadsheet/pull/2325)
+- isFormula Referencing Sheet with Space in Title [Issue #2304](https://github.com/PHPOffice/PhpSpreadsheet/issues/2304) [PR #2306](https://github.com/PHPOffice/PhpSpreadsheet/pull/2306)
+- Xls Reader Fatal Error due to Undefined Offset [Issue #1114](https://github.com/PHPOffice/PhpSpreadsheet/issues/1114) [PR #2308](https://github.com/PHPOffice/PhpSpreadsheet/pull/2308)
+- Permit Csv Reader delimiter to be set to null [Issue #2287](https://github.com/PHPOffice/PhpSpreadsheet/issues/2287) [PR #2288](https://github.com/PHPOffice/PhpSpreadsheet/pull/2288)
+- Csv Reader did not handle booleans correctly [PR #2232](https://github.com/PHPOffice/PhpSpreadsheet/pull/2232)
+- Problems when deleting sheet with local defined name [Issue #2266](https://github.com/PHPOffice/PhpSpreadsheet/issues/2266) [PR #2284](https://github.com/PHPOffice/PhpSpreadsheet/pull/2284)
+- Worksheet passwords were not always handled correctly [Issue #1897](https://github.com/PHPOffice/PhpSpreadsheet/issues/1897) [PR #2197](https://github.com/PHPOffice/PhpSpreadsheet/pull/2197)
+- Gnumeric Reader will now distinguish between Created and Modified timestamp [PR #2133](https://github.com/PHPOffice/PhpSpreadsheet/pull/2133)
+- Xls Reader will now handle MACCENTRALEUROPE with or without hyphen [Issue #549](https://github.com/PHPOffice/PhpSpreadsheet/issues/549) [PR #2213](https://github.com/PHPOffice/PhpSpreadsheet/pull/2213)
+- Tweaks to input file validation [Issue #1718](https://github.com/PHPOffice/PhpSpreadsheet/issues/1718) [PR #2217](https://github.com/PHPOffice/PhpSpreadsheet/pull/2217)
+- Html Reader did not handle comments correctly [Issue #2234](https://github.com/PHPOffice/PhpSpreadsheet/issues/2234) [PR #2235](https://github.com/PHPOffice/PhpSpreadsheet/pull/2235)
+- Apache OpenOffice Uses Unexpected Case for General format [Issue #2239](https://github.com/PHPOffice/PhpSpreadsheet/issues/2239) [PR #2242](https://github.com/PHPOffice/PhpSpreadsheet/pull/2242)
+- Problems with fraction formatting [Issue #2253](https://github.com/PHPOffice/PhpSpreadsheet/issues/2253) [PR #2254](https://github.com/PHPOffice/PhpSpreadsheet/pull/2254)
+- Xlsx Reader had problems reading file with no styles.xml or empty styles.xml [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246) [PR #2247](https://github.com/PHPOffice/PhpSpreadsheet/pull/2247)
+- Xlsx Reader did not read Data Validation flags correctly [Issue #2224](https://github.com/PHPOffice/PhpSpreadsheet/issues/2224) [PR #2225](https://github.com/PHPOffice/PhpSpreadsheet/pull/2225)
+- Better handling of empty arguments in Calculation engine [PR #2143](https://github.com/PHPOffice/PhpSpreadsheet/pull/2143)
+- Many fixes for Autofilter [Issue #2216](https://github.com/PHPOffice/PhpSpreadsheet/issues/2216) [PR #2141](https://github.com/PHPOffice/PhpSpreadsheet/pull/2141) [PR #2162](https://github.com/PHPOffice/PhpSpreadsheet/pull/2162) [PR #2218](https://github.com/PHPOffice/PhpSpreadsheet/pull/2218)
+- Locale generator will now use Unix line endings even on Windows [Issue #2172](https://github.com/PHPOffice/PhpSpreadsheet/issues/2172) [PR #2174](https://github.com/PHPOffice/PhpSpreadsheet/pull/2174)
+- Support differences in implementation of Text functions between Excel/Ods/Gnumeric [PR #2151](https://github.com/PHPOffice/PhpSpreadsheet/pull/2151)
+- Fixes to places where PHP8.1 enforces new or previously unenforced restrictions [PR #2137](https://github.com/PHPOffice/PhpSpreadsheet/pull/2137) [PR #2191](https://github.com/PHPOffice/PhpSpreadsheet/pull/2191) [PR #2231](https://github.com/PHPOffice/PhpSpreadsheet/pull/2231)
+- Clone for HashTable was incorrect [PR #2130](https://github.com/PHPOffice/PhpSpreadsheet/pull/2130)
+- Xlsx Reader was not evaluating Document Security Lock correctly [PR #2128](https://github.com/PHPOffice/PhpSpreadsheet/pull/2128)
+- Error in COUPNCD handling end of month [Issue #2116](https://github.com/PHPOffice/PhpSpreadsheet/issues/2116) [PR #2119](https://github.com/PHPOffice/PhpSpreadsheet/pull/2119)
+- Xls Writer Parser did not handle concatenation operator correctly [PR #2080](https://github.com/PHPOffice/PhpSpreadsheet/pull/2080)
+- Xlsx Writer did not handle boolean false correctly [Issue #2082](https://github.com/PHPOffice/PhpSpreadsheet/issues/2082) [PR #2087](https://github.com/PHPOffice/PhpSpreadsheet/pull/2087)
+- SUM needs to treat invalid strings differently depending on whether they come from a cell or are used as literals [Issue #2042](https://github.com/PHPOffice/PhpSpreadsheet/issues/2042) [PR #2045](https://github.com/PHPOffice/PhpSpreadsheet/pull/2045)
+- Html reader could have set illegal coordinates when dealing with embedded tables [Issue #2029](https://github.com/PHPOffice/PhpSpreadsheet/issues/2029) [PR #2032](https://github.com/PHPOffice/PhpSpreadsheet/pull/2032)
+- Documentation for printing gridlines was wrong [PR #2188](https://github.com/PHPOffice/PhpSpreadsheet/pull/2188)
+- Return Value Error - DatabaseAbstruct::buildQuery() return null but must be string [Issue #2158](https://github.com/PHPOffice/PhpSpreadsheet/issues/2158) [PR #2160](https://github.com/PHPOffice/PhpSpreadsheet/pull/2160)
+- Xlsx reader not recognize data validations that references another sheet [Issue #1432](https://github.com/PHPOffice/PhpSpreadsheet/issues/1432) [Issue #2149](https://github.com/PHPOffice/PhpSpreadsheet/issues/2149) [PR #2150](https://github.com/PHPOffice/PhpSpreadsheet/pull/2150) [PR #2265](https://github.com/PHPOffice/PhpSpreadsheet/pull/2265)
+- Don't calculate cell width for autosize columns if a cell contains a null or empty string value [Issue #2165](https://github.com/PHPOffice/PhpSpreadsheet/issues/2165) [PR #2167](https://github.com/PHPOffice/PhpSpreadsheet/pull/2167)
+- Allow negative interest rate values in a number of the Financial functions (`PPMT()`, `PMT()`, `FV()`, `PV()`, `NPER()`, etc) [Issue #2163](https://github.com/PHPOffice/PhpSpreadsheet/issues/2163) [PR #2164](https://github.com/PHPOffice/PhpSpreadsheet/pull/2164)
+- Xls Reader changing grey background to black in Excel template [Issue #2147](https://github.com/PHPOffice/PhpSpreadsheet/issues/2147) [PR #2156](https://github.com/PHPOffice/PhpSpreadsheet/pull/2156)
+- Column width and Row height styles in the Html Reader when the value includes a unit of measure [Issue #2145](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145).
+- Data Validation flags not set correctly when reading XLSX files [Issue #2224](https://github.com/PHPOffice/PhpSpreadsheet/issues/2224) [PR #2225](https://github.com/PHPOffice/PhpSpreadsheet/pull/2225)
+- Reading XLSX files without styles.xml throws an exception [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246)
+- Improved performance of `Style::applyFromArray()` when applied to several cells [PR #1785](https://github.com/PHPOffice/PhpSpreadsheet/issues/1785).
+- Improve XLSX parsing speed if no readFilter is applied (again) - [#772](https://github.com/PHPOffice/PhpSpreadsheet/issues/772)
+
+## 1.18.0 - 2021-05-31
+
+### Added
+
+- Enhancements to CSV Reader, allowing options to be set when using `IOFactory::load()` with a callback to set delimiter, enclosure, charset etc [PR #2103](https://github.com/PHPOffice/PhpSpreadsheet/pull/2103) - See [documentation](https://github.com/PHPOffice/PhpSpreadsheet/blob/master/docs/topics/reading-and-writing-to-file.md#csv-comma-separated-values) for details.
+- Implemented basic AutoFiltering for Ods Reader and Writer [PR #2053](https://github.com/PHPOffice/PhpSpreadsheet/pull/2053)
+- Implemented basic AutoFiltering for Gnumeric Reader [PR #2055](https://github.com/PHPOffice/PhpSpreadsheet/pull/2055)
+- Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028)
+- Implemented URLENCODE() Web Function
+- Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions.
+- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
+- Support for notContainsText Conditional Style in xlsx [Issue #984](https://github.com/PHPOffice/PhpSpreadsheet/issues/984)
+
+### Changed
+
+- Use of `nb` rather than `no` as the locale code for Norsk Bokmål.
+
+### Deprecated
+
+- All Excel Function implementations in `Calculation\Database`, `Calculation\DateTime`, `Calculation\Engineering`, `Calculation\Financial`, `Calculation\Logical`, `Calculation\LookupRef`, `Calculation\MathTrig`, `Calculation\Statistical`, `Calculation\TextData` and `Calculation\Web` have been moved to dedicated classes for individual functions or groups of related functions. See the docblocks against all the deprecated methods for details of the new methods to call instead. At some point, these old classes will be deleted.
+
+### Removed
+
+- Use of `nb` rather than `no` as the locale language code for Norsk Bokmål.
+
+### Fixed
+
+- Fixed error in COUPNCD() calculation for end of month [Issue #2116](https://github.com/PHPOffice/PhpSpreadsheet/issues/2116) - [PR #2119](https://github.com/PHPOffice/PhpSpreadsheet/pull/2119)
+- Resolve default values when a null argument is passed for HLOOKUP(), VLOOKUP() and ADDRESS() functions [Issue #2120](https://github.com/PHPOffice/PhpSpreadsheet/issues/2120) - [PR #2121](https://github.com/PHPOffice/PhpSpreadsheet/pull/2121)
+- Fixed incorrect R1C1 to A1 subtraction formula conversion (`R[-2]C-R[2]C`) [Issue #2076](https://github.com/PHPOffice/PhpSpreadsheet/pull/2076) [PR #2086](https://github.com/PHPOffice/PhpSpreadsheet/pull/2086)
+- Correctly handle absolute A1 references when converting to R1C1 format [PR #2060](https://github.com/PHPOffice/PhpSpreadsheet/pull/2060)
+- Correct default fill style for conditional without a pattern defined [Issue #2035](https://github.com/PHPOffice/PhpSpreadsheet/issues/2035) [PR #2050](https://github.com/PHPOffice/PhpSpreadsheet/pull/2050)
+- Fixed issue where array key check for existince before accessing arrays in Xlsx.php [PR #1970](https://github.com/PHPOffice/PhpSpreadsheet/pull/1970)
+- Fixed issue with quoted strings in number format mask rendered with toFormattedString() [Issue 1972#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) [PR #1978](https://github.com/PHPOffice/PhpSpreadsheet/pull/1978)
+- Fixed issue with percentage formats in number format mask rendered with toFormattedString() [Issue 1929#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #1928](https://github.com/PHPOffice/PhpSpreadsheet/pull/1928)
+- Fixed issue with _ spacing character in number format mask corrupting output from toFormattedString() [Issue 1924#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1924) [PR #1927](https://github.com/PHPOffice/PhpSpreadsheet/pull/1927)
+- Fix for [Issue #1887](https://github.com/PHPOffice/PhpSpreadsheet/issues/1887) - Lose Track of Selected Cells After Save
+- Fixed issue with Xlsx@listWorksheetInfo not returning any data
+- Fixed invalid arguments triggering mb_substr() error in LEFT(), MID() and RIGHT() text functions [Issue #640](https://github.com/PHPOffice/PhpSpreadsheet/issues/640)
+- Fix for [Issue #1916](https://github.com/PHPOffice/PhpSpreadsheet/issues/1916) - Invalid signature check for XML files
+- Fix change in `Font::setSize()` behavior for PHP8 [PR #2100](https://github.com/PHPOffice/PhpSpreadsheet/pull/2100)
+
+## 1.17.1 - 2021-03-01
+
+### Added
+
+- Implementation of the Excel `AVERAGEIFS()` functions as part of a restructuring of Database functions and Conditional Statistical functions.
+- Support for date values and percentages in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1875](https://github.com/PHPOffice/PhpSpreadsheet/pull/1875)
+- Support for booleans, and for wildcard text search in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1876](https://github.com/PHPOffice/PhpSpreadsheet/pull/1876)
+- Implemented DataBar for conditional formatting in Xlsx, providing read/write and creation of (type, value, direction, fills, border, axis position, color settings) as DataBar options in Excel. [#1754](https://github.com/PHPOffice/PhpSpreadsheet/pull/1754)
+- Alignment for ODS Writer [#1796](https://github.com/PHPOffice/PhpSpreadsheet/issues/1796)
+- Basic implementation of the PERMUTATIONA() Statistical Function
+
+### Changed
+
+- Formula functions that previously called PHP functions directly are now processed through the Excel Functions classes; resolving issues with PHP8 stricter typing. [#1789](https://github.com/PHPOffice/PhpSpreadsheet/issues/1789)
+
+ The following MathTrig functions are affected:
+ `ABS()`, `ACOS()`, `ACOSH()`, `ASIN()`, `ASINH()`, `ATAN()`, `ATANH()`,
+ `COS()`, `COSH()`, `DEGREES()` (rad2deg), `EXP()`, `LN()` (log), `LOG10()`,
+ `RADIANS()` (deg2rad), `SIN()`, `SINH()`, `SQRT()`, `TAN()`, `TANH()`.
+
+ One TextData function is also affected: `REPT()` (str_repeat).
+- `formatAsDate` correctly matches language metadata, reverting c55272e
+- Formulae that previously crashed on sub function call returning excel error value now return said value.
+ The following functions are affected `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`,
+ `AMORDEGRC()`.
+- Adapt some function error return value to match excel's error.
+ The following functions are affected `PPMT()`, `IPMT()`.
+
+### Deprecated
+
+- Calling many of the Excel formula functions directly rather than through the Calculation Engine.
+
+ The logic for these Functions is now being moved out of the categorised `Database`, `DateTime`, `Engineering`, `Financial`, `Logical`, `LookupRef`, `MathTrig`, `Statistical`, `TextData` and `Web` classes into small, dedicated classes for individual functions or related groups of functions.
+
+ This makes the logic in these classes easier to maintain; and will reduce the memory footprint required to execute formulae when calling these functions.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- Avoid Duplicate Titles When Reading Multiple HTML Files.[Issue #1823](https://github.com/PHPOffice/PhpSpreadsheet/issues/1823) [PR #1829](https://github.com/PHPOffice/PhpSpreadsheet/pull/1829)
+- Fixed issue with Worksheet's `getCell()` method when trying to get a cell by defined name. [#1858](https://github.com/PHPOffice/PhpSpreadsheet/issues/1858)
+- Fix possible endless loop in NumberFormat Masks [#1792](https://github.com/PHPOffice/PhpSpreadsheet/issues/1792)
+- Fix problem resulting from literal dot inside quotes in number format masks [PR #1830](https://github.com/PHPOffice/PhpSpreadsheet/pull/1830)
+- Resolve Google Sheets Xlsx charts issue. Google Sheets uses oneCellAnchor positioning and does not include *Cache values in the exported Xlsx [PR #1761](https://github.com/PHPOffice/PhpSpreadsheet/pull/1761)
+- Fix for Xlsx Chart axis titles mapping to correct X or Y axis label when only one is present [PR #1760](https://github.com/PHPOffice/PhpSpreadsheet/pull/1760)
+- Fix For Null Exception on ODS Read of Page Settings. [#1772](https://github.com/PHPOffice/PhpSpreadsheet/issues/1772)
+- Fix Xlsx reader overriding manually set number format with builtin number format [PR #1805](https://github.com/PHPOffice/PhpSpreadsheet/pull/1805)
+- Fix Xlsx reader cell alignment [PR #1710](https://github.com/PHPOffice/PhpSpreadsheet/pull/1710)
+- Fix for not yet implemented data-types in Open Document writer [Issue #1674](https://github.com/PHPOffice/PhpSpreadsheet/issues/1674)
+- Fix XLSX reader when having a corrupt numeric cell data type [PR #1664](https://github.com/phpoffice/phpspreadsheet/pull/1664)
+- Fix on `CUMPRINC()`, `CUMIPMT()`, `AMORLINC()`, `AMORDEGRC()` usage. When those functions called one of `YEARFRAC()`, `PPMT()`, `IPMT()` and they would get back an error value (represented as a string), trying to use numeral operands (`+`, `/`, `-`, `*`) on said return value and a number (`float or `int`) would fail.
+
+## 1.16.0 - 2020-12-31
+
+### Added
+
+- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647)
+
+### Changed
+
+- Updated the CONVERT() function to support all current MS Excel categories and Units of Measure.
+
+### Deprecated
+
+- All Excel Function implementations in `Calculation\Database`, `Calculation\DateTime`, `Calculation\Engineering`, `Calculation\Financial`, `Calculation\Logical`, `Calculation\LookupRef`, `Calculation\MathTrig`, `Calculation\Statistical`, `Calculation\TextData` and `Calculation\Web` have been moved to dedicated classes for individual functions or groups of related functions. See the docblocks against all the deprecated methods for details of the new methods to call instead. At some point, these old classes will be deleted.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- Fixed issue with absolute path in worksheets' Target [PR #1769](https://github.com/PHPOffice/PhpSpreadsheet/pull/1769)
+- Fix for Xls Reader when SST has a bad length [#1592](https://github.com/PHPOffice/PhpSpreadsheet/issues/1592)
+- Resolve Xlsx loader issue whe hyperlinks don't have a destination
+- Resolve issues when printer settings resources IDs clash with drawing IDs
+- Resolve issue with SLK long filenames [#1612](https://github.com/PHPOffice/PhpSpreadsheet/issues/1612)
+- ROUNDUP and ROUNDDOWN return incorrect results for values of 0 [#1627](https://github.com/phpoffice/phpspreadsheet/pull/1627)
+- Apply Column and Row Styles to Existing Cells [#1712](https://github.com/PHPOffice/PhpSpreadsheet/issues/1712) [PR #1721](https://github.com/PHPOffice/PhpSpreadsheet/pull/1721)
+- Resolve issues with defined names where worksheet doesn't exist (#1686)[https://github.com/PHPOffice/PhpSpreadsheet/issues/1686] and [#1723](https://github.com/PHPOffice/PhpSpreadsheet/issues/1723) - [PR #1742](https://github.com/PHPOffice/PhpSpreadsheet/pull/1742)
+- Fix for issue [#1735](https://github.com/PHPOffice/PhpSpreadsheet/issues/1735) Incorrect activeSheetIndex after RemoveSheetByIndex - [PR #1743](https://github.com/PHPOffice/PhpSpreadsheet/pull/1743)
+- Ensure that the list of shared formulae is maintained when an xlsx file is chunked with readFilter[Issue #169](https://github.com/PHPOffice/PhpSpreadsheet/issues/1669).
+- Fix for notice during accessing "cached magnification factor" offset [#1354](https://github.com/PHPOffice/PhpSpreadsheet/pull/1354)
+- Fix compatibility with ext-gd on php 8
+
+### Security Fix (CVE-2020-7776)
+
+- Prevent XSS through cell comments in the HTML Writer.
+
+## 1.15.0 - 2020-10-11
+
+### Added
+
+- Implemented Page Order for Xlsx and Xls Readers, and provided Page Settings (Orientation, Scale, Horizontal/Vertical Centering, Page Order, Margins) support for Ods, Gnumeric and Xls Readers [#1559](https://github.com/PHPOffice/PhpSpreadsheet/pull/1559)
+- Implementation of the Excel `LOGNORM.DIST()`, `NORM.S.DIST()`, `GAMMA()` and `GAUSS()` functions. [#1588](https://github.com/PHPOffice/PhpSpreadsheet/pull/1588)
+- Named formula implementation, and improved handling of Defined Names generally [#1535](https://github.com/PHPOffice/PhpSpreadsheet/pull/1535)
+ - Defined Names are now case-insensitive
+ - Distinction between named ranges and named formulae
+ - Correct handling of union and intersection operators in named ranges
+ - Correct evaluation of named range operators in calculations
+ - fix resolution of relative named range values in the calculation engine; previously all named range values had been treated as absolute.
+ - Calculation support for named formulae
+ - Support for nested ranges and formulae (named ranges and formulae that reference other named ranges/formulae) in calculations
+ - Introduction of a helper to convert address formats between R1C1 and A1 (and the reverse)
+ - Proper support for both named ranges and named formulae in all appropriate Readers
+ - **Xlsx** (Previously only simple named ranges were supported)
+ - **Xls** (Previously only simple named ranges were supported)
+ - **Gnumeric** (Previously neither named ranges nor formulae were supported)
+ - **Ods** (Previously neither named ranges nor formulae were supported)
+ - **Xml** (Previously neither named ranges nor formulae were supported)
+ - Proper support for named ranges and named formulae in all appropriate Writers
+ - **Xlsx** (Previously only simple named ranges were supported)
+ - **Xls** (Previously neither named ranges nor formulae were supported) - Still not supported, but some parser issues resolved that previously failed to differentiate between a defined name and a function name
+ - **Ods** (Previously neither named ranges nor formulae were supported)
+- Support for PHP 8.0
+
+### Changed
+
+- Improve Coverage for ODS Reader [#1545](https://github.com/phpoffice/phpspreadsheet/pull/1545)
+- Named formula implementation, and improved handling of Defined Names generally [#1535](https://github.com/PHPOffice/PhpSpreadsheet/pull/1535)
+- fix resolution of relative named range values in the calculation engine; previously all named range values had been treated as absolute.
+- Drop $this->spreadSheet null check from Xlsx Writer [#1646](https://github.com/phpoffice/phpspreadsheet/pull/1646)
+- Improving Coverage for Excel2003 XML Reader [#1557](https://github.com/phpoffice/phpspreadsheet/pull/1557)
+
+### Deprecated
+
+- **IMPORTANT NOTE:** This Introduces a **BC break** in the handling of named ranges. Previously, a named range cell reference of `B2` would be treated identically to a named range cell reference of `$B2` or `B$2` or `$B$2` because the calculation engine treated then all as absolute references. These changes "fix" that, so the calculation engine now handles relative references in named ranges correctly.
+ This change that resolves previously incorrect behaviour in the calculation may affect users who have dynamically defined named ranges using relative references when they should have used absolute references.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- PrintArea causes exception [#1544](https://github.com/phpoffice/phpspreadsheet/pull/1544)
+- Calculation/DateTime Failure With PHP8 [#1661](https://github.com/phpoffice/phpspreadsheet/pull/1661)
+- Reader/Gnumeric Failure with PHP8 [#1662](https://github.com/phpoffice/phpspreadsheet/pull/1662)
+- ReverseSort bug, exposed but not caused by PHP8 [#1660](https://github.com/phpoffice/phpspreadsheet/pull/1660)
+- Bug setting Superscript/Subscript to false [#1567](https://github.com/phpoffice/phpspreadsheet/pull/1567)
+
+## 1.14.1 - 2020-07-19
+
+### Added
+
+- nothing
+
+### Fixed
+
+- WEBSERVICE is HTTP client agnostic and must be configured via `Settings::setHttpClient()` [#1562](https://github.com/PHPOffice/PhpSpreadsheet/issues/1562)
+- Borders were not complete on rowspanned columns using HTML reader [#1473](https://github.com/PHPOffice/PhpSpreadsheet/pull/1473)
+
+### Changed
+
+## 1.14.0 - 2020-06-29
+
+### Added
+
+- Add support for IFS() logical function [#1442](https://github.com/PHPOffice/PhpSpreadsheet/pull/1442)
+- Add Cell Address Helper to provide conversions between the R1C1 and A1 address formats [#1558](https://github.com/PHPOffice/PhpSpreadsheet/pull/1558)
+- Add ability to edit Html/Pdf before saving [#1499](https://github.com/PHPOffice/PhpSpreadsheet/pull/1499)
+- Add ability to set codepage explicitly for BIFF5 [#1018](https://github.com/PHPOffice/PhpSpreadsheet/issues/1018)
+- Added support for the WEBSERVICE function [#1409](https://github.com/PHPOffice/PhpSpreadsheet/pull/1409)
+
+### Fixed
+
+- Resolve evaluation of utf-8 named ranges in calculation engine [#1522](https://github.com/PHPOffice/PhpSpreadsheet/pull/1522)
+- Fix HLOOKUP on single row [#1512](https://github.com/PHPOffice/PhpSpreadsheet/pull/1512)
+- Fix MATCH when comparing different numeric types [#1521](https://github.com/PHPOffice/PhpSpreadsheet/pull/1521)
+- Fix exact MATCH on ranges with empty cells [#1520](https://github.com/PHPOffice/PhpSpreadsheet/pull/1520)
+- Fix for Issue [#1516](https://github.com/PHPOffice/PhpSpreadsheet/issues/1516) (Cloning worksheet makes corrupted Xlsx) [#1530](https://github.com/PHPOffice/PhpSpreadsheet/pull/1530)
+- Fix For Issue [#1509](https://github.com/PHPOffice/PhpSpreadsheet/issues/1509) (Can not set empty enclosure for CSV) [#1518](https://github.com/PHPOffice/PhpSpreadsheet/pull/1518)
+- Fix for Issue [#1505](https://github.com/PHPOffice/PhpSpreadsheet/issues/1505) (TypeError : Argument 4 passed to PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeAttributeIf() must be of the type string) [#1525](https://github.com/PHPOffice/PhpSpreadsheet/pull/1525)
+- Fix for Issue [#1495](https://github.com/PHPOffice/PhpSpreadsheet/issues/1495) (Sheet index being changed when multiple sheets are used in formula) [#1500]((https://github.com/PHPOffice/PhpSpreadsheet/pull/1500))
+- Fix for Issue [#1533](https://github.com/PHPOffice/PhpSpreadsheet/issues/1533) (A reference to a cell containing a string starting with "#" leads to errors in the generated xlsx.) [#1534](https://github.com/PHPOffice/PhpSpreadsheet/pull/1534)
+- Xls Writer - Correct Timestamp Bug [#1493](https://github.com/PHPOffice/PhpSpreadsheet/pull/1493)
+- Don't ouput row and columns without any cells in HTML writer [#1235](https://github.com/PHPOffice/PhpSpreadsheet/issues/1235)
+
+## 1.13.0 - 2020-05-31
+
+### Added
+
+- Support writing to streams in all writers [#1292](https://github.com/PHPOffice/PhpSpreadsheet/issues/1292)
+- Support CSV files with data wrapping a lot of lines [#1468](https://github.com/PHPOffice/PhpSpreadsheet/pull/1468)
+- Support protection of worksheet by a specific hash algorithm [#1485](https://github.com/PHPOffice/PhpSpreadsheet/pull/1485)
+
+### Fixed
+
+- Fix Chart samples by updating chart parameter from 0 to DataSeries::EMPTY_AS_GAP [#1448](https://github.com/PHPOffice/PhpSpreadsheet/pull/1448)
+- Fix return type in docblock for the Cells::get() [#1398](https://github.com/PHPOffice/PhpSpreadsheet/pull/1398)
+- Fix RATE, PRICE, XIRR, and XNPV Functions [#1456](https://github.com/PHPOffice/PhpSpreadsheet/pull/1456)
+- Save Excel 2010+ functions properly in XLSX [#1461](https://github.com/PHPOffice/PhpSpreadsheet/pull/1461)
+- Several improvements in HTML writer [#1464](https://github.com/PHPOffice/PhpSpreadsheet/pull/1464)
+- Fix incorrect behaviour when saving XLSX file with drawings [#1462](https://github.com/PHPOffice/PhpSpreadsheet/pull/1462),
+- Fix Crash while trying setting a cell the value "123456\n" [#1476](https://github.com/PHPOffice/PhpSpreadsheet/pull/1481)
+- Improved DATEDIF() function and reduced errors for Y and YM units [#1466](https://github.com/PHPOffice/PhpSpreadsheet/pull/1466)
+- Stricter typing for mergeCells [#1494](https://github.com/PHPOffice/PhpSpreadsheet/pull/1494)
+
+### Changed
+
+- Drop support for PHP 7.1, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support
+- Drop partial migration tool in favor of complete migration via RectorPHP [#1445](https://github.com/PHPOffice/PhpSpreadsheet/issues/1445)
+- Limit composer package to `src/` [#1424](https://github.com/PHPOffice/PhpSpreadsheet/pull/1424)
+
+## 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)
+- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625)
+
+### 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).
+
+### Changed
+- Replace ezyang/htmlpurifier (LGPL2.1) with voku/anti-xss (MIT)
diff --git a/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md b/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
new file mode 100644
index 0000000..e89e99e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
@@ -0,0 +1,45 @@
+# Want to contribute?
+
+If you would like to contribute, here are some notes and guidelines:
+
+ - All new development should be on feature/fix branches, which are then merged to the `master` branch once stable and approved; so the `master` branch is always the most up-to-date, working code
+ - If you are going to submit a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
+ - The code must work with all PHP versions that we support.
+ - You can call `composer versions` to test version compatibility.
+ - Code style should be maintained.
+ - `composer style` will identify any issues with Coding Style`.
+ - `composer fix` will fix most issues with Coding Style.
+ - All code changes must be validated by `composer check`.
+ - Please include Unit Tests to verify that a bug exists, and that this PR fixes it.
+ - Please include Unit Tests to show that a new Feature works as expected.
+ - Please don't "bundle" several changes into a single PR; submit a PR for each discrete change/fix.
+ - Remember to update documentation if necessary.
+
+ - [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository")
+ - [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests")
+
+## Unit Tests
+
+When writing Unit Tests, please
+ - Always try to write Unit Tests for both the happy and unhappy paths.
+ - Put all assertions in the Test itself, not in an abstract class that the Test extends (even if this means code duplication between tests).
+ - Include any necessary `setup()` and `tearDown()` in the Test itself.
+ - If you change any global settings (such as system locale, or Compatibility Mode for Excel Function tests), make sure that you reset to the default in the `tearDown()`.
+ - Use the `ExcelError` functions in assertions for Excel Error values in Excel Function implementations.
+ Not only does it reduce the risk of typos; but at some point in the future, ExcelError values will be an object rather than a string, and we won't then need to update all the tests.
+ - Don't over-complicate test code by testing happy and unhappy paths in the same test.
+
+This makes it easier to see exactly what is being tested when reviewing the PR. I want to be able to see it in the PR, not have to hunt in other unchanged classes to see what the test is doing.
+
+## How to release
+
+1. Complete CHANGELOG.md and commit
+2. Create an annotated tag
+ 1. `git tag -a 1.2.3`
+ 2. Tag subject must be the version number, eg: `1.2.3`
+ 3. Tag body must be a copy-paste of the changelog entries.
+3. Push the tag with `git push --tags`, GitHub Actions will create a GitHub release automatically, and the release details will automatically be sent to packagist.
+4. Github seems to remove markdown headings in the Release Notes, so you should edit to restore these.
+
+> **Note:** Tagged releases are made from the `master` branch. Only in an emergency should a tagged release be made from the `release` branch. (i.e. cherry-picked hot-fixes.)
+
diff --git a/vendor/phpoffice/phpspreadsheet/LICENSE b/vendor/phpoffice/phpspreadsheet/LICENSE
new file mode 100644
index 0000000..3ec5723
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/LICENSE
@@ -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.
diff --git a/vendor/phpoffice/phpspreadsheet/README.md b/vendor/phpoffice/phpspreadsheet/README.md
new file mode 100644
index 0000000..b76b527
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/README.md
@@ -0,0 +1,144 @@
+# PhpSpreadsheet
+
+[](https://github.com/PHPOffice/PhpSpreadsheet/actions)
+[](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
+[](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
+[](https://packagist.org/packages/phpoffice/phpspreadsheet)
+[](https://packagist.org/packages/phpoffice/phpspreadsheet)
+[](https://packagist.org/packages/phpoffice/phpspreadsheet)
+[](https://gitter.im/PHPOffice/PhpSpreadsheet)
+
+PhpSpreadsheet is a library written in pure PHP and offers a set of classes that
+allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc.
+
+## PHP Version Support
+
+LTS: Support for PHP versions will only be maintained for a period of six months beyond the
+[end of life](https://www.php.net/supported-versions) of that PHP version.
+
+Currently the required PHP minimum version is PHP __8.0__, and we [will support that version](https://www.php.net/eol.php) until May 2024.
+
+See the `composer.json` for other requirements.
+
+## Installation
+
+Use [composer](https://getcomposer.org) to install PhpSpreadsheet into your project:
+
+```sh
+composer require phpoffice/phpspreadsheet
+```
+
+If you are building your installation on a development machine that is on a different PHP version to the server where it will be deployed, or if your PHP CLI version is not the same as your run-time such as `php-fpm` or Apache's `mod_php`, then you might want to add the following to your `composer.json` before installing:
+```json
+{
+ "require": {
+ "phpoffice/phpspreadsheet": "^1.28"
+ },
+ "config": {
+ "platform": {
+ "php": "8.0"
+ }
+ }
+}
+```
+and then run
+```sh
+composer install
+```
+to ensure that the correct dependencies are retrieved to match your deployment environment.
+
+See [CLI vs Application run-time](https://php.watch/articles/composer-platform-check) for more details.
+
+### Additional Installation Options
+
+If you want to write to PDF, or to include Charts when you write to HTML or PDF, then you will need to install additional libraries:
+
+#### PDF
+
+For PDF Generation, you can install any of the following, and then configure PhpSpreadsheet to indicate which library you are going to use:
+ - mpdf/mpdf
+ - dompdf/dompdf
+ - tecnickcom/tcpdf
+
+and configure PhpSpreadsheet using:
+
+```php
+// Dompdf, Mpdf or Tcpdf (as appropriate)
+$className = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class;
+IOFactory::registerWriter('Pdf', $className);
+```
+or the appropriate PDF Writer wrapper for the library that you have chosen to install.
+
+#### Chart Export
+
+For Chart export, we support following packages, which you will also need to install yourself using `composer require`
+ - [jpgraph/jpgraph](https://packagist.org/packages/jpgraph/jpgraph) (this package was abandoned at version 4.0.
+ You can manually download the latest version that supports PHP 8 and above from [jpgraph.net](https://jpgraph.net/))
+ - [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) - up to date fork with modern PHP versions support and some bugs fixed.
+
+and then configure PhpSpreadsheet using:
+```php
+// to use jpgraph/jpgraph
+Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph::class);
+//or
+// to use mitoteam/jpgraph
+Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer::class);
+```
+
+One or the other of these libraries is necessary if you want to generate HTML or PDF files that include charts; or to render a Chart to an Image format from within your code.
+They are not necessary to define charts for writing to `Xlsx` files.
+Other file formats don't support writing Charts.
+
+## Documentation
+
+Read more about it, including install instructions, in the [official documentation](https://phpspreadsheet.readthedocs.io). Or check out the [API documentation](https://phpoffice.github.io/PhpSpreadsheet).
+
+Please ask your support questions on [StackOverflow](https://stackoverflow.com/questions/tagged/phpspreadsheet), or have a quick chat on [Gitter](https://gitter.im/PHPOffice/PhpSpreadsheet).
+
+## Patreon
+
+I am now running a [Patreon](https://www.patreon.com/MarkBaker) to support the work that I do on PhpSpreadsheet.
+
+Supporters will receive access to articles about working with PhpSpreadsheet, and how to use some of its more advanced features.
+
+Posts already available to Patreon supporters:
+ - The Dating Game
+ - A look at how MS Excel (and PhpSpreadsheet) handle date and time values.
+- Looping the Loop
+ - Advice on Iterating through the rows and cells in a worksheet.
+
+And for Patrons at levels actively using PhpSpreadsheet:
+ - Behind the Mask
+ - A look at Number Format Masks.
+
+The Next Article (currently Work in Progress):
+ - Formula for Success
+ - How to debug formulae that don't produce the expected result.
+
+
+My aim is to post at least one article each month, taking a detailed look at some feature of MS Excel and how to use that feature in PhpSpreadsheet, or on how to perform different activities in PhpSpreadsheet.
+
+Planned posts for the future include topics like:
+ - Tables
+ - Structured References
+ - AutoFiltering
+ - Array Formulae
+ - Conditional Formatting
+ - Data Validation
+ - Value Binders
+ - Images
+ - Charts
+
+After a period of six months exclusive to Patreon supporters, articles will be incorporated into the public documentation for the library.
+
+## PHPExcel vs PhpSpreadsheet ?
+
+PhpSpreadsheet is the next version of PHPExcel. It breaks compatibility to dramatically improve the code base quality (namespaces, PSR compliance, use of latest PHP language features, etc.).
+
+Because all efforts have shifted to PhpSpreadsheet, PHPExcel will no longer be maintained. All contributions for PHPExcel, patches and new features, should target PhpSpreadsheet `master` branch.
+
+Do you need to migrate? There is [an automated tool](/docs/topics/migration-from-PHPExcel.md) for that.
+
+## License
+
+PhpSpreadsheet is licensed under [MIT](https://github.com/PHPOffice/PhpSpreadsheet/blob/master/LICENSE).
diff --git a/vendor/phpoffice/phpspreadsheet/composer.json b/vendor/phpoffice/phpspreadsheet/composer.json
new file mode 100644
index 0000000..feec991
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/composer.json
@@ -0,0 +1,121 @@
+{
+ "name": "phpoffice/phpspreadsheet",
+ "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+ "keywords": [
+ "PHP",
+ "OpenXML",
+ "Excel",
+ "xlsx",
+ "xls",
+ "ods",
+ "gnumeric",
+ "spreadsheet"
+ ],
+ "config": {
+ "platform": {
+ "php" : "8.0.99"
+ },
+ "sort-packages": true,
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ },
+ "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": [
+ "./bin/check-phpdoc-types",
+ "phpcs src/ tests/ --report=checkstyle",
+ "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 8.0- -n",
+ "php-cs-fixer fix --ansi --dry-run --diff",
+ "phpunit --color=always",
+ "phpstan analyse --ansi --memory-limit=2048M"
+ ],
+ "style": [
+ "phpcs src/ tests/ --report=checkstyle",
+ "php-cs-fixer fix --ansi --dry-run --diff"
+ ],
+ "fix": [
+ "phpcbf src/ tests/ --report=checkstyle",
+ "php-cs-fixer fix"
+ ],
+ "versions": [
+ "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 8.0- -n"
+ ]
+ },
+ "require": {
+ "php": "^8.0",
+ "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": "*",
+ "maennchen/zipstream-php": "^2.1 || ^3.0",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
+ "voku/anti-xss": "^4.1"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+ "dompdf/dompdf": "^2.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "mitoteam/jpgraph": "^10.3",
+ "mpdf/mpdf": "^8.1.1",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.6",
+ "squizlabs/php_codesniffer": "^3.7",
+ "tecnickcom/tcpdf": "^6.5"
+ },
+ "suggest": {
+ "ext-intl": "PHP Internationalization Functions",
+ "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",
+ "mitoteam/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",
+ "PhpOffice\\PhpSpreadsheetInfra\\": "infra"
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon b/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon
new file mode 100644
index 0000000..c976234
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon
@@ -0,0 +1,6 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Binary operation \"/\" between float and array\\|float\\|int\\|string results in an error\\.$#"
+ count: 1
+ path: src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
diff --git a/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist b/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist
new file mode 100644
index 0000000..de87870
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist
@@ -0,0 +1,28 @@
+includes:
+ - phpstan-baseline.neon
+ - vendor/phpstan/phpstan-phpunit/extension.neon
+ - vendor/phpstan/phpstan-phpunit/rules.neon
+
+parameters:
+ level: 8
+ paths:
+ - src/
+ - tests/
+ - samples/
+ - infra/
+ - bin/generate-document
+ - bin/generate-locales
+ excludePaths:
+ - src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
+ - src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
+ - src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php
+ - src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
+ - src/PhpSpreadsheet/Writer/ZipStream2.php
+ - src/PhpSpreadsheet/Writer/ZipStream3.php
+ parallel:
+ processTimeout: 300.0
+ checkMissingIterableValueType: false
+ ignoreErrors:
+ # Accept a bit anything for assert methods
+ - '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~'
+ - '~^Variable \$helper might not be defined\.$~'
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php
new file mode 100644
index 0000000..e20203a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php
@@ -0,0 +1,122 @@
+initialise(($arguments === false) ? [] : $arguments);
+ }
+
+ /**
+ * Handles array argument processing when the function accepts a single argument that can be an array argument.
+ * Example use for:
+ * DAYOFMONTH() or FACT().
+ */
+ protected static function evaluateSingleArgumentArray(callable $method, array $values): array
+ {
+ $result = [];
+ foreach ($values as $value) {
+ $result[] = $method($value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * and any of them can be an array argument.
+ * Example use for:
+ * ROUND() or DATE().
+ */
+ protected static function evaluateArrayArguments(callable $method, mixed ...$arguments): array
+ {
+ self::initialiseHelper($arguments);
+ $arguments = self::$arrayArgumentHelper->arguments();
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * but only the first few (up to limit) can be an array arguments.
+ * Example use for:
+ * NETWORKDAYS() or CONCATENATE(), where the last argument is a matrix (or a series of values) that need
+ * to be treated as a such rather than as an array arguments.
+ */
+ protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, mixed ...$arguments): array
+ {
+ self::initialiseHelper(array_slice($arguments, 0, $limit));
+ $trailingArguments = array_slice($arguments, $limit);
+ $arguments = self::$arrayArgumentHelper->arguments();
+ $arguments = array_merge($arguments, $trailingArguments);
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+
+ private static function testFalse(mixed $value): bool
+ {
+ return $value === false;
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * but only the last few (from start) can be an array arguments.
+ * Example use for:
+ * Z.TEST() or INDEX(), where the first argument 1 is a matrix that needs to be treated as a dataset
+ * rather than as an array argument.
+ */
+ protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, mixed ...$arguments): array
+ {
+ $arrayArgumentsSubset = array_combine(
+ range($start, count($arguments) - $start),
+ array_slice($arguments, $start)
+ );
+ if (self::testFalse($arrayArgumentsSubset)) {
+ return ['#VALUE!'];
+ }
+
+ self::initialiseHelper($arrayArgumentsSubset);
+ $leadingArguments = array_slice($arguments, 0, $start);
+ $arguments = self::$arrayArgumentHelper->arguments();
+ $arguments = array_merge($leadingArguments, $arguments);
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+
+ /**
+ * Handles array argument processing when the function accepts multiple arguments,
+ * and any of them can be an array argument except for the one specified by ignore.
+ * Example use for:
+ * HLOOKUP() and VLOOKUP(), where argument 1 is a matrix that needs to be treated as a database
+ * rather than as an array argument.
+ */
+ protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, mixed ...$arguments): array
+ {
+ $leadingArguments = array_slice($arguments, 0, $ignore);
+ $ignoreArgument = array_slice($arguments, $ignore, 1);
+ $trailingArguments = array_slice($arguments, $ignore + 1);
+
+ self::initialiseHelper(array_merge($leadingArguments, [[null]], $trailingArguments));
+ $arguments = self::$arrayArgumentHelper->arguments();
+
+ array_splice($arguments, $ignore, 1, $ignoreArgument);
+
+ return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php
new file mode 100644
index 0000000..6b6dd97
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php
@@ -0,0 +1,136 @@
+ '' && $operand1[0] == Calculation::FORMULA_STRING_QUOTE) {
+ $operand1 = Calculation::unwrapResult($operand1);
+ }
+ if (is_string($operand2) && $operand2 > '' && $operand2[0] == Calculation::FORMULA_STRING_QUOTE) {
+ $operand2 = Calculation::unwrapResult($operand2);
+ }
+
+ // Use case insensitive comparaison if not OpenOffice mode
+ if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
+ if (is_string($operand1)) {
+ $operand1 = StringHelper::strToUpper($operand1);
+ }
+ if (is_string($operand2)) {
+ $operand2 = StringHelper::strToUpper($operand2);
+ }
+ }
+
+ $useLowercaseFirstComparison = is_string($operand1)
+ && is_string($operand2)
+ && Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
+
+ return self::evaluateComparison($operand1, $operand2, $operator, $useLowercaseFirstComparison);
+ }
+
+ private static function evaluateComparison(mixed $operand1, mixed $operand2, string $operator, bool $useLowercaseFirstComparison): bool
+ {
+ return match ($operator) {
+ '=' => self::equal($operand1, $operand2),
+ '>' => self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison),
+ '<' => self::lessThan($operand1, $operand2, $useLowercaseFirstComparison),
+ '>=' => self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
+ '<=' => self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
+ '<>' => self::notEqual($operand1, $operand2),
+ default => throw new Exception('Unsupported binary comparison operator'),
+ };
+ }
+
+ private static function equal(mixed $operand1, mixed $operand2): bool
+ {
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = (abs($operand1 - $operand2) < self::DELTA);
+ } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+ $result = $operand1 == $operand2;
+ } else {
+ $result = self::strcmpAllowNull($operand1, $operand2) == 0;
+ }
+
+ return $result;
+ }
+
+ private static function greaterThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 > $operand2));
+ } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+ $result = $operand1 >= $operand2;
+ } elseif ($useLowercaseFirstComparison) {
+ $result = self::strcmpLowercaseFirst($operand1, $operand2) >= 0;
+ } else {
+ $result = self::strcmpAllowNull($operand1, $operand2) >= 0;
+ }
+
+ return $result;
+ }
+
+ private static function lessThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 < $operand2));
+ } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+ $result = $operand1 <= $operand2;
+ } elseif ($useLowercaseFirstComparison) {
+ $result = self::strcmpLowercaseFirst($operand1, $operand2) <= 0;
+ } else {
+ $result = self::strcmpAllowNull($operand1, $operand2) <= 0;
+ }
+
+ return $result;
+ }
+
+ private static function greaterThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
+ }
+
+ private static function lessThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
+ {
+ return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
+ }
+
+ private static function notEqual(mixed $operand1, mixed $operand2): bool
+ {
+ return self::equal($operand1, $operand2) !== true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
new file mode 100644
index 0000000..03800f0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
@@ -0,0 +1,5759 @@
+=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
+ // Cell reference (with or without a sheet reference) ensuring absolute/relative
+ const CALCULATION_REGEXP_CELLREF_RELATIVE = '((([^\s\(,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?(\$?\b[a-z]{1,3})(\$?\d{1,7})(?![\w.])';
+ const CALCULATION_REGEXP_COLUMN_RANGE = '(((([^\s\(,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\".(?:[^\"]|\"[^!])?\"))!)?(\$?[a-z]{1,3})):(?![.*])';
+ const CALCULATION_REGEXP_ROW_RANGE = '(((([^\s\(,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?(\$?[1-9][0-9]{0,6})):(?![.*])';
+ // Cell reference (with or without a sheet reference) ensuring absolute/relative
+ // Cell ranges ensuring absolute/relative
+ const CALCULATION_REGEXP_COLUMNRANGE_RELATIVE = '(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
+ const CALCULATION_REGEXP_ROWRANGE_RELATIVE = '(\$?\d{1,7}):(\$?\d{1,7})';
+ // Defined Names: Named Range of cells, or Named Formulae
+ const CALCULATION_REGEXP_DEFINEDNAME = '((([^\s,!&%^\/\*\+<>=-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?([_\p{L}][_\p{L}\p{N}\.]*)';
+ // Structured Reference (Fully Qualified and Unqualified)
+ const CALCULATION_REGEXP_STRUCTURED_REFERENCE = '([\p{L}_\\\\][\p{L}\p{N}\._]+)?(\[(?:[^\d\]+-])?)';
+ // Error
+ const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?';
+
+ /** constants */
+ const RETURN_ARRAY_AS_ERROR = 'error';
+ const RETURN_ARRAY_AS_VALUE = 'value';
+ const RETURN_ARRAY_AS_ARRAY = 'array';
+
+ const FORMULA_OPEN_FUNCTION_BRACE = '(';
+ const FORMULA_CLOSE_FUNCTION_BRACE = ')';
+ const FORMULA_OPEN_MATRIX_BRACE = '{';
+ const FORMULA_CLOSE_MATRIX_BRACE = '}';
+ const FORMULA_STRING_QUOTE = '"';
+
+ /** @var string */
+ private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
+
+ /**
+ * Instance of this class.
+ *
+ * @var ?Calculation
+ */
+ private static $instance;
+
+ /**
+ * Instance of the spreadsheet this Calculation Engine is using.
+ */
+ private ?Spreadsheet $spreadsheet;
+
+ /**
+ * Calculation cache.
+ *
+ * @var array
+ */
+ private $calculationCache = [];
+
+ /**
+ * Calculation cache enabled.
+ *
+ * @var bool
+ */
+ private $calculationCacheEnabled = true;
+
+ private BranchPruner $branchPruner;
+
+ /**
+ * @var bool
+ */
+ 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.
+ */
+ private const CALCULATION_OPERATORS = [
+ '+' => true, '-' => true, '*' => true, '/' => true,
+ '^' => true, '&' => true, '%' => false, '~' => false,
+ '>' => true, '<' => true, '=' => true, '>=' => true,
+ '<=' => true, '<>' => true, '∩' => true, '∪' => true,
+ ':' => true,
+ ];
+
+ /**
+ * List of binary operators (those that expect two operands).
+ */
+ private const BINARY_OPERATORS = [
+ '+' => true, '-' => true, '*' => true, '/' => true,
+ '^' => true, '&' => true, '>' => true, '<' => true,
+ '=' => true, '>=' => true, '<=' => true, '<>' => true,
+ '∩' => true, '∪' => true, ':' => true,
+ ];
+
+ /**
+ * The debug log generated by the calculation engine.
+ */
+ private Logger $debugLog;
+
+ /**
+ * Flag to determine how formula errors should be handled
+ * If true, then a user error will be triggered
+ * If false, then an exception will be thrown.
+ *
+ * @var ?bool
+ *
+ * @deprecated 1.25.2 use setSuppressFormulaErrors() instead
+ */
+ public $suppressFormulaErrors;
+
+ /** @var bool */
+ private $suppressFormulaErrorsNew = false;
+
+ /**
+ * Error message for any error that was raised/thrown by the calculation engine.
+ *
+ * @var null|string
+ */
+ public $formulaError;
+
+ /**
+ * Reference Helper.
+ */
+ private static ReferenceHelper $referenceHelper;
+
+ /**
+ * An array of the nested cell references accessed by the calculation engine, used for the debug log.
+ */
+ private CyclicReferenceStack $cyclicReferenceStack;
+
+ /** @var array */
+ private $cellStack = [];
+
+ /**
+ * Current iteration counter for cyclic formulae
+ * If the value is 0 (or less) then cyclic formulae will throw an exception,
+ * otherwise they will iterate to the limit defined here before returning a result.
+ *
+ * @var int
+ */
+ private $cyclicFormulaCounter = 1;
+
+ /** @var string */
+ private $cyclicFormulaCell = '';
+
+ /**
+ * Number of iterations for cyclic formulae.
+ *
+ * @var int
+ */
+ public $cyclicFormulaCount = 1;
+
+ /**
+ * The current locale setting.
+ *
+ * @var string
+ */
+ private static $localeLanguage = 'en_us'; // US English (default locale)
+
+ /**
+ * List of available locale settings
+ * Note that this is read for the locale subdirectory only when requested.
+ *
+ * @var string[]
+ */
+ private static $validLocaleLanguages = [
+ 'en', // English (default language)
+ ];
+
+ /**
+ * Locale-specific argument separator for function arguments.
+ *
+ * @var string
+ */
+ private static $localeArgumentSeparator = ',';
+
+ /** @var array */
+ private static $localeFunctions = [];
+
+ /**
+ * Locale-specific translations for Excel constants (True, False and Null).
+ *
+ * @var array
+ */
+ private static $localeBoolean = [
+ 'TRUE' => 'TRUE',
+ 'FALSE' => 'FALSE',
+ 'NULL' => 'NULL',
+ ];
+
+ public static function getLocaleBoolean(string $index): string
+ {
+ return self::$localeBoolean[$index];
+ }
+
+ /**
+ * Excel constant string translations to their PHP equivalents
+ * Constant conversion from text name/value to actual (datatyped) value.
+ *
+ * @var array
+ */
+ private static $excelConstants = [
+ 'TRUE' => true,
+ 'FALSE' => false,
+ 'NULL' => null,
+ ];
+
+ public static function keyInExcelConstants(string $key): bool
+ {
+ return array_key_exists($key, self::$excelConstants);
+ }
+
+ /** @return mixed */
+ public static function getExcelConstants(string $key)
+ {
+ return self::$excelConstants[$key];
+ }
+
+ /**
+ * Array of functions usable on Spreadsheet.
+ * In theory, this could be const rather than static;
+ * however, Phpstan breaks trying to analyze it when attempted.
+ *
+ *@var array
+ */
+ private static $phpSpreadsheetFunctions = [
+ 'ABS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Absolute::class, 'evaluate'],
+ 'argumentCount' => '1',
+ ],
+ 'ACCRINT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\AccruedInterest::class, 'periodic'],
+ 'argumentCount' => '4-8',
+ ],
+ 'ACCRINTM' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\AccruedInterest::class, 'atMaturity'],
+ 'argumentCount' => '3-5',
+ ],
+ 'ACOS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'acos'],
+ 'argumentCount' => '1',
+ ],
+ 'ACOSH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'acosh'],
+ 'argumentCount' => '1',
+ ],
+ 'ACOT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'acot'],
+ 'argumentCount' => '1',
+ ],
+ 'ACOTH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'acoth'],
+ 'argumentCount' => '1',
+ ],
+ 'ADDRESS' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Address::class, 'cell'],
+ 'argumentCount' => '2-5',
+ ],
+ 'AGGREGATE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3+',
+ ],
+ 'AMORDEGRC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Amortization::class, 'AMORDEGRC'],
+ 'argumentCount' => '6,7',
+ ],
+ 'AMORLINC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Amortization::class, 'AMORLINC'],
+ 'argumentCount' => '6,7',
+ ],
+ 'ANCHORARRAY' => [
+ 'category' => Category::CATEGORY_UNCATEGORISED,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'AND' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Operations::class, 'logicalAnd'],
+ 'argumentCount' => '1+',
+ ],
+ 'ARABIC' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Arabic::class, 'evaluate'],
+ 'argumentCount' => '1',
+ ],
+ 'AREAS' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'ARRAYTOTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'fromArray'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ASC' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'ASIN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'asin'],
+ 'argumentCount' => '1',
+ ],
+ 'ASINH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'asinh'],
+ 'argumentCount' => '1',
+ ],
+ 'ATAN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atan'],
+ 'argumentCount' => '1',
+ ],
+ 'ATAN2' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atan2'],
+ 'argumentCount' => '2',
+ ],
+ 'ATANH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'atanh'],
+ 'argumentCount' => '1',
+ ],
+ 'AVEDEV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages::class, 'averageDeviations'],
+ 'argumentCount' => '1+',
+ ],
+ 'AVERAGE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages::class, 'average'],
+ 'argumentCount' => '1+',
+ ],
+ 'AVERAGEA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages::class, 'averageA'],
+ 'argumentCount' => '1+',
+ ],
+ 'AVERAGEIF' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Conditional::class, 'AVERAGEIF'],
+ 'argumentCount' => '2,3',
+ ],
+ 'AVERAGEIFS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Conditional::class, 'AVERAGEIFS'],
+ 'argumentCount' => '3+',
+ ],
+ 'BAHTTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'BASE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Base::class, 'evaluate'],
+ 'argumentCount' => '2,3',
+ ],
+ 'BESSELI' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BesselI::class, 'BESSELI'],
+ 'argumentCount' => '2',
+ ],
+ 'BESSELJ' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BesselJ::class, 'BESSELJ'],
+ 'argumentCount' => '2',
+ ],
+ 'BESSELK' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BesselK::class, 'BESSELK'],
+ 'argumentCount' => '2',
+ ],
+ 'BESSELY' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BesselY::class, 'BESSELY'],
+ 'argumentCount' => '2',
+ ],
+ 'BETADIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'distribution'],
+ 'argumentCount' => '3-5',
+ ],
+ 'BETA.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4-6',
+ ],
+ 'BETAINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'],
+ 'argumentCount' => '3-5',
+ ],
+ 'BETA.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'],
+ 'argumentCount' => '3-5',
+ ],
+ 'BIN2DEC' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toDecimal'],
+ 'argumentCount' => '1',
+ ],
+ 'BIN2HEX' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toHex'],
+ 'argumentCount' => '1,2',
+ ],
+ 'BIN2OCT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertBinary::class, 'toOctal'],
+ 'argumentCount' => '1,2',
+ ],
+ 'BINOMDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'BINOM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'BINOM.DIST.RANGE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'range'],
+ 'argumentCount' => '3,4',
+ ],
+ 'BINOM.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'BITAND' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BitWise::class, 'BITAND'],
+ 'argumentCount' => '2',
+ ],
+ 'BITOR' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BitWise::class, 'BITOR'],
+ 'argumentCount' => '2',
+ ],
+ 'BITXOR' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BitWise::class, 'BITXOR'],
+ 'argumentCount' => '2',
+ ],
+ 'BITLSHIFT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BitWise::class, 'BITLSHIFT'],
+ 'argumentCount' => '2',
+ ],
+ 'BITRSHIFT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\BitWise::class, 'BITRSHIFT'],
+ 'argumentCount' => '2',
+ ],
+ 'BYCOL' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'BYROW' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'CEILING' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Ceiling::class, 'ceiling'],
+ 'argumentCount' => '1-2', // 2 for Excel, 1-2 for Ods/Gnumeric
+ ],
+ 'CEILING.MATH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Ceiling::class, 'math'],
+ 'argumentCount' => '1-3',
+ ],
+ 'CEILING.PRECISE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Ceiling::class, 'precise'],
+ 'argumentCount' => '1,2',
+ ],
+ 'CELL' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1,2',
+ ],
+ 'CHAR' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CharacterConvert::class, 'character'],
+ 'argumentCount' => '1',
+ ],
+ 'CHIDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionLeftTail'],
+ 'argumentCount' => '3',
+ ],
+ 'CHISQ.DIST.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHIINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseLeftTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.INV.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
+ 'argumentCount' => '2',
+ ],
+ 'CHITEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'test'],
+ 'argumentCount' => '2',
+ ],
+ 'CHISQ.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'test'],
+ 'argumentCount' => '2',
+ ],
+ 'CHOOSE' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Selection::class, 'CHOOSE'],
+ 'argumentCount' => '2+',
+ ],
+ 'CHOOSECOLS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2+',
+ ],
+ 'CHOOSEROWS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2+',
+ ],
+ 'CLEAN' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Trim::class, 'nonPrintable'],
+ 'argumentCount' => '1',
+ ],
+ 'CODE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CharacterConvert::class, 'code'],
+ 'argumentCount' => '1',
+ ],
+ 'COLUMN' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMN'],
+ 'argumentCount' => '-1',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
+ ],
+ 'COLUMNS' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMNS'],
+ 'argumentCount' => '1',
+ ],
+ 'COMBIN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Combinations::class, 'withoutRepetition'],
+ 'argumentCount' => '2',
+ ],
+ 'COMBINA' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Combinations::class, 'withRepetition'],
+ 'argumentCount' => '2',
+ ],
+ 'COMPLEX' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Complex::class, 'COMPLEX'],
+ 'argumentCount' => '2,3',
+ ],
+ 'CONCAT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Concatenate::class, 'CONCATENATE'],
+ 'argumentCount' => '1+',
+ ],
+ 'CONCATENATE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Concatenate::class, 'CONCATENATE'],
+ 'argumentCount' => '1+',
+ ],
+ 'CONFIDENCE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
+ 'argumentCount' => '3',
+ ],
+ 'CONFIDENCE.NORM' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
+ 'argumentCount' => '3',
+ ],
+ 'CONFIDENCE.T' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'CONVERT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertUOM::class, 'CONVERT'],
+ 'argumentCount' => '3',
+ ],
+ 'CORREL' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'CORREL'],
+ 'argumentCount' => '2',
+ ],
+ 'COS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'cos'],
+ 'argumentCount' => '1',
+ ],
+ 'COSH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cosine::class, 'cosh'],
+ 'argumentCount' => '1',
+ ],
+ 'COT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'cot'],
+ 'argumentCount' => '1',
+ ],
+ 'COTH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cotangent::class, 'coth'],
+ 'argumentCount' => '1',
+ ],
+ 'COUNT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Counts::class, 'COUNT'],
+ 'argumentCount' => '1+',
+ ],
+ 'COUNTA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Counts::class, 'COUNTA'],
+ 'argumentCount' => '1+',
+ ],
+ 'COUNTBLANK' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Counts::class, 'COUNTBLANK'],
+ 'argumentCount' => '1',
+ ],
+ 'COUNTIF' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Conditional::class, 'COUNTIF'],
+ 'argumentCount' => '2',
+ ],
+ 'COUNTIFS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Conditional::class, 'COUNTIFS'],
+ 'argumentCount' => '2+',
+ ],
+ 'COUPDAYBS' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYBS'],
+ 'argumentCount' => '3,4',
+ ],
+ 'COUPDAYS' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYS'],
+ 'argumentCount' => '3,4',
+ ],
+ 'COUPDAYSNC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Coupons::class, 'COUPDAYSNC'],
+ 'argumentCount' => '3,4',
+ ],
+ 'COUPNCD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Coupons::class, 'COUPNCD'],
+ 'argumentCount' => '3,4',
+ ],
+ 'COUPNUM' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Coupons::class, 'COUPNUM'],
+ 'argumentCount' => '3,4',
+ ],
+ 'COUPPCD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Coupons::class, 'COUPPCD'],
+ 'argumentCount' => '3,4',
+ ],
+ 'COVAR' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'COVAR'],
+ 'argumentCount' => '2',
+ ],
+ 'COVARIANCE.P' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'COVAR'],
+ 'argumentCount' => '2',
+ ],
+ 'COVARIANCE.S' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'CRITBINOM' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'CSC' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cosecant::class, 'csc'],
+ 'argumentCount' => '1',
+ ],
+ 'CSCH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Cosecant::class, 'csch'],
+ 'argumentCount' => '1',
+ ],
+ 'CUBEKPIMEMBER' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUBEMEMBER' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUBEMEMBERPROPERTY' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUBERANKEDMEMBER' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUBESET' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUBESETCOUNT' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUBEVALUE' => [
+ 'category' => Category::CATEGORY_CUBE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'CUMIPMT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Cumulative::class, 'interest'],
+ 'argumentCount' => '6',
+ ],
+ 'CUMPRINC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Cumulative::class, 'principal'],
+ 'argumentCount' => '6',
+ ],
+ 'DATE' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Date::class, 'fromYMD'],
+ 'argumentCount' => '3',
+ ],
+ 'DATEDIF' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Difference::class, 'interval'],
+ 'argumentCount' => '2,3',
+ ],
+ 'DATESTRING' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'DATEVALUE' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\DateValue::class, 'fromString'],
+ 'argumentCount' => '1',
+ ],
+ 'DAVERAGE' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DAverage::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DAY' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'day'],
+ 'argumentCount' => '1',
+ ],
+ 'DAYS' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Days::class, 'between'],
+ 'argumentCount' => '2',
+ ],
+ 'DAYS360' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Days360::class, 'between'],
+ 'argumentCount' => '2,3',
+ ],
+ 'DB' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Depreciation::class, 'DB'],
+ 'argumentCount' => '4,5',
+ ],
+ 'DBCS' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'DCOUNT' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DCount::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DCOUNTA' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DCountA::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DDB' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Depreciation::class, 'DDB'],
+ 'argumentCount' => '4,5',
+ ],
+ 'DEC2BIN' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toBinary'],
+ 'argumentCount' => '1,2',
+ ],
+ 'DEC2HEX' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toHex'],
+ 'argumentCount' => '1,2',
+ ],
+ 'DEC2OCT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertDecimal::class, 'toOctal'],
+ 'argumentCount' => '1,2',
+ ],
+ 'DECIMAL' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'DEGREES' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Angle::class, 'toDegrees'],
+ 'argumentCount' => '1',
+ ],
+ 'DELTA' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Compare::class, 'DELTA'],
+ 'argumentCount' => '1,2',
+ ],
+ 'DEVSQ' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Deviations::class, 'sumSquares'],
+ 'argumentCount' => '1+',
+ ],
+ 'DGET' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DGet::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DISC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Rates::class, 'discount'],
+ 'argumentCount' => '4,5',
+ ],
+ 'DMAX' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DMax::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DMIN' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DMin::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DOLLAR' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'DOLLAR'],
+ 'argumentCount' => '1,2',
+ ],
+ 'DOLLARDE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Dollar::class, 'decimal'],
+ 'argumentCount' => '2',
+ ],
+ 'DOLLARFR' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Dollar::class, 'fractional'],
+ 'argumentCount' => '2',
+ ],
+ 'DPRODUCT' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DProduct::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DROP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
+ 'DSTDEV' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DStDev::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DSTDEVP' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DStDevP::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DSUM' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DSum::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DURATION' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '5,6',
+ ],
+ 'DVAR' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DVar::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'DVARP' => [
+ 'category' => Category::CATEGORY_DATABASE,
+ 'functionCall' => [Database\DVarP::class, 'evaluate'],
+ 'argumentCount' => '3',
+ ],
+ 'ECMA.CEILING' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1,2',
+ ],
+ 'EDATE' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Month::class, 'adjust'],
+ 'argumentCount' => '2',
+ ],
+ 'EFFECT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\InterestRate::class, 'effective'],
+ 'argumentCount' => '2',
+ ],
+ 'ENCODEURL' => [
+ 'category' => Category::CATEGORY_WEB,
+ 'functionCall' => [Web\Service::class, 'urlEncode'],
+ 'argumentCount' => '1',
+ ],
+ 'EOMONTH' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Month::class, 'lastDay'],
+ 'argumentCount' => '2',
+ ],
+ 'ERF' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Erf::class, 'ERF'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ERF.PRECISE' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Erf::class, 'ERFPRECISE'],
+ 'argumentCount' => '1',
+ ],
+ 'ERFC' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ErfC::class, 'ERFC'],
+ 'argumentCount' => '1',
+ ],
+ 'ERFC.PRECISE' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ErfC::class, 'ERFC'],
+ 'argumentCount' => '1',
+ ],
+ 'ERROR.TYPE' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\ExcelError::class, 'type'],
+ 'argumentCount' => '1',
+ ],
+ 'EVEN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Round::class, 'even'],
+ 'argumentCount' => '1',
+ ],
+ 'EXACT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'exact'],
+ 'argumentCount' => '2',
+ ],
+ 'EXP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Exp::class, 'evaluate'],
+ 'argumentCount' => '1',
+ ],
+ 'EXPAND' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
+ 'EXPONDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Exponential::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'EXPON.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Exponential::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'FACT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Factorial::class, 'fact'],
+ 'argumentCount' => '1',
+ ],
+ 'FACTDOUBLE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Factorial::class, 'factDouble'],
+ 'argumentCount' => '1',
+ ],
+ 'FALSE' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Boolean::class, 'FALSE'],
+ 'argumentCount' => '0',
+ ],
+ 'FDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'F.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\F::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'F.DIST.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'FILTER' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Filter::class, 'filter'],
+ 'argumentCount' => '2-3',
+ ],
+ 'FILTERXML' => [
+ 'category' => Category::CATEGORY_WEB,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'FIND' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Search::class, 'sensitive'],
+ 'argumentCount' => '2,3',
+ ],
+ 'FINDB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Search::class, 'sensitive'],
+ 'argumentCount' => '2,3',
+ ],
+ 'FINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'F.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'F.INV.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'FISHER' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Fisher::class, 'distribution'],
+ 'argumentCount' => '1',
+ ],
+ 'FISHERINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Fisher::class, 'inverse'],
+ 'argumentCount' => '1',
+ ],
+ 'FIXED' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'FIXEDFORMAT'],
+ 'argumentCount' => '1-3',
+ ],
+ 'FLOOR' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Floor::class, 'floor'],
+ 'argumentCount' => '1-2', // Excel requries 2, Ods/Gnumeric 1-2
+ ],
+ 'FLOOR.MATH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Floor::class, 'math'],
+ 'argumentCount' => '1-3',
+ ],
+ 'FLOOR.PRECISE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Floor::class, 'precise'],
+ 'argumentCount' => '1-2',
+ ],
+ 'FORECAST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'FORECAST'],
+ 'argumentCount' => '3',
+ ],
+ 'FORECAST.ETS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'FORECAST.ETS.CONFINT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'FORECAST.ETS.SEASONALITY' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
+ 'FORECAST.ETS.STAT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'FORECAST.LINEAR' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'FORECAST'],
+ 'argumentCount' => '3',
+ ],
+ 'FORMULATEXT' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Formula::class, 'text'],
+ 'argumentCount' => '1',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
+ ],
+ 'FREQUENCY' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'FTEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'F.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'FV' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'futureValue'],
+ 'argumentCount' => '3-5',
+ ],
+ 'FVSCHEDULE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Single::class, 'futureValue'],
+ 'argumentCount' => '2',
+ ],
+ 'GAMMA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'gamma'],
+ 'argumentCount' => '1',
+ ],
+ 'GAMMADIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'GAMMA.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'GAMMAINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'GAMMA.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'GAMMALN' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'],
+ 'argumentCount' => '1',
+ ],
+ 'GAMMALN.PRECISE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'],
+ 'argumentCount' => '1',
+ ],
+ 'GAUSS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'gauss'],
+ 'argumentCount' => '1',
+ ],
+ 'GCD' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Gcd::class, 'evaluate'],
+ 'argumentCount' => '1+',
+ ],
+ 'GEOMEAN' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages\Mean::class, 'geometric'],
+ 'argumentCount' => '1+',
+ ],
+ 'GESTEP' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Compare::class, 'GESTEP'],
+ 'argumentCount' => '1,2',
+ ],
+ 'GETPIVOTDATA' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2+',
+ ],
+ 'GROWTH' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'GROWTH'],
+ 'argumentCount' => '1-4',
+ ],
+ 'HARMEAN' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages\Mean::class, 'harmonic'],
+ 'argumentCount' => '1+',
+ ],
+ 'HEX2BIN' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertHex::class, 'toBinary'],
+ 'argumentCount' => '1,2',
+ ],
+ 'HEX2DEC' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertHex::class, 'toDecimal'],
+ 'argumentCount' => '1',
+ ],
+ 'HEX2OCT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertHex::class, 'toOctal'],
+ 'argumentCount' => '1,2',
+ ],
+ 'HLOOKUP' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\HLookup::class, 'lookup'],
+ 'argumentCount' => '3,4',
+ ],
+ 'HOUR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'hour'],
+ 'argumentCount' => '1',
+ ],
+ 'HSTACK' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
+ 'HYPERLINK' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Hyperlink::class, 'set'],
+ 'argumentCount' => '1,2',
+ 'passCellReference' => true,
+ ],
+ 'HYPGEOMDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\HyperGeometric::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'HYPGEOM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '5',
+ ],
+ 'IF' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Conditional::class, 'statementIf'],
+ 'argumentCount' => '1-3',
+ ],
+ 'IFERROR' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Conditional::class, 'IFERROR'],
+ 'argumentCount' => '2',
+ ],
+ 'IFNA' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Conditional::class, 'IFNA'],
+ 'argumentCount' => '2',
+ ],
+ 'IFS' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Conditional::class, 'IFS'],
+ 'argumentCount' => '2+',
+ ],
+ 'IMABS' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMABS'],
+ 'argumentCount' => '1',
+ ],
+ 'IMAGINARY' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Complex::class, 'IMAGINARY'],
+ 'argumentCount' => '1',
+ ],
+ 'IMARGUMENT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMARGUMENT'],
+ 'argumentCount' => '1',
+ ],
+ 'IMCONJUGATE' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCONJUGATE'],
+ 'argumentCount' => '1',
+ ],
+ 'IMCOS' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOS'],
+ 'argumentCount' => '1',
+ ],
+ 'IMCOSH' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOSH'],
+ 'argumentCount' => '1',
+ ],
+ 'IMCOT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCOT'],
+ 'argumentCount' => '1',
+ ],
+ 'IMCSC' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCSC'],
+ 'argumentCount' => '1',
+ ],
+ 'IMCSCH' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMCSCH'],
+ 'argumentCount' => '1',
+ ],
+ 'IMDIV' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMDIV'],
+ 'argumentCount' => '2',
+ ],
+ 'IMEXP' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMEXP'],
+ 'argumentCount' => '1',
+ ],
+ 'IMLN' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLN'],
+ 'argumentCount' => '1',
+ ],
+ 'IMLOG10' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLOG10'],
+ 'argumentCount' => '1',
+ ],
+ 'IMLOG2' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMLOG2'],
+ 'argumentCount' => '1',
+ ],
+ 'IMPOWER' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMPOWER'],
+ 'argumentCount' => '2',
+ ],
+ 'IMPRODUCT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMPRODUCT'],
+ 'argumentCount' => '1+',
+ ],
+ 'IMREAL' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\Complex::class, 'IMREAL'],
+ 'argumentCount' => '1',
+ ],
+ 'IMSEC' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSEC'],
+ 'argumentCount' => '1',
+ ],
+ 'IMSECH' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSECH'],
+ 'argumentCount' => '1',
+ ],
+ 'IMSIN' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSIN'],
+ 'argumentCount' => '1',
+ ],
+ 'IMSINH' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSINH'],
+ 'argumentCount' => '1',
+ ],
+ 'IMSQRT' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMSQRT'],
+ 'argumentCount' => '1',
+ ],
+ 'IMSUB' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMSUB'],
+ 'argumentCount' => '2',
+ ],
+ 'IMSUM' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexOperations::class, 'IMSUM'],
+ 'argumentCount' => '1+',
+ ],
+ 'IMTAN' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ComplexFunctions::class, 'IMTAN'],
+ 'argumentCount' => '1',
+ ],
+ 'INDEX' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Matrix::class, 'index'],
+ 'argumentCount' => '2-4',
+ ],
+ 'INDIRECT' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Indirect::class, 'INDIRECT'],
+ 'argumentCount' => '1,2',
+ 'passCellReference' => true,
+ ],
+ 'INFO' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'INT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\IntClass::class, 'evaluate'],
+ 'argumentCount' => '1',
+ ],
+ 'INTERCEPT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'INTERCEPT'],
+ 'argumentCount' => '2',
+ ],
+ 'INTRATE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Rates::class, 'interest'],
+ 'argumentCount' => '4,5',
+ ],
+ 'IPMT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'payment'],
+ 'argumentCount' => '4-6',
+ ],
+ 'IRR' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'rate'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ISBLANK' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isBlank'],
+ 'argumentCount' => '1',
+ ],
+ 'ISERR' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\ErrorValue::class, 'isErr'],
+ 'argumentCount' => '1',
+ ],
+ 'ISERROR' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\ErrorValue::class, 'isError'],
+ 'argumentCount' => '1',
+ ],
+ 'ISEVEN' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isEven'],
+ 'argumentCount' => '1',
+ ],
+ 'ISFORMULA' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isFormula'],
+ 'argumentCount' => '1',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
+ ],
+ 'ISLOGICAL' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isLogical'],
+ 'argumentCount' => '1',
+ ],
+ 'ISNA' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\ErrorValue::class, 'isNa'],
+ 'argumentCount' => '1',
+ ],
+ 'ISNONTEXT' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isNonText'],
+ 'argumentCount' => '1',
+ ],
+ 'ISNUMBER' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isNumber'],
+ 'argumentCount' => '1',
+ ],
+ 'ISO.CEILING' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ISODD' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isOdd'],
+ 'argumentCount' => '1',
+ ],
+ 'ISOMITTED' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'ISOWEEKNUM' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Week::class, 'isoWeekNumber'],
+ 'argumentCount' => '1',
+ ],
+ 'ISPMT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'schedulePayment'],
+ 'argumentCount' => '4',
+ ],
+ 'ISREF' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isRef'],
+ 'argumentCount' => '1',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
+ ],
+ 'ISTEXT' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'isText'],
+ 'argumentCount' => '1',
+ ],
+ 'ISTHAIDIGIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'JIS' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'KURT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Deviations::class, 'kurtosis'],
+ 'argumentCount' => '1+',
+ ],
+ 'LAMBDA' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'LARGE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Size::class, 'large'],
+ 'argumentCount' => '2',
+ ],
+ 'LCM' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Lcm::class, 'evaluate'],
+ 'argumentCount' => '1+',
+ ],
+ 'LEFT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'left'],
+ 'argumentCount' => '1,2',
+ ],
+ 'LEFTB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'left'],
+ 'argumentCount' => '1,2',
+ ],
+ 'LEN' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'length'],
+ 'argumentCount' => '1',
+ ],
+ 'LENB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'length'],
+ 'argumentCount' => '1',
+ ],
+ 'LET' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'LINEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'LINEST'],
+ 'argumentCount' => '1-4',
+ ],
+ 'LN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Logarithms::class, 'natural'],
+ 'argumentCount' => '1',
+ ],
+ 'LOG' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Logarithms::class, 'withBase'],
+ 'argumentCount' => '1,2',
+ ],
+ 'LOG10' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Logarithms::class, 'base10'],
+ 'argumentCount' => '1',
+ ],
+ 'LOGEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'LOGEST'],
+ 'argumentCount' => '1-4',
+ ],
+ 'LOGINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'LOGNORMDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'cumulative'],
+ 'argumentCount' => '3',
+ ],
+ 'LOGNORM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'LOGNORM.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\LogNormal::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'LOOKUP' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Lookup::class, 'lookup'],
+ 'argumentCount' => '2,3',
+ ],
+ 'LOWER' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CaseConvert::class, 'lower'],
+ 'argumentCount' => '1',
+ ],
+ 'MAKEARRAY' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'MAP' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'MATCH' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\ExcelMatch::class, 'MATCH'],
+ 'argumentCount' => '2,3',
+ ],
+ 'MAX' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Maximum::class, 'max'],
+ 'argumentCount' => '1+',
+ ],
+ 'MAXA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Maximum::class, 'maxA'],
+ 'argumentCount' => '1+',
+ ],
+ 'MAXIFS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Conditional::class, 'MAXIFS'],
+ 'argumentCount' => '3+',
+ ],
+ 'MDETERM' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'determinant'],
+ 'argumentCount' => '1',
+ ],
+ 'MDURATION' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '5,6',
+ ],
+ 'MEDIAN' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages::class, 'median'],
+ 'argumentCount' => '1+',
+ ],
+ 'MEDIANIF' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2+',
+ ],
+ 'MID' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'mid'],
+ 'argumentCount' => '3',
+ ],
+ 'MIDB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'mid'],
+ 'argumentCount' => '3',
+ ],
+ 'MIN' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Minimum::class, 'min'],
+ 'argumentCount' => '1+',
+ ],
+ 'MINA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Minimum::class, 'minA'],
+ 'argumentCount' => '1+',
+ ],
+ 'MINIFS' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Conditional::class, 'MINIFS'],
+ 'argumentCount' => '3+',
+ ],
+ 'MINUTE' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'minute'],
+ 'argumentCount' => '1',
+ ],
+ 'MINVERSE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'inverse'],
+ 'argumentCount' => '1',
+ ],
+ 'MIRR' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'modifiedRate'],
+ 'argumentCount' => '3',
+ ],
+ 'MMULT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'multiply'],
+ 'argumentCount' => '2',
+ ],
+ 'MOD' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Operations::class, 'mod'],
+ 'argumentCount' => '2',
+ ],
+ 'MODE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages::class, 'mode'],
+ 'argumentCount' => '1+',
+ ],
+ 'MODE.MULT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
+ 'MODE.SNGL' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages::class, 'mode'],
+ 'argumentCount' => '1+',
+ ],
+ 'MONTH' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'month'],
+ 'argumentCount' => '1',
+ ],
+ 'MROUND' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Round::class, 'multiple'],
+ 'argumentCount' => '2',
+ ],
+ 'MULTINOMIAL' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Factorial::class, 'multinomial'],
+ 'argumentCount' => '1+',
+ ],
+ 'MUNIT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'identity'],
+ 'argumentCount' => '1',
+ ],
+ 'N' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'asNumber'],
+ 'argumentCount' => '1',
+ ],
+ 'NA' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\ExcelError::class, 'NA'],
+ 'argumentCount' => '0',
+ ],
+ 'NEGBINOMDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Binomial::class, 'negative'],
+ 'argumentCount' => '3',
+ ],
+ 'NEGBINOM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4',
+ ],
+ 'NETWORKDAYS' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\NetworkDays::class, 'count'],
+ 'argumentCount' => '2-3',
+ ],
+ 'NETWORKDAYS.INTL' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
+ 'NOMINAL' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\InterestRate::class, 'nominal'],
+ 'argumentCount' => '2',
+ ],
+ 'NORMDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'NORM.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'NORMINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'NORM.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Normal::class, 'inverse'],
+ 'argumentCount' => '3',
+ ],
+ 'NORMSDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'cumulative'],
+ 'argumentCount' => '1',
+ ],
+ 'NORM.S.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'distribution'],
+ 'argumentCount' => '1,2',
+ ],
+ 'NORMSINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'inverse'],
+ 'argumentCount' => '1',
+ ],
+ 'NORM.S.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'inverse'],
+ 'argumentCount' => '1',
+ ],
+ 'NOT' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Operations::class, 'NOT'],
+ 'argumentCount' => '1',
+ ],
+ 'NOW' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Current::class, 'now'],
+ 'argumentCount' => '0',
+ ],
+ 'NPER' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'periods'],
+ 'argumentCount' => '3-5',
+ ],
+ 'NPV' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Variable\Periodic::class, 'presentValue'],
+ 'argumentCount' => '2+',
+ ],
+ 'NUMBERSTRING' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'NUMBERVALUE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'NUMBERVALUE'],
+ 'argumentCount' => '1+',
+ ],
+ 'OCT2BIN' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toBinary'],
+ 'argumentCount' => '1,2',
+ ],
+ 'OCT2DEC' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toDecimal'],
+ 'argumentCount' => '1',
+ ],
+ 'OCT2HEX' => [
+ 'category' => Category::CATEGORY_ENGINEERING,
+ 'functionCall' => [Engineering\ConvertOctal::class, 'toHex'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ODD' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Round::class, 'odd'],
+ 'argumentCount' => '1',
+ ],
+ 'ODDFPRICE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '8,9',
+ ],
+ 'ODDFYIELD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '8,9',
+ ],
+ 'ODDLPRICE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '7,8',
+ ],
+ 'ODDLYIELD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '7,8',
+ ],
+ 'OFFSET' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Offset::class, 'OFFSET'],
+ 'argumentCount' => '3-5',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
+ ],
+ 'OR' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Operations::class, 'logicalOr'],
+ 'argumentCount' => '1+',
+ ],
+ 'PDURATION' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Single::class, 'periods'],
+ 'argumentCount' => '3',
+ ],
+ 'PEARSON' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'CORREL'],
+ 'argumentCount' => '2',
+ ],
+ 'PERCENTILE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTILE'],
+ 'argumentCount' => '2',
+ ],
+ 'PERCENTILE.EXC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'PERCENTILE.INC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTILE'],
+ 'argumentCount' => '2',
+ ],
+ 'PERCENTRANK' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTRANK'],
+ 'argumentCount' => '2,3',
+ ],
+ 'PERCENTRANK.EXC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2,3',
+ ],
+ 'PERCENTRANK.INC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'PERCENTRANK'],
+ 'argumentCount' => '2,3',
+ ],
+ 'PERMUT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Permutations::class, 'PERMUT'],
+ 'argumentCount' => '2',
+ ],
+ 'PERMUTATIONA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Permutations::class, 'PERMUTATIONA'],
+ 'argumentCount' => '2',
+ ],
+ 'PHONETIC' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'PHI' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1',
+ ],
+ 'PI' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => 'pi',
+ 'argumentCount' => '0',
+ ],
+ 'PMT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Payments::class, 'annuity'],
+ 'argumentCount' => '3-5',
+ ],
+ 'POISSON' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'POISSON.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'POWER' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Operations::class, 'power'],
+ 'argumentCount' => '2',
+ ],
+ 'PPMT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Payments::class, 'interestPayment'],
+ 'argumentCount' => '4-6',
+ ],
+ 'PRICE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Price::class, 'price'],
+ 'argumentCount' => '6,7',
+ ],
+ 'PRICEDISC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Price::class, 'priceDiscounted'],
+ 'argumentCount' => '4,5',
+ ],
+ 'PRICEMAT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Price::class, 'priceAtMaturity'],
+ 'argumentCount' => '5,6',
+ ],
+ 'PROB' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3,4',
+ ],
+ 'PRODUCT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Operations::class, 'product'],
+ 'argumentCount' => '1+',
+ ],
+ 'PROPER' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CaseConvert::class, 'proper'],
+ 'argumentCount' => '1',
+ ],
+ 'PV' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic::class, 'presentValue'],
+ 'argumentCount' => '3-5',
+ ],
+ 'QUARTILE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'QUARTILE'],
+ 'argumentCount' => '2',
+ ],
+ 'QUARTILE.EXC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'QUARTILE.INC' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'QUARTILE'],
+ 'argumentCount' => '2',
+ ],
+ 'QUOTIENT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Operations::class, 'quotient'],
+ 'argumentCount' => '2',
+ ],
+ 'RADIANS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Angle::class, 'toRadians'],
+ 'argumentCount' => '1',
+ ],
+ 'RAND' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Random::class, 'rand'],
+ 'argumentCount' => '0',
+ ],
+ 'RANDARRAY' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Random::class, 'randArray'],
+ 'argumentCount' => '0-5',
+ ],
+ 'RANDBETWEEN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Random::class, 'randBetween'],
+ 'argumentCount' => '2',
+ ],
+ 'RANK' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'RANK'],
+ 'argumentCount' => '2,3',
+ ],
+ 'RANK.AVG' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2,3',
+ ],
+ 'RANK.EQ' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Percentiles::class, 'RANK'],
+ 'argumentCount' => '2,3',
+ ],
+ 'RATE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Constant\Periodic\Interest::class, 'rate'],
+ 'argumentCount' => '3-6',
+ ],
+ 'RECEIVED' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Price::class, 'received'],
+ 'argumentCount' => '4-5',
+ ],
+ 'REDUCE' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'REPLACE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Replace::class, 'replace'],
+ 'argumentCount' => '4',
+ ],
+ 'REPLACEB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Replace::class, 'replace'],
+ 'argumentCount' => '4',
+ ],
+ 'REPT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Concatenate::class, 'builtinREPT'],
+ 'argumentCount' => '2',
+ ],
+ 'RIGHT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'right'],
+ 'argumentCount' => '1,2',
+ ],
+ 'RIGHTB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'right'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ROMAN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Roman::class, 'evaluate'],
+ 'argumentCount' => '1,2',
+ ],
+ 'ROUND' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Round::class, 'round'],
+ 'argumentCount' => '2',
+ ],
+ 'ROUNDBAHTDOWN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'ROUNDBAHTUP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'ROUNDDOWN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Round::class, 'down'],
+ 'argumentCount' => '2',
+ ],
+ 'ROUNDUP' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Round::class, 'up'],
+ 'argumentCount' => '2',
+ ],
+ 'ROW' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'ROW'],
+ 'argumentCount' => '-1',
+ 'passCellReference' => true,
+ 'passByReference' => [true],
+ ],
+ 'ROWS' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\RowColumnInformation::class, 'ROWS'],
+ 'argumentCount' => '1',
+ ],
+ 'RRI' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Single::class, 'interestRate'],
+ 'argumentCount' => '3',
+ ],
+ 'RSQ' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'RSQ'],
+ 'argumentCount' => '2',
+ ],
+ 'RTD' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
+ 'SEARCH' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Search::class, 'insensitive'],
+ 'argumentCount' => '2,3',
+ ],
+ 'SCAN' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'SEARCHB' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Search::class, 'insensitive'],
+ 'argumentCount' => '2,3',
+ ],
+ 'SEC' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Secant::class, 'sec'],
+ 'argumentCount' => '1',
+ ],
+ 'SECH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Secant::class, 'sech'],
+ 'argumentCount' => '1',
+ ],
+ 'SECOND' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\TimeParts::class, 'second'],
+ 'argumentCount' => '1',
+ ],
+ 'SEQUENCE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\MatrixFunctions::class, 'sequence'],
+ 'argumentCount' => '1-4',
+ ],
+ 'SERIESSUM' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\SeriesSum::class, 'evaluate'],
+ 'argumentCount' => '4',
+ ],
+ 'SHEET' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '0,1',
+ ],
+ 'SHEETS' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '0,1',
+ ],
+ 'SIGN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Sign::class, 'evaluate'],
+ 'argumentCount' => '1',
+ ],
+ 'SIN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'sin'],
+ 'argumentCount' => '1',
+ ],
+ 'SINGLE' => [
+ 'category' => Category::CATEGORY_UNCATEGORISED,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '*',
+ ],
+ 'SINH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Sine::class, 'sinh'],
+ 'argumentCount' => '1',
+ ],
+ 'SKEW' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Deviations::class, 'skew'],
+ 'argumentCount' => '1+',
+ ],
+ 'SKEW.P' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
+ 'SLN' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Depreciation::class, 'SLN'],
+ 'argumentCount' => '3',
+ ],
+ 'SLOPE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'SLOPE'],
+ 'argumentCount' => '2',
+ ],
+ 'SMALL' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Size::class, 'small'],
+ 'argumentCount' => '2',
+ ],
+ 'SORT' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Sort::class, 'sort'],
+ 'argumentCount' => '1-4',
+ ],
+ 'SORTBY' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Sort::class, 'sortBy'],
+ 'argumentCount' => '2+',
+ ],
+ 'SQRT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Sqrt::class, 'sqrt'],
+ 'argumentCount' => '1',
+ ],
+ 'SQRTPI' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Sqrt::class, 'pi'],
+ 'argumentCount' => '1',
+ ],
+ 'STANDARDIZE' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Standardize::class, 'execute'],
+ 'argumentCount' => '3',
+ ],
+ 'STDEV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEV'],
+ 'argumentCount' => '1+',
+ ],
+ 'STDEV.S' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEV'],
+ 'argumentCount' => '1+',
+ ],
+ 'STDEV.P' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
+ 'argumentCount' => '1+',
+ ],
+ 'STDEVA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVA'],
+ 'argumentCount' => '1+',
+ ],
+ 'STDEVP' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
+ 'argumentCount' => '1+',
+ ],
+ 'STDEVPA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVPA'],
+ 'argumentCount' => '1+',
+ ],
+ 'STEYX' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'STEYX'],
+ 'argumentCount' => '2',
+ ],
+ 'SUBSTITUTE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Replace::class, 'substitute'],
+ 'argumentCount' => '3,4',
+ ],
+ 'SUBTOTAL' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Subtotal::class, 'evaluate'],
+ 'argumentCount' => '2+',
+ 'passCellReference' => true,
+ ],
+ 'SUM' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Sum::class, 'sumErroringStrings'],
+ 'argumentCount' => '1+',
+ ],
+ 'SUMIF' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Statistical\Conditional::class, 'SUMIF'],
+ 'argumentCount' => '2,3',
+ ],
+ 'SUMIFS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Statistical\Conditional::class, 'SUMIFS'],
+ 'argumentCount' => '3+',
+ ],
+ 'SUMPRODUCT' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Sum::class, 'product'],
+ 'argumentCount' => '1+',
+ ],
+ 'SUMSQ' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumSquare'],
+ 'argumentCount' => '1+',
+ ],
+ 'SUMX2MY2' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXSquaredMinusYSquared'],
+ 'argumentCount' => '2',
+ ],
+ 'SUMX2PY2' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXSquaredPlusYSquared'],
+ 'argumentCount' => '2',
+ ],
+ 'SUMXMY2' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\SumSquares::class, 'sumXMinusYSquared'],
+ 'argumentCount' => '2',
+ ],
+ 'SWITCH' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Conditional::class, 'statementSwitch'],
+ 'argumentCount' => '3+',
+ ],
+ 'SYD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Depreciation::class, 'SYD'],
+ 'argumentCount' => '4',
+ ],
+ 'T' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'test'],
+ 'argumentCount' => '1',
+ ],
+ 'TAKE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
+ 'TAN' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'tan'],
+ 'argumentCount' => '1',
+ ],
+ 'TANH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trig\Tangent::class, 'tanh'],
+ 'argumentCount' => '1',
+ ],
+ 'TBILLEQ' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\TreasuryBill::class, 'bondEquivalentYield'],
+ 'argumentCount' => '3',
+ ],
+ 'TBILLPRICE' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\TreasuryBill::class, 'price'],
+ 'argumentCount' => '3',
+ ],
+ 'TBILLYIELD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\TreasuryBill::class, 'yield'],
+ 'argumentCount' => '3',
+ ],
+ 'TDIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'distribution'],
+ 'argumentCount' => '3',
+ ],
+ 'T.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3',
+ ],
+ 'T.DIST.2T' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'T.DIST.RT' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'TEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'TEXTFORMAT'],
+ 'argumentCount' => '2',
+ ],
+ 'TEXTAFTER' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'after'],
+ 'argumentCount' => '2-6',
+ ],
+ 'TEXTBEFORE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Extract::class, 'before'],
+ 'argumentCount' => '2-6',
+ ],
+ 'TEXTJOIN' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Concatenate::class, 'TEXTJOIN'],
+ 'argumentCount' => '3+',
+ ],
+ 'TEXTSPLIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Text::class, 'split'],
+ 'argumentCount' => '2-6',
+ ],
+ 'THAIDAYOFWEEK' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIDIGIT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIMONTHOFYEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAINUMSOUND' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAINUMSTRING' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAISTRINGLENGTH' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'THAIYEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '?',
+ ],
+ 'TIME' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Time::class, 'fromHMS'],
+ 'argumentCount' => '3',
+ ],
+ 'TIMEVALUE' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\TimeValue::class, 'fromString'],
+ 'argumentCount' => '1',
+ ],
+ 'TINV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'inverse'],
+ 'argumentCount' => '2',
+ ],
+ 'T.INV' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StudentT::class, 'inverse'],
+ 'argumentCount' => '2',
+ ],
+ 'T.INV.2T' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2',
+ ],
+ 'TODAY' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Current::class, 'today'],
+ 'argumentCount' => '0',
+ ],
+ 'TOCOL' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1-3',
+ ],
+ 'TOROW' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1-3',
+ ],
+ 'TRANSPOSE' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Matrix::class, 'transpose'],
+ 'argumentCount' => '1',
+ ],
+ 'TREND' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Trends::class, 'TREND'],
+ 'argumentCount' => '1-4',
+ ],
+ 'TRIM' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Trim::class, 'spaces'],
+ 'argumentCount' => '1',
+ ],
+ 'TRIMMEAN' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Averages\Mean::class, 'trim'],
+ 'argumentCount' => '2',
+ ],
+ 'TRUE' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Boolean::class, 'TRUE'],
+ 'argumentCount' => '0',
+ ],
+ 'TRUNC' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig\Trunc::class, 'evaluate'],
+ 'argumentCount' => '1,2',
+ ],
+ 'TTEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4',
+ ],
+ 'T.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '4',
+ ],
+ 'TYPE' => [
+ 'category' => Category::CATEGORY_INFORMATION,
+ 'functionCall' => [Information\Value::class, 'type'],
+ 'argumentCount' => '1',
+ ],
+ 'UNICHAR' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CharacterConvert::class, 'character'],
+ 'argumentCount' => '1',
+ ],
+ 'UNICODE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CharacterConvert::class, 'code'],
+ 'argumentCount' => '1',
+ ],
+ 'UNIQUE' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\Unique::class, 'unique'],
+ 'argumentCount' => '1+',
+ ],
+ 'UPPER' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\CaseConvert::class, 'upper'],
+ 'argumentCount' => '1',
+ ],
+ 'USDOLLAR' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Dollar::class, 'format'],
+ 'argumentCount' => '2',
+ ],
+ 'VALUE' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'VALUE'],
+ 'argumentCount' => '1',
+ ],
+ 'VALUETOTEXT' => [
+ 'category' => Category::CATEGORY_TEXT_AND_DATA,
+ 'functionCall' => [TextData\Format::class, 'valueToText'],
+ 'argumentCount' => '1,2',
+ ],
+ 'VAR' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Variances::class, 'VAR'],
+ 'argumentCount' => '1+',
+ ],
+ 'VAR.P' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Variances::class, 'VARP'],
+ 'argumentCount' => '1+',
+ ],
+ 'VAR.S' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Variances::class, 'VAR'],
+ 'argumentCount' => '1+',
+ ],
+ 'VARA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Variances::class, 'VARA'],
+ 'argumentCount' => '1+',
+ ],
+ 'VARP' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Variances::class, 'VARP'],
+ 'argumentCount' => '1+',
+ ],
+ 'VARPA' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Variances::class, 'VARPA'],
+ 'argumentCount' => '1+',
+ ],
+ 'VDB' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '5-7',
+ ],
+ 'VLOOKUP' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [LookupRef\VLookup::class, 'lookup'],
+ 'argumentCount' => '3,4',
+ ],
+ 'VSTACK' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '1+',
+ ],
+ 'WEBSERVICE' => [
+ 'category' => Category::CATEGORY_WEB,
+ 'functionCall' => [Web\Service::class, 'webService'],
+ 'argumentCount' => '1',
+ ],
+ 'WEEKDAY' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Week::class, 'day'],
+ 'argumentCount' => '1,2',
+ ],
+ 'WEEKNUM' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\Week::class, 'number'],
+ 'argumentCount' => '1,2',
+ ],
+ 'WEIBULL' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Weibull::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'WEIBULL.DIST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\Weibull::class, 'distribution'],
+ 'argumentCount' => '4',
+ ],
+ 'WORKDAY' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\WorkDay::class, 'date'],
+ 'argumentCount' => '2-3',
+ ],
+ 'WORKDAY.INTL' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
+ ],
+ 'WRAPCOLS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
+ 'WRAPROWS' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-3',
+ ],
+ 'XIRR' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'rate'],
+ 'argumentCount' => '2,3',
+ ],
+ 'XLOOKUP' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '3-6',
+ ],
+ 'XNPV' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'presentValue'],
+ 'argumentCount' => '3',
+ ],
+ 'XMATCH' => [
+ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2,3',
+ ],
+ 'XOR' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical\Operations::class, 'logicalXor'],
+ 'argumentCount' => '1+',
+ ],
+ 'YEAR' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\DateParts::class, 'year'],
+ 'argumentCount' => '1',
+ ],
+ 'YEARFRAC' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [DateTimeExcel\YearFrac::class, 'fraction'],
+ 'argumentCount' => '2,3',
+ ],
+ 'YIELD' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '6,7',
+ ],
+ 'YIELDDISC' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Yields::class, 'yieldDiscounted'],
+ 'argumentCount' => '4,5',
+ ],
+ 'YIELDMAT' => [
+ 'category' => Category::CATEGORY_FINANCIAL,
+ 'functionCall' => [Financial\Securities\Yields::class, 'yieldAtMaturity'],
+ 'argumentCount' => '5,6',
+ ],
+ 'ZTEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'zTest'],
+ 'argumentCount' => '2-3',
+ ],
+ 'Z.TEST' => [
+ 'category' => Category::CATEGORY_STATISTICAL,
+ 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'zTest'],
+ 'argumentCount' => '2-3',
+ ],
+ ];
+
+ /**
+ * Internal functions used for special control purposes.
+ *
+ * @var array
+ */
+ private static $controlFunctions = [
+ 'MKMATRIX' => [
+ 'argumentCount' => '*',
+ 'functionCall' => [Internal\MakeMatrix::class, 'make'],
+ ],
+ 'NAME.ERROR' => [
+ 'argumentCount' => '*',
+ 'functionCall' => [Functions::class, 'NAME'],
+ ],
+ 'WILDCARDMATCH' => [
+ 'argumentCount' => '2',
+ 'functionCall' => [Internal\WildcardMatch::class, 'compare'],
+ ],
+ ];
+
+ public function __construct(?Spreadsheet $spreadsheet = null)
+ {
+ $this->spreadsheet = $spreadsheet;
+ $this->cyclicReferenceStack = new CyclicReferenceStack();
+ $this->debugLog = new Logger($this->cyclicReferenceStack);
+ $this->branchPruner = new BranchPruner($this->branchPruningEnabled);
+ self::$referenceHelper = ReferenceHelper::getInstance();
+ }
+
+ private static function loadLocales(): void
+ {
+ $localeFileDirectory = __DIR__ . '/locale/';
+ $localeFileNames = glob($localeFileDirectory . '*', GLOB_ONLYDIR) ?: [];
+ foreach ($localeFileNames as $filename) {
+ $filename = substr($filename, strlen($localeFileDirectory));
+ if ($filename != 'en') {
+ self::$validLocaleLanguages[] = $filename;
+ }
+ }
+ }
+
+ /**
+ * Get an instance of this class.
+ *
+ * @param ?Spreadsheet $spreadsheet Injected spreadsheet for working with a PhpSpreadsheet Spreadsheet object,
+ * or NULL to create a standalone calculation engine
+ */
+ public static function getInstance(?Spreadsheet $spreadsheet = null): self
+ {
+ if ($spreadsheet !== null) {
+ $instance = $spreadsheet->getCalculationEngine();
+ if (isset($instance)) {
+ return $instance;
+ }
+ }
+
+ if (!isset(self::$instance) || (self::$instance === null)) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Flush the calculation cache for any existing instance of this class
+ * but only if a Calculation instance exists.
+ */
+ public function flushInstance(): void
+ {
+ $this->clearCalculationCache();
+ $this->branchPruner->clearBranchStore();
+ }
+
+ /**
+ * Get the Logger for this calculation engine instance.
+ *
+ * @return Logger
+ */
+ public function getDebugLog()
+ {
+ return $this->debugLog;
+ }
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ */
+ final public function __clone()
+ {
+ throw new Exception('Cloning the calculation engine is not allowed!');
+ }
+
+ /**
+ * Return the locale-specific translation of TRUE.
+ *
+ * @return string locale-specific translation of TRUE
+ */
+ public static function getTRUE(): string
+ {
+ return self::$localeBoolean['TRUE'];
+ }
+
+ /**
+ * Return the locale-specific translation of FALSE.
+ *
+ * @return string locale-specific translation of FALSE
+ */
+ public static function getFALSE(): string
+ {
+ return self::$localeBoolean['FALSE'];
+ }
+
+ /**
+ * Set the Array Return Type (Array or Value of first element in the array).
+ *
+ * @param string $returnType Array return type
+ *
+ * @return bool Success or failure
+ */
+ public static function setArrayReturnType($returnType): bool
+ {
+ if (
+ ($returnType == self::RETURN_ARRAY_AS_VALUE)
+ || ($returnType == self::RETURN_ARRAY_AS_ERROR)
+ || ($returnType == self::RETURN_ARRAY_AS_ARRAY)
+ ) {
+ self::$returnArrayAsType = $returnType;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the Array Return Type (Array or Value of first element in the array).
+ *
+ * @return string $returnType Array return type
+ */
+ public static function getArrayReturnType()
+ {
+ return self::$returnArrayAsType;
+ }
+
+ /**
+ * Is calculation caching enabled?
+ *
+ * @return bool
+ */
+ public function getCalculationCacheEnabled()
+ {
+ return $this->calculationCacheEnabled;
+ }
+
+ /**
+ * Enable/disable calculation cache.
+ *
+ * @param bool $calculationCacheEnabled
+ */
+ public function setCalculationCacheEnabled($calculationCacheEnabled): void
+ {
+ $this->calculationCacheEnabled = $calculationCacheEnabled;
+ $this->clearCalculationCache();
+ }
+
+ /**
+ * Enable calculation cache.
+ */
+ public function enableCalculationCache(): void
+ {
+ $this->setCalculationCacheEnabled(true);
+ }
+
+ /**
+ * Disable calculation cache.
+ */
+ public function disableCalculationCache(): void
+ {
+ $this->setCalculationCacheEnabled(false);
+ }
+
+ /**
+ * Clear calculation cache.
+ */
+ public function clearCalculationCache(): void
+ {
+ $this->calculationCache = [];
+ }
+
+ /**
+ * Clear calculation cache for a specified worksheet.
+ *
+ * @param string $worksheetName
+ */
+ public function clearCalculationCacheForWorksheet($worksheetName): void
+ {
+ if (isset($this->calculationCache[$worksheetName])) {
+ unset($this->calculationCache[$worksheetName]);
+ }
+ }
+
+ /**
+ * Rename calculation cache for a specified worksheet.
+ *
+ * @param string $fromWorksheetName
+ * @param string $toWorksheetName
+ */
+ public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName): void
+ {
+ if (isset($this->calculationCache[$fromWorksheetName])) {
+ $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
+ unset($this->calculationCache[$fromWorksheetName]);
+ }
+ }
+
+ /**
+ * Enable/disable calculation cache.
+ */
+ public function setBranchPruningEnabled(mixed $enabled): void
+ {
+ $this->branchPruningEnabled = $enabled;
+ $this->branchPruner = new BranchPruner($this->branchPruningEnabled);
+ }
+
+ public function enableBranchPruning(): void
+ {
+ $this->setBranchPruningEnabled(true);
+ }
+
+ public function disableBranchPruning(): void
+ {
+ $this->setBranchPruningEnabled(false);
+ }
+
+ /**
+ * Get the currently defined locale code.
+ *
+ * @return string
+ */
+ public function getLocale()
+ {
+ return self::$localeLanguage;
+ }
+
+ private function getLocaleFile(string $localeDir, string $locale, string $language, string $file): string
+ {
+ $localeFileName = $localeDir . str_replace('_', DIRECTORY_SEPARATOR, $locale)
+ . DIRECTORY_SEPARATOR . $file;
+ if (!file_exists($localeFileName)) {
+ // If there isn't a locale specific file, look for a language specific file
+ $localeFileName = $localeDir . $language . DIRECTORY_SEPARATOR . $file;
+ if (!file_exists($localeFileName)) {
+ throw new Exception('Locale file not found');
+ }
+ }
+
+ return $localeFileName;
+ }
+
+ /**
+ * Set the locale code.
+ *
+ * @param string $locale The locale to use for formula translation, eg: 'en_us'
+ */
+ public function setLocale(string $locale): bool
+ {
+ // Identify our locale and language
+ $language = $locale = strtolower($locale);
+ if (str_contains($locale, '_')) {
+ [$language] = explode('_', $locale);
+ }
+ if (count(self::$validLocaleLanguages) == 1) {
+ self::loadLocales();
+ }
+
+ // Test whether we have any language data for this language (any locale)
+ if (in_array($language, self::$validLocaleLanguages, true)) {
+ // initialise language/locale settings
+ self::$localeFunctions = [];
+ self::$localeArgumentSeparator = ',';
+ self::$localeBoolean = ['TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL'];
+
+ // Default is US English, if user isn't requesting US english, then read the necessary data from the locale files
+ if ($locale !== 'en_us') {
+ $localeDir = implode(DIRECTORY_SEPARATOR, [__DIR__, 'locale', null]);
+
+ // Search for a file with a list of function names for locale
+ try {
+ $functionNamesFile = $this->getLocaleFile($localeDir, $locale, $language, 'functions');
+ } catch (Exception $e) {
+ return false;
+ }
+
+ // Retrieve the list of locale or language specific function names
+ $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
+ foreach ($localeFunctions as $localeFunction) {
+ [$localeFunction] = explode('##', $localeFunction); // Strip out comments
+ if (str_contains($localeFunction, '=')) {
+ [$fName, $lfName] = array_map('trim', explode('=', $localeFunction));
+ if ((str_starts_with($fName, '*') || isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
+ self::$localeFunctions[$fName] = $lfName;
+ }
+ }
+ }
+ // Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
+ if (isset(self::$localeFunctions['TRUE'])) {
+ self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE'];
+ }
+ if (isset(self::$localeFunctions['FALSE'])) {
+ self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
+ }
+
+ try {
+ $configFile = $this->getLocaleFile($localeDir, $locale, $language, 'config');
+ } catch (Exception) {
+ return false;
+ }
+
+ $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
+ foreach ($localeSettings as $localeSetting) {
+ [$localeSetting] = explode('##', $localeSetting); // Strip out comments
+ if (str_contains($localeSetting, '=')) {
+ [$settingName, $settingValue] = array_map('trim', explode('=', $localeSetting));
+ $settingName = strtoupper($settingName);
+ if ($settingValue !== '') {
+ switch ($settingName) {
+ case 'ARGUMENTSEPARATOR':
+ self::$localeArgumentSeparator = $settingValue;
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ self::$functionReplaceFromExcel = self::$functionReplaceToExcel
+ = self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
+ self::$localeLanguage = $locale;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function translateSeparator(
+ string $fromSeparator,
+ string $toSeparator,
+ string $formula,
+ int &$inBracesLevel,
+ string $openBrace = self::FORMULA_OPEN_FUNCTION_BRACE,
+ string $closeBrace = self::FORMULA_CLOSE_FUNCTION_BRACE
+ ): string {
+ $strlen = mb_strlen($formula);
+ for ($i = 0; $i < $strlen; ++$i) {
+ $chr = mb_substr($formula, $i, 1);
+ switch ($chr) {
+ case $openBrace:
+ ++$inBracesLevel;
+
+ break;
+ case $closeBrace:
+ --$inBracesLevel;
+
+ break;
+ case $fromSeparator:
+ if ($inBracesLevel > 0) {
+ $formula = mb_substr($formula, 0, $i) . $toSeparator . mb_substr($formula, $i + 1);
+ }
+ }
+ }
+
+ return $formula;
+ }
+
+ private static function translateFormulaBlock(
+ array $from,
+ array $to,
+ string $formula,
+ int &$inFunctionBracesLevel,
+ int &$inMatrixBracesLevel,
+ string $fromSeparator,
+ string $toSeparator
+ ): string {
+ // Function Names
+ $formula = (string) preg_replace($from, $to, $formula);
+
+ // Temporarily adjust matrix separators so that they won't be confused with function arguments
+ $formula = self::translateSeparator(';', '|', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+ $formula = self::translateSeparator(',', '!', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+ // Function Argument Separators
+ $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inFunctionBracesLevel);
+ // Restore matrix separators
+ $formula = self::translateSeparator('|', ';', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+ $formula = self::translateSeparator('!', ',', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
+
+ return $formula;
+ }
+
+ private static function translateFormula(array $from, array $to, string $formula, string $fromSeparator, string $toSeparator): string
+ {
+ // Convert any Excel function names and constant names to the required language;
+ // and adjust function argument separators
+ if (self::$localeLanguage !== 'en_us') {
+ $inFunctionBracesLevel = 0;
+ $inMatrixBracesLevel = 0;
+ // If there is the possibility of separators within a quoted string, then we treat them as literals
+ if (str_contains($formula, self::FORMULA_STRING_QUOTE)) {
+ // So instead we skip replacing in any quoted strings by only replacing in every other array element
+ // after we've exploded the formula
+ $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
+ $notWithinQuotes = false;
+ foreach ($temp as &$value) {
+ // Only adjust in alternating array entries
+ $notWithinQuotes = $notWithinQuotes === false;
+ if ($notWithinQuotes === true) {
+ $value = self::translateFormulaBlock($from, $to, $value, $inFunctionBracesLevel, $inMatrixBracesLevel, $fromSeparator, $toSeparator);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
+ } else {
+ // If there's no quoted strings, then we do a simple count/replace
+ $formula = self::translateFormulaBlock($from, $to, $formula, $inFunctionBracesLevel, $inMatrixBracesLevel, $fromSeparator, $toSeparator);
+ }
+ }
+
+ return $formula;
+ }
+
+ /** @var ?array */
+ private static $functionReplaceFromExcel;
+
+ /** @var ?array */
+ private static $functionReplaceToLocale;
+
+ /**
+ * @deprecated 1.30.0 use translateFormulaToLocale() instead
+ *
+ * @codeCoverageIgnore
+ */
+ public function _translateFormulaToLocale(string $formula): string
+ {
+ return $this->translateFormulaToLocale($formula);
+ }
+
+ public function translateFormulaToLocale(string $formula): string
+ {
+ $formula = preg_replace(self::CALCULATION_REGEXP_STRIP_XLFN_XLWS, '', $formula) ?? '';
+ // Build list of function names and constants for translation
+ if (self::$functionReplaceFromExcel === null) {
+ self::$functionReplaceFromExcel = [];
+ foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelFunctionName, '/') . '([\s]*\()/ui';
+ }
+ foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
+ self::$functionReplaceFromExcel[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/ui';
+ }
+ }
+
+ if (self::$functionReplaceToLocale === null) {
+ self::$functionReplaceToLocale = [];
+ foreach (self::$localeFunctions as $localeFunctionName) {
+ self::$functionReplaceToLocale[] = '$1' . trim($localeFunctionName) . '$2';
+ }
+ foreach (self::$localeBoolean as $localeBoolean) {
+ self::$functionReplaceToLocale[] = '$1' . trim($localeBoolean) . '$2';
+ }
+ }
+
+ return self::translateFormula(
+ self::$functionReplaceFromExcel,
+ self::$functionReplaceToLocale,
+ $formula,
+ ',',
+ self::$localeArgumentSeparator
+ );
+ }
+
+ /** @var ?array */
+ private static $functionReplaceFromLocale;
+
+ /** @var ?array */
+ private static $functionReplaceToExcel;
+
+ /**
+ * @deprecated 1.30.0 use translateFormulaToEnglish() instead
+ *
+ * @codeCoverageIgnore
+ */
+ public function _translateFormulaToEnglish(string $formula): string
+ {
+ return $this->translateFormulaToEnglish($formula);
+ }
+
+ public function translateFormulaToEnglish(string $formula): string
+ {
+ if (self::$functionReplaceFromLocale === null) {
+ self::$functionReplaceFromLocale = [];
+ foreach (self::$localeFunctions as $localeFunctionName) {
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($localeFunctionName, '/') . '([\s]*\()/ui';
+ }
+ foreach (self::$localeBoolean as $excelBoolean) {
+ self::$functionReplaceFromLocale[] = '/(@?[^\w\.])' . preg_quote($excelBoolean, '/') . '([^\w\.])/ui';
+ }
+ }
+
+ if (self::$functionReplaceToExcel === null) {
+ self::$functionReplaceToExcel = [];
+ foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
+ self::$functionReplaceToExcel[] = '$1' . trim($excelFunctionName) . '$2';
+ }
+ foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
+ self::$functionReplaceToExcel[] = '$1' . trim($excelBoolean) . '$2';
+ }
+ }
+
+ return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
+ }
+
+ /**
+ * @param string $function
+ *
+ * @return string
+ */
+ public static function localeFunc($function)
+ {
+ if (self::$localeLanguage !== 'en_us') {
+ $functionName = trim($function, '(');
+ if (isset(self::$localeFunctions[$functionName])) {
+ $brace = ($functionName != $function);
+ $function = self::$localeFunctions[$functionName];
+ if ($brace) {
+ $function .= '(';
+ }
+ }
+ }
+
+ return $function;
+ }
+
+ /**
+ * Wrap string values in quotes.
+ *
+ * @return mixed
+ */
+ public static function wrapResult(mixed $value)
+ {
+ if (is_string($value)) {
+ // Error values cannot be "wrapped"
+ if (preg_match('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
+ // Return Excel errors "as is"
+ return $value;
+ }
+
+ // Return strings wrapped in quotes
+ return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
+ } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+ // Convert numeric errors to NaN error
+ return Information\ExcelError::NAN();
+ }
+
+ return $value;
+ }
+
+ /**
+ * Remove quotes used as a wrapper to identify string values.
+ *
+ * @return mixed
+ */
+ public static function unwrapResult(mixed $value)
+ {
+ if (is_string($value)) {
+ if ((isset($value[0])) && ($value[0] == self::FORMULA_STRING_QUOTE) && (substr($value, -1) == self::FORMULA_STRING_QUOTE)) {
+ return substr($value, 1, -1);
+ }
+ // Convert numeric errors to NAN error
+ } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
+ return Information\ExcelError::NAN();
+ }
+
+ return $value;
+ }
+
+ /**
+ * Calculate cell value (using formula from a cell ID)
+ * Retained for backward compatibility.
+ *
+ * @param Cell $cell Cell to calculate
+ *
+ * @return mixed
+ */
+ public function calculate(?Cell $cell = null)
+ {
+ try {
+ return $this->calculateCellValue($cell);
+ } catch (\Exception $e) {
+ throw new Exception($e->getMessage());
+ }
+ }
+
+ /**
+ * Calculate the value of a cell formula.
+ *
+ * @param Cell $cell Cell to calculate
+ * @param bool $resetLog Flag indicating whether the debug log should be reset or not
+ *
+ * @return mixed
+ */
+ public function calculateCellValue(?Cell $cell = null, $resetLog = true)
+ {
+ if ($cell === null) {
+ return null;
+ }
+
+ $returnArrayAsType = self::$returnArrayAsType;
+ if ($resetLog) {
+ // Initialise the logging settings if requested
+ $this->formulaError = null;
+ $this->debugLog->clearLog();
+ $this->cyclicReferenceStack->clear();
+ $this->cyclicFormulaCounter = 1;
+
+ self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
+ }
+
+ // Execute the calculation for the cell formula
+ $this->cellStack[] = [
+ 'sheet' => $cell->getWorksheet()->getTitle(),
+ 'cell' => $cell->getCoordinate(),
+ ];
+
+ $cellAddressAttempted = false;
+ $cellAddress = null;
+
+ try {
+ $result = self::unwrapResult($this->_calculateFormulaValue($cell->getValue(), $cell->getCoordinate(), $cell));
+ if ($this->spreadsheet === null) {
+ throw new Exception('null spreadsheet in calculateCellValue');
+ }
+ $cellAddressAttempted = true;
+ $cellAddress = array_pop($this->cellStack);
+ if ($cellAddress === null) {
+ throw new Exception('null cellAddress in calculateCellValue');
+ }
+ $testSheet = $this->spreadsheet->getSheetByName($cellAddress['sheet']);
+ if ($testSheet === null) {
+ throw new Exception('worksheet not found in calculateCellValue');
+ }
+ $testSheet->getCell($cellAddress['cell']);
+ } catch (\Exception $e) {
+ if (!$cellAddressAttempted) {
+ $cellAddress = array_pop($this->cellStack);
+ }
+ if ($this->spreadsheet !== null && is_array($cellAddress) && array_key_exists('sheet', $cellAddress)) {
+ $testSheet = $this->spreadsheet->getSheetByName($cellAddress['sheet']);
+ if ($testSheet !== null && array_key_exists('cell', $cellAddress)) {
+ $testSheet->getCell($cellAddress['cell']);
+ }
+ }
+
+ throw new Exception($e->getMessage(), $e->getCode(), $e);
+ }
+
+ if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+ self::$returnArrayAsType = $returnArrayAsType;
+ $testResult = Functions::flattenArray($result);
+ if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
+ return Information\ExcelError::VALUE();
+ }
+ // If there's only a single cell in the array, then we allow it
+ if (count($testResult) != 1) {
+ // If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
+ $r = array_keys($result);
+ $r = array_shift($r);
+ if (!is_numeric($r)) {
+ return Information\ExcelError::VALUE();
+ }
+ if (is_array($result[$r])) {
+ $c = array_keys($result[$r]);
+ $c = array_shift($c);
+ if (!is_numeric($c)) {
+ return Information\ExcelError::VALUE();
+ }
+ }
+ }
+ $result = array_shift($testResult);
+ }
+ self::$returnArrayAsType = $returnArrayAsType;
+
+ if ($result === null && $cell->getWorksheet()->getSheetView()->getShowZeros()) {
+ return 0;
+ } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
+ return Information\ExcelError::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validate and parse a formula string.
+ *
+ * @param string $formula Formula to parse
+ *
+ * @return array|bool
+ */
+ public function parseFormula($formula)
+ {
+ // Basic validation that this is indeed a formula
+ // We return an empty array if not
+ $formula = trim($formula);
+ if ((!isset($formula[0])) || ($formula[0] != '=')) {
+ return [];
+ }
+ $formula = ltrim(substr($formula, 1));
+ if (!isset($formula[0])) {
+ return [];
+ }
+
+ // Parse the formula and return the token stack
+ return $this->internalParseFormula($formula);
+ }
+
+ /**
+ * Calculate the value of a formula.
+ *
+ * @param string $formula Formula to parse
+ * @param string $cellID Address of the cell to calculate
+ * @param Cell $cell Cell to calculate
+ *
+ * @return mixed
+ */
+ public function calculateFormula($formula, $cellID = null, ?Cell $cell = null)
+ {
+ // Initialise the logging settings
+ $this->formulaError = null;
+ $this->debugLog->clearLog();
+ $this->cyclicReferenceStack->clear();
+
+ $resetCache = $this->getCalculationCacheEnabled();
+ if ($this->spreadsheet !== null && $cellID === null && $cell === null) {
+ $cellID = 'A1';
+ $cell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
+ } else {
+ // Disable calculation cacheing because it only applies to cell calculations, not straight formulae
+ // But don't actually flush any cache
+ $this->calculationCacheEnabled = false;
+ }
+
+ // Execute the calculation
+ try {
+ $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $cell));
+ } catch (\Exception $e) {
+ throw new Exception($e->getMessage());
+ }
+
+ if ($this->spreadsheet === null) {
+ // Reset calculation cacheing to its previous state
+ $this->calculationCacheEnabled = $resetCache;
+ }
+
+ return $result;
+ }
+
+ public function getValueFromCache(string $cellReference, mixed &$cellValue): bool
+ {
+ $this->debugLog->writeDebugLog('Testing cache value for cell %s', $cellReference);
+ // Is calculation cacheing enabled?
+ // If so, is the required value present in calculation cache?
+ if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
+ $this->debugLog->writeDebugLog('Retrieving value for cell %s from cache', $cellReference);
+ // Return the cached result
+
+ $cellValue = $this->calculationCache[$cellReference];
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param string $cellReference
+ */
+ public function saveValueToCache($cellReference, mixed $cellValue): void
+ {
+ if ($this->calculationCacheEnabled) {
+ $this->calculationCache[$cellReference] = $cellValue;
+ }
+ }
+
+ /**
+ * Parse a cell formula and calculate its value.
+ *
+ * @param string $formula The formula to parse and calculate
+ * @param string $cellID The ID (e.g. A3) of the cell that we are calculating
+ * @param Cell $cell Cell to calculate
+ * @param bool $ignoreQuotePrefix If set to true, evaluate the formyla even if the referenced cell is quote prefixed
+ *
+ * @return mixed
+ */
+ public function _calculateFormulaValue($formula, $cellID = null, ?Cell $cell = null, bool $ignoreQuotePrefix = false)
+ {
+ $cellValue = null;
+
+ // Quote-Prefixed cell values cannot be formulae, but are treated as strings
+ if ($cell !== null && $ignoreQuotePrefix === false && $cell->getStyle()->getQuotePrefix() === true) {
+ return self::wrapResult((string) $formula);
+ }
+
+ if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
+ return self::wrapResult($formula);
+ }
+
+ // Basic validation that this is indeed a formula
+ // We simply return the cell value if not
+ $formula = trim($formula);
+ if ($formula[0] != '=') {
+ return self::wrapResult($formula);
+ }
+ $formula = ltrim(substr($formula, 1));
+ if (!isset($formula[0])) {
+ return self::wrapResult($formula);
+ }
+
+ $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
+ $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
+ $wsCellReference = $wsTitle . '!' . $cellID;
+
+ if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
+ return $cellValue;
+ }
+ $this->debugLog->writeDebugLog('Evaluating formula for cell %s', $wsCellReference);
+
+ if (($wsTitle[0] !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
+ if ($this->cyclicFormulaCount <= 0) {
+ $this->cyclicFormulaCell = '';
+
+ return $this->raiseFormulaError('Cyclic Reference in Formula');
+ } elseif ($this->cyclicFormulaCell === $wsCellReference) {
+ ++$this->cyclicFormulaCounter;
+ if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
+ $this->cyclicFormulaCell = '';
+
+ return $cellValue;
+ }
+ } elseif ($this->cyclicFormulaCell == '') {
+ if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
+ return $cellValue;
+ }
+ $this->cyclicFormulaCell = $wsCellReference;
+ }
+ }
+
+ $this->debugLog->writeDebugLog('Formula for cell %s is %s', $wsCellReference, $formula);
+ // Parse the formula onto the token stack and calculate the value
+ $this->cyclicReferenceStack->push($wsCellReference);
+
+ $cellValue = $this->processTokenStack($this->internalParseFormula($formula, $cell), $cellID, $cell);
+ $this->cyclicReferenceStack->pop();
+
+ // Save to calculation cache
+ if ($cellID !== null) {
+ $this->saveValueToCache($wsCellReference, $cellValue);
+ }
+
+ // Return the calculated value
+ return $cellValue;
+ }
+
+ /**
+ * Ensure that paired matrix operands are both matrices and of the same size.
+ *
+ * @param mixed $operand1 First matrix operand
+ * @param mixed $operand2 Second matrix operand
+ * @param int $resize Flag indicating whether the matrices should be resized to match
+ * and (if so), whether the smaller dimension should grow or the
+ * larger should shrink.
+ * 0 = no resize
+ * 1 = shrink to fit
+ * 2 = extend to fit
+ */
+ private static function checkMatrixOperands(mixed &$operand1, mixed &$operand2, $resize = 1): array
+ {
+ // Examine each of the two operands, and turn them into an array if they aren't one already
+ // Note that this function should only be called if one or both of the operand is already an array
+ if (!is_array($operand1)) {
+ [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand2);
+ $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
+ $resize = 0;
+ } elseif (!is_array($operand2)) {
+ [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand1);
+ $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
+ $resize = 0;
+ }
+
+ [$matrix1Rows, $matrix1Columns] = self::getMatrixDimensions($operand1);
+ [$matrix2Rows, $matrix2Columns] = self::getMatrixDimensions($operand2);
+ if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
+ $resize = 1;
+ }
+
+ if ($resize == 2) {
+ // Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
+ self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
+ } elseif ($resize == 1) {
+ // Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
+ self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
+ }
+
+ return [$matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns];
+ }
+
+ /**
+ * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0.
+ *
+ * @param array $matrix matrix operand
+ *
+ * @return int[] An array comprising the number of rows, and number of columns
+ */
+ public static function getMatrixDimensions(array &$matrix): array
+ {
+ $matrixRows = count($matrix);
+ $matrixColumns = 0;
+ foreach ($matrix as $rowKey => $rowValue) {
+ if (!is_array($rowValue)) {
+ $matrix[$rowKey] = [$rowValue];
+ $matrixColumns = max(1, $matrixColumns);
+ } else {
+ $matrix[$rowKey] = array_values($rowValue);
+ $matrixColumns = max(count($rowValue), $matrixColumns);
+ }
+ }
+ $matrix = array_values($matrix);
+
+ return [$matrixRows, $matrixColumns];
+ }
+
+ /**
+ * Ensure that paired matrix operands are both matrices of the same size.
+ *
+ * @param array $matrix1 First matrix operand
+ * @param array $matrix2 Second matrix operand
+ * @param int $matrix1Rows Row size of first matrix operand
+ * @param int $matrix1Columns Column size of first matrix operand
+ * @param int $matrix2Rows Row size of second matrix operand
+ * @param int $matrix2Columns Column size of second matrix operand
+ */
+ private static function resizeMatricesShrink(array &$matrix1, array &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
+ {
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Rows < $matrix1Rows) {
+ for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
+ unset($matrix1[$i]);
+ }
+ }
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ unset($matrix1[$i][$j]);
+ }
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Rows < $matrix2Rows) {
+ for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
+ unset($matrix2[$i]);
+ }
+ }
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ unset($matrix2[$i][$j]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Ensure that paired matrix operands are both matrices of the same size.
+ *
+ * @param array $matrix1 First matrix operand
+ * @param array $matrix2 Second matrix operand
+ * @param int $matrix1Rows Row size of first matrix operand
+ * @param int $matrix1Columns Column size of first matrix operand
+ * @param int $matrix2Rows Row size of second matrix operand
+ * @param int $matrix2Columns Column size of second matrix operand
+ */
+ private static function resizeMatricesExtend(array &$matrix1, array &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
+ {
+ if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
+ if ($matrix2Columns < $matrix1Columns) {
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ $x = $matrix2[$i][$matrix2Columns - 1];
+ for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
+ $matrix2[$i][$j] = $x;
+ }
+ }
+ }
+ if ($matrix2Rows < $matrix1Rows) {
+ $x = $matrix2[$matrix2Rows - 1];
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ $matrix2[$i] = $x;
+ }
+ }
+ }
+
+ if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
+ if ($matrix1Columns < $matrix2Columns) {
+ for ($i = 0; $i < $matrix1Rows; ++$i) {
+ $x = $matrix1[$i][$matrix1Columns - 1];
+ for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
+ $matrix1[$i][$j] = $x;
+ }
+ }
+ }
+ if ($matrix1Rows < $matrix2Rows) {
+ $x = $matrix1[$matrix1Rows - 1];
+ for ($i = 0; $i < $matrix2Rows; ++$i) {
+ $matrix1[$i] = $x;
+ }
+ }
+ }
+ }
+
+ /**
+ * Format details of an operand for display in the log (based on operand type).
+ *
+ * @param mixed $value First matrix operand
+ *
+ * @return mixed
+ */
+ private function showValue(mixed $value)
+ {
+ if ($this->debugLog->getWriteDebugLog()) {
+ $testArray = Functions::flattenArray($value);
+ if (count($testArray) == 1) {
+ $value = array_pop($testArray);
+ }
+
+ if (is_array($value)) {
+ $returnMatrix = [];
+ $pad = $rpad = ', ';
+ foreach ($value as $row) {
+ if (is_array($row)) {
+ $returnMatrix[] = implode($pad, array_map([$this, 'showValue'], $row));
+ $rpad = '; ';
+ } else {
+ $returnMatrix[] = $this->showValue($row);
+ }
+ }
+
+ return '{ ' . implode($rpad, $returnMatrix) . ' }';
+ } elseif (is_string($value) && (trim($value, self::FORMULA_STRING_QUOTE) == $value)) {
+ return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
+ } elseif (is_bool($value)) {
+ return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+ } elseif ($value === null) {
+ return self::$localeBoolean['NULL'];
+ }
+ }
+
+ return Functions::flattenSingleValue($value);
+ }
+
+ /**
+ * Format type and details of an operand for display in the log (based on operand type).
+ *
+ * @param mixed $value First matrix operand
+ */
+ private function showTypeDetails(mixed $value): ?string
+ {
+ if ($this->debugLog->getWriteDebugLog()) {
+ $testArray = Functions::flattenArray($value);
+ if (count($testArray) == 1) {
+ $value = array_pop($testArray);
+ }
+
+ if ($value === null) {
+ return 'a NULL value';
+ } elseif (is_float($value)) {
+ $typeString = 'a floating point number';
+ } elseif (is_int($value)) {
+ $typeString = 'an integer number';
+ } elseif (is_bool($value)) {
+ $typeString = 'a boolean';
+ } elseif (is_array($value)) {
+ $typeString = 'a matrix';
+ } else {
+ if ($value == '') {
+ return 'an empty string';
+ } elseif ($value[0] == '#') {
+ return 'a ' . $value . ' error';
+ }
+ $typeString = 'a string';
+ }
+
+ return $typeString . ' with a value of ' . $this->showValue($value);
+ }
+
+ return null;
+ }
+
+ /**
+ * @return false|string False indicates an error
+ */
+ private function convertMatrixReferences(string $formula): false|string
+ {
+ static $matrixReplaceFrom = [self::FORMULA_OPEN_MATRIX_BRACE, ';', self::FORMULA_CLOSE_MATRIX_BRACE];
+ static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'];
+
+ // Convert any Excel matrix references to the MKMATRIX() function
+ if (str_contains($formula, self::FORMULA_OPEN_MATRIX_BRACE)) {
+ // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
+ if (str_contains($formula, self::FORMULA_STRING_QUOTE)) {
+ // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
+ // the formula
+ $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
+ // Open and Closed counts used for trapping mismatched braces in the formula
+ $openCount = $closeCount = 0;
+ $notWithinQuotes = false;
+ foreach ($temp as &$value) {
+ // Only count/replace in alternating array entries
+ $notWithinQuotes = $notWithinQuotes === false;
+ if ($notWithinQuotes === true) {
+ $openCount += substr_count($value, self::FORMULA_OPEN_MATRIX_BRACE);
+ $closeCount += substr_count($value, self::FORMULA_CLOSE_MATRIX_BRACE);
+ $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
+ } else {
+ // If there's no quoted strings, then we do a simple count/replace
+ $openCount = substr_count($formula, self::FORMULA_OPEN_MATRIX_BRACE);
+ $closeCount = substr_count($formula, self::FORMULA_CLOSE_MATRIX_BRACE);
+ $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
+ }
+ // Trap for mismatched braces and trigger an appropriate error
+ if ($openCount < $closeCount) {
+ if ($openCount > 0) {
+ return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
+ }
+
+ return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered");
+ } elseif ($openCount > $closeCount) {
+ if ($closeCount > 0) {
+ return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
+ }
+
+ return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered");
+ }
+ }
+
+ return $formula;
+ }
+
+ /**
+ * Binary Operators.
+ * These operators always work on two values.
+ * Array key is the operator, the value indicates whether this is a left or right associative operator.
+ *
+ * @var array
+ */
+ private static $operatorAssociativity = [
+ '^' => 0, // Exponentiation
+ '*' => 0, '/' => 0, // Multiplication and Division
+ '+' => 0, '-' => 0, // Addition and Subtraction
+ '&' => 0, // Concatenation
+ '∪' => 0, '∩' => 0, ':' => 0, // Union, Intersect and Range
+ '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, // Comparison
+ ];
+
+ /**
+ * Comparison (Boolean) Operators.
+ * These operators work on two values, but always return a boolean result.
+ *
+ * @var array
+ */
+ private static $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true];
+
+ /**
+ * Operator Precedence.
+ * This list includes all valid operators, whether binary (including boolean) or unary (such as %).
+ * Array key is the operator, the value is its precedence.
+ *
+ * @var array
+ */
+ private static $operatorPrecedence = [
+ ':' => 9, // Range
+ '∩' => 8, // Intersect
+ '∪' => 7, // Union
+ '~' => 6, // Negation
+ '%' => 5, // Percentage
+ '^' => 4, // Exponentiation
+ '*' => 3, '/' => 3, // Multiplication and Division
+ '+' => 2, '-' => 2, // Addition and Subtraction
+ '&' => 1, // Concatenation
+ '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0, // Comparison
+ ];
+
+ // Convert infix to postfix notation
+
+ /**
+ * @param string $formula
+ *
+ * @return array|false
+ */
+ private function internalParseFormula($formula, ?Cell $cell = null): bool|array
+ {
+ if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
+ return false;
+ }
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
+ // so we store the parent worksheet so that we can re-attach it when necessary
+ $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
+
+ $regexpMatchString = '/^((?' . self::CALCULATION_REGEXP_STRING
+ . ')|(?' . self::CALCULATION_REGEXP_FUNCTION
+ . ')|(?' . self::CALCULATION_REGEXP_CELLREF
+ . ')|(?' . self::CALCULATION_REGEXP_COLUMN_RANGE
+ . ')|(?' . self::CALCULATION_REGEXP_ROW_RANGE
+ . ')|(?' . self::CALCULATION_REGEXP_NUMBER
+ . ')|(?' . self::CALCULATION_REGEXP_OPENBRACE
+ . ')|(?' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE
+ . ')|(?' . self::CALCULATION_REGEXP_DEFINEDNAME
+ . ')|(?' . self::CALCULATION_REGEXP_ERROR
+ . '))/sui';
+
+ // Start with initialisation
+ $index = 0;
+ $stack = new Stack($this->branchPruner);
+ $output = [];
+ $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a
+ // - is a negation or + is a positive operator rather than an operation
+ $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand
+ // should be null in a function call
+
+ // 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)
+ $this->branchPruner->initialiseForLoop();
+
+ $opCharacter = $formula[$index]; // Get the first character of the value at the current index position
+
+ // Check for two-character operators (e.g. >=, <=, <>)
+ if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
+ $opCharacter .= $formula[++$index];
+ }
+ // Find out if we're currently at the beginning of a number, variable, cell/row/column reference,
+ // function, defined name, structured reference, parenthesis, error or operand
+ $isOperandOrFunction = (bool) preg_match($regexpMatchString, substr($formula, $index), $match);
+
+ $expectingOperatorCopy = $expectingOperator;
+ if ($opCharacter === '-' && !$expectingOperator) { // Is it a negation instead of a minus?
+ // Put a negation on the stack
+ $stack->push('Unary Operator', '~');
+ ++$index; // and drop the negation symbol
+ } elseif ($opCharacter === '%' && $expectingOperator) {
+ // Put a percentage on the stack
+ $stack->push('Unary Operator', '%');
+ ++$index;
+ } elseif ($opCharacter === '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
+ ++$index; // Drop the redundant plus symbol
+ } elseif ((($opCharacter === '~') || ($opCharacter === '∩') || ($opCharacter === '∪')) && (!$isOperandOrFunction)) {
+ // We have to explicitly deny a tilde, union or intersect because they are legal
+ return $this->raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression
+ } elseif ((isset(self::CALCULATION_OPERATORS[$opCharacter]) || $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack?
+ while (
+ $stack->count() > 0
+ && ($o2 = $stack->last())
+ && isset(self::CALCULATION_OPERATORS[$o2['value']])
+ && @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
+ ) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+
+ // Finally put our current operator onto the stack
+ $stack->push('Binary Operator', $opCharacter);
+
+ ++$index;
+ $expectingOperator = false;
+ } elseif ($opCharacter === ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
+ $expectingOperand = false;
+ while (($o2 = $stack->pop()) && $o2['value'] !== '(') { // Pop off the stack back to the last (
+ $output[] = $o2;
+ }
+ $d = $stack->last(2);
+
+ // Branch pruning we decrease the depth whether is it a function
+ // call or a parenthesis
+ $this->branchPruner->decrementDepth();
+
+ if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'], $matches)) {
+ // Did this parenthesis just close a function?
+ try {
+ $this->branchPruner->closingBrace($d['value']);
+ } catch (Exception $e) {
+ return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
+ }
+
+ $functionName = $matches[1]; // Get the function name
+ $d = $stack->pop();
+ $argumentCount = $d['value'] ?? 0; // See how many arguments there were (argument count is the next value stored on the stack)
+ $output[] = $d; // Dump the argument count on the output
+ $output[] = $stack->pop(); // Pop the function and push onto the output
+ if (isset(self::$controlFunctions[$functionName])) {
+ $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
+ } elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) {
+ $expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount'];
+ } else { // did we somehow push a non-function on the stack? this should never happen
+ return $this->raiseFormulaError('Formula Error: Internal error, non-function on stack');
+ }
+ // Check the argument count
+ $argumentCountError = false;
+ $expectedArgumentCountString = null;
+ if (is_numeric($expectedArgumentCount)) {
+ if ($expectedArgumentCount < 0) {
+ if ($argumentCount > abs($expectedArgumentCount)) {
+ $argumentCountError = true;
+ $expectedArgumentCountString = 'no more than ' . abs($expectedArgumentCount);
+ }
+ } else {
+ if ($argumentCount != $expectedArgumentCount) {
+ $argumentCountError = true;
+ $expectedArgumentCountString = $expectedArgumentCount;
+ }
+ }
+ } elseif ($expectedArgumentCount != '*') {
+ preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
+ switch ($argMatch[2] ?? '') {
+ case '+':
+ if ($argumentCount < $argMatch[1]) {
+ $argumentCountError = true;
+ $expectedArgumentCountString = $argMatch[1] . ' or more ';
+ }
+
+ break;
+ case '-':
+ if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
+ $argumentCountError = true;
+ $expectedArgumentCountString = 'between ' . $argMatch[1] . ' and ' . $argMatch[3];
+ }
+
+ break;
+ case ',':
+ if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
+ $argumentCountError = true;
+ $expectedArgumentCountString = 'either ' . $argMatch[1] . ' or ' . $argMatch[3];
+ }
+
+ break;
+ }
+ }
+ if ($argumentCountError) {
+ return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, " . $expectedArgumentCountString . ' expected');
+ }
+ }
+ ++$index;
+ } elseif ($opCharacter === ',') { // Is this the separator for function arguments?
+ try {
+ $this->branchPruner->argumentSeparator();
+ } catch (Exception $e) {
+ return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
+ }
+
+ while (($o2 = $stack->pop()) && $o2['value'] !== '(') { // Pop off the stack back to the last (
+ $output[] = $o2; // pop the argument expression stuff and push onto the output
+ }
+ // If we've a comma when we're expecting an operand, then what we actually have is a null operand;
+ // so push a null onto the stack
+ if (($expectingOperand) || (!$expectingOperator)) {
+ $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => 'NULL'];
+ }
+ // make sure there was a function
+ $d = $stack->last(2);
+ if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'] ?? '', $matches)) {
+ // Can we inject a dummy function at this point so that the braces at least have some context
+ // because at least the braces are paired up (at this stage in the formula)
+ // MS Excel allows this if the content is cell references; but doesn't allow actual values,
+ // but at this point, we can't differentiate (so allow both)
+ return $this->raiseFormulaError('Formula Error: Unexpected ,');
+ }
+
+ /** @var array $d */
+ $d = $stack->pop();
+ ++$d['value']; // increment the argument count
+
+ $stack->pushStackItem($d);
+ $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
+
+ $expectingOperator = false;
+ $expectingOperand = true;
+ ++$index;
+ } elseif ($opCharacter === '(' && !$expectingOperator) {
+ // Branch pruning: we go deeper
+ $this->branchPruner->incrementDepth();
+ $stack->push('Brace', '(', null);
+ ++$index;
+ } elseif ($isOperandOrFunction && !$expectingOperatorCopy) {
+ // do we now have a function/variable/number?
+ $expectingOperator = true;
+ $expectingOperand = false;
+ $val = $match[1];
+ $length = strlen($val);
+
+ if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
+ $val = (string) preg_replace('/\s/u', '', $val);
+ if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
+ $valToUpper = strtoupper($val);
+ } else {
+ $valToUpper = 'NAME.ERROR(';
+ }
+ // here $matches[1] will contain values like "IF"
+ // and $val "IF("
+
+ $this->branchPruner->functionCall($valToUpper);
+
+ $stack->push('Function', $valToUpper);
+ // tests if the function is closed right after opening
+ $ax = preg_match('/^\s*\)/u', substr($formula, $index + $length));
+ if ($ax) {
+ $stack->push('Operand Count for Function ' . $valToUpper . ')', 0);
+ $expectingOperator = true;
+ } else {
+ $stack->push('Operand Count for Function ' . $valToUpper . ')', 1);
+ $expectingOperator = false;
+ }
+ $stack->push('Brace', '(');
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $val, $matches)) {
+ // Watch for this case-change when modifying to allow cell references in different worksheets...
+ // Should only be applied to the actual cell column, not the worksheet name
+ // If the last entry on the stack was a : operator, then we have a cell range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
+ // If we have a worksheet reference, then we're playing with a 3D reference
+ if ($matches[2] === '') {
+ // Otherwise, we 'inherit' the worksheet reference from the start cell reference
+ // The start of the cell range reference should be the last entry in $output
+ $rangeStartCellRef = $output[count($output) - 1]['value'] ?? '';
+ if ($rangeStartCellRef === ':') {
+ // Do we have chained range operators?
+ $rangeStartCellRef = $output[count($output) - 2]['value'] ?? '';
+ }
+ preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $rangeStartCellRef, $rangeStartMatches);
+ if (array_key_exists(2, $rangeStartMatches)) {
+ if ($rangeStartMatches[2] > '') {
+ $val = $rangeStartMatches[2] . '!' . $val;
+ }
+ } else {
+ $val = Information\ExcelError::REF();
+ }
+ } else {
+ $rangeStartCellRef = $output[count($output) - 1]['value'] ?? '';
+ if ($rangeStartCellRef === ':') {
+ // Do we have chained range operators?
+ $rangeStartCellRef = $output[count($output) - 2]['value'] ?? '';
+ }
+ preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $rangeStartCellRef, $rangeStartMatches);
+ if ($rangeStartMatches[2] !== $matches[2]) {
+ return $this->raiseFormulaError('3D Range references are not yet supported');
+ }
+ }
+ } elseif (!str_contains($val, '!') && $pCellParent !== null) {
+ $worksheet = $pCellParent->getTitle();
+ $val = "'{$worksheet}'!{$val}";
+ }
+ // unescape any apostrophes or double quotes in worksheet name
+ $val = str_replace(["''", '""'], ["'", '"'], $val);
+ $outputItem = $stack->getStackItem('Cell Reference', $val, $val);
+
+ $output[] = $outputItem;
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE . '$/miu', $val, $matches)) {
+ try {
+ $structuredReference = Operands\StructuredReference::fromParser($formula, $index, $matches);
+ } catch (Exception $e) {
+ return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
+ }
+
+ $val = $structuredReference->value();
+ $length = strlen($val);
+ $outputItem = $stack->getStackItem(Operands\StructuredReference::NAME, $structuredReference, null);
+
+ $output[] = $outputItem;
+ $expectingOperator = true;
+ } else {
+ // it's a variable, constant, string, number or boolean
+ $localeConstant = false;
+ $stackItemType = 'Value';
+ $stackItemReference = null;
+
+ // If the last entry on the stack was a : operator, then we may have a row or column range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
+ $stackItemType = 'Cell Reference';
+
+ if (
+ !is_numeric($val)
+ && ((ctype_alpha($val) === false || strlen($val) > 3))
+ && (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $val) !== false)
+ && ($this->spreadsheet === null || $this->spreadsheet->getNamedRange($val) !== null)
+ ) {
+ $namedRange = ($this->spreadsheet === null) ? null : $this->spreadsheet->getNamedRange($val);
+ if ($namedRange !== null) {
+ $stackItemType = 'Defined Name';
+ $address = str_replace('$', '', $namedRange->getValue());
+ $stackItemReference = $val;
+ if (str_contains($address, ':')) {
+ // We'll need to manipulate the stack for an actual named range rather than a named cell
+ $fromTo = explode(':', $address);
+ $to = array_pop($fromTo);
+ foreach ($fromTo as $from) {
+ $output[] = $stack->getStackItem($stackItemType, $from, $stackItemReference);
+ $output[] = $stack->getStackItem('Binary Operator', ':');
+ }
+ $address = $to;
+ }
+ $val = $address;
+ }
+ } elseif ($val === Information\ExcelError::REF()) {
+ $stackItemReference = $val;
+ } else {
+ /** @var non-empty-string $startRowColRef */
+ $startRowColRef = $output[count($output) - 1]['value'] ?? '';
+ [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
+ $rangeSheetRef = $rangeWS1;
+ if ($rangeWS1 !== '') {
+ $rangeWS1 .= '!';
+ }
+ $rangeSheetRef = trim($rangeSheetRef, "'");
+ [$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
+ if ($rangeWS2 !== '') {
+ $rangeWS2 .= '!';
+ } else {
+ $rangeWS2 = $rangeWS1;
+ }
+
+ $refSheet = $pCellParent;
+ if ($pCellParent !== null && $rangeSheetRef !== '' && $rangeSheetRef !== $pCellParent->getTitle()) {
+ $refSheet = $pCellParent->getParentOrThrow()->getSheetByName($rangeSheetRef);
+ }
+
+ if (ctype_digit($val) && $val <= 1048576) {
+ // Row range
+ $stackItemType = 'Row Reference';
+ /** @var int $valx */
+ $valx = $val;
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($valx) : AddressRange::MAX_COLUMN; // Max 16,384 columns for Excel2007
+ $val = "{$rangeWS2}{$endRowColRef}{$val}";
+ } elseif (ctype_alpha($val) && strlen($val ?? '') <= 3) {
+ // Column range
+ $stackItemType = 'Column Reference';
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : AddressRange::MAX_ROW; // Max 1,048,576 rows for Excel2007
+ $val = "{$rangeWS2}{$val}{$endRowColRef}";
+ }
+ $stackItemReference = $val;
+ }
+ } elseif ($opCharacter === self::FORMULA_STRING_QUOTE) {
+ // UnEscape any quotes within the string
+ $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val)));
+ } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
+ $stackItemType = 'Constant';
+ $excelConstant = trim(strtoupper($val));
+ $val = self::$excelConstants[$excelConstant];
+ $stackItemReference = $excelConstant;
+ } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
+ $stackItemType = 'Constant';
+ $val = self::$excelConstants[$localeConstant];
+ $stackItemReference = $localeConstant;
+ } elseif (
+ preg_match('/^' . self::CALCULATION_REGEXP_ROW_RANGE . '/miu', substr($formula, $index), $rowRangeReference)
+ ) {
+ $val = $rowRangeReference[1];
+ $length = strlen($rowRangeReference[1]);
+ $stackItemType = 'Row Reference';
+ // unescape any apostrophes or double quotes in worksheet name
+ $val = str_replace(["''", '""'], ["'", '"'], $val);
+ $column = 'A';
+ if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) {
+ $column = $pCellParent->getHighestDataColumn($val);
+ }
+ $val = "{$rowRangeReference[2]}{$column}{$rowRangeReference[7]}";
+ $stackItemReference = $val;
+ } elseif (
+ preg_match('/^' . self::CALCULATION_REGEXP_COLUMN_RANGE . '/miu', substr($formula, $index), $columnRangeReference)
+ ) {
+ $val = $columnRangeReference[1];
+ $length = strlen($val);
+ $stackItemType = 'Column Reference';
+ // unescape any apostrophes or double quotes in worksheet name
+ $val = str_replace(["''", '""'], ["'", '"'], $val);
+ $row = '1';
+ if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) {
+ $row = $pCellParent->getHighestDataRow($val);
+ }
+ $val = "{$val}{$row}";
+ $stackItemReference = $val;
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', $val, $match)) {
+ $stackItemType = 'Defined Name';
+ $stackItemReference = $val;
+ } elseif (is_numeric($val)) {
+ if ((str_contains((string) $val, '.')) || (stripos((string) $val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+ $val = (float) $val;
+ } else {
+ $val = (int) $val;
+ }
+ }
+
+ $details = $stack->getStackItem($stackItemType, $val, $stackItemReference);
+ if ($localeConstant) {
+ $details['localeValue'] = $localeConstant;
+ }
+ $output[] = $details;
+ }
+ $index += $length;
+ } elseif ($opCharacter === '$') { // absolute row or column range
+ ++$index;
+ } elseif ($opCharacter === ')') { // miscellaneous error checking
+ if ($expectingOperand) {
+ $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => 'NULL'];
+ $expectingOperand = false;
+ $expectingOperator = true;
+ } else {
+ return $this->raiseFormulaError("Formula Error: Unexpected ')'");
+ }
+ } elseif (isset(self::CALCULATION_OPERATORS[$opCharacter]) && !$expectingOperator) {
+ return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
+ } else { // I don't even want to know what you did to get here
+ return $this->raiseFormulaError('Formula Error: An unexpected error occurred');
+ }
+ // Test for end of formula string
+ if ($index == strlen($formula)) {
+ // Did we end with an operator?.
+ // Only valid for the % unary operator
+ if ((isset(self::CALCULATION_OPERATORS[$opCharacter])) && ($opCharacter != '%')) {
+ return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
+ }
+
+ break;
+ }
+ // Ignore white space
+ while (($formula[$index] === "\n") || ($formula[$index] === "\r")) {
+ ++$index;
+ }
+
+ if ($formula[$index] === ' ') {
+ while ($formula[$index] === ' ') {
+ ++$index;
+ }
+
+ // If we're expecting an operator, but only have a space between the previous and next operands (and both are
+ // Cell References, Defined Names or Structured References) then we have an INTERSECTION operator
+ $countOutputMinus1 = count($output) - 1;
+ if (
+ ($expectingOperator)
+ && array_key_exists($countOutputMinus1, $output)
+ && is_array($output[$countOutputMinus1])
+ && array_key_exists('type', $output[$countOutputMinus1])
+ && (
+ (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/miu', substr($formula, $index), $match))
+ && ($output[$countOutputMinus1]['type'] === 'Cell Reference')
+ || (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match))
+ && ($output[$countOutputMinus1]['type'] === 'Defined Name' || $output[$countOutputMinus1]['type'] === 'Value')
+ || (preg_match('/^' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE . '.*/miu', substr($formula, $index), $match))
+ && ($output[$countOutputMinus1]['type'] === Operands\StructuredReference::NAME || $output[$countOutputMinus1]['type'] === 'Value')
+ )
+ ) {
+ while (
+ $stack->count() > 0
+ && ($o2 = $stack->last())
+ && isset(self::CALCULATION_OPERATORS[$o2['value']])
+ && @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
+ ) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+ $stack->push('Binary Operator', '∩'); // Put an Intersect Operator on the stack
+ $expectingOperator = false;
+ }
+ }
+ }
+
+ while (($op = $stack->pop()) !== null) {
+ // pop everything off the stack and push onto output
+ if ((is_array($op) && $op['value'] == '(')) {
+ return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
+ }
+ $output[] = $op;
+ }
+
+ return $output;
+ }
+
+ /**
+ * @return mixed
+ */
+ private static function dataTestReference(array &$operandData)
+ {
+ $operand = $operandData['value'];
+ if (($operandData['reference'] === null) && (is_array($operand))) {
+ $rKeys = array_keys($operand);
+ $rowKey = array_shift($rKeys);
+ if (is_array($operand[$rowKey]) === false) {
+ $operandData['value'] = $operand[$rowKey];
+
+ return $operand[$rowKey];
+ }
+
+ $cKeys = array_keys(array_keys($operand[$rowKey]));
+ $colKey = array_shift($cKeys);
+ if (ctype_upper("$colKey")) {
+ $operandData['reference'] = $colKey . $rowKey;
+ }
+ }
+
+ return $operand;
+ }
+
+ /**
+ * @param null|string $cellID
+ *
+ * @return array|false
+ */
+ private function processTokenStack(mixed $tokens, $cellID = null, ?Cell $cell = null)
+ {
+ if ($tokens === false) {
+ return false;
+ }
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
+ // so we store the parent cell collection so that we can re-attach it when necessary
+ $pCellWorksheet = ($cell !== null) ? $cell->getWorksheet() : null;
+ $pCellParent = ($cell !== null) ? $cell->getParent() : null;
+ $stack = new Stack($this->branchPruner);
+
+ // 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 = is_array($wrappedItem) ? end($wrappedItem) : $wrappedItem;
+ }
+
+ if (
+ (isset($storeValue) || $tokenData['reference'] === 'NULL')
+ && (!$storeValueAsBool || Information\ErrorValue::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 = is_array($wrappedItem) ? end($wrappedItem) : $wrappedItem;
+ }
+
+ if (
+ (isset($storeValue) || $tokenData['reference'] === 'NULL')
+ && ($storeValueAsBool || Information\ErrorValue::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 ($token instanceof Operands\StructuredReference) {
+ if ($cell === null) {
+ return $this->raiseFormulaError('Structured References must exist in a Cell context');
+ }
+
+ try {
+ $cellRange = $token->parse($cell);
+ if (str_contains($cellRange, ':')) {
+ $this->debugLog->writeDebugLog('Evaluating Structured Reference %s as Cell Range %s', $token->value(), $cellRange);
+ $rangeValue = self::getInstance($cell->getWorksheet()->getParent())->_calculateFormulaValue("={$cellRange}", $cellRange, $cell);
+ $stack->push('Value', $rangeValue);
+ $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as value %s', $token->value(), $this->showValue($rangeValue));
+ } else {
+ $this->debugLog->writeDebugLog('Evaluating Structured Reference %s as Cell %s', $token->value(), $cellRange);
+ $cellValue = $cell->getWorksheet()->getCell($cellRange)->getCalculatedValue(false);
+ $stack->push('Cell Reference', $cellValue, $cellRange);
+ $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as value %s', $token->value(), $this->showValue($cellValue));
+ }
+ } catch (Exception $e) {
+ if ($e->getCode() === Exception::CALCULATION_ENGINE_PUSH_TO_STACK) {
+ $stack->push('Error', Information\ExcelError::REF(), null);
+ $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as error value %s', $token->value(), Information\ExcelError::REF());
+ } else {
+ return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ } elseif (!is_numeric($token) && !is_object($token) && isset(self::BINARY_OPERATORS[$token])) {
+ // 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
+ // We must have two operands, error if we don't
+ if (($operand2Data = $stack->pop()) === null) {
+ return $this->raiseFormulaError('Internal error - Operand value missing from stack');
+ }
+ if (($operand1Data = $stack->pop()) === null) { // @phpstan-ignore-line
+ return $this->raiseFormulaError('Internal error - Operand value missing from stack');
+ }
+
+ $operand1 = self::dataTestReference($operand1Data);
+ $operand2 = self::dataTestReference($operand2Data);
+
+ // Log what we're doing
+ if ($token == ':') {
+ $this->debugLog->writeDebugLog('Evaluating Range %s %s %s', $this->showValue($operand1Data['reference']), $token, $this->showValue($operand2Data['reference']));
+ } else {
+ $this->debugLog->writeDebugLog('Evaluating %s %s %s', $this->showValue($operand1), $token, $this->showValue($operand2));
+ }
+
+ // Process the operation in the appropriate manner
+ switch ($token) {
+ // Comparison (Boolean) Operators
+ case '>': // Greater than
+ case '<': // Less than
+ case '>=': // Greater than or Equal to
+ case '<=': // Less than or Equal to
+ case '=': // Equality
+ case '<>': // Inequality
+ $result = $this->executeBinaryComparisonOperation($operand1, $operand2, (string) $token, $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
+
+ break;
+ // Binary Operators
+ case ':': // Range
+ if ($operand1Data['type'] === 'Defined Name') {
+ if (preg_match('/$' . self::CALCULATION_REGEXP_DEFINEDNAME . '^/mui', $operand1Data['reference']) !== false && $this->spreadsheet !== null) {
+ $definedName = $this->spreadsheet->getNamedRange($operand1Data['reference']);
+ if ($definedName !== null) {
+ $operand1Data['reference'] = $operand1Data['value'] = str_replace('$', '', $definedName->getValue());
+ }
+ }
+ }
+ if (str_contains($operand1Data['reference'] ?? '', '!')) {
+ [$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
+ } else {
+ $sheet1 = ($pCellWorksheet !== null) ? $pCellWorksheet->getTitle() : '';
+ }
+ $sheet1 ??= '';
+
+ [$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
+ if (empty($sheet2)) {
+ $sheet2 = $sheet1;
+ }
+
+ if (trim($sheet1, "'") === trim($sheet2, "'")) {
+ if ($operand1Data['reference'] === null && $cell !== null) {
+ if (is_array($operand1Data['value'])) {
+ $operand1Data['reference'] = $cell->getCoordinate();
+ } elseif ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
+ $operand1Data['reference'] = $cell->getColumn() . $operand1Data['value'];
+ } elseif (trim($operand1Data['value']) == '') {
+ $operand1Data['reference'] = $cell->getCoordinate();
+ } else {
+ $operand1Data['reference'] = $operand1Data['value'] . $cell->getRow();
+ }
+ }
+ if ($operand2Data['reference'] === null && $cell !== null) {
+ if (is_array($operand2Data['value'])) {
+ $operand2Data['reference'] = $cell->getCoordinate();
+ } elseif ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
+ $operand2Data['reference'] = $cell->getColumn() . $operand2Data['value'];
+ } elseif (trim($operand2Data['value']) == '') {
+ $operand2Data['reference'] = $cell->getCoordinate();
+ } else {
+ $operand2Data['reference'] = $operand2Data['value'] . $cell->getRow();
+ }
+ }
+
+ $oData = array_merge(explode(':', $operand1Data['reference'] ?? ''), explode(':', $operand2Data['reference'] ?? ''));
+ $oCol = $oRow = [];
+ $breakNeeded = false;
+ foreach ($oData as $oDatum) {
+ try {
+ $oCR = Coordinate::coordinateFromString($oDatum);
+ $oCol[] = Coordinate::columnIndexFromString($oCR[0]) - 1;
+ $oRow[] = $oCR[1];
+ } catch (\Exception) {
+ $stack->push('Error', Information\ExcelError::REF(), null);
+ $breakNeeded = true;
+
+ break;
+ }
+ }
+ if ($breakNeeded) {
+ break;
+ }
+ $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
+ if ($pCellParent !== null && $this->spreadsheet !== null) {
+ $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false);
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellValue));
+ $stack->push('Cell Reference', $cellValue, $cellRef);
+ } else {
+ $this->debugLog->writeDebugLog('Evaluation Result is a #REF! Error');
+ $stack->push('Error', Information\ExcelError::REF(), null);
+ }
+
+ break;
+ case '+': // Addition
+ case '-': // Subtraction
+ case '*': // Multiplication
+ case '/': // Division
+ case '^': // Exponential
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
+
+ break;
+ case '&': // Concatenation
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ $operand1 = self::boolToString($operand1);
+ $operand2 = self::boolToString($operand2);
+ if (is_array($operand1) || is_array($operand2)) {
+ if (is_string($operand1)) {
+ $operand1 = self::unwrapResult($operand1);
+ }
+ if (is_string($operand2)) {
+ $operand2 = self::unwrapResult($operand2);
+ }
+ // Ensure that both operands are arrays/matrices
+ [$rows, $columns] = self::checkMatrixOperands($operand1, $operand2, 2);
+
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ $operand1[$row][$column]
+ = Shared\StringHelper::substring(
+ self::boolToString($operand1[$row][$column])
+ . self::boolToString($operand2[$row][$column]),
+ 0,
+ DataType::MAX_STRING_LENGTH
+ );
+ }
+ }
+ $result = $operand1;
+ } else {
+ // In theory, we should truncate here.
+ // But I can't figure out a formula
+ // using the concatenation operator
+ // with literals that fits in 32K,
+ // so I don't think we can overflow here.
+ $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
+ }
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
+ $stack->push('Value', $result);
+
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
+
+ break;
+ case '∩': // Intersect
+ $rowIntersect = array_intersect_key($operand1, $operand2);
+ $cellIntersect = $oCol = $oRow = [];
+ foreach (array_keys($rowIntersect) as $row) {
+ $oRow[] = $row;
+ foreach ($rowIntersect[$row] as $col => $data) {
+ $oCol[] = Coordinate::columnIndexFromString($col) - 1;
+ $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
+ }
+ }
+ if (count(Functions::flattenArray($cellIntersect)) === 0) {
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
+ $stack->push('Error', Information\ExcelError::null(), null);
+ } else {
+ $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':'
+ . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
+ $stack->push('Value', $cellIntersect, $cellRef);
+ }
+
+ break;
+ }
+ } elseif (($token === '~') || ($token === '%')) {
+ // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
+ if (($arg = $stack->pop()) === null) {
+ return $this->raiseFormulaError('Internal error - Operand value missing from stack');
+ }
+ $arg = $arg['value'];
+ if ($token === '~') {
+ $this->debugLog->writeDebugLog('Evaluating Negation of %s', $this->showValue($arg));
+ $multiplier = -1;
+ } else {
+ $this->debugLog->writeDebugLog('Evaluating Percentile of %s', $this->showValue($arg));
+ $multiplier = 0.01;
+ }
+ if (is_array($arg)) {
+ $operand2 = $multiplier;
+ $result = $arg;
+ [$rows, $columns] = self::checkMatrixOperands($result, $operand2, 0);
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ if (self::isNumericOrBool($result[$row][$column])) {
+ $result[$row][$column] *= $multiplier;
+ } else {
+ $result[$row][$column] = self::makeError($result[$row][$column]);
+ }
+ }
+ }
+
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
+ $stack->push('Value', $result);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
+ } else {
+ $this->executeNumericBinaryOperation($multiplier, $arg, '*', $stack);
+ }
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token ?? '', $matches)) {
+ $cellRef = null;
+
+ if (isset($matches[8])) {
+ if ($cell === null) {
+ // We can't access the range, so return a REF error
+ $cellValue = Information\ExcelError::REF();
+ } else {
+ $cellRef = $matches[6] . $matches[7] . ':' . $matches[9] . $matches[10];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2], "\"'");
+ if ((str_contains($matches[2], '[')) || (str_contains($matches[2], ']'))) {
+ // It's a Reference to an external spreadsheet (not currently supported)
+ return $this->raiseFormulaError('Unable to access External Workbook');
+ }
+ $matches[2] = trim($matches[2], "\"'");
+ $this->debugLog->writeDebugLog('Evaluating Cell Range %s in worksheet %s', $cellRef, $matches[2]);
+ if ($pCellParent !== null && $this->spreadsheet !== null) {
+ $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->debugLog->writeDebugLog('Evaluation Result for cells %s in worksheet %s is %s', $cellRef, $matches[2], $this->showTypeDetails($cellValue));
+ } else {
+ $this->debugLog->writeDebugLog('Evaluating Cell Range %s in current worksheet', $cellRef);
+ if ($pCellParent !== null) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->debugLog->writeDebugLog('Evaluation Result for cells %s is %s', $cellRef, $this->showTypeDetails($cellValue));
+ }
+ }
+ } else {
+ if ($cell === null) {
+ // We can't access the cell, so return a REF error
+ $cellValue = Information\ExcelError::REF();
+ } else {
+ $cellRef = $matches[6] . $matches[7];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2], "\"'");
+ if ((str_contains($matches[2], '[')) || (str_contains($matches[2], ']'))) {
+ // It's a Reference to an external spreadsheet (not currently supported)
+ return $this->raiseFormulaError('Unable to access External Workbook');
+ }
+ $this->debugLog->writeDebugLog('Evaluating Cell %s in worksheet %s', $cellRef, $matches[2]);
+ if ($pCellParent !== null && $this->spreadsheet !== null) {
+ $cellSheet = $this->spreadsheet->getSheetByName($matches[2]);
+ if ($cellSheet && $cellSheet->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false);
+ $cell->attach($pCellParent);
+ } else {
+ $cellRef = ($cellSheet !== null) ? "'{$matches[2]}'!{$cellRef}" : $cellRef;
+ $cellValue = ($cellSheet !== null) ? null : Information\ExcelError::REF();
+ }
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->debugLog->writeDebugLog('Evaluation Result for cell %s in worksheet %s is %s', $cellRef, $matches[2], $this->showTypeDetails($cellValue));
+ } else {
+ $this->debugLog->writeDebugLog('Evaluating Cell %s in current worksheet', $cellRef);
+ if ($pCellParent !== null && $pCellParent->has($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
+ $cell->attach($pCellParent);
+ } else {
+ $cellValue = null;
+ }
+ $this->debugLog->writeDebugLog('Evaluation Result for cell %s is %s', $cellRef, $this->showTypeDetails($cellValue));
+ }
+ }
+ }
+
+ $stack->push('Cell Value', $cellValue, $cellRef);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $cellValue;
+ }
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $token ?? '', $matches)) {
+ // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
+ if ($cell !== null && $pCellParent !== null) {
+ $cell->attach($pCellParent);
+ }
+
+ $functionName = $matches[1];
+ $argCount = $stack->pop();
+ $argCount = $argCount['value'];
+ if ($functionName !== 'MKMATRIX') {
+ $this->debugLog->writeDebugLog('Evaluating Function %s() with %s argument%s', self::localeFunc($functionName), (($argCount == 0) ? 'no' : $argCount), (($argCount == 1) ? '' : 's'));
+ }
+ if ((isset(self::$phpSpreadsheetFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function
+ $passByReference = false;
+ $passCellReference = false;
+ $functionCall = null;
+ if (isset(self::$phpSpreadsheetFunctions[$functionName])) {
+ $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$phpSpreadsheetFunctions[$functionName]['passCellReference']);
+ } elseif (isset(self::$controlFunctions[$functionName])) {
+ $functionCall = self::$controlFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
+ }
+
+ // get the arguments for this function
+ $args = $argArrayVals = [];
+ $emptyArguments = [];
+ for ($i = 0; $i < $argCount; ++$i) {
+ $arg = $stack->pop();
+ $a = $argCount - $i - 1;
+ if (
+ ($passByReference)
+ && (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a]))
+ && (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])
+ ) {
+ if ($arg['reference'] === null) {
+ $args[] = $cellID;
+ if ($functionName !== 'MKMATRIX') {
+ $argArrayVals[] = $this->showValue($cellID);
+ }
+ } else {
+ $args[] = $arg['reference'];
+ if ($functionName !== 'MKMATRIX') {
+ $argArrayVals[] = $this->showValue($arg['reference']);
+ }
+ }
+ } else {
+ $emptyArguments[] = ($arg['type'] === 'Empty Argument');
+ $args[] = self::unwrapResult($arg['value']);
+ if ($functionName !== 'MKMATRIX') {
+ $argArrayVals[] = $this->showValue($arg['value']);
+ }
+ }
+ }
+
+ // Reverse the order of the arguments
+ krsort($args);
+ krsort($emptyArguments);
+
+ if ($argCount > 0 && is_array($functionCall)) {
+ $args = $this->addDefaultArgumentValues($functionCall, $args, $emptyArguments);
+ }
+
+ if (($passByReference) && ($argCount == 0)) {
+ $args[] = $cellID;
+ $argArrayVals[] = $this->showValue($cellID);
+ }
+
+ if ($functionName !== 'MKMATRIX') {
+ if ($this->debugLog->getWriteDebugLog()) {
+ krsort($argArrayVals);
+ $this->debugLog->writeDebugLog('Evaluating %s ( %s )', self::localeFunc($functionName), implode(self::$localeArgumentSeparator . ' ', Functions::flattenArray($argArrayVals)));
+ }
+ }
+
+ // Process the argument with the appropriate function call
+ $args = $this->addCellReference($args, $passCellReference, $functionCall, $cell);
+
+ if (!is_array($functionCall)) {
+ foreach ($args as &$arg) {
+ $arg = Functions::flattenSingleValue($arg);
+ }
+ unset($arg);
+ }
+
+ $result = call_user_func_array($functionCall, $args);
+
+ if ($functionName !== 'MKMATRIX') {
+ $this->debugLog->writeDebugLog('Evaluation Result for %s() function call is %s', self::localeFunc($functionName), $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 %s as %s', $excelConstant, $this->showTypeDetails(self::$excelConstants[$excelConstant]));
+ } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == self::FORMULA_STRING_QUOTE) || ($token[0] == '#')) {
+ $stack->push($tokenData['type'], $token, $tokenData['reference']);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $token;
+ }
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) {
+ // if the token is a named range or formula, evaluate it and push the result onto the stack
+ $definedName = $matches[6];
+ if ($cell === null || $pCellWorksheet === null) {
+ return $this->raiseFormulaError("undefined name '$token'");
+ }
+
+ $this->debugLog->writeDebugLog('Evaluating Defined Name %s', $definedName);
+ $namedRange = DefinedName::resolveName($definedName, $pCellWorksheet);
+ // If not Defined Name, try as Table.
+ if ($namedRange === null && $this->spreadsheet !== null) {
+ $table = $this->spreadsheet->getTableByName($definedName);
+ if ($table !== null) {
+ $tableRange = Coordinate::getRangeBoundaries($table->getRange());
+ if ($table->getShowHeaderRow()) {
+ ++$tableRange[0][1];
+ }
+ if ($table->getShowTotalsRow()) {
+ --$tableRange[1][1];
+ }
+ $tableRangeString
+ = '$' . $tableRange[0][0]
+ . '$' . $tableRange[0][1]
+ . ':'
+ . '$' . $tableRange[1][0]
+ . '$' . $tableRange[1][1];
+ $namedRange = new NamedRange($definedName, $table->getWorksheet(), $tableRangeString);
+ }
+ }
+ if ($namedRange === null) {
+ return $this->raiseFormulaError("undefined name '$definedName'");
+ }
+
+ $result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
+ } else {
+ return $this->raiseFormulaError("undefined name '$token'");
+ }
+ }
+ }
+ // when we're out of tokens, the stack should have a single element, the final result
+ if ($stack->count() != 1) {
+ return $this->raiseFormulaError('internal error');
+ }
+ $output = $stack->pop();
+ $output = $output['value'];
+
+ return $output;
+ }
+
+ private function validateBinaryOperand(mixed &$operand, mixed &$stack): bool
+ {
+ if (is_array($operand)) {
+ if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
+ do {
+ $operand = array_pop($operand);
+ } while (is_array($operand));
+ }
+ }
+ // Numbers, matrices and booleans can pass straight through, as they're already valid
+ if (is_string($operand)) {
+ // We only need special validations for the operand if it is a string
+ // Start by stripping off the quotation marks we use to identify true excel string values internally
+ if ($operand > '' && $operand[0] == self::FORMULA_STRING_QUOTE) {
+ $operand = self::unwrapResult($operand);
+ }
+ // If the string is a numeric value, we treat it as a numeric, so no further testing
+ if (!is_numeric($operand)) {
+ // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
+ if ($operand > '' && $operand[0] == '#') {
+ $stack->push('Value', $operand);
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($operand));
+
+ return false;
+ } elseif (Engine\FormattedNumber::convertToNumberIfFormatted($operand) === false) {
+ // If not a numeric, a fraction or a percentage, then it's a text string, and so can't be used in mathematical binary operations
+ $stack->push('Error', '#VALUE!');
+ $this->debugLog->writeDebugLog('Evaluation Result is a %s', $this->showTypeDetails('#VALUE!'));
+
+ return false;
+ }
+ }
+ }
+
+ // return a true if the value of the operand is one that we can use in normal binary mathematical operations
+ return true;
+ }
+
+ private function executeArrayComparison(mixed $operand1, mixed $operand2, string $operation, Stack &$stack, bool $recursingArrays): array
+ {
+ $result = [];
+ if (!is_array($operand2)) {
+ // Operand 1 is an array, Operand 2 is a scalar
+ foreach ($operand1 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2));
+ $this->executeBinaryComparisonOperation($operandData, $operand2, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } elseif (!is_array($operand1)) {
+ // Operand 1 is a scalar, Operand 2 is an array
+ foreach ($operand2 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operand1), $operation, $this->showValue($operandData));
+ $this->executeBinaryComparisonOperation($operand1, $operandData, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } else {
+ // Operand 1 and Operand 2 are both arrays
+ if (!$recursingArrays) {
+ self::checkMatrixOperands($operand1, $operand2, 2);
+ }
+ foreach ($operand1 as $x => $operandData) {
+ $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2[$x]));
+ $this->executeBinaryComparisonOperation($operandData, $operand2[$x], $operation, $stack, true);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->debugLog->writeDebugLog('Comparison Evaluation Result is %s', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Array', $result);
+
+ return $result;
+ }
+
+ private function executeBinaryComparisonOperation(mixed $operand1, mixed $operand2, string $operation, Stack &$stack, bool $recursingArrays = false): array|bool
+ {
+ // If we're dealing with matrix operations, we want a matrix result
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ return $this->executeArrayComparison($operand1, $operand2, $operation, $stack, $recursingArrays);
+ }
+
+ $result = BinaryComparison::compare($operand1, $operand2, $operation);
+
+ // Log the result details
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value', $result);
+
+ return $result;
+ }
+
+ /**
+ * @param string $operation
+ * @param Stack $stack
+ *
+ * @return bool|mixed
+ */
+ private function executeNumericBinaryOperation(mixed $operand1, mixed $operand2, $operation, &$stack)
+ {
+ // Validate the two operands
+ if (
+ ($this->validateBinaryOperand($operand1, $stack) === false)
+ || ($this->validateBinaryOperand($operand2, $stack) === false)
+ ) {
+ return false;
+ }
+
+ if (
+ (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE)
+ && ((is_string($operand1) && !is_numeric($operand1) && $operand1 !== '')
+ || (is_string($operand2) && !is_numeric($operand2) && $operand2 !== ''))
+ ) {
+ $result = Information\ExcelError::VALUE();
+ } elseif (is_array($operand1) || is_array($operand2)) {
+ // Ensure that both operands are arrays/matrices
+ if (is_array($operand1)) {
+ foreach ($operand1 as $key => $value) {
+ $operand1[$key] = Functions::flattenArray($value);
+ }
+ }
+ if (is_array($operand2)) {
+ foreach ($operand2 as $key => $value) {
+ $operand2[$key] = Functions::flattenArray($value);
+ }
+ }
+ [$rows, $columns] = self::checkMatrixOperands($operand1, $operand2, 2);
+
+ for ($row = 0; $row < $rows; ++$row) {
+ for ($column = 0; $column < $columns; ++$column) {
+ if ($operand1[$row][$column] === null) {
+ $operand1[$row][$column] = 0;
+ } elseif (!self::isNumericOrBool($operand1[$row][$column])) {
+ $operand1[$row][$column] = self::makeError($operand1[$row][$column]);
+
+ continue;
+ }
+ if ($operand2[$row][$column] === null) {
+ $operand2[$row][$column] = 0;
+ } elseif (!self::isNumericOrBool($operand2[$row][$column])) {
+ $operand1[$row][$column] = self::makeError($operand2[$row][$column]);
+
+ continue;
+ }
+ switch ($operation) {
+ case '+':
+ $operand1[$row][$column] += $operand2[$row][$column];
+
+ break;
+ case '-':
+ $operand1[$row][$column] -= $operand2[$row][$column];
+
+ break;
+ case '*':
+ $operand1[$row][$column] *= $operand2[$row][$column];
+
+ break;
+ case '/':
+ if ($operand2[$row][$column] == 0) {
+ $operand1[$row][$column] = Information\ExcelError::DIV0();
+ } else {
+ $operand1[$row][$column] /= $operand2[$row][$column];
+ }
+
+ break;
+ case '^':
+ $operand1[$row][$column] = $operand1[$row][$column] ** $operand2[$row][$column];
+
+ break;
+
+ default:
+ throw new Exception('Unsupported numeric binary operation');
+ }
+ }
+ }
+ $result = $operand1;
+ } else {
+ // If we're dealing with non-matrix operations, execute the necessary operation
+ switch ($operation) {
+ // Addition
+ case '+':
+ $result = $operand1 + $operand2;
+
+ break;
+ // Subtraction
+ case '-':
+ $result = $operand1 - $operand2;
+
+ break;
+ // Multiplication
+ case '*':
+ $result = $operand1 * $operand2;
+
+ break;
+ // Division
+ case '/':
+ if ($operand2 == 0) {
+ // Trap for Divide by Zero error
+ $stack->push('Error', Information\ExcelError::DIV0());
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails(Information\ExcelError::DIV0()));
+
+ return false;
+ }
+ $result = $operand1 / $operand2;
+
+ break;
+ // Power
+ case '^':
+ $result = $operand1 ** $operand2;
+
+ break;
+
+ default:
+ throw new Exception('Unsupported numeric binary operation');
+ }
+ }
+
+ // Log the result details
+ $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value', $result);
+
+ return $result;
+ }
+
+ /**
+ * Trigger an error, but nicely, if need be.
+ *
+ * @return false
+ */
+ protected function raiseFormulaError(string $errorMessage, int $code = 0, ?Throwable $exception = null): bool
+ {
+ $this->formulaError = $errorMessage;
+ $this->cyclicReferenceStack->clear();
+ $suppress = $this->suppressFormulaErrors ?? $this->suppressFormulaErrorsNew;
+ if (!$suppress) {
+ throw new Exception($errorMessage, $code, $exception);
+ }
+
+ return false;
+ }
+
+ /**
+ * Extract range values.
+ *
+ * @param string $range String based range representation
+ * @param Worksheet $worksheet Worksheet
+ * @param bool $resetLog Flag indicating whether calculation log should be reset or not
+ *
+ * @return array Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ */
+ public function extractCellRange(&$range = 'A1', ?Worksheet $worksheet = null, bool $resetLog = true): array
+ {
+ // Return value
+ $returnValue = [];
+
+ if ($worksheet !== null) {
+ $worksheetName = $worksheet->getTitle();
+
+ if (str_contains($range, '!')) {
+ [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
+ $worksheet = ($this->spreadsheet === null) ? null : $this->spreadsheet->getSheetByName($worksheetName);
+ }
+
+ // Extract range
+ $aReferences = Coordinate::extractAllCellReferencesInRange($range);
+ $range = "'" . $worksheetName . "'" . '!' . $range;
+ $currentCol = '';
+ $currentRow = 0;
+ if (!isset($aReferences[1])) {
+ // Single cell in range
+ sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
+ if ($worksheet !== null && $worksheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ } else {
+ // Extract cell data for all cells in the range
+ foreach ($aReferences as $reference) {
+ // Extract range
+ sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
+ if ($worksheet !== null && $worksheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ }
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Extract range values.
+ *
+ * @param string $range String based range representation
+ * @param null|Worksheet $worksheet Worksheet
+ * @param bool $resetLog Flag indicating whether calculation log should be reset or not
+ *
+ * @return array|string Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ */
+ public function extractNamedRange(string &$range = 'A1', ?Worksheet $worksheet = null, bool $resetLog = true): string|array
+ {
+ // Return value
+ $returnValue = [];
+
+ if ($worksheet !== null) {
+ if (str_contains($range, '!')) {
+ [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
+ $worksheet = ($this->spreadsheet === null) ? null : $this->spreadsheet->getSheetByName($worksheetName);
+ }
+
+ // Named range?
+ $namedRange = ($worksheet === null) ? null : DefinedName::resolveName($range, $worksheet);
+ if ($namedRange === null) {
+ return Information\ExcelError::REF();
+ }
+
+ $worksheet = $namedRange->getWorksheet();
+ $range = $namedRange->getValue();
+ $splitRange = Coordinate::splitRange($range);
+ // Convert row and column references
+ if ($worksheet !== null && ctype_alpha($splitRange[0][0])) {
+ $range = $splitRange[0][0] . '1:' . $splitRange[0][1] . $worksheet->getHighestRow();
+ } elseif ($worksheet !== null && ctype_digit($splitRange[0][0])) {
+ $range = 'A' . $splitRange[0][0] . ':' . $worksheet->getHighestColumn() . $splitRange[0][1];
+ }
+
+ // Extract range
+ $aReferences = Coordinate::extractAllCellReferencesInRange($range);
+ if (!isset($aReferences[1])) {
+ // Single cell (or single column or row) in range
+ [$currentCol, $currentRow] = Coordinate::coordinateFromString($aReferences[0]);
+ if ($worksheet !== null && $worksheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ } else {
+ // Extract cell data for all cells in the range
+ foreach ($aReferences as $reference) {
+ // Extract range
+ [$currentCol, $currentRow] = Coordinate::coordinateFromString($reference);
+ if ($worksheet !== null && $worksheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $worksheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ }
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Is a specific function implemented?
+ *
+ * @param string $function Function Name
+ */
+ public function isImplemented($function): bool
+ {
+ $function = strtoupper($function);
+ $notImplemented = !isset(self::$phpSpreadsheetFunctions[$function]) || (is_array(self::$phpSpreadsheetFunctions[$function]['functionCall']) && self::$phpSpreadsheetFunctions[$function]['functionCall'][1] === 'DUMMY');
+
+ return !$notImplemented;
+ }
+
+ /**
+ * Get a list of all implemented functions as an array of function objects.
+ */
+ public static function getFunctions(): array
+ {
+ return self::$phpSpreadsheetFunctions;
+ }
+
+ /**
+ * Get a list of implemented Excel function names.
+ */
+ public function getImplementedFunctionNames(): array
+ {
+ $returnValue = [];
+ foreach (self::$phpSpreadsheetFunctions as $functionName => $function) {
+ if ($this->isImplemented($functionName)) {
+ $returnValue[] = $functionName;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ private function addDefaultArgumentValues(array $functionCall, array $args, array $emptyArguments): array
+ {
+ $reflector = new ReflectionMethod($functionCall[0], $functionCall[1]);
+ $methodArguments = $reflector->getParameters();
+
+ if (count($methodArguments) > 0) {
+ // Apply any defaults for empty argument values
+ foreach ($emptyArguments as $argumentId => $isArgumentEmpty) {
+ if ($isArgumentEmpty === true) {
+ $reflectedArgumentId = count($args) - (int) $argumentId - 1;
+ if (
+ !array_key_exists($reflectedArgumentId, $methodArguments)
+ || $methodArguments[$reflectedArgumentId]->isVariadic()
+ ) {
+ break;
+ }
+
+ $args[$argumentId] = $this->getArgumentDefaultValue($methodArguments[$reflectedArgumentId]);
+ }
+ }
+ }
+
+ return $args;
+ }
+
+ /**
+ * @return null|mixed
+ */
+ private function getArgumentDefaultValue(ReflectionParameter $methodArgument)
+ {
+ $defaultValue = null;
+
+ if ($methodArgument->isDefaultValueAvailable()) {
+ $defaultValue = $methodArgument->getDefaultValue();
+ if ($methodArgument->isDefaultValueConstant()) {
+ $constantName = $methodArgument->getDefaultValueConstantName() ?? '';
+ // read constant value
+ if (str_contains($constantName, '::')) {
+ [$className, $constantName] = explode('::', $constantName);
+ $constantReflector = new ReflectionClassConstant($className, $constantName);
+
+ return $constantReflector->getValue();
+ }
+
+ return constant($constantName);
+ }
+ }
+
+ return $defaultValue;
+ }
+
+ /**
+ * Add cell reference if needed while making sure that it is the last argument.
+ *
+ * @param array|string $functionCall
+ */
+ private function addCellReference(array $args, bool $passCellReference, $functionCall, ?Cell $cell = null): array
+ {
+ if ($passCellReference) {
+ if (is_array($functionCall)) {
+ $className = $functionCall[0];
+ $methodName = $functionCall[1];
+
+ $reflectionMethod = new ReflectionMethod($className, $methodName);
+ $argumentCount = count($reflectionMethod->getParameters());
+ while (count($args) < $argumentCount - 1) {
+ $args[] = null;
+ }
+ }
+
+ $args[] = $cell;
+ }
+
+ return $args;
+ }
+
+ /**
+ * @return mixed|string
+ */
+ private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksheet $cellWorksheet, Stack $stack)
+ {
+ $definedNameScope = $namedRange->getScope();
+ if ($definedNameScope !== null && $definedNameScope !== $cellWorksheet) {
+ // The defined name isn't in our current scope, so #REF
+ $result = Information\ExcelError::REF();
+ $stack->push('Error', $result, $namedRange->getName());
+
+ return $result;
+ }
+
+ $definedNameValue = $namedRange->getValue();
+ $definedNameType = $namedRange->isFormula() ? 'Formula' : 'Range';
+ $definedNameWorksheet = $namedRange->getWorksheet();
+
+ if ($definedNameValue[0] !== '=') {
+ $definedNameValue = '=' . $definedNameValue;
+ }
+
+ $this->debugLog->writeDebugLog('Defined Name is a %s with a value of %s', $definedNameType, $definedNameValue);
+
+ $originalCoordinate = $cell->getCoordinate();
+ $recursiveCalculationCell = ($definedNameType !== 'Formula' && $definedNameWorksheet !== null && $definedNameWorksheet !== $cellWorksheet)
+ ? $definedNameWorksheet->getCell('A1')
+ : $cell;
+ $recursiveCalculationCellAddress = $recursiveCalculationCell->getCoordinate();
+
+ // Adjust relative references in ranges and formulae so that we execute the calculation for the correct rows and columns
+ $definedNameValue = self::$referenceHelper->updateFormulaReferencesAnyWorksheet(
+ $definedNameValue,
+ Coordinate::columnIndexFromString($cell->getColumn()) - 1,
+ $cell->getRow() - 1
+ );
+
+ $this->debugLog->writeDebugLog('Value adjusted for relative references is %s', $definedNameValue);
+
+ $recursiveCalculator = new self($this->spreadsheet);
+ $recursiveCalculator->getDebugLog()->setWriteDebugLog($this->getDebugLog()->getWriteDebugLog());
+ $recursiveCalculator->getDebugLog()->setEchoDebugLog($this->getDebugLog()->getEchoDebugLog());
+ $result = $recursiveCalculator->_calculateFormulaValue($definedNameValue, $recursiveCalculationCellAddress, $recursiveCalculationCell, true);
+ $cellWorksheet->getCell($originalCoordinate);
+
+ if ($this->getDebugLog()->getWriteDebugLog()) {
+ $this->debugLog->mergeDebugLog(array_slice($recursiveCalculator->getDebugLog()->getLog(), 3));
+ $this->debugLog->writeDebugLog('Evaluation Result for Named %s %s is %s', $definedNameType, $namedRange->getName(), $this->showTypeDetails($result));
+ }
+
+ $stack->push('Defined Name', $result, $namedRange->getName());
+
+ return $result;
+ }
+
+ public function setSuppressFormulaErrors(bool $suppressFormulaErrors): void
+ {
+ $this->suppressFormulaErrorsNew = $suppressFormulaErrors;
+ }
+
+ public function getSuppressFormulaErrors(): bool
+ {
+ return $this->suppressFormulaErrorsNew;
+ }
+
+ /**
+ * @return mixed
+ */
+ private static function boolToString(mixed $operand1)
+ {
+ if (is_bool($operand1)) {
+ $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+ } elseif ($operand1 === null) {
+ $operand1 = '';
+ }
+
+ return $operand1;
+ }
+
+ private static function isNumericOrBool(mixed $operand): bool
+ {
+ return is_numeric($operand) || is_bool($operand);
+ }
+
+ private static function makeError(mixed $operand = ''): string
+ {
+ return Information\ErrorValue::isError($operand) ? $operand : Information\ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php
new file mode 100644
index 0000000..b661faf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php
@@ -0,0 +1,21 @@
+ 1) {
+ return ExcelError::NAN();
+ }
+
+ $row = array_pop($columnData);
+
+ return array_pop($row);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
new file mode 100644
index 0000000..512557c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
@@ -0,0 +1,47 @@
+= count($fieldNames)) {
+ return null;
+ }
+
+ return $field;
+ }
+ $key = array_search($field, array_values($fieldNames), true);
+
+ return ($key !== false) ? (int) $key : null;
+ }
+
+ /**
+ * filter.
+ *
+ * Parses the selection criteria, extracts the database rows that match those criteria, and
+ * returns that subset of rows.
+ *
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ *
+ * @return mixed[]
+ */
+ protected static function filter(array $database, array $criteria): array
+ {
+ $fieldNames = array_shift($database);
+ $criteriaNames = array_shift($criteria);
+
+ // Convert the criteria into a set of AND/OR conditions with [:placeholders]
+ $query = self::buildQuery($criteriaNames, $criteria);
+
+ // Loop through each row of the database
+ return self::executeQuery($database, $query, $criteriaNames, $fieldNames);
+ }
+
+ protected static function getFilteredColumn(array $database, ?int $field, array $criteria): array
+ {
+ // reduce the database to a set of rows that match all the criteria
+ $database = self::filter($database, $criteria);
+ $defaultReturnColumnValue = ($field === null) ? 1 : null;
+
+ // extract an array of values for the requested column
+ $columnData = [];
+ foreach ($database as $rowKey => $row) {
+ $keys = array_keys($row);
+ $key = $keys[$field] ?? null;
+ $columnKey = $key ?? 'A';
+ $columnData[$rowKey][$columnKey] = $row[$key] ?? $defaultReturnColumnValue;
+ }
+
+ return $columnData;
+ }
+
+ private static function buildQuery(array $criteriaNames, array $criteria): string
+ {
+ $baseQuery = [];
+ foreach ($criteria as $key => $criterion) {
+ foreach ($criterion as $field => $value) {
+ $criterionName = $criteriaNames[$field];
+ if ($value !== null) {
+ $condition = self::buildCondition($value, $criterionName);
+ $baseQuery[$key][] = $condition;
+ }
+ }
+ }
+
+ $rowQuery = array_map(
+ fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''),
+ $baseQuery
+ );
+
+ return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
+ }
+
+ private static function buildCondition(mixed $criterion, string $criterionName): string
+ {
+ $ifCondition = Functions::ifCondition($criterion);
+
+ // Check for wildcard characters used in the condition
+ $result = preg_match('/(?[^"]*)(?".*[*?].*")/ui', $ifCondition, $matches);
+ if ($result !== 1) {
+ return "[:{$criterionName}]{$ifCondition}";
+ }
+
+ $trueFalse = ($matches['operator'] !== '<>');
+ $wildcard = WildcardMatch::wildcard($matches['operand']);
+ $condition = "WILDCARDMATCH([:{$criterionName}],{$wildcard})";
+ if ($trueFalse === false) {
+ $condition = "NOT({$condition})";
+ }
+
+ return $condition;
+ }
+
+ private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
+ {
+ foreach ($database as $dataRow => $dataValues) {
+ // Substitute actual values from the database row for our [:placeholders]
+ $conditions = $query;
+ foreach ($criteria as $criterion) {
+ $conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
+ }
+
+ // evaluate the criteria against the row data
+ $result = Calculation::getInstance()->_calculateFormulaValue('=' . $conditions);
+
+ // If the row failed to meet the criteria, remove it from the database
+ if ($result !== true) {
+ unset($database[$dataRow]);
+ }
+ }
+
+ return $database;
+ }
+
+ /**
+ * @return mixed
+ */
+ private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
+ {
+ $key = array_search($criterion, $fields, true);
+
+ $dataValue = 'NULL';
+ if (is_bool($dataValues[$key])) {
+ $dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
+ } elseif ($dataValues[$key] !== null) {
+ $dataValue = $dataValues[$key];
+ // escape quotes if we have a string containing quotes
+ if (is_string($dataValue) && str_contains($dataValue, '"')) {
+ $dataValue = str_replace('"', '""', $dataValue);
+ }
+ $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
+ }
+
+ return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
new file mode 100644
index 0000000..81169f4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
@@ -0,0 +1,886 @@
+getMessage();
+ }
+ }
+
+ /**
+ * DATETIMENOW.
+ *
+ * Returns the current date and time.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * NOW()
+ *
+ * @deprecated 1.18.0
+ * Use the now method in the DateTimeExcel\Current class instead
+ * @see DateTimeExcel\Current::now()
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATETIMENOW()
+ {
+ return DateTimeExcel\Current::now();
+ }
+
+ /**
+ * DATENOW.
+ *
+ * Returns the current date.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * TODAY()
+ *
+ * @deprecated 1.18.0
+ * Use the today method in the DateTimeExcel\Current class instead
+ * @see DateTimeExcel\Current::today()
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATENOW()
+ {
+ return DateTimeExcel\Current::today();
+ }
+
+ /**
+ * DATE.
+ *
+ * The DATE function returns a value that represents a particular date.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ *
+ * Excel Function:
+ * DATE(year,month,day)
+ *
+ * @deprecated 1.18.0
+ * Use the fromYMD method in the DateTimeExcel\Date class instead
+ * @see DateTimeExcel\Date::fromYMD()
+ *
+ * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
+ * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
+ * as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
+ *
+ * @param int $year The value of the year argument can include one to four digits.
+ * Excel interprets the year argument according to the configured
+ * date system: 1900 or 1904.
+ * If year is between 0 (zero) and 1899 (inclusive), Excel adds that
+ * value to 1900 to calculate the year. For example, DATE(108,1,2)
+ * returns January 2, 2008 (1900+108).
+ * If year is between 1900 and 9999 (inclusive), Excel uses that
+ * value as the year. For example, DATE(2008,1,2) returns January 2,
+ * 2008.
+ * If year is less than 0 or is 10000 or greater, Excel returns the
+ * #NUM! error value.
+ * @param int $month A positive or negative integer representing the month of the year
+ * from 1 to 12 (January to December).
+ * If month is greater than 12, month adds that number of months to
+ * the first month in the year specified. For example, DATE(2008,14,2)
+ * returns the serial number representing February 2, 2009.
+ * If month is less than 1, month subtracts the magnitude of that
+ * number of months, plus 1, from the first month in the year
+ * specified. For example, DATE(2008,-3,2) returns the serial number
+ * representing September 2, 2007.
+ * @param int $day A positive or negative integer representing the day of the month
+ * from 1 to 31.
+ * If day is greater than the number of days in the month specified,
+ * day adds that number of days to the first day in the month. For
+ * example, DATE(2008,1,35) returns the serial number representing
+ * February 4, 2008.
+ * If day is less than 1, day subtracts the magnitude that number of
+ * days, plus one, from the first day of the month specified. For
+ * example, DATE(2008,1,-15) returns the serial number representing
+ * December 16, 2007.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATE($year = 0, $month = 1, $day = 1)
+ {
+ return DateTimeExcel\Date::fromYMD($year, $month, $day);
+ }
+
+ /**
+ * TIME.
+ *
+ * The TIME function returns a value that represents a particular time.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
+ * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * TIME(hour,minute,second)
+ *
+ * @deprecated 1.18.0
+ * Use the fromHMS method in the DateTimeExcel\Time class instead
+ * @see DateTimeExcel\Time::fromHMS()
+ *
+ * @param int $hour A number from 0 (zero) to 32767 representing the hour.
+ * Any value greater than 23 will be divided by 24 and the remainder
+ * will be treated as the hour value. For example, TIME(27,0,0) =
+ * TIME(3,0,0) = .125 or 3:00 AM.
+ * @param int $minute A number from 0 to 32767 representing the minute.
+ * Any value greater than 59 will be converted to hours and minutes.
+ * For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
+ * @param int $second A number from 0 to 32767 representing the second.
+ * Any value greater than 59 will be converted to hours, minutes,
+ * and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
+ * or 12:33:20 AM
+ *
+ * @return array|\DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIME($hour = 0, $minute = 0, $second = 0): string|float|int|\DateTime|array
+ {
+ return DateTimeExcel\Time::fromHMS($hour, $minute, $second);
+ }
+
+ /**
+ * DATEVALUE.
+ *
+ * Returns a value that represents a particular date.
+ * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
+ * value.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * DATEVALUE(dateValue)
+ *
+ * @deprecated 1.18.0
+ * Use the fromString method in the DateTimeExcel\DateValue class instead
+ * @see DateTimeExcel\DateValue::fromString()
+ *
+ * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
+ * For example, "1/30/2008" or "30-Jan-2008" are text strings within
+ * quotation marks that represent dates. Using the default date
+ * system in Excel for Windows, date_text must represent a date from
+ * January 1, 1900, to December 31, 9999. Using the default date
+ * system in Excel for the Macintosh, date_text must represent a date
+ * from January 1, 1904, to December 31, 9999. DATEVALUE returns the
+ * #VALUE! error value if date_text is out of this range.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATEVALUE($dateValue)
+ {
+ return DateTimeExcel\DateValue::fromString($dateValue);
+ }
+
+ /**
+ * TIMEVALUE.
+ *
+ * Returns a value that represents a particular time.
+ * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
+ * value.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
+ * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * TIMEVALUE(timeValue)
+ *
+ * @deprecated 1.18.0
+ * Use the fromString method in the DateTimeExcel\TimeValue class instead
+ * @see DateTimeExcel\TimeValue::fromString()
+ *
+ * @param string $timeValue A text string that represents a time in any one of the Microsoft
+ * Excel time formats; for example, "6:45 PM" and "18:45" text strings
+ * within quotation marks that represent time.
+ * Date information in time_text is ignored.
+ *
+ * @return array|\Datetime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIMEVALUE($timeValue): string|\Datetime|int|float|array
+ {
+ return DateTimeExcel\TimeValue::fromString($timeValue);
+ }
+
+ /**
+ * DATEDIF.
+ *
+ * Excel Function:
+ * DATEDIF(startdate, enddate, unit)
+ *
+ * @deprecated 1.18.0
+ * Use the interval method in the DateTimeExcel\Difference class instead
+ * @see DateTimeExcel\Difference::interval()
+ *
+ * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
+ * or a standard date string
+ * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
+ * or a standard date string
+ * @param array|string $unit
+ *
+ * @return array|int|string Interval between the dates
+ */
+ public static function DATEDIF(mixed $startDate = 0, mixed $endDate = 0, $unit = 'D')
+ {
+ return DateTimeExcel\Difference::interval($startDate, $endDate, $unit);
+ }
+
+ /**
+ * DAYS.
+ *
+ * Returns the number of days between two dates
+ *
+ * Excel Function:
+ * DAYS(endDate, startDate)
+ *
+ * @deprecated 1.18.0
+ * Use the between method in the DateTimeExcel\Days class instead
+ * @see DateTimeExcel\Days::between()
+ *
+ * @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
+ * PHP date timestamp (integer), PHP DateTime object, or a standard date string
+ * @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
+ * PHP date timestamp (integer), PHP DateTime object, or a standard date string
+ *
+ * @return array|int|string Number of days between start date and end date or an error
+ */
+ public static function DAYS($endDate = 0, $startDate = 0)
+ {
+ return DateTimeExcel\Days::between($endDate, $startDate);
+ }
+
+ /**
+ * DAYS360.
+ *
+ * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
+ * which is used in some accounting calculations. Use this function to help compute payments if
+ * your accounting system is based on twelve 30-day months.
+ *
+ * Excel Function:
+ * DAYS360(startDate,endDate[,method])
+ *
+ * @deprecated 1.18.0
+ * Use the between method in the DateTimeExcel\Days360 class instead
+ * @see DateTimeExcel\Days360::between()
+ *
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param array|bool $method US or European Method
+ * FALSE or omitted: U.S. (NASD) method. If the starting date is
+ * the last day of a month, it becomes equal to the 30th of the
+ * same month. If the ending date is the last day of a month and
+ * the starting date is earlier than the 30th of a month, the
+ * ending date becomes equal to the 1st of the next month;
+ * otherwise the ending date becomes equal to the 30th of the
+ * same month.
+ * TRUE: European method. Starting dates and ending dates that
+ * occur on the 31st of a month become equal to the 30th of the
+ * same month.
+ *
+ * @return array|int|string Number of days between start date and end date
+ */
+ public static function DAYS360(mixed $startDate = 0, mixed $endDate = 0, $method = false): string|int|array
+ {
+ return DateTimeExcel\Days360::between($startDate, $endDate, $method);
+ }
+
+ /**
+ * YEARFRAC.
+ *
+ * Calculates the fraction of the year represented by the number of whole days between two dates
+ * (the start_date and the end_date).
+ * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
+ * obligations to assign to a specific term.
+ *
+ * Excel Function:
+ * YEARFRAC(startDate,endDate[,method])
+ *
+ * @deprecated 1.18.0
+ * Use the fraction method in the DateTimeExcel\YearFrac class instead
+ * @see DateTimeExcel\YearFrac::fraction()
+ *
+ * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
+ * for description of algorithm used in Excel
+ *
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param array|int $method Method used for the calculation
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return array|float|string fraction of the year, or a string containing an error
+ */
+ public static function YEARFRAC(mixed $startDate = 0, mixed $endDate = 0, $method = 0): string|int|float|array
+ {
+ return DateTimeExcel\YearFrac::fraction($startDate, $endDate, $method);
+ }
+
+ /**
+ * NETWORKDAYS.
+ *
+ * Returns the number of whole working days between start_date and end_date. Working days
+ * exclude weekends and any dates identified in holidays.
+ * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
+ * worked during a specific term.
+ *
+ * Excel Function:
+ * NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
+ *
+ * @deprecated 1.18.0
+ * Use the count method in the DateTimeExcel\NetworkDays class instead
+ * @see DateTimeExcel\NetworkDays::count()
+ *
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return array|int|string Interval between the dates
+ */
+ public static function NETWORKDAYS(mixed $startDate, mixed $endDate, mixed ...$dateArgs): string|int|array
+ {
+ return DateTimeExcel\NetworkDays::count($startDate, $endDate, ...$dateArgs);
+ }
+
+ /**
+ * WORKDAY.
+ *
+ * Returns the date that is the indicated number of working days before or after a date (the
+ * starting date). Working days exclude weekends and any dates identified as holidays.
+ * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
+ * delivery times, or the number of days of work performed.
+ *
+ * Excel Function:
+ * WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
+ *
+ * @deprecated 1.18.0
+ * Use the date method in the DateTimeExcel\WorkDay class instead
+ * @see DateTimeExcel\WorkDay::date()
+ *
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $endDays The number of nonweekend and nonholiday days before or after
+ * startDate. A positive value for days yields a future date; a
+ * negative value yields a past date.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function WORKDAY(mixed $startDate, $endDays, mixed ...$dateArgs)
+ {
+ return DateTimeExcel\WorkDay::date($startDate, $endDays, ...$dateArgs);
+ }
+
+ /**
+ * DAYOFMONTH.
+ *
+ * Returns the day of the month, for a specified date. The day is given as an integer
+ * ranging from 1 to 31.
+ *
+ * Excel Function:
+ * DAY(dateValue)
+ *
+ * @deprecated 1.18.0
+ * Use the day method in the DateTimeExcel\DateParts class instead
+ * @see DateTimeExcel\DateParts::day()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return array|int|string Day of the month
+ */
+ public static function DAYOFMONTH(mixed $dateValue = 1): int|string|array
+ {
+ return DateTimeExcel\DateParts::day($dateValue);
+ }
+
+ /**
+ * WEEKDAY.
+ *
+ * Returns the day of the week for a specified date. The day is given as an integer
+ * ranging from 0 to 7 (dependent on the requested style).
+ *
+ * Excel Function:
+ * WEEKDAY(dateValue[,style])
+ *
+ * @deprecated 1.18.0
+ * Use the day method in the DateTimeExcel\Week class instead
+ * @see DateTimeExcel\Week::day()
+ *
+ * @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $style A number that determines the type of return value
+ * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
+ * 2 Numbers 1 (Monday) through 7 (Sunday).
+ * 3 Numbers 0 (Monday) through 6 (Sunday).
+ *
+ * @return array|int|string Day of the week value
+ */
+ public static function WEEKDAY($dateValue = 1, $style = 1): string|int|array
+ {
+ return DateTimeExcel\Week::day($dateValue, $style);
+ }
+
+ /**
+ * STARTWEEK_SUNDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_SUNDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_SUNDAY
+ */
+ const STARTWEEK_SUNDAY = 1;
+
+ /**
+ * STARTWEEK_MONDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_MONDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_MONDAY
+ */
+ const STARTWEEK_MONDAY = 2;
+
+ /**
+ * STARTWEEK_MONDAY_ALT.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
+ * @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
+ */
+ const STARTWEEK_MONDAY_ALT = 11;
+
+ /**
+ * STARTWEEK_TUESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_TUESDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_TUESDAY
+ */
+ const STARTWEEK_TUESDAY = 12;
+
+ /**
+ * STARTWEEK_WEDNESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
+ */
+ const STARTWEEK_WEDNESDAY = 13;
+
+ /**
+ * STARTWEEK_THURSDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_THURSDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_THURSDAY
+ */
+ const STARTWEEK_THURSDAY = 14;
+
+ /**
+ * STARTWEEK_FRIDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_FRIDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_FRIDAY
+ */
+ const STARTWEEK_FRIDAY = 15;
+
+ /**
+ * STARTWEEK_SATURDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_SATURDAY
+ * @see DateTimeExcel\Constants::STARTWEEK_SATURDAY
+ */
+ const STARTWEEK_SATURDAY = 16;
+
+ /**
+ * STARTWEEK_SUNDAY_ALT.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
+ * @see DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
+ */
+ const STARTWEEK_SUNDAY_ALT = 17;
+
+ /**
+ * DOW_SUNDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_SUNDAY
+ * @see DateTimeExcel\Constants::DOW_SUNDAY
+ */
+ const DOW_SUNDAY = 1;
+
+ /**
+ * DOW_MONDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_MONDAY
+ * @see DateTimeExcel\Constants::DOW_MONDAY
+ */
+ const DOW_MONDAY = 2;
+
+ /**
+ * DOW_TUESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_TUESDAY
+ * @see DateTimeExcel\Constants::DOW_TUESDAY
+ */
+ const DOW_TUESDAY = 3;
+
+ /**
+ * DOW_WEDNESDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_WEDNESDAY
+ * @see DateTimeExcel\Constants::DOW_WEDNESDAY
+ */
+ const DOW_WEDNESDAY = 4;
+
+ /**
+ * DOW_THURSDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_THURSDAY
+ * @see DateTimeExcel\Constants::DOW_THURSDAY
+ */
+ const DOW_THURSDAY = 5;
+
+ /**
+ * DOW_FRIDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_FRIDAY
+ * @see DateTimeExcel\Constants::DOW_FRIDAY
+ */
+ const DOW_FRIDAY = 6;
+
+ /**
+ * DOW_SATURDAY.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::DOW_SATURDAY
+ * @see DateTimeExcel\Constants::DOW_SATURDAY
+ */
+ const DOW_SATURDAY = 7;
+
+ /**
+ * STARTWEEK_MONDAY_ISO.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
+ * @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
+ */
+ const STARTWEEK_MONDAY_ISO = 21;
+
+ /**
+ * METHODARR.
+ *
+ * @deprecated 1.18.0
+ * Use DateTimeExcel\Constants::METHODARR
+ * @see DateTimeExcel\Constants::METHODARR
+ */
+ const METHODARR = [
+ self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
+ self::DOW_MONDAY,
+ self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
+ self::DOW_TUESDAY,
+ self::DOW_WEDNESDAY,
+ self::DOW_THURSDAY,
+ self::DOW_FRIDAY,
+ self::DOW_SATURDAY,
+ self::DOW_SUNDAY,
+ self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
+ ];
+
+ /**
+ * WEEKNUM.
+ *
+ * Returns the week of the year for a specified date.
+ * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
+ * However, there is a European standard that defines the first week as the one with the majority
+ * of days (four or more) falling in the new year. This means that for years in which there are
+ * three days or less in the first week of January, the WEEKNUM function returns week numbers
+ * that are incorrect according to the European standard.
+ *
+ * Excel Function:
+ * WEEKNUM(dateValue[,style])
+ *
+ * @deprecated 1.18.0
+ * Use the number method in the DateTimeExcel\Week class instead
+ * @see DateTimeExcel\Week::number()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $method Week begins on Sunday or Monday
+ * 1 or omitted Week begins on Sunday.
+ * 2 Week begins on Monday.
+ * 11 Week begins on Monday.
+ * 12 Week begins on Tuesday.
+ * 13 Week begins on Wednesday.
+ * 14 Week begins on Thursday.
+ * 15 Week begins on Friday.
+ * 16 Week begins on Saturday.
+ * 17 Week begins on Sunday.
+ * 21 ISO (Jan. 4 is week 1, begins on Monday).
+ *
+ * @return array|int|string Week Number
+ */
+ public static function WEEKNUM(mixed $dateValue = 1, $method = self::STARTWEEK_SUNDAY): int|string|array
+ {
+ return DateTimeExcel\Week::number($dateValue, $method);
+ }
+
+ /**
+ * ISOWEEKNUM.
+ *
+ * Returns the ISO 8601 week number of the year for a specified date.
+ *
+ * Excel Function:
+ * ISOWEEKNUM(dateValue)
+ *
+ * @deprecated 1.18.0
+ * Use the isoWeekNumber method in the DateTimeExcel\Week class instead
+ * @see DateTimeExcel\Week::isoWeekNumber()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return array|int|string Week Number
+ */
+ public static function ISOWEEKNUM(mixed $dateValue = 1): int|string|array
+ {
+ return DateTimeExcel\Week::isoWeekNumber($dateValue);
+ }
+
+ /**
+ * MONTHOFYEAR.
+ *
+ * Returns the month of a date represented by a serial number.
+ * The month is given as an integer, ranging from 1 (January) to 12 (December).
+ *
+ * Excel Function:
+ * MONTH(dateValue)
+ *
+ * @deprecated 1.18.0
+ * Use the month method in the DateTimeExcel\DateParts class instead
+ * @see DateTimeExcel\DateParts::month()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return array|int|string Month of the year
+ */
+ public static function MONTHOFYEAR(mixed $dateValue = 1): string|int|array
+ {
+ return DateTimeExcel\DateParts::month($dateValue);
+ }
+
+ /**
+ * YEAR.
+ *
+ * Returns the year corresponding to a date.
+ * The year is returned as an integer in the range 1900-9999.
+ *
+ * Excel Function:
+ * YEAR(dateValue)
+ *
+ * @deprecated 1.18.0
+ * Use the ear method in the DateTimeExcel\DateParts class instead
+ * @see DateTimeExcel\DateParts::year()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ *
+ * @return array|int|string Year
+ */
+ public static function YEAR(mixed $dateValue = 1): string|int|array
+ {
+ return DateTimeExcel\DateParts::year($dateValue);
+ }
+
+ /**
+ * HOUROFDAY.
+ *
+ * Returns the hour of a time value.
+ * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
+ *
+ * Excel Function:
+ * HOUR(timeValue)
+ *
+ * @deprecated 1.18.0
+ * Use the hour method in the DateTimeExcel\TimeParts class instead
+ * @see DateTimeExcel\TimeParts::hour()
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ *
+ * @return array|int|string Hour
+ */
+ public static function HOUROFDAY(mixed $timeValue = 0): string|int|array
+ {
+ return DateTimeExcel\TimeParts::hour($timeValue);
+ }
+
+ /**
+ * MINUTE.
+ *
+ * Returns the minutes of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * MINUTE(timeValue)
+ *
+ * @deprecated 1.18.0
+ * Use the minute method in the DateTimeExcel\TimeParts class instead
+ * @see DateTimeExcel\TimeParts::minute()
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ *
+ * @return array|int|string Minute
+ */
+ public static function MINUTE(mixed $timeValue = 0): string|int|array
+ {
+ return DateTimeExcel\TimeParts::minute($timeValue);
+ }
+
+ /**
+ * SECOND.
+ *
+ * Returns the seconds of a time value.
+ * The second is given as an integer in the range 0 (zero) to 59.
+ *
+ * Excel Function:
+ * SECOND(timeValue)
+ *
+ * @deprecated 1.18.0
+ * Use the second method in the DateTimeExcel\TimeParts class instead
+ * @see DateTimeExcel\TimeParts::second()
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ *
+ * @return array|int|string Second
+ */
+ public static function SECOND(mixed $timeValue = 0): string|int|array
+ {
+ return DateTimeExcel\TimeParts::second($timeValue);
+ }
+
+ /**
+ * EDATE.
+ *
+ * Returns the serial number that represents the date that is the indicated number of months
+ * before or after a specified date (the start_date).
+ * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
+ * as the date of issue.
+ *
+ * Excel Function:
+ * EDATE(dateValue,adjustmentMonths)
+ *
+ * @deprecated 1.18.0
+ * Use the adjust method in the DateTimeExcel\Edate class instead
+ * @see DateTimeExcel\Month::adjust()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function EDATE(mixed $dateValue = 1, $adjustmentMonths = 0)
+ {
+ return DateTimeExcel\Month::adjust($dateValue, $adjustmentMonths);
+ }
+
+ /**
+ * EOMONTH.
+ *
+ * Returns the date value for the last day of the month that is the indicated number of months
+ * before or after start_date.
+ * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
+ *
+ * Excel Function:
+ * EOMONTH(dateValue,adjustmentMonths)
+ *
+ * @deprecated 1.18.0
+ * Use the lastDay method in the DateTimeExcel\EoMonth class instead
+ * @see DateTimeExcel\Month::lastDay()
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function EOMONTH(mixed $dateValue = 1, $adjustmentMonths = 0)
+ {
+ return DateTimeExcel\Month::lastDay($dateValue, $adjustmentMonths);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
new file mode 100644
index 0000000..1165eb1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php
@@ -0,0 +1,38 @@
+ 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,
+ ];
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
new file mode 100644
index 0000000..5de671d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
@@ -0,0 +1,59 @@
+format('c'));
+
+ return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : ExcelError::VALUE();
+ }
+
+ /**
+ * DATETIMENOW.
+ *
+ * Returns the current date and time.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * NOW()
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function now()
+ {
+ $dti = new DateTimeImmutable();
+ $dateArray = Helpers::dateParse($dti->format('c'));
+
+ return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
new file mode 100644
index 0000000..040cca9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
@@ -0,0 +1,166 @@
+getMessage();
+ }
+
+ // Execute function
+ $excelDateValue = SharedDateHelper::formattedPHPToExcel($year, $month, $day);
+
+ return Helpers::returnIn3FormatsFloat($excelDateValue);
+ }
+
+ /**
+ * Convert year from multiple formats to int.
+ */
+ private static function getYear(mixed $year, int $baseYear): int
+ {
+ $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
+ if (!is_numeric($year)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ $year = (int) $year;
+
+ if ($year < ($baseYear - 1900)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
+ $year += 1900;
+ }
+
+ return (int) $year;
+ }
+
+ /**
+ * Convert month from multiple formats to int.
+ */
+ private static function getMonth(mixed $month): int
+ {
+ if (($month !== null) && (!is_numeric($month))) {
+ $month = SharedDateHelper::monthStringToNumber($month);
+ }
+
+ $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
+ if (!is_numeric($month)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return (int) $month;
+ }
+
+ /**
+ * Convert day from multiple formats to int.
+ */
+ private static function getDay(mixed $day): int
+ {
+ if (($day !== null) && (!is_numeric($day))) {
+ $day = SharedDateHelper::dayStringToNumber($day);
+ }
+
+ $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
+ if (!is_numeric($day)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return (int) $day;
+ }
+
+ private static function adjustYearMonth(int &$year, int &$month, int $baseYear): void
+ {
+ if ($month < 1) {
+ // Handle year/month adjustment if month < 1
+ --$month;
+ $year += ceil($month / 12) - 1;
+ $month = 13 - abs($month % 12);
+ } elseif ($month > 12) {
+ // Handle year/month adjustment if month > 12
+ $year += floor($month / 12);
+ $month = ($month % 12);
+ }
+
+ // Re-validate the year parameter after adjustments
+ if (($year < $baseYear) || ($year >= 10000)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
new file mode 100644
index 0000000..60e4de1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
@@ -0,0 +1,154 @@
+= 0) {
+ return $weirdResult;
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ SharedDateHelper::roundMicroseconds($PHPDateObject);
+
+ return (int) $PHPDateObject->format('j');
+ }
+
+ /**
+ * MONTHOFYEAR.
+ *
+ * Returns the month of a date represented by a serial number.
+ * The month is given as an integer, ranging from 1 (January) to 12 (December).
+ *
+ * Excel Function:
+ * MONTH(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ *
+ * @return array|int|string Month of the year
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function month(mixed $dateValue): array|string|int
+ {
+ if (is_array($dateValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
+ return 1;
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ SharedDateHelper::roundMicroseconds($PHPDateObject);
+
+ return (int) $PHPDateObject->format('n');
+ }
+
+ /**
+ * YEAR.
+ *
+ * Returns the year corresponding to a date.
+ * The year is returned as an integer in the range 1900-9999.
+ *
+ * Excel Function:
+ * YEAR(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ *
+ * @return array|int|string Year
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function year(mixed $dateValue): array|string|int
+ {
+ if (is_array($dateValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
+ return 1900;
+ }
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ SharedDateHelper::roundMicroseconds($PHPDateObject);
+
+ return (int) $PHPDateObject->format('Y');
+ }
+
+ /**
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ */
+ private static function weirdCondition(mixed $dateValue): int
+ {
+ // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
+ if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
+ if (is_bool($dateValue)) {
+ return (int) $dateValue;
+ }
+ if ($dateValue === null) {
+ return 0;
+ }
+ if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) {
+ return 0;
+ }
+ }
+
+ return -1;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
new file mode 100644
index 0000000..0cb2277
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
@@ -0,0 +1,162 @@
+ 31)) {
+ if ($yearFound) {
+ return ExcelError::VALUE();
+ }
+ if ($t < 100) {
+ $t += 1900;
+ }
+ $yearFound = true;
+ }
+ }
+ if (count($t1) === 1) {
+ // We've been fed a time value without any date
+ return ((!str_contains((string) $t, ':'))) ? ExcelError::Value() : 0.0;
+ }
+ unset($t);
+
+ $dateValue = self::t1ToString($t1, $dti, $yearFound);
+
+ $PHPDateArray = self::setUpArray($dateValue, $dti);
+
+ return self::finalResults($PHPDateArray, $dti, $baseYear);
+ }
+
+ private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
+ {
+ if (count($t1) == 2) {
+ // We only have two parts of the date: either day/month or month/year
+ if ($yearFound) {
+ array_unshift($t1, 1);
+ } else {
+ if (is_numeric($t1[1]) && $t1[1] > 29) {
+ $t1[1] += 1900;
+ array_unshift($t1, 1);
+ } else {
+ $t1[] = $dti->format('Y');
+ }
+ }
+ }
+ $dateValue = implode(' ', $t1);
+
+ return $dateValue;
+ }
+
+ /**
+ * Parse date.
+ */
+ private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
+ {
+ $PHPDateArray = Helpers::dateParse($dateValue);
+ if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+ // If original count was 1, we've already returned.
+ // If it was 2, we added another.
+ // Therefore, neither of the first 2 stroks below can fail.
+ $testVal1 = strtok($dateValue, '- ');
+ $testVal2 = strtok('- ');
+ $testVal3 = strtok('- ') ?: $dti->format('Y');
+ Helpers::adjustYear((string) $testVal1, (string) $testVal2, $testVal3);
+ $PHPDateArray = Helpers::dateParse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
+ if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+ $PHPDateArray = Helpers::dateParse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
+ }
+ }
+
+ return $PHPDateArray;
+ }
+
+ /**
+ * Final results.
+ *
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
+ {
+ $retValue = ExcelError::Value();
+ if (Helpers::dateParseSucceeded($PHPDateArray)) {
+ // Execute function
+ Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
+ if ($PHPDateArray['year'] < $baseYear) {
+ return ExcelError::VALUE();
+ }
+ Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
+ Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
+ $PHPDateArray['hour'] = 0;
+ $PHPDateArray['minute'] = 0;
+ $PHPDateArray['second'] = 0;
+ $month = (int) $PHPDateArray['month'];
+ $day = (int) $PHPDateArray['day'];
+ $year = (int) $PHPDateArray['year'];
+ if (!checkdate($month, $day, $year)) {
+ return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
+ }
+ $retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
+ }
+
+ return $retValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
new file mode 100644
index 0000000..a3b9745
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
@@ -0,0 +1,62 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+
+ $days = ExcelError::VALUE();
+ $diff = $PHPStartDateObject->diff($PHPEndDateObject);
+ if ($diff !== false && !is_bool($diff->days)) {
+ $days = $diff->days;
+ if ($diff->invert) {
+ $days = -$days;
+ }
+ }
+
+ return $days;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
new file mode 100644
index 0000000..9ac9ccb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
@@ -0,0 +1,118 @@
+getMessage();
+ }
+
+ if (!is_bool($method)) {
+ return ExcelError::VALUE();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $startDay = $PHPStartDateObject->format('j');
+ $startMonth = $PHPStartDateObject->format('n');
+ $startYear = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+ $endDay = $PHPEndDateObject->format('j');
+ $endMonth = $PHPEndDateObject->format('n');
+ $endYear = $PHPEndDateObject->format('Y');
+
+ return self::dateDiff360((int) $startDay, (int) $startMonth, (int) $startYear, (int) $endDay, (int) $endMonth, (int) $endYear, !$method);
+ }
+
+ /**
+ * Return the number of days between two dates based on a 360 day calendar.
+ */
+ private static function dateDiff360(int $startDay, int $startMonth, int $startYear, int $endDay, int $endMonth, int $endYear, bool $methodUS): int
+ {
+ $startDay = self::getStartDay($startDay, $startMonth, $startYear, $methodUS);
+ $endDay = self::getEndDay($endDay, $endMonth, $endYear, $startDay, $methodUS);
+
+ return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
+ }
+
+ private static function getStartDay(int $startDay, int $startMonth, int $startYear, bool $methodUS): int
+ {
+ if ($startDay == 31) {
+ --$startDay;
+ } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !Helpers::isLeapYear($startYear))))) {
+ $startDay = 30;
+ }
+
+ return $startDay;
+ }
+
+ private static function getEndDay(int $endDay, int &$endMonth, int &$endYear, int $startDay, bool $methodUS): int
+ {
+ if ($endDay == 31) {
+ if ($methodUS && $startDay != 30) {
+ $endDay = 1;
+ if ($endMonth == 12) {
+ ++$endYear;
+ $endMonth = 1;
+ } else {
+ ++$endMonth;
+ }
+ } else {
+ $endDay = 30;
+ }
+ }
+
+ return $endDay;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
new file mode 100644
index 0000000..2822f08
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
@@ -0,0 +1,158 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
+ $startDays = (int) $PHPStartDateObject->format('j');
+ //$startMonths = (int) $PHPStartDateObject->format('n');
+ $startYears = (int) $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
+ $endDays = (int) $PHPEndDateObject->format('j');
+ //$endMonths = (int) $PHPEndDateObject->format('n');
+ $endYears = (int) $PHPEndDateObject->format('Y');
+
+ $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
+
+ $retVal = false;
+ $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
+ $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
+ $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
+
+ return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
+ }
+
+ private static function initialDiff(float $startDate, float $endDate): float
+ {
+ // Validate parameters
+ if ($startDate > $endDate) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $endDate - $startDate;
+ }
+
+ /**
+ * Decide whether it's time to set retVal.
+ *
+ * @param bool|int $retVal
+ *
+ * @return null|bool|int
+ */
+ private static function replaceRetValue($retVal, string $unit, string $compare)
+ {
+ if ($retVal !== false || $unit !== $compare) {
+ return $retVal;
+ }
+
+ return null;
+ }
+
+ private static function datedifD(float $difference): int
+ {
+ return (int) $difference;
+ }
+
+ private static function datedifM(DateInterval $PHPDiffDateObject): int
+ {
+ return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
+ }
+
+ private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
+ {
+ if ($endDays < $startDays) {
+ $retVal = $endDays;
+ $PHPEndDateObject->modify('-' . $endDays . ' days');
+ $adjustDays = (int) $PHPEndDateObject->format('j');
+ $retVal += ($adjustDays - $startDays);
+ } else {
+ $retVal = (int) $PHPDiffDateObject->format('%d');
+ }
+
+ return $retVal;
+ }
+
+ private static function datedifY(DateInterval $PHPDiffDateObject): int
+ {
+ return (int) $PHPDiffDateObject->format('%y');
+ }
+
+ private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
+ {
+ $retVal = (int) $difference;
+ if ($endYears > $startYears) {
+ $isLeapStartYear = $PHPStartDateObject->format('L');
+ $wasLeapEndYear = $PHPEndDateObject->format('L');
+
+ // Adjust end year to be as close as possible as start year
+ while ($PHPEndDateObject >= $PHPStartDateObject) {
+ $PHPEndDateObject->modify('-1 year');
+ //$endYears = $PHPEndDateObject->format('Y');
+ }
+ $PHPEndDateObject->modify('+1 year');
+
+ // Get the result
+ $retVal = (int) $PHPEndDateObject->diff($PHPStartDateObject)->days;
+
+ // Adjust for leap years cases
+ $isLeapEndYear = $PHPEndDateObject->format('L');
+ $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
+ if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
+ --$retVal;
+ }
+ }
+
+ return (int) $retVal;
+ }
+
+ private static function datedifYM(DateInterval $PHPDiffDateObject): int
+ {
+ return (int) $PHPDiffDateObject->format('%m');
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
new file mode 100644
index 0000000..e329be7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
@@ -0,0 +1,288 @@
+format('m');
+ $oYear = (int) $PHPDateObject->format('Y');
+
+ $adjustmentMonthsString = (string) $adjustmentMonths;
+ if ($adjustmentMonths > 0) {
+ $adjustmentMonthsString = '+' . $adjustmentMonths;
+ }
+ if ($adjustmentMonths != 0) {
+ $PHPDateObject->modify($adjustmentMonthsString . ' months');
+ }
+ $nMonth = (int) $PHPDateObject->format('m');
+ $nYear = (int) $PHPDateObject->format('Y');
+
+ $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
+ if ($monthDiff != $adjustmentMonths) {
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-' . $adjustDays . ' days';
+ $PHPDateObject->modify($adjustDaysString);
+ }
+
+ return $PHPDateObject;
+ }
+
+ /**
+ * Help reduce perceived complexity of some tests.
+ */
+ public static function replaceIfEmpty(mixed &$value, mixed $altValue): void
+ {
+ $value = $value ?: $altValue;
+ }
+
+ /**
+ * Adjust year in ambiguous situations.
+ */
+ public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
+ {
+ if (!is_numeric($testVal1) || $testVal1 < 31) {
+ if (!is_numeric($testVal2) || $testVal2 < 12) {
+ if (is_numeric($testVal3) && $testVal3 < 12) {
+ $testVal3 += 2000;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return result in one of three formats.
+ */
+ public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
+ return new DateTime(
+ $dateArray['year']
+ . '-' . $dateArray['month']
+ . '-' . $dateArray['day']
+ . ' ' . $dateArray['hour']
+ . ':' . $dateArray['minute']
+ . ':' . $dateArray['second']
+ );
+ }
+ $excelDateValue
+ = SharedDateHelper::formattedPHPToExcel(
+ $dateArray['year'],
+ $dateArray['month'],
+ $dateArray['day'],
+ $dateArray['hour'],
+ $dateArray['minute'],
+ $dateArray['second']
+ );
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return $noFrac ? floor($excelDateValue) : $excelDateValue;
+ }
+ // RETURNDATE_UNIX_TIMESTAMP)
+
+ return SharedDateHelper::excelToTimestamp($excelDateValue);
+ }
+
+ /**
+ * Return result in one of three formats.
+ *
+ * @return mixed
+ */
+ public static function returnIn3FormatsFloat(float $excelDateValue)
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return $excelDateValue;
+ }
+ if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+ }
+ // RETURNDATE_PHP_DATETIME_OBJECT
+
+ return SharedDateHelper::excelToDateTimeObject($excelDateValue);
+ }
+
+ /**
+ * Return result in one of three formats.
+ */
+ public static function returnIn3FormatsObject(DateTime $PHPDateObject): DateTime|float|int
+ {
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
+ return $PHPDateObject;
+ }
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
+ }
+ // RETURNDATE_UNIX_TIMESTAMP
+ $stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
+ $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
+
+ return SharedDateHelper::excelToTimestamp($stamp);
+ }
+
+ private static function baseDate(): int
+ {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ return 0;
+ }
+ if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ */
+ public static function nullFalseTrueToNumber(mixed &$number, bool $allowBool = true): void
+ {
+ $number = Functions::flattenSingleValue($number);
+ $nullVal = self::baseDate();
+ if ($number === null) {
+ $number = $nullVal;
+ } elseif ($allowBool && is_bool($number)) {
+ $number = $nullVal + (int) $number;
+ }
+ }
+
+ /**
+ * Many functions accept null argument treated as 0.
+ */
+ public static function validateNumericNull(mixed $number): int|float
+ {
+ $number = Functions::flattenSingleValue($number);
+ if ($number === null) {
+ return 0;
+ }
+ if (is_int($number)) {
+ return $number;
+ }
+ if (is_numeric($number)) {
+ return (float) $number;
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @return float
+ */
+ public static function validateNotNegative(mixed $number)
+ {
+ if (!is_numeric($number)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ if ($number >= 0) {
+ return (float) $number;
+ }
+
+ throw new Exception(ExcelError::NAN());
+ }
+
+ public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
+ {
+ $isoDate = $PHPDateObject->format('c');
+ if ($isoDate < '1900-03-01') {
+ $PHPDateObject->modify($mod);
+ }
+ }
+
+ public static function dateParse(string $string): array
+ {
+ return self::forceArray(date_parse($string));
+ }
+
+ public static function dateParseSucceeded(array $dateArray): bool
+ {
+ return $dateArray['error_count'] === 0;
+ }
+
+ /**
+ * Despite documentation, date_parse probably never returns false.
+ * Just in case, this routine helps guarantee it.
+ *
+ * @param array|false $dateArray
+ */
+ private static function forceArray($dateArray): array
+ {
+ return is_array($dateArray) ? $dateArray : ['error_count' => 1];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
new file mode 100644
index 0000000..33f661a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
@@ -0,0 +1,103 @@
+getMessage();
+ }
+ $dateValue = floor($dateValue);
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ // Execute function
+ $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths);
+
+ return Helpers::returnIn3FormatsObject($PHPDateObject);
+ }
+
+ /**
+ * EOMONTH.
+ *
+ * Returns the date value for the last day of the month that is the indicated number of months
+ * before or after start_date.
+ * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
+ *
+ * Excel Function:
+ * EOMONTH(dateValue,adjustmentMonths)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ * @param array|int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ * Or can be an array of adjustment values
+ *
+ * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ * If an array of values is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function lastDay(mixed $dateValue, $adjustmentMonths)
+ {
+ if (is_array($dateValue) || is_array($adjustmentMonths)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue, false);
+ $adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ $dateValue = floor($dateValue);
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ // Execute function
+ $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-' . $adjustDays . ' days';
+ $PHPDateObject->modify($adjustDaysString);
+
+ return Helpers::returnIn3FormatsObject($PHPDateObject);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
new file mode 100644
index 0000000..503e30e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
@@ -0,0 +1,119 @@
+getMessage();
+ }
+
+ // Execute function
+ $startDow = self::calcStartDow($startDate);
+ $endDow = self::calcEndDow($endDate);
+ $wholeWeekDays = (int) floor(($endDate - $startDate) / 7) * 5;
+ $partWeekDays = self::calcPartWeekDays($startDow, $endDow);
+
+ // Test any extra holiday parameters
+ $holidayCountedArray = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((Week::day($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
+ --$partWeekDays;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+
+ return self::applySign($wholeWeekDays + $partWeekDays, $sDate, $eDate);
+ }
+
+ private static function calcStartDow(float $startDate): int
+ {
+ $startDow = 6 - (int) Week::day($startDate, 2);
+ if ($startDow < 0) {
+ $startDow = 5;
+ }
+
+ return $startDow;
+ }
+
+ private static function calcEndDow(float $endDate): int
+ {
+ $endDow = (int) Week::day($endDate, 2);
+ if ($endDow >= 6) {
+ $endDow = 0;
+ }
+
+ return $endDow;
+ }
+
+ private static function calcPartWeekDays(int $startDow, int $endDow): int
+ {
+ $partWeekDays = $endDow + $startDow;
+ if ($partWeekDays > 5) {
+ $partWeekDays -= 5;
+ }
+
+ return $partWeekDays;
+ }
+
+ private static function applySign(int $result, float $sDate, float $eDate): int
+ {
+ return ($sDate > $eDate) ? -$result : $result;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
new file mode 100644
index 0000000..defcd17
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
@@ -0,0 +1,130 @@
+getMessage();
+ }
+
+ self::adjustSecond($second, $minute);
+ self::adjustMinute($minute, $hour);
+
+ if ($hour > 23) {
+ $hour = $hour % 24;
+ } elseif ($hour < 0) {
+ return ExcelError::NAN();
+ }
+
+ // Execute function
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ $calendar = SharedDateHelper::getExcelCalendar();
+ $date = (int) ($calendar !== SharedDateHelper::CALENDAR_WINDOWS_1900);
+
+ return (float) SharedDateHelper::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
+ }
+ if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ return (int) SharedDateHelper::excelToTimestamp(SharedDateHelper::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
+ }
+ // RETURNDATE_PHP_DATETIME_OBJECT
+ // Hour has already been normalized (0-23) above
+ $phpDateObject = new DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
+
+ return $phpDateObject;
+ }
+
+ private static function adjustSecond(int &$second, int &$minute): void
+ {
+ if ($second < 0) {
+ $minute += floor($second / 60);
+ $second = 60 - abs($second % 60);
+ if ($second == 60) {
+ $second = 0;
+ }
+ } elseif ($second >= 60) {
+ $minute += floor($second / 60);
+ $second = $second % 60;
+ }
+ }
+
+ private static function adjustMinute(int &$minute, int &$hour): void
+ {
+ if ($minute < 0) {
+ $hour += floor($minute / 60);
+ $minute = 60 - abs($minute % 60);
+ if ($minute == 60) {
+ $minute = 0;
+ }
+ } elseif ($minute >= 60) {
+ $hour += floor($minute / 60);
+ $minute = $minute % 60;
+ }
+ }
+
+ /**
+ * @param mixed $value expect int
+ */
+ private static function toIntWithNullBool(mixed $value): int
+ {
+ $value = $value ?? 0;
+ if (is_bool($value)) {
+ $value = (int) $value;
+ }
+ if (!is_numeric($value)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return (int) $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
new file mode 100644
index 0000000..de52269
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
@@ -0,0 +1,135 @@
+getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+ SharedDateHelper::roundMicroseconds($timeValue);
+
+ return (int) $timeValue->format('H');
+ }
+
+ /**
+ * MINUTE.
+ *
+ * Returns the minutes of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * MINUTE(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * Or can be an array of date/time values
+ *
+ * @return array|int|string Minute
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function minute(mixed $timeValue): array|string|int
+ {
+ if (is_array($timeValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+ }
+
+ try {
+ Helpers::nullFalseTrueToNumber($timeValue);
+ if (!is_numeric($timeValue)) {
+ $timeValue = Helpers::getTimeValue($timeValue);
+ }
+ Helpers::validateNotNegative($timeValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+ SharedDateHelper::roundMicroseconds($timeValue);
+
+ return (int) $timeValue->format('i');
+ }
+
+ /**
+ * SECOND.
+ *
+ * Returns the seconds of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * SECOND(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * Or can be an array of date/time values
+ *
+ * @return array|int|string Second
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function second(mixed $timeValue): array|string|int
+ {
+ if (is_array($timeValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+ }
+
+ try {
+ Helpers::nullFalseTrueToNumber($timeValue);
+ if (!is_numeric($timeValue)) {
+ $timeValue = Helpers::getTimeValue($timeValue);
+ }
+ Helpers::validateNotNegative($timeValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $timeValue = fmod($timeValue, 1);
+ $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+ SharedDateHelper::roundMicroseconds($timeValue);
+
+ return (int) $timeValue->format('s');
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
new file mode 100644
index 0000000..b5024d1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
@@ -0,0 +1,83 @@
+ 24) {
+ $arraySplit[0] = ($arraySplit[0] % 24);
+ $timeValue = implode(':', $arraySplit);
+ }
+
+ $PHPDateArray = Helpers::dateParse($timeValue);
+ $retValue = ExcelError::VALUE();
+ if (Helpers::dateParseSucceeded($PHPDateArray)) {
+ /** @var int */
+ $hour = $PHPDateArray['hour'];
+ /** @var int */
+ $minute = $PHPDateArray['minute'];
+ /** @var int */
+ $second = $PHPDateArray['second'];
+ // OpenOffice-specific code removed - it works just like Excel
+ $excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
+
+ $retType = Functions::getReturnDateType();
+ if ($retType === Functions::RETURNDATE_EXCEL) {
+ $retValue = (float) $excelDateValue;
+ } elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
+ $retValue = (int) SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
+ } else {
+ $retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
+ }
+ }
+
+ return $retValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
new file mode 100644
index 0000000..6434cd7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
@@ -0,0 +1,274 @@
+getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ if ($method == Constants::STARTWEEK_MONDAY_ISO) {
+ Helpers::silly1900($PHPDateObject);
+
+ return (int) $PHPDateObject->format('W');
+ }
+ if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
+ return 0;
+ }
+ Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
+ $dayOfYear = (int) $PHPDateObject->format('z');
+ $PHPDateObject->modify('-' . $dayOfYear . ' days');
+ $firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
+ $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
+ $daysInFirstWeek += 7 * !$daysInFirstWeek;
+ $endFirstWeek = $daysInFirstWeek - 1;
+ $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
+
+ return (int) $weekOfYear;
+ }
+
+ /**
+ * ISOWEEKNUM.
+ *
+ * Returns the ISO 8601 week number of the year for a specified date.
+ *
+ * Excel Function:
+ * ISOWEEKNUM(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ *
+ * @return array|int|string Week Number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isoWeekNumber(mixed $dateValue): array|int|string
+ {
+ if (is_array($dateValue)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+ }
+
+ if (self::apparentBug($dateValue)) {
+ return 52;
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ Helpers::silly1900($PHPDateObject);
+
+ return (int) $PHPDateObject->format('W');
+ }
+
+ /**
+ * WEEKDAY.
+ *
+ * Returns the day of the week for a specified date. The day is given as an integer
+ * ranging from 0 to 7 (dependent on the requested style).
+ *
+ * Excel Function:
+ * WEEKDAY(dateValue[,style])
+ *
+ * @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * Or can be an array of date values
+ * @param mixed $style A number that determines the type of return value
+ * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
+ * 2 Numbers 1 (Monday) through 7 (Sunday).
+ * 3 Numbers 0 (Monday) through 6 (Sunday).
+ * Or can be an array of styles
+ *
+ * @return array|int|string Day of the week value
+ * If an array of values is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function day($dateValue, mixed $style = 1): array|string|int
+ {
+ if (is_array($dateValue) || is_array($style)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
+ }
+
+ try {
+ $dateValue = Helpers::getDateValue($dateValue);
+ $style = self::validateStyle($style);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Execute function
+ $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+ Helpers::silly1900($PHPDateObject);
+ $DoW = (int) $PHPDateObject->format('w');
+
+ switch ($style) {
+ case 1:
+ ++$DoW;
+
+ break;
+ case 2:
+ $DoW = self::dow0Becomes7($DoW);
+
+ break;
+ case 3:
+ $DoW = self::dow0Becomes7($DoW) - 1;
+
+ break;
+ }
+
+ return $DoW;
+ }
+
+ /**
+ * @param mixed $style expect int
+ */
+ private static function validateStyle(mixed $style): int
+ {
+ if (!is_numeric($style)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ $style = (int) $style;
+ if (($style < 1) || ($style > 3)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $style;
+ }
+
+ private static function dow0Becomes7(int $DoW): int
+ {
+ return ($DoW === 0) ? 7 : $DoW;
+ }
+
+ /**
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ */
+ private static function apparentBug(mixed $dateValue): bool
+ {
+ if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
+ if (is_bool($dateValue)) {
+ return true;
+ }
+ if (is_numeric($dateValue) && !((int) $dateValue)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Validate dateValue parameter.
+ */
+ private static function validateDateValue(mixed $dateValue): float
+ {
+ if (is_bool($dateValue)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return Helpers::getDateValue($dateValue);
+ }
+
+ /**
+ * Validate method parameter.
+ */
+ private static function validateMethod(mixed $method): int
+ {
+ if ($method === null) {
+ $method = Constants::STARTWEEK_SUNDAY;
+ }
+
+ if (!is_numeric($method)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $method = (int) $method;
+ if (!array_key_exists($method, Constants::METHODARR)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ $method = Constants::METHODARR[$method];
+
+ return $method;
+ }
+
+ private static function buggyWeekNum1900(int $method): bool
+ {
+ return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
+ }
+
+ private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
+ {
+ // This appears to be another Excel bug.
+
+ return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904
+ && !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
new file mode 100644
index 0000000..1f5735e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
@@ -0,0 +1,201 @@
+getMessage();
+ }
+
+ $startDate = (float) floor($startDate);
+ $endDays = (int) floor($endDays);
+ // If endDays is 0, we always return startDate
+ if ($endDays == 0) {
+ return $startDate;
+ }
+ if ($endDays < 0) {
+ return self::decrementing($startDate, $endDays, $holidayArray);
+ }
+
+ return self::incrementing($startDate, $endDays, $holidayArray);
+ }
+
+ /**
+ * Use incrementing logic to determine Workday.
+ *
+ * @return mixed
+ */
+ private static function incrementing(float $startDate, int $endDays, array $holidayArray)
+ {
+ // Adjust the start date if it falls over a weekend
+ $startDoW = self::getWeekDay($startDate, 3);
+ if ($startDoW >= 5) {
+ $startDate += 7 - $startDoW;
+ --$endDays;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
+ $endDays = $endDays % 5;
+ while ($endDays > 0) {
+ ++$endDate;
+ // Adjust the calculated end date if it falls over a weekend
+ $endDow = self::getWeekDay($endDate, 3);
+ if ($endDow >= 5) {
+ $endDate += 7 - $endDow;
+ }
+ --$endDays;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($holidayArray)) {
+ $endDate = self::incrementingArray($startDate, $endDate, $holidayArray);
+ }
+
+ return Helpers::returnIn3FormatsFloat($endDate);
+ }
+
+ private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
+ {
+ $holidayCountedArray = $holidayDates = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (self::getWeekDay($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ sort($holidayDates, SORT_NUMERIC);
+ foreach ($holidayDates as $holidayDate) {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if (!in_array($holidayDate, $holidayCountedArray)) {
+ ++$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::getWeekDay($endDate, 3);
+ if ($endDoW >= 5) {
+ $endDate += 7 - $endDoW;
+ }
+ }
+
+ return $endDate;
+ }
+
+ /**
+ * Use decrementing logic to determine Workday.
+ *
+ * @return mixed
+ */
+ private static function decrementing(float $startDate, int $endDays, array $holidayArray)
+ {
+ // Adjust the start date if it falls over a weekend
+ $startDoW = self::getWeekDay($startDate, 3);
+ if ($startDoW >= 5) {
+ $startDate += -$startDoW + 4;
+ ++$endDays;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
+ $endDays = $endDays % 5;
+ while ($endDays < 0) {
+ --$endDate;
+ // Adjust the calculated end date if it falls over a weekend
+ $endDow = self::getWeekDay($endDate, 3);
+ if ($endDow >= 5) {
+ $endDate += 4 - $endDow;
+ }
+ ++$endDays;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($holidayArray)) {
+ $endDate = self::decrementingArray($startDate, $endDate, $holidayArray);
+ }
+
+ return Helpers::returnIn3FormatsFloat($endDate);
+ }
+
+ private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
+ {
+ $holidayCountedArray = $holidayDates = [];
+ foreach ($holidayArray as $holidayDate) {
+ if (self::getWeekDay($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ rsort($holidayDates, SORT_NUMERIC);
+ foreach ($holidayDates as $holidayDate) {
+ if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
+ if (!in_array($holidayDate, $holidayCountedArray)) {
+ --$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::getWeekDay($endDate, 3);
+ /** int $endDoW */
+ if ($endDoW >= 5) {
+ $endDate += -$endDoW + 4;
+ }
+ }
+
+ return $endDate;
+ }
+
+ private static function getWeekDay(float $date, int $wd): int
+ {
+ $result = Functions::scalar(Week::day($date, $wd));
+
+ return is_int($result) ? $result : -1;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
new file mode 100644
index 0000000..4ea431f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
@@ -0,0 +1,124 @@
+getMessage();
+ }
+
+ return match ($method) {
+ 0 => Functions::scalar(Days360::between($startDate, $endDate)) / 360,
+ 1 => self::method1($startDate, $endDate),
+ 2 => Functions::scalar(Difference::interval($startDate, $endDate)) / 360,
+ 3 => Functions::scalar(Difference::interval($startDate, $endDate)) / 365,
+ 4 => Functions::scalar(Days360::between($startDate, $endDate, true)) / 360,
+ default => ExcelError::NAN(),
+ };
+ }
+
+ /**
+ * Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
+ */
+ private static function excelBug(float $sDate, mixed $startDate, mixed $endDate, int $method): float
+ {
+ if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
+ if ($endDate === null && $startDate !== null) {
+ if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) {
+ $sDate += 2;
+ } else {
+ ++$sDate;
+ }
+ }
+ }
+
+ return $sDate;
+ }
+
+ private static function method1(float $startDate, float $endDate): float
+ {
+ $days = Functions::scalar(Difference::interval($startDate, $endDate));
+ $startYear = (int) DateParts::year($startDate);
+ $endYear = (int) DateParts::year($endDate);
+ $years = $endYear - $startYear + 1;
+ $startMonth = (int) DateParts::month($startDate);
+ $startDay = (int) DateParts::day($startDate);
+ $endMonth = (int) DateParts::month($endDate);
+ $endDay = (int) DateParts::day($endDate);
+ $startMonthDay = 100 * $startMonth + $startDay;
+ $endMonthDay = 100 * $endMonth + $endDay;
+ if ($years == 1) {
+ $tmpCalcAnnualBasis = 365 + (int) Helpers::isLeapYear($endYear);
+ } elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
+ if (Helpers::isLeapYear($startYear)) {
+ $tmpCalcAnnualBasis = 365 + (int) ($startMonthDay <= 229);
+ } elseif (Helpers::isLeapYear($endYear)) {
+ $tmpCalcAnnualBasis = 365 + (int) ($endMonthDay >= 229);
+ } else {
+ $tmpCalcAnnualBasis = 365;
+ }
+ } else {
+ $tmpCalcAnnualBasis = 0;
+ for ($year = $startYear; $year <= $endYear; ++$year) {
+ $tmpCalcAnnualBasis += 365 + (int) Helpers::isLeapYear($year);
+ }
+ $tmpCalcAnnualBasis /= $years;
+ }
+
+ return $days / $tmpCalcAnnualBasis;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
new file mode 100644
index 0000000..bd019ac
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
@@ -0,0 +1,205 @@
+indexStart = (int) array_shift($keys);
+ $this->rows = $this->rows($arguments);
+ $this->columns = $this->columns($arguments);
+
+ $this->argumentCount = count($arguments);
+ $this->arguments = $this->flattenSingleCellArrays($arguments, $this->rows, $this->columns);
+
+ $this->rows = $this->rows($arguments);
+ $this->columns = $this->columns($arguments);
+
+ if ($this->arrayArguments() > 2) {
+ throw new Exception('Formulae with more than two array arguments are not supported');
+ }
+ }
+
+ public function arguments(): array
+ {
+ return $this->arguments;
+ }
+
+ public function hasArrayArgument(): bool
+ {
+ return $this->arrayArguments() > 0;
+ }
+
+ public function getFirstArrayArgumentNumber(): int
+ {
+ $rowArrays = $this->filterArray($this->rows);
+ $columnArrays = $this->filterArray($this->columns);
+
+ for ($index = $this->indexStart; $index < $this->argumentCount; ++$index) {
+ if (isset($rowArrays[$index]) || isset($columnArrays[$index])) {
+ return ++$index;
+ }
+ }
+
+ return 0;
+ }
+
+ public function getSingleRowVector(): ?int
+ {
+ $rowVectors = $this->getRowVectors();
+
+ return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
+ }
+
+ private function getRowVectors(): array
+ {
+ $rowVectors = [];
+ for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
+ if ($this->rows[$index] === 1 && $this->columns[$index] > 1) {
+ $rowVectors[] = $index;
+ }
+ }
+
+ return $rowVectors;
+ }
+
+ public function getSingleColumnVector(): ?int
+ {
+ $columnVectors = $this->getColumnVectors();
+
+ return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
+ }
+
+ private function getColumnVectors(): array
+ {
+ $columnVectors = [];
+ for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
+ if ($this->rows[$index] > 1 && $this->columns[$index] === 1) {
+ $columnVectors[] = $index;
+ }
+ }
+
+ return $columnVectors;
+ }
+
+ public function getMatrixPair(): array
+ {
+ for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
+ for ($j = $i + 1; $j < $this->argumentCount; ++$j) {
+ if (isset($this->rows[$i], $this->rows[$j])) {
+ return [$i, $j];
+ }
+ }
+ }
+
+ return [];
+ }
+
+ public function isVector(int $argument): bool
+ {
+ return $this->rows[$argument] === 1 || $this->columns[$argument] === 1;
+ }
+
+ public function isRowVector(int $argument): bool
+ {
+ return $this->rows[$argument] === 1;
+ }
+
+ public function isColumnVector(int $argument): bool
+ {
+ return $this->columns[$argument] === 1;
+ }
+
+ public function rowCount(int $argument): int
+ {
+ return $this->rows[$argument];
+ }
+
+ public function columnCount(int $argument): int
+ {
+ return $this->columns[$argument];
+ }
+
+ private function rows(array $arguments): array
+ {
+ return array_map(
+ fn ($argument): int => is_countable($argument) ? count($argument) : 1,
+ $arguments
+ );
+ }
+
+ private function columns(array $arguments): array
+ {
+ return array_map(
+ function ($argument): int {
+ return is_array($argument) && is_array($argument[array_keys($argument)[0]])
+ ? count($argument[array_keys($argument)[0]])
+ : 1;
+ },
+ $arguments
+ );
+ }
+
+ public function arrayArguments(): int
+ {
+ $count = 0;
+ foreach (array_keys($this->arguments) as $argument) {
+ if ($this->rows[$argument] > 1 || $this->columns[$argument] > 1) {
+ ++$count;
+ }
+ }
+
+ return $count;
+ }
+
+ private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
+ {
+ foreach ($arguments as $index => $argument) {
+ if ($rows[$index] === 1 && $columns[$index] === 1) {
+ while (is_array($argument)) {
+ $argument = array_pop($argument);
+ }
+ $arguments[$index] = $argument;
+ }
+ }
+
+ return $arguments;
+ }
+
+ private function filterArray(array $array): array
+ {
+ return array_filter(
+ $array,
+ fn ($value): bool => $value > 1
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
new file mode 100644
index 0000000..0b1ec58
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
@@ -0,0 +1,162 @@
+hasArrayArgument() === false) {
+ return [$method(...$arguments)];
+ }
+
+ if (self::$arrayArgumentHelper->arrayArguments() === 1) {
+ $nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber();
+
+ return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments);
+ }
+
+ $singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector();
+ $singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector();
+
+ if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) {
+ // Basic logic for a single row vector and a single column vector
+ return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments);
+ }
+
+ $matrixPair = self::$arrayArgumentHelper->getMatrixPair();
+ if ($matrixPair !== []) {
+ if (
+ (self::$arrayArgumentHelper->isVector($matrixPair[0]) === true
+ && self::$arrayArgumentHelper->isVector($matrixPair[1]) === false)
+ || (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false
+ && self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
+ ) {
+ // Logic for a matrix and a vector (row or column)
+ return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments);
+ }
+
+ // Logic for matrix/matrix, column vector/column vector or row vector/row vector
+ return self::evaluateMatrixPair($method, $matrixPair, ...$arguments);
+ }
+
+ // Still need to work out the logic for more than two array arguments,
+ // For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper
+ return ['#VALUE!'];
+ }
+
+ private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
+ {
+ $matrix2 = array_pop($matrixIndexes);
+ /** @var array $matrixValues2 */
+ $matrixValues2 = $arguments[$matrix2];
+ $matrix1 = array_pop($matrixIndexes);
+ /** @var array $matrixValues1 */
+ $matrixValues1 = $arguments[$matrix1];
+
+ $rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
+ $columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
+
+ if ($rows === 1) {
+ $rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
+ }
+ if ($columns === 1) {
+ $columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
+ }
+
+ $result = [];
+ for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) {
+ for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) {
+ $rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex;
+ $columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex;
+ $value1 = $matrixValues1[$rowIndex1][$columnIndex1];
+ $rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex;
+ $columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex;
+ $value2 = $matrixValues2[$rowIndex2][$columnIndex2];
+ $arguments[$matrix1] = $value1;
+ $arguments[$matrix2] = $value2;
+
+ $result[$rowIndex][$columnIndex] = $method(...$arguments);
+ }
+ }
+
+ return $result;
+ }
+
+ private static function evaluateMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
+ {
+ $matrix2 = array_pop($matrixIndexes);
+ /** @var array $matrixValues2 */
+ $matrixValues2 = $arguments[$matrix2];
+ $matrix1 = array_pop($matrixIndexes);
+ /** @var array $matrixValues1 */
+ $matrixValues1 = $arguments[$matrix1];
+
+ $result = [];
+ foreach ($matrixValues1 as $rowIndex => $row) {
+ foreach ($row as $columnIndex => $value1) {
+ if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) {
+ continue;
+ }
+
+ $value2 = $matrixValues2[$rowIndex][$columnIndex];
+ $arguments[$matrix1] = $value1;
+ $arguments[$matrix2] = $value2;
+
+ $result[$rowIndex][$columnIndex] = $method(...$arguments);
+ }
+ }
+
+ return $result;
+ }
+
+ private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, mixed ...$arguments): array
+ {
+ $rowVector = Functions::flattenArray($arguments[$rowIndex]);
+ $columnVector = Functions::flattenArray($arguments[$columnIndex]);
+
+ $result = [];
+ foreach ($columnVector as $column) {
+ $rowResults = [];
+ foreach ($rowVector as $row) {
+ $arguments[$rowIndex] = $row;
+ $arguments[$columnIndex] = $column;
+
+ $rowResults[] = $method(...$arguments);
+ }
+ $result[] = $rowResults;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Note, offset is from 1 (for the first argument) rather than from 0.
+ */
+ private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, mixed ...$arguments): array
+ {
+ $values = array_slice($arguments, $nthArgument - 1, 1);
+ /** @var array $values */
+ $values = array_pop($values);
+
+ $result = [];
+ foreach ($values as $value) {
+ $arguments[$nthArgument - 1] = $value;
+ $result[] = $method(...$arguments);
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
new file mode 100644
index 0000000..00328fb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
@@ -0,0 +1,208 @@
+branchPruningEnabled = $branchPruningEnabled;
+ }
+
+ public function clearBranchStore(): void
+ {
+ $this->branchStoreKeyCounter = 0;
+ }
+
+ public function initialiseForLoop(): void
+ {
+ $this->currentCondition = null;
+ $this->currentOnlyIf = null;
+ $this->currentOnlyIfNot = null;
+ $this->previousStoreKey = null;
+ $this->pendingStoreKey = empty($this->storeKeysStack) ? null : end($this->storeKeysStack);
+
+ if ($this->branchPruningEnabled) {
+ $this->initialiseCondition();
+ $this->initialiseThen();
+ $this->initialiseElse();
+ }
+ }
+
+ private function initialiseCondition(): void
+ {
+ if (isset($this->conditionMap[$this->pendingStoreKey]) && $this->conditionMap[$this->pendingStoreKey]) {
+ $this->currentCondition = $this->pendingStoreKey;
+ $stackDepth = count($this->storeKeysStack);
+ if ($stackDepth > 1) {
+ // nested if
+ $this->previousStoreKey = $this->storeKeysStack[$stackDepth - 2];
+ }
+ }
+ }
+
+ private function initialiseThen(): void
+ {
+ if (isset($this->thenMap[$this->pendingStoreKey]) && $this->thenMap[$this->pendingStoreKey]) {
+ $this->currentOnlyIf = $this->pendingStoreKey;
+ } elseif (
+ isset($this->previousStoreKey, $this->thenMap[$this->previousStoreKey])
+ && $this->thenMap[$this->previousStoreKey]
+ ) {
+ $this->currentOnlyIf = $this->previousStoreKey;
+ }
+ }
+
+ private function initialiseElse(): void
+ {
+ if (isset($this->elseMap[$this->pendingStoreKey]) && $this->elseMap[$this->pendingStoreKey]) {
+ $this->currentOnlyIfNot = $this->pendingStoreKey;
+ } elseif (
+ isset($this->previousStoreKey, $this->elseMap[$this->previousStoreKey])
+ && $this->elseMap[$this->previousStoreKey]
+ ) {
+ $this->currentOnlyIfNot = $this->previousStoreKey;
+ }
+ }
+
+ public function decrementDepth(): void
+ {
+ if (!empty($this->pendingStoreKey)) {
+ --$this->braceDepthMap[$this->pendingStoreKey];
+ }
+ }
+
+ public function incrementDepth(): void
+ {
+ if (!empty($this->pendingStoreKey)) {
+ ++$this->braceDepthMap[$this->pendingStoreKey];
+ }
+ }
+
+ public function functionCall(string $functionName): void
+ {
+ if ($this->branchPruningEnabled && ($functionName === 'IF(')) {
+ // we handle a new if
+ $this->pendingStoreKey = $this->getUnusedBranchStoreKey();
+ $this->storeKeysStack[] = $this->pendingStoreKey;
+ $this->conditionMap[$this->pendingStoreKey] = true;
+ $this->braceDepthMap[$this->pendingStoreKey] = 0;
+ } elseif (!empty($this->pendingStoreKey) && array_key_exists($this->pendingStoreKey, $this->braceDepthMap)) {
+ // this is not an if but we go deeper
+ ++$this->braceDepthMap[$this->pendingStoreKey];
+ }
+ }
+
+ public function argumentSeparator(): void
+ {
+ if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === 0) {
+ // We must go to the IF next argument
+ if ($this->conditionMap[$this->pendingStoreKey]) {
+ $this->conditionMap[$this->pendingStoreKey] = false;
+ $this->thenMap[$this->pendingStoreKey] = true;
+ } elseif ($this->thenMap[$this->pendingStoreKey]) {
+ $this->thenMap[$this->pendingStoreKey] = false;
+ $this->elseMap[$this->pendingStoreKey] = true;
+ } elseif ($this->elseMap[$this->pendingStoreKey]) {
+ throw new Exception('Reaching fourth argument of an IF');
+ }
+ }
+ }
+
+ public function closingBrace(mixed $value): void
+ {
+ if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
+ // we are closing an IF(
+ if ($value !== 'IF(') {
+ throw new Exception('Parser bug we should be in an "IF("');
+ }
+
+ if ($this->conditionMap[$this->pendingStoreKey]) {
+ throw new Exception('We should not be expecting a condition');
+ }
+
+ $this->thenMap[$this->pendingStoreKey] = false;
+ $this->elseMap[$this->pendingStoreKey] = false;
+ --$this->braceDepthMap[$this->pendingStoreKey];
+ array_pop($this->storeKeysStack);
+ $this->pendingStoreKey = null;
+ }
+ }
+
+ public function currentCondition(): ?string
+ {
+ return $this->currentCondition;
+ }
+
+ public function currentOnlyIf(): ?string
+ {
+ return $this->currentOnlyIf;
+ }
+
+ public function currentOnlyIfNot(): ?string
+ {
+ return $this->currentOnlyIfNot;
+ }
+
+ private function getUnusedBranchStoreKey(): string
+ {
+ $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
+ ++$this->branchStoreKeyCounter;
+
+ return $storeKeyValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
new file mode 100644
index 0000000..11d0925
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
@@ -0,0 +1,67 @@
+stack);
+ }
+
+ /**
+ * Push a new entry onto the stack.
+ */
+ public function push(mixed $value): void
+ {
+ $this->stack[$value] = $value;
+ }
+
+ /**
+ * Pop the last entry from the stack.
+ *
+ * @return mixed
+ */
+ public function pop()
+ {
+ return array_pop($this->stack);
+ }
+
+ /**
+ * Test to see if a specified entry exists on the stack.
+ *
+ * @param mixed $value The value to test
+ */
+ public function onStack(mixed $value): bool
+ {
+ return isset($this->stack[$value]);
+ }
+
+ /**
+ * Clear the stack.
+ */
+ public function clear(): void
+ {
+ $this->stack = [];
+ }
+
+ /**
+ * Return an array of all entries on the stack.
+ *
+ * @return mixed[]
+ */
+ public function showStack()
+ {
+ return $this->stack;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
new file mode 100644
index 0000000..331fa44
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
@@ -0,0 +1,147 @@
+[-+])? *\% *(?[-+])? *(?[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *\% *))$~i';
+
+ // preg_quoted string for major currency symbols, with a %s for locale currency
+ private const CURRENCY_CONVERSION_LIST = '\$€£¥%s';
+
+ private const STRING_CONVERSION_LIST = [
+ [self::class, 'convertToNumberIfNumeric'],
+ [self::class, 'convertToNumberIfFraction'],
+ [self::class, 'convertToNumberIfPercent'],
+ [self::class, 'convertToNumberIfCurrency'],
+ ];
+
+ /**
+ * Identify whether a string contains a formatted numeric value,
+ * and convert it to a numeric if it is.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfFormatted(string &$operand): bool
+ {
+ foreach (self::STRING_CONVERSION_LIST as $conversionMethod) {
+ if ($conversionMethod($operand) === true) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a numeric value,
+ * and convert it to a numeric if it is.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfNumeric(string &$operand): bool
+ {
+ $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+ $value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
+ $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
+ $value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
+
+ if (is_numeric($value)) {
+ $operand = (float) $value;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a fractional numeric value,
+ * and convert it to a numeric if it is.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfFraction(string &$operand): bool
+ {
+ if (preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
+ $sign = ($match[1] === '-') ? '-' : '+';
+ $wholePart = ($match[3] === '') ? '' : ($sign . $match[3]);
+ $fractionFormula = '=' . $wholePart . $sign . $match[4];
+ $operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a percentage, and if so,
+ * convert it to a numeric.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfPercent(string &$operand): bool
+ {
+ $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+ $value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim($operand));
+ $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
+ $value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
+
+ $match = [];
+ if ($value !== null && preg_match(self::STRING_REGEXP_PERCENT, $value, $match, PREG_UNMATCHED_AS_NULL)) {
+ //Calculate the percentage
+ $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
+ $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue'])) / 100;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Identify whether a string contains a currency value, and if so,
+ * convert it to a numeric.
+ *
+ * @param string $operand string value to test
+ */
+ public static function convertToNumberIfCurrency(string &$operand): bool
+ {
+ $currencyRegexp = self::currencyMatcherRegexp();
+ $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+ $value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
+
+ $match = [];
+ if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
+ //Determine the sign
+ $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
+ $decimalSeparator = StringHelper::getDecimalSeparator();
+ //Cast to a float
+ $intermediate = (string) ($match['PostfixedValue'] ?? $match['PrefixedValue']);
+ $intermediate = str_replace($decimalSeparator, '.', $intermediate);
+ if (is_numeric($intermediate)) {
+ $operand = (float) ($sign . str_replace($decimalSeparator, '.', $intermediate));
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static function currencyMatcherRegexp(): string
+ {
+ $currencyCodes = sprintf(self::CURRENCY_CONVERSION_LIST, preg_quote(StringHelper::getCurrencyCode(), '/'));
+ $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
+
+ return '~^(?:(?: *(?[-+])? *(?[' . $currencyCodes . ']) *(?[-+])? *(?[0-9]+[' . $decimalSeparator . ']?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+' . $decimalSeparator . '?[0-9]*(?:E[-+]?[0-9]*)?) *(?[' . $currencyCodes . ']) *))$~ui';
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
new file mode 100644
index 0000000..bd7ba70
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
@@ -0,0 +1,138 @@
+cellStack = $stack;
+ }
+
+ /**
+ * Enable/Disable Calculation engine logging.
+ *
+ * @param bool $writeDebugLog
+ */
+ public function setWriteDebugLog($writeDebugLog): void
+ {
+ $this->writeDebugLog = $writeDebugLog;
+ }
+
+ /**
+ * Return whether calculation engine logging is enabled or disabled.
+ *
+ * @return bool
+ */
+ public function getWriteDebugLog()
+ {
+ return $this->writeDebugLog;
+ }
+
+ /**
+ * Enable/Disable echoing of debug log information.
+ *
+ * @param bool $echoDebugLog
+ */
+ public function setEchoDebugLog($echoDebugLog): void
+ {
+ $this->echoDebugLog = $echoDebugLog;
+ }
+
+ /**
+ * Return whether echoing of debug log information is enabled or disabled.
+ *
+ * @return bool
+ */
+ public function getEchoDebugLog()
+ {
+ return $this->echoDebugLog;
+ }
+
+ /**
+ * Write an entry to the calculation engine debug log.
+ */
+ public function writeDebugLog(string $message, mixed ...$args): void
+ {
+ // Only write the debug log if logging is enabled
+ if ($this->writeDebugLog) {
+ $message = sprintf($message, ...$args);
+ $cellReference = implode(' -> ', $this->cellStack->showStack());
+ if ($this->echoDebugLog) {
+ echo $cellReference,
+ ($this->cellStack->count() > 0 ? ' => ' : ''),
+ $message,
+ PHP_EOL;
+ }
+ $this->debugLog[] = $cellReference
+ . ($this->cellStack->count() > 0 ? ' => ' : '')
+ . $message;
+ }
+ }
+
+ /**
+ * Write a series of entries to the calculation engine debug log.
+ *
+ * @param string[] $args
+ */
+ public function mergeDebugLog(array $args): void
+ {
+ if ($this->writeDebugLog) {
+ foreach ($args as $entry) {
+ $this->writeDebugLog($entry);
+ }
+ }
+ }
+
+ /**
+ * Clear the calculation engine debug log.
+ */
+ public function clearLog(): void
+ {
+ $this->debugLog = [];
+ }
+
+ /**
+ * Return the calculation engine debug log.
+ *
+ * @return string[]
+ */
+ public function getLog()
+ {
+ return $this->debugLog;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php
new file mode 100644
index 0000000..05264c3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/Operand.php
@@ -0,0 +1,10 @@
+value = $structuredReference;
+ }
+
+ public static function fromParser(string $formula, int $index, array $matches): self
+ {
+ $val = $matches[0];
+
+ $srCount = substr_count($val, self::OPEN_BRACE)
+ - substr_count($val, self::CLOSE_BRACE);
+ while ($srCount > 0) {
+ $srIndex = strlen($val);
+ $srStringRemainder = substr($formula, $index + $srIndex);
+ $closingPos = strpos($srStringRemainder, self::CLOSE_BRACE);
+ if ($closingPos === false) {
+ throw new Exception("Formula Error: No closing ']' to match opening '['");
+ }
+ $srStringRemainder = substr($srStringRemainder, 0, $closingPos + 1);
+ --$srCount;
+ if (str_contains($srStringRemainder, self::OPEN_BRACE)) {
+ ++$srCount;
+ }
+ $val .= $srStringRemainder;
+ }
+
+ return new self($val);
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public function parse(Cell $cell): string
+ {
+ $this->getTableStructure($cell);
+ $cellRange = ($this->isRowReference()) ? $this->getRowReference($cell) : $this->getColumnReference();
+ $sheetName = '';
+ $worksheet = $this->table->getWorksheet();
+ if ($worksheet !== null && $worksheet !== $cell->getWorksheet()) {
+ $sheetName = "'" . $worksheet->getTitle() . "'!";
+ }
+
+ return $sheetName . $cellRange;
+ }
+
+ private function isRowReference(): bool
+ {
+ return str_contains($this->value, '[@')
+ || str_contains($this->value, '[' . self::ITEM_SPECIFIER_THIS_ROW . ']');
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getTableStructure(Cell $cell): void
+ {
+ preg_match(self::TABLE_REFERENCE, $this->value, $matches);
+
+ $this->tableName = $matches[1];
+ $this->table = ($this->tableName === '')
+ ? $this->getTableForCell($cell)
+ : $this->getTableByName($cell);
+ $this->reference = $matches[2];
+ $tableRange = Coordinate::getRangeBoundaries($this->table->getRange());
+
+ $this->headersRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] : null;
+ $this->firstDataRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] + 1 : $tableRange[0][1];
+ $this->totalsRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] : null;
+ $this->lastDataRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] - 1 : $tableRange[1][1];
+
+ $cellParam = $cell;
+ $worksheet = $this->table->getWorksheet();
+ if ($worksheet !== null && $worksheet !== $cell->getWorksheet()) {
+ $cellParam = $worksheet->getCell('A1');
+ }
+ $this->columns = $this->getColumns($cellParam, $tableRange);
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getTableForCell(Cell $cell): Table
+ {
+ $tables = $cell->getWorksheet()->getTableCollection();
+ foreach ($tables as $table) {
+ /** @var Table $table */
+ $range = $table->getRange();
+ if ($cell->isInRange($range) === true) {
+ $this->tableName = $table->getName();
+
+ return $table;
+ }
+ }
+
+ throw new Exception('Table for Structured Reference cannot be identified');
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getTableByName(Cell $cell): Table
+ {
+ $table = $cell->getWorksheet()->getTableByName($this->tableName);
+
+ if ($table === null) {
+ $spreadsheet = $cell->getWorksheet()->getParent();
+ if ($spreadsheet !== null) {
+ $table = $spreadsheet->getTableByName($this->tableName);
+ }
+ }
+
+ if ($table === null) {
+ throw new Exception("Table {$this->tableName} for Structured Reference cannot be located");
+ }
+
+ return $table;
+ }
+
+ private function getColumns(Cell $cell, array $tableRange): array
+ {
+ $worksheet = $cell->getWorksheet();
+ $cellReference = $cell->getCoordinate();
+
+ $columns = [];
+ $lastColumn = ++$tableRange[1][0];
+ for ($column = $tableRange[0][0]; $column !== $lastColumn; ++$column) {
+ $columns[$column] = $worksheet
+ ->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
+ ->getCalculatedValue();
+ }
+
+ $worksheet->getCell($cellReference);
+
+ return $columns;
+ }
+
+ private function getRowReference(Cell $cell): string
+ {
+ $reference = str_replace("\u{a0}", ' ', $this->reference);
+ /** @var string $reference */
+ $reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
+
+ foreach ($this->columns as $columnId => $columnName) {
+ $columnName = str_replace("\u{a0}", ' ', $columnName);
+ $reference = $this->adjustRowReference($columnName, $reference, $cell, $columnId);
+ }
+
+ /** @var string $reference */
+ return $this->validateParsedReference(trim($reference, '[]@, '));
+ }
+
+ private function adjustRowReference(string $columnName, string $reference, Cell $cell, string $columnId): string
+ {
+ if ($columnName !== '') {
+ $cellReference = $columnId . $cell->getRow();
+ $pattern1 = '/\[' . preg_quote($columnName, '/') . '\]/miu';
+ $pattern2 = '/@' . preg_quote($columnName, '/') . '/miu';
+ if (preg_match($pattern1, $reference) === 1) {
+ $reference = preg_replace($pattern1, $cellReference, $reference);
+ } elseif (preg_match($pattern2, $reference) === 1) {
+ $reference = preg_replace($pattern2, $cellReference, $reference);
+ }
+ /** @var string $reference */
+ }
+
+ return $reference;
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function getColumnReference(): string
+ {
+ $reference = str_replace("\u{a0}", ' ', $this->reference);
+ $startRow = ($this->totalsRow === null) ? $this->lastDataRow : $this->totalsRow;
+ $endRow = ($this->headersRow === null) ? $this->firstDataRow : $this->headersRow;
+
+ [$startRow, $endRow] = $this->getRowsForColumnReference($reference, $startRow, $endRow);
+ $reference = $this->getColumnsForColumnReference($reference, $startRow, $endRow);
+
+ $reference = trim($reference, '[]@, ');
+ if (substr_count($reference, ':') > 1) {
+ $cells = explode(':', $reference);
+ $firstCell = array_shift($cells);
+ $lastCell = array_pop($cells);
+ $reference = "{$firstCell}:{$lastCell}";
+ }
+
+ return $this->validateParsedReference($reference);
+ }
+
+ /**
+ * @throws Exception
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ private function validateParsedReference(string $reference): string
+ {
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . ':' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
+ throw new Exception(
+ "Invalid Structured Reference {$this->reference} {$reference}",
+ Exception::CALCULATION_ENGINE_PUSH_TO_STACK
+ );
+ }
+ }
+
+ return $reference;
+ }
+
+ private function fullData(int $startRow, int $endRow): string
+ {
+ $columns = array_keys($this->columns);
+ $firstColumn = array_shift($columns);
+ $lastColumn = (empty($columns)) ? $firstColumn : array_pop($columns);
+
+ return "{$firstColumn}{$startRow}:{$lastColumn}{$endRow}";
+ }
+
+ private function getMinimumRow(string $reference): int
+ {
+ return match ($reference) {
+ self::ITEM_SPECIFIER_ALL, self::ITEM_SPECIFIER_HEADERS => $this->headersRow ?? $this->firstDataRow,
+ self::ITEM_SPECIFIER_DATA => $this->firstDataRow,
+ self::ITEM_SPECIFIER_TOTALS => $this->totalsRow ?? $this->lastDataRow,
+ default => $this->headersRow ?? $this->firstDataRow,
+ };
+ }
+
+ private function getMaximumRow(string $reference): int
+ {
+ return match ($reference) {
+ self::ITEM_SPECIFIER_HEADERS => $this->headersRow ?? $this->firstDataRow,
+ self::ITEM_SPECIFIER_DATA => $this->lastDataRow,
+ self::ITEM_SPECIFIER_ALL, self::ITEM_SPECIFIER_TOTALS => $this->totalsRow ?? $this->lastDataRow,
+ default => $this->totalsRow ?? $this->lastDataRow,
+ };
+ }
+
+ public function value(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * @return array
+ */
+ private function getRowsForColumnReference(string &$reference, int $startRow, int $endRow): array
+ {
+ $rowsSelected = false;
+ foreach (self::ITEM_SPECIFIER_ROWS_SET as $rowReference) {
+ $pattern = '/\[' . $rowReference . '\]/mui';
+ /** @var string $reference */
+ if (preg_match($pattern, $reference) === 1) {
+ if (($rowReference === self::ITEM_SPECIFIER_HEADERS) && ($this->table->getShowHeaderRow() === false)) {
+ throw new Exception(
+ 'Table Headers are Hidden, and should not be Referenced',
+ Exception::CALCULATION_ENGINE_PUSH_TO_STACK
+ );
+ }
+ $rowsSelected = true;
+ $startRow = min($startRow, $this->getMinimumRow($rowReference));
+ $endRow = max($endRow, $this->getMaximumRow($rowReference));
+ $reference = preg_replace($pattern, '', $reference);
+ }
+ }
+ if ($rowsSelected === false) {
+ // If there isn't any Special Item Identifier specified, then the selection defaults to data rows only.
+ $startRow = $this->firstDataRow;
+ $endRow = $this->lastDataRow;
+ }
+
+ return [$startRow, $endRow];
+ }
+
+ private function getColumnsForColumnReference(string $reference, int $startRow, int $endRow): string
+ {
+ $columnsSelected = false;
+ foreach ($this->columns as $columnId => $columnName) {
+ $columnName = str_replace("\u{a0}", ' ', $columnName ?? '');
+ $cellFrom = "{$columnId}{$startRow}";
+ $cellTo = "{$columnId}{$endRow}";
+ $cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
+ $pattern = '/\[' . preg_quote($columnName, '/') . '\]/mui';
+ if (preg_match($pattern, $reference) === 1) {
+ $columnsSelected = true;
+ $reference = preg_replace($pattern, $cellReference, $reference);
+ }
+ /** @var string $reference */
+ }
+ if ($columnsSelected === false) {
+ return $this->fullData($startRow, $endRow);
+ }
+
+ return $reference;
+ }
+
+ public function __toString(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
new file mode 100644
index 0000000..1479fab
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
@@ -0,0 +1,1400 @@
+ 511, DEC2BIN returns the #NUM! error
+ * value.
+ * If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If DEC2BIN requires more than places characters, it returns the #NUM!
+ * error value.
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If places is zero or negative, DEC2BIN returns the #NUM! error value.
+ */
+ public static function DECTOBIN(mixed $x, mixed $places = null): string|array
+ {
+ return Engineering\ConvertDecimal::toBinary($x, $places);
+ }
+
+ /**
+ * DECTOHEX.
+ *
+ * Return a decimal value as hex.
+ *
+ * Excel Function:
+ * DEC2HEX(x[,places])
+ *
+ * @deprecated 1.17.0
+ * Use the toHex() method in the Engineering\ConvertDecimal class instead
+ * @see Engineering\ConvertDecimal::toHex()
+ *
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2HEX returns a 10-character (40-bit)
+ * hexadecimal number in which the most significant bit is the sign
+ * bit. The remaining 39 bits are magnitude bits. Negative numbers
+ * are represented using two's-complement notation.
+ * If number < -549,755,813,888 or if number > 549,755,813,887,
+ * DEC2HEX returns the #NUM! error value.
+ * If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If DEC2HEX requires more than places characters, it returns the
+ * #NUM! error value.
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If places is zero or negative, DEC2HEX returns the #NUM! error value.
+ */
+ public static function DECTOHEX(mixed $x, mixed $places = null): string|array
+ {
+ return Engineering\ConvertDecimal::toHex($x, $places);
+ }
+
+ /**
+ * DECTOOCT.
+ *
+ * Return an decimal value as octal.
+ *
+ * Excel Function:
+ * DEC2OCT(x[,places])
+ *
+ * @deprecated 1.17.0
+ * Use the toOctal() method in the Engineering\ConvertDecimal class instead
+ * @see Engineering\ConvertDecimal::toOctal()
+ *
+ * @param mixed $x The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2OCT returns a 10-character (30-bit)
+ * octal number in which the most significant bit is the sign bit.
+ * The remaining 29 bits are magnitude bits. Negative numbers are
+ * represented using two's-complement notation.
+ * If number < -536,870,912 or if number > 536,870,911, DEC2OCT
+ * returns the #NUM! error value.
+ * If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If DEC2OCT requires more than places characters, it returns the
+ * #NUM! error value.
+ * @param mixed $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If places is zero or negative, DEC2OCT returns the #NUM! error value.
+ */
+ public static function DECTOOCT(mixed $x, mixed $places = null): string|array
+ {
+ return Engineering\ConvertDecimal::toOctal($x, $places);
+ }
+
+ /**
+ * HEXTOBIN.
+ *
+ * Return a hex value as binary.
+ *
+ * Excel Function:
+ * HEX2BIN(x[,places])
+ *
+ * @deprecated 1.17.0
+ * Use the toBinary() method in the Engineering\ConvertHex class instead
+ * @see Engineering\ConvertHex::toBinary()
+ *
+ * @param mixed $x the hexadecimal number (as a string) that you want to convert.
+ * Number cannot contain more than 10 characters.
+ * The most significant bit of number is the sign bit (40th bit from the right).
+ * The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is negative, HEX2BIN ignores places and returns a 10-character binary number.
+ * If number is negative, it cannot be less than FFFFFFFE00,
+ * and if number is positive, it cannot be greater than 1FF.
+ * If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
+ * If HEX2BIN requires more than places characters, it returns the #NUM! error value.
+ * @param mixed $places The number of characters to use. If places is omitted,
+ * HEX2BIN uses the minimum number of characters necessary. Places
+ * is useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
+ * If places is negative, HEX2BIN returns the #NUM! error value.
+ *
+ * @return array|string
+ */
+ public static function HEXTOBIN(mixed $x, mixed $places = null)
+ {
+ return Engineering\ConvertHex::toBinary($x, $places);
+ }
+
+ /**
+ * HEXTODEC.
+ *
+ * Return a hex value as decimal.
+ *
+ * Excel Function:
+ * HEX2DEC(x)
+ *
+ * @deprecated 1.17.0
+ * Use the toDecimal() method in the Engineering\ConvertHex class instead
+ * @see Engineering\ConvertHex::toDecimal()
+ *
+ * @param mixed $x The hexadecimal number (as a string) that you want to convert. This number cannot
+ * contain more than 10 characters (40 bits). The most significant
+ * bit of number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is not a valid hexadecimal number, HEX2DEC returns the
+ * #NUM! error value.
+ *
+ * @return array|string
+ */
+ public static function HEXTODEC(mixed $x)
+ {
+ return Engineering\ConvertHex::toDecimal($x);
+ }
+
+ /**
+ * HEXTOOCT.
+ *
+ * Return a hex value as octal.
+ *
+ * Excel Function:
+ * HEX2OCT(x[,places])
+ *
+ * @deprecated 1.17.0
+ * Use the toOctal() method in the Engineering\ConvertHex class instead
+ * @see Engineering\ConvertHex::toOctal()
+ *
+ * @param mixed $x The hexadecimal number (as a string) that you want to convert. Number cannot
+ * contain more than 10 characters. The most significant bit of
+ * number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is negative, HEX2OCT ignores places and returns a
+ * 10-character octal number.
+ * If number is negative, it cannot be less than FFE0000000, and
+ * if number is positive, it cannot be greater than 1FFFFFFF.
+ * If number is not a valid hexadecimal number, HEX2OCT returns
+ * the #NUM! error value.
+ * If HEX2OCT requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param mixed $places The number of characters to use. If places is omitted, HEX2OCT
+ * uses the minimum number of characters necessary. Places is
+ * useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2OCT returns the #VALUE! error
+ * value.
+ * If places is negative, HEX2OCT returns the #NUM! error value.
+ *
+ * @return array|string
+ */
+ public static function HEXTOOCT(mixed $x, mixed $places = null)
+ {
+ return Engineering\ConvertHex::toOctal($x, $places);
+ }
+
+ /**
+ * OCTTOBIN.
+ *
+ * Return an octal value as binary.
+ *
+ * Excel Function:
+ * OCT2BIN(x[,places])
+ *
+ * @deprecated 1.17.0
+ * Use the toBinary() method in the Engineering\ConvertOctal class instead
+ * @see Engineering\ConvertOctal::toBinary()
+ *
+ * @param mixed $x The octal number you want to convert. Number may not
+ * contain more than 10 characters. The most significant
+ * bit of number is the sign bit. The remaining 29 bits
+ * are magnitude bits. Negative numbers are represented
+ * using two's-complement notation.
+ * If number is negative, OCT2BIN ignores places and returns
+ * a 10-character binary number.
+ * If number is negative, it cannot be less than 7777777000,
+ * and if number is positive, it cannot be greater than 777.
+ * If number is not a valid octal number, OCT2BIN returns
+ * the #NUM! error value.
+ * If OCT2BIN requires more than places characters, it
+ * returns the #NUM! error value.
+ * @param mixed $places The number of characters to use. If places is omitted,
+ * OCT2BIN uses the minimum number of characters necessary.
+ * Places is useful for padding the return value with
+ * leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2BIN returns the #VALUE!
+ * error value.
+ * If places is negative, OCT2BIN returns the #NUM! error
+ * value.
+ *
+ * @return array|string
+ */
+ public static function OCTTOBIN(mixed $x, mixed $places = null)
+ {
+ return Engineering\ConvertOctal::toBinary($x, $places);
+ }
+
+ /**
+ * OCTTODEC.
+ *
+ * Return an octal value as decimal.
+ *
+ * Excel Function:
+ * OCT2DEC(x)
+ *
+ * @deprecated 1.17.0
+ * Use the toDecimal() method in the Engineering\ConvertOctal class instead
+ * @see Engineering\ConvertOctal::toDecimal()
+ *
+ * @param mixed $x The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is not a valid octal number, OCT2DEC returns the
+ * #NUM! error value.
+ *
+ * @return array|string
+ */
+ public static function OCTTODEC(mixed $x)
+ {
+ return Engineering\ConvertOctal::toDecimal($x);
+ }
+
+ /**
+ * OCTTOHEX.
+ *
+ * Return an octal value as hex.
+ *
+ * Excel Function:
+ * OCT2HEX(x[,places])
+ *
+ * @deprecated 1.17.0
+ * Use the toHex() method in the Engineering\ConvertOctal class instead
+ * @see Engineering\ConvertOctal::toHex()
+ *
+ * @param mixed $x The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is negative, OCT2HEX ignores places and returns a
+ * 10-character hexadecimal number.
+ * If number is not a valid octal number, OCT2HEX returns the
+ * #NUM! error value.
+ * If OCT2HEX requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param mixed $places The number of characters to use. If places is omitted, OCT2HEX
+ * uses the minimum number of characters necessary. Places is useful
+ * for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
+ * If places is negative, OCT2HEX returns the #NUM! error value.
+ */
+ public static function OCTTOHEX(mixed $x, mixed $places = null): string|array
+ {
+ return Engineering\ConvertOctal::toHex($x, $places);
+ }
+
+ /**
+ * COMPLEX.
+ *
+ * Converts real and imaginary coefficients into a complex number of the form x +/- yi or x +/- yj.
+ *
+ * Excel Function:
+ * COMPLEX(realNumber,imaginary[,suffix])
+ *
+ * @deprecated 1.18.0
+ * Use the COMPLEX() method in the Engineering\Complex class instead
+ * @see Engineering\Complex::COMPLEX()
+ *
+ * @param array|float $realNumber the real coefficient of the complex number
+ * @param array|float $imaginary the imaginary coefficient of the complex number
+ * @param array|string $suffix The suffix for the imaginary component of the complex number.
+ * If omitted, the suffix is assumed to be "i".
+ *
+ * @return array|string
+ */
+ public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
+ {
+ return Engineering\Complex::COMPLEX($realNumber, $imaginary, $suffix);
+ }
+
+ /**
+ * IMAGINARY.
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMAGINARY(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMAGINARY() method in the Engineering\Complex class instead
+ * @see Engineering\Complex::IMAGINARY()
+ *
+ * @param string $complexNumber the complex number for which you want the imaginary
+ * coefficient
+ */
+ public static function IMAGINARY($complexNumber): string|float|array
+ {
+ return Engineering\Complex::IMAGINARY($complexNumber);
+ }
+
+ /**
+ * IMREAL.
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMREAL(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMREAL() method in the Engineering\Complex class instead
+ * @see Engineering\Complex::IMREAL()
+ *
+ * @param string $complexNumber the complex number for which you want the real coefficient
+ */
+ public static function IMREAL($complexNumber): string|float|array
+ {
+ return Engineering\Complex::IMREAL($complexNumber);
+ }
+
+ /**
+ * IMABS.
+ *
+ * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMABS(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMABS() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMABS()
+ *
+ * @param string $complexNumber the complex number for which you want the absolute value
+ *
+ * @return array|float|string
+ */
+ public static function IMABS($complexNumber)
+ {
+ return ComplexFunctions::IMABS($complexNumber);
+ }
+
+ /**
+ * IMARGUMENT.
+ *
+ * Returns the argument theta of a complex number, i.e. the angle in radians from the real
+ * axis to the representation of the number in polar coordinates.
+ *
+ * Excel Function:
+ * IMARGUMENT(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMARGUMENT()
+ *
+ * @param array|string $complexNumber the complex number for which you want the argument theta
+ *
+ * @return array|float|string
+ */
+ public static function IMARGUMENT($complexNumber)
+ {
+ return ComplexFunctions::IMARGUMENT($complexNumber);
+ }
+
+ /**
+ * IMCONJUGATE.
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCONJUGATE(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMCONJUGATE() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCONJUGATE()
+ *
+ * @param array|string $complexNumber the complex number for which you want the conjugate
+ *
+ * @return array|string
+ */
+ public static function IMCONJUGATE($complexNumber)
+ {
+ return ComplexFunctions::IMCONJUGATE($complexNumber);
+ }
+
+ /**
+ * IMCOS.
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOS(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMCOS() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCOS()
+ *
+ * @param array|string $complexNumber the complex number for which you want the cosine
+ *
+ * @return array|float|string
+ */
+ public static function IMCOS($complexNumber)
+ {
+ return ComplexFunctions::IMCOS($complexNumber);
+ }
+
+ /**
+ * IMCOSH.
+ *
+ * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOSH(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMCOSH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCOSH()
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
+ *
+ * @return array|float|string
+ */
+ public static function IMCOSH($complexNumber)
+ {
+ return ComplexFunctions::IMCOSH($complexNumber);
+ }
+
+ /**
+ * IMCOT.
+ *
+ * Returns the cotangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOT(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMCOT() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCOT()
+ *
+ * @param array|string $complexNumber the complex number for which you want the cotangent
+ *
+ * @return array|float|string
+ */
+ public static function IMCOT($complexNumber)
+ {
+ return ComplexFunctions::IMCOT($complexNumber);
+ }
+
+ /**
+ * IMCSC.
+ *
+ * Returns the cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSC(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMCSC() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCSC()
+ *
+ * @param array|string $complexNumber the complex number for which you want the cosecant
+ *
+ * @return array|float|string
+ */
+ public static function IMCSC($complexNumber)
+ {
+ return ComplexFunctions::IMCSC($complexNumber);
+ }
+
+ /**
+ * IMCSCH.
+ *
+ * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSCH(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMCSCH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMCSCH()
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
+ *
+ * @return array|float|string
+ */
+ public static function IMCSCH($complexNumber)
+ {
+ return ComplexFunctions::IMCSCH($complexNumber);
+ }
+
+ /**
+ * IMSIN.
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSIN(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMSIN() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSIN()
+ *
+ * @param string $complexNumber the complex number for which you want the sine
+ *
+ * @return array|float|string
+ */
+ public static function IMSIN($complexNumber)
+ {
+ return ComplexFunctions::IMSIN($complexNumber);
+ }
+
+ /**
+ * IMSINH.
+ *
+ * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSINH(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMSINH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSINH()
+ *
+ * @param string $complexNumber the complex number for which you want the hyperbolic sine
+ *
+ * @return array|float|string
+ */
+ public static function IMSINH($complexNumber)
+ {
+ return ComplexFunctions::IMSINH($complexNumber);
+ }
+
+ /**
+ * IMSEC.
+ *
+ * Returns the secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSEC(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMSEC() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSEC()
+ *
+ * @param string $complexNumber the complex number for which you want the secant
+ *
+ * @return array|float|string
+ */
+ public static function IMSEC($complexNumber)
+ {
+ return ComplexFunctions::IMSEC($complexNumber);
+ }
+
+ /**
+ * IMSECH.
+ *
+ * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSECH(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMSECH() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSECH()
+ *
+ * @param string $complexNumber the complex number for which you want the hyperbolic secant
+ *
+ * @return array|float|string
+ */
+ public static function IMSECH($complexNumber)
+ {
+ return ComplexFunctions::IMSECH($complexNumber);
+ }
+
+ /**
+ * IMTAN.
+ *
+ * Returns the tangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMTAN(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMTAN() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMTAN()
+ *
+ * @param string $complexNumber the complex number for which you want the tangent
+ *
+ * @return array|float|string
+ */
+ public static function IMTAN($complexNumber)
+ {
+ return ComplexFunctions::IMTAN($complexNumber);
+ }
+
+ /**
+ * IMSQRT.
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSQRT(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMSQRT() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMSQRT()
+ *
+ * @param string $complexNumber the complex number for which you want the square root
+ *
+ * @return array|string
+ */
+ public static function IMSQRT($complexNumber)
+ {
+ return ComplexFunctions::IMSQRT($complexNumber);
+ }
+
+ /**
+ * IMLN.
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLN(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMLN() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMLN()
+ *
+ * @param string $complexNumber the complex number for which you want the natural logarithm
+ *
+ * @return array|string
+ */
+ public static function IMLN($complexNumber)
+ {
+ return ComplexFunctions::IMLN($complexNumber);
+ }
+
+ /**
+ * IMLOG10.
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG10(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMLOG10() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMLOG10()
+ *
+ * @param string $complexNumber the complex number for which you want the common logarithm
+ *
+ * @return array|string
+ */
+ public static function IMLOG10($complexNumber)
+ {
+ return ComplexFunctions::IMLOG10($complexNumber);
+ }
+
+ /**
+ * IMLOG2.
+ *
+ * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG2(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMLOG2() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMLOG2()
+ *
+ * @param string $complexNumber the complex number for which you want the base-2 logarithm
+ *
+ * @return array|string
+ */
+ public static function IMLOG2($complexNumber)
+ {
+ return ComplexFunctions::IMLOG2($complexNumber);
+ }
+
+ /**
+ * IMEXP.
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMEXP(complexNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMEXP() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMEXP()
+ *
+ * @param string $complexNumber the complex number for which you want the exponential
+ *
+ * @return array|string
+ */
+ public static function IMEXP($complexNumber)
+ {
+ return ComplexFunctions::IMEXP($complexNumber);
+ }
+
+ /**
+ * IMPOWER.
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * Excel Function:
+ * IMPOWER(complexNumber,realNumber)
+ *
+ * @deprecated 1.18.0
+ * Use the IMPOWER() method in the Engineering\ComplexFunctions class instead
+ * @see ComplexFunctions::IMPOWER()
+ *
+ * @param string $complexNumber the complex number you want to raise to a power
+ * @param float $realNumber the power to which you want to raise the complex number
+ *
+ * @return array|string
+ */
+ public static function IMPOWER($complexNumber, $realNumber)
+ {
+ return ComplexFunctions::IMPOWER($complexNumber, $realNumber);
+ }
+
+ /**
+ * IMDIV.
+ *
+ * Returns the quotient of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMDIV(complexDividend,complexDivisor)
+ *
+ * @deprecated 1.18.0
+ * Use the IMDIV() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMDIV()
+ *
+ * @param string $complexDividend the complex numerator or dividend
+ * @param string $complexDivisor the complex denominator or divisor
+ *
+ * @return array|string
+ */
+ public static function IMDIV($complexDividend, $complexDivisor)
+ {
+ return ComplexOperations::IMDIV($complexDividend, $complexDivisor);
+ }
+
+ /**
+ * IMSUB.
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUB(complexNumber1,complexNumber2)
+ *
+ * @deprecated 1.18.0
+ * Use the IMSUB() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMSUB()
+ *
+ * @param string $complexNumber1 the complex number from which to subtract complexNumber2
+ * @param string $complexNumber2 the complex number to subtract from complexNumber1
+ *
+ * @return array|string
+ */
+ public static function IMSUB($complexNumber1, $complexNumber2)
+ {
+ return ComplexOperations::IMSUB($complexNumber1, $complexNumber2);
+ }
+
+ /**
+ * IMSUM.
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUM(complexNumber[,complexNumber[,...]])
+ *
+ * @deprecated 1.18.0
+ * Use the IMSUM() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMSUM()
+ *
+ * @param string ...$complexNumbers Series of complex numbers to add
+ */
+ public static function IMSUM(...$complexNumbers): string
+ {
+ return ComplexOperations::IMSUM(...$complexNumbers);
+ }
+
+ /**
+ * IMPRODUCT.
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMPRODUCT(complexNumber[,complexNumber[,...]])
+ *
+ * @deprecated 1.18.0
+ * Use the IMPRODUCT() method in the Engineering\ComplexOperations class instead
+ * @see ComplexOperations::IMPRODUCT()
+ *
+ * @param string ...$complexNumbers Series of complex numbers to multiply
+ */
+ public static function IMPRODUCT(...$complexNumbers): string
+ {
+ return ComplexOperations::IMPRODUCT(...$complexNumbers);
+ }
+
+ /**
+ * DELTA.
+ *
+ * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
+ * Use this function to filter a set of values. For example, by summing several DELTA
+ * functions you calculate the count of equal pairs. This function is also known as the
+ * Kronecker Delta function.
+ *
+ * Excel Function:
+ * DELTA(a[,b])
+ *
+ * @deprecated 1.17.0
+ * Use the DELTA() method in the Engineering\Compare class instead
+ * @see Engineering\Compare::DELTA()
+ *
+ * @param float $a the first number
+ * @param float $b The second number. If omitted, b is assumed to be zero.
+ *
+ * @return array|int|string (string in the event of an error)
+ */
+ public static function DELTA($a, $b = 0)
+ {
+ return Engineering\Compare::DELTA($a, $b);
+ }
+
+ /**
+ * GESTEP.
+ *
+ * Excel Function:
+ * GESTEP(number[,step])
+ *
+ * Returns 1 if number >= step; returns 0 (zero) otherwise
+ * Use this function to filter a set of values. For example, by summing several GESTEP
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @deprecated 1.17.0
+ * Use the GESTEP() method in the Engineering\Compare class instead
+ * @see Engineering\Compare::GESTEP()
+ *
+ * @param float $number the value to test against step
+ * @param float $step The threshold value. If you omit a value for step, GESTEP uses zero.
+ *
+ * @return array|int|string (string in the event of an error)
+ */
+ public static function GESTEP($number, $step = 0)
+ {
+ return Engineering\Compare::GESTEP($number, $step);
+ }
+
+ /**
+ * BITAND.
+ *
+ * Returns the bitwise AND of two integer values.
+ *
+ * Excel Function:
+ * BITAND(number1, number2)
+ *
+ * @deprecated 1.17.0
+ * Use the BITAND() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITAND()
+ *
+ * @param int $number1
+ * @param int $number2
+ *
+ * @return array|int|string
+ */
+ public static function BITAND($number1, $number2)
+ {
+ return Engineering\BitWise::BITAND($number1, $number2);
+ }
+
+ /**
+ * BITOR.
+ *
+ * Returns the bitwise OR of two integer values.
+ *
+ * Excel Function:
+ * BITOR(number1, number2)
+ *
+ * @deprecated 1.17.0
+ * Use the BITOR() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITOR()
+ *
+ * @param int $number1
+ * @param int $number2
+ *
+ * @return array|int|string
+ */
+ public static function BITOR($number1, $number2)
+ {
+ return Engineering\BitWise::BITOR($number1, $number2);
+ }
+
+ /**
+ * BITXOR.
+ *
+ * Returns the bitwise XOR of two integer values.
+ *
+ * Excel Function:
+ * BITXOR(number1, number2)
+ *
+ * @deprecated 1.17.0
+ * Use the BITXOR() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITXOR()
+ *
+ * @param int $number1
+ * @param int $number2
+ *
+ * @return array|int|string
+ */
+ public static function BITXOR($number1, $number2)
+ {
+ return Engineering\BitWise::BITXOR($number1, $number2);
+ }
+
+ /**
+ * BITLSHIFT.
+ *
+ * Returns the number value shifted left by shift_amount bits.
+ *
+ * Excel Function:
+ * BITLSHIFT(number, shift_amount)
+ *
+ * @deprecated 1.17.0
+ * Use the BITLSHIFT() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITLSHIFT()
+ *
+ * @param int $number
+ * @param int $shiftAmount
+ *
+ * @return array|float|int|string
+ */
+ public static function BITLSHIFT($number, $shiftAmount)
+ {
+ return Engineering\BitWise::BITLSHIFT($number, $shiftAmount);
+ }
+
+ /**
+ * BITRSHIFT.
+ *
+ * Returns the number value shifted right by shift_amount bits.
+ *
+ * Excel Function:
+ * BITRSHIFT(number, shift_amount)
+ *
+ * @deprecated 1.17.0
+ * Use the BITRSHIFT() method in the Engineering\BitWise class instead
+ * @see Engineering\BitWise::BITRSHIFT()
+ *
+ * @param int $number
+ * @param int $shiftAmount
+ *
+ * @return array|float|int|string
+ */
+ public static function BITRSHIFT($number, $shiftAmount)
+ {
+ return Engineering\BitWise::BITRSHIFT($number, $shiftAmount);
+ }
+
+ /**
+ * ERF.
+ *
+ * Returns the error function integrated between the lower and upper bound arguments.
+ *
+ * Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
+ * the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
+ * improved, so that it can now calculate the function for both positive and negative ranges.
+ * PhpSpreadsheet follows Excel 2010 behaviour, and accepts negative arguments.
+ *
+ * Excel Function:
+ * ERF(lower[,upper])
+ *
+ * @deprecated 1.17.0
+ * Use the ERF() method in the Engineering\Erf class instead
+ * @see Engineering\Erf::ERF()
+ *
+ * @param float $lower lower bound for integrating ERF
+ * @param float $upper upper bound for integrating ERF.
+ * If omitted, ERF integrates between zero and lower_limit
+ */
+ public static function ERF($lower, $upper = null): float|string|array
+ {
+ return Engineering\Erf::ERF($lower, $upper);
+ }
+
+ /**
+ * ERFPRECISE.
+ *
+ * Returns the error function integrated between the lower and upper bound arguments.
+ *
+ * Excel Function:
+ * ERF.PRECISE(limit)
+ *
+ * @deprecated 1.17.0
+ * Use the ERFPRECISE() method in the Engineering\Erf class instead
+ * @see Engineering\Erf::ERFPRECISE()
+ *
+ * @param float $limit bound for integrating ERF
+ *
+ * @return array|float|string
+ */
+ public static function ERFPRECISE($limit)
+ {
+ return Engineering\Erf::ERFPRECISE($limit);
+ }
+
+ /**
+ * ERFC.
+ *
+ * Returns the complementary ERF function integrated between x and infinity
+ *
+ * Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
+ * the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
+ * improved, so that it can now calculate the function for both positive and negative x values.
+ * PhpSpreadsheet follows Excel 2010 behaviour, and accepts nagative arguments.
+ *
+ * Excel Function:
+ * ERFC(x)
+ *
+ * @deprecated 1.17.0
+ * Use the ERFC() method in the Engineering\ErfC class instead
+ * @see Engineering\ErfC::ERFC()
+ *
+ * @param float $x The lower bound for integrating ERFC
+ *
+ * @return array|float|string
+ */
+ public static function ERFC($x)
+ {
+ return Engineering\ErfC::ERFC($x);
+ }
+
+ /**
+ * getConversionGroups
+ * Returns a list of the different conversion groups for UOM conversions.
+ *
+ * @deprecated 1.16.0
+ * Use the getConversionCategories() method in the Engineering\ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionCategories()
+ */
+ public static function getConversionGroups(): array
+ {
+ return Engineering\ConvertUOM::getConversionCategories();
+ }
+
+ /**
+ * getConversionGroupUnits
+ * Returns an array of units of measure, for a specified conversion group, or for all groups.
+ *
+ * @deprecated 1.16.0
+ * Use the getConversionCategoryUnits() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionCategoryUnits()
+ *
+ * @param null|mixed $category
+ */
+ public static function getConversionGroupUnits($category = null): array
+ {
+ return Engineering\ConvertUOM::getConversionCategoryUnits($category);
+ }
+
+ /**
+ * getConversionGroupUnitDetails.
+ *
+ * @deprecated 1.16.0
+ * Use the getConversionCategoryUnitDetails() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionCategoryUnitDetails()
+ *
+ * @param null|mixed $category
+ */
+ public static function getConversionGroupUnitDetails($category = null): array
+ {
+ return Engineering\ConvertUOM::getConversionCategoryUnitDetails($category);
+ }
+
+ /**
+ * getConversionMultipliers
+ * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @deprecated 1.16.0
+ * Use the getConversionMultipliers() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getConversionMultipliers()
+ *
+ * @return mixed[]
+ */
+ public static function getConversionMultipliers()
+ {
+ return Engineering\ConvertUOM::getConversionMultipliers();
+ }
+
+ /**
+ * getBinaryConversionMultipliers.
+ *
+ * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure
+ * in CONVERTUOM().
+ *
+ * @deprecated 1.16.0
+ * Use the getBinaryConversionMultipliers() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::getBinaryConversionMultipliers()
+ *
+ * @return mixed[]
+ */
+ public static function getBinaryConversionMultipliers()
+ {
+ return Engineering\ConvertUOM::getBinaryConversionMultipliers();
+ }
+
+ /**
+ * CONVERTUOM.
+ *
+ * Converts a number from one measurement system to another.
+ * For example, CONVERT can translate a table of distances in miles to a table of distances
+ * in kilometers.
+ *
+ * Excel Function:
+ * CONVERT(value,fromUOM,toUOM)
+ *
+ * @deprecated 1.16.0
+ * Use the CONVERT() method in the ConvertUOM class instead
+ * @see Engineering\ConvertUOM::CONVERT()
+ *
+ * @param float|int $value the value in fromUOM to convert
+ * @param string $fromUOM the units for value
+ * @param string $toUOM the units for the result
+ *
+ * @return array|float|string
+ */
+ public static function CONVERTUOM($value, $fromUOM, $toUOM)
+ {
+ return Engineering\ConvertUOM::CONVERT($value, $fromUOM, $toUOM);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
new file mode 100644
index 0000000..5d564a0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
@@ -0,0 +1,141 @@
+getMessage();
+ }
+
+ if ($ord < 0) {
+ return ExcelError::NAN();
+ }
+
+ $fResult = self::calculate($x, $ord);
+
+ return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ return match ($ord) {
+ 0 => self::besselI0($x),
+ 1 => self::besselI1($x),
+ default => self::besselI2($x, $ord),
+ };
+ }
+
+ private static function besselI0(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 3.75) {
+ $y = $x / 3.75;
+ $y = $y * $y;
+
+ return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
+ + $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
+ }
+
+ $y = 3.75 / $ax;
+
+ return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
+ + $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1
+ + $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
+ }
+
+ private static function besselI1(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 3.75) {
+ $y = $x / 3.75;
+ $y = $y * $y;
+ $ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1
+ + $y * (0.301532e-2 + $y * 0.32411e-3))))));
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ $y = 3.75 / $ax;
+ $ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
+ $ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2
+ + $y * (-0.1031555e-1 + $y * $ans))));
+ $ans *= exp($ax) / sqrt($ax);
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ private static function besselI2(float $x, int $ord): float
+ {
+ if ($x === 0.0) {
+ return 0.0;
+ }
+
+ $tox = 2.0 / abs($x);
+ $bip = 0;
+ $ans = 0.0;
+ $bi = 1.0;
+
+ for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
+ $bim = $bip + $j * $tox * $bi;
+ $bip = $bi;
+ $bi = $bim;
+
+ if (abs($bi) > 1.0e+12) {
+ $ans *= 1.0e-12;
+ $bi *= 1.0e-12;
+ $bip *= 1.0e-12;
+ }
+
+ if ($j === $ord) {
+ $ans = $bip;
+ }
+ }
+
+ $ans *= self::besselI0($x) / $bi;
+
+ return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
new file mode 100644
index 0000000..4a9d9ff
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
@@ -0,0 +1,176 @@
+ 8. This code provides a more accurate calculation
+ *
+ * @param mixed $x A float value at which to evaluate the function.
+ * If x is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * Or can be an array of values
+ * @param mixed $ord The integer order of the Bessel function.
+ * If ord is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * If $ord < 0, BESSELJ returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|float|string Result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BESSELJ(mixed $x, mixed $ord): array|string|float
+ {
+ if (is_array($x) || is_array($ord)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
+ }
+
+ try {
+ $x = EngineeringValidations::validateFloat($x);
+ $ord = EngineeringValidations::validateInt($ord);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($ord < 0) {
+ return ExcelError::NAN();
+ }
+
+ $fResult = self::calculate($x, $ord);
+
+ return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ return match ($ord) {
+ 0 => self::besselJ0($x),
+ 1 => self::besselJ1($x),
+ default => self::besselJ2($x, $ord),
+ };
+ }
+
+ private static function besselJ0(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 8.0) {
+ $y = $x * $x;
+ $ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y
+ * (77392.33017 + $y * (-184.9052456)))));
+ $ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y
+ * (267.8532712 + $y * 1.0))));
+
+ return $ans1 / $ans2;
+ }
+
+ $z = 8.0 / $ax;
+ $y = $z * $z;
+ $xx = $ax - 0.785398164;
+ $ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y
+ * (0.7621095161e-6 - $y * 0.934935152e-7)));
+
+ return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
+ }
+
+ private static function besselJ1(float $x): float
+ {
+ $ax = abs($x);
+
+ if ($ax < 8.0) {
+ $y = $x * $x;
+ $ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y
+ * (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
+ $ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y
+ * (376.9991397 + $y * 1.0))));
+
+ return $ans1 / $ans2;
+ }
+
+ $z = 8.0 / $ax;
+ $y = $z * $z;
+ $xx = $ax - 2.356194491;
+
+ $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
+ $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
+ * (-0.88228987e-6 + $y * 0.105787412e-6)));
+ $ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
+
+ return ($x < 0.0) ? -$ans : $ans;
+ }
+
+ private static function besselJ2(float $x, int $ord): float
+ {
+ $ax = abs($x);
+ if ($ax === 0.0) {
+ return 0.0;
+ }
+
+ if ($ax > $ord) {
+ return self::besselj2a($ax, $ord, $x);
+ }
+
+ return self::besselj2b($ax, $ord, $x);
+ }
+
+ private static function besselj2a(float $ax, int $ord, float $x): float
+ {
+ $tox = 2.0 / $ax;
+ $bjm = self::besselJ0($ax);
+ $bj = self::besselJ1($ax);
+ for ($j = 1; $j < $ord; ++$j) {
+ $bjp = $j * $tox * $bj - $bjm;
+ $bjm = $bj;
+ $bj = $bjp;
+ }
+ $ans = $bj;
+
+ return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
+ }
+
+ private static function besselj2b(float $ax, int $ord, float $x): float
+ {
+ $tox = 2.0 / $ax;
+ $jsum = false;
+ $bjp = $ans = $sum = 0.0;
+ $bj = 1.0;
+ for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
+ $bjm = $j * $tox * $bj - $bjp;
+ $bjp = $bj;
+ $bj = $bjm;
+ if (abs($bj) > 1.0e+10) {
+ $bj *= 1.0e-10;
+ $bjp *= 1.0e-10;
+ $ans *= 1.0e-10;
+ $sum *= 1.0e-10;
+ }
+ if ($jsum === true) {
+ $sum += $bj;
+ }
+ $jsum = $jsum === false;
+ if ($j === $ord) {
+ $ans = $bjp;
+ }
+ }
+ $sum = 2.0 * $sum - $bj;
+ $ans /= $sum;
+
+ return ($x < 0.0 && ($ord % 2) === 1) ? -$ans : $ans;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
new file mode 100644
index 0000000..5a9bd54
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
@@ -0,0 +1,130 @@
+getMessage();
+ }
+
+ if (($ord < 0) || ($x <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ $fBk = self::calculate($x, $ord);
+
+ return (is_nan($fBk)) ? ExcelError::NAN() : $fBk;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ return match ($ord) {
+ 0 => self::besselK0($x),
+ 1 => self::besselK1($x),
+ default => self::besselK2($x, $ord),
+ };
+ }
+
+ /**
+ * Mollify Phpstan.
+ *
+ * @codeCoverageIgnore
+ */
+ private static function callBesselI(float $x, int $ord): float
+ {
+ $rslt = BesselI::BESSELI($x, $ord);
+ if (!is_float($rslt)) {
+ throw new Exception('Unexpected array or string');
+ }
+
+ return $rslt;
+ }
+
+ private static function besselK0(float $x): float
+ {
+ if ($x <= 2) {
+ $fNum2 = $x * 0.5;
+ $y = ($fNum2 * $fNum2);
+
+ return -log($fNum2) * self::callBesselI($x, 0)
+ + (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y
+ * (0.10750e-3 + $y * 0.74e-5))))));
+ }
+
+ $y = 2 / $x;
+
+ return exp(-$x) / sqrt($x)
+ * (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y
+ * (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
+ }
+
+ private static function besselK1(float $x): float
+ {
+ if ($x <= 2) {
+ $fNum2 = $x * 0.5;
+ $y = ($fNum2 * $fNum2);
+
+ return log($fNum2) * self::callBesselI($x, 1)
+ + (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y
+ * (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
+ }
+
+ $y = 2 / $x;
+
+ return exp(-$x) / sqrt($x)
+ * (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y
+ * (0.325614e-2 + $y * (-0.68245e-3)))))));
+ }
+
+ private static function besselK2(float $x, int $ord): float
+ {
+ $fTox = 2 / $x;
+ $fBkm = self::besselK0($x);
+ $fBk = self::besselK1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fBkp = $fBkm + $n * $fTox * $fBk;
+ $fBkm = $fBk;
+ $fBk = $fBkp;
+ }
+
+ return $fBk;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
new file mode 100644
index 0000000..5d99638
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
@@ -0,0 +1,137 @@
+getMessage();
+ }
+
+ if (($ord < 0) || ($x <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ $fBy = self::calculate($x, $ord);
+
+ return (is_nan($fBy)) ? ExcelError::NAN() : $fBy;
+ }
+
+ private static function calculate(float $x, int $ord): float
+ {
+ return match ($ord) {
+ 0 => self::besselY0($x),
+ 1 => self::besselY1($x),
+ default => self::besselY2($x, $ord),
+ };
+ }
+
+ /**
+ * Mollify Phpstan.
+ *
+ * @codeCoverageIgnore
+ */
+ private static function callBesselJ(float $x, int $ord): float
+ {
+ $rslt = BesselJ::BESSELJ($x, $ord);
+ if (!is_float($rslt)) {
+ throw new Exception('Unexpected array or string');
+ }
+
+ return $rslt;
+ }
+
+ private static function besselY0(float $x): float
+ {
+ if ($x < 8.0) {
+ $y = ($x * $x);
+ $ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y
+ * (-86327.92757 + $y * 228.4622733))));
+ $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y
+ * (47447.26470 + $y * (226.1030244 + $y))));
+
+ return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
+ }
+
+ $z = 8.0 / $x;
+ $y = ($z * $z);
+ $xx = $x - 0.785398164;
+ $ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y
+ * (-0.934945152e-7))));
+
+ return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
+ }
+
+ private static function besselY1(float $x): float
+ {
+ if ($x < 8.0) {
+ $y = ($x * $x);
+ $ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y
+ * (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
+ $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y
+ * (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+
+ return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
+ }
+
+ $z = 8.0 / $x;
+ $y = $z * $z;
+ $xx = $x - 2.356194491;
+ $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
+ $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
+ * (-0.88228987e-6 + $y * 0.105787412e-6)));
+
+ return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
+ }
+
+ private static function besselY2(float $x, int $ord): float
+ {
+ $fTox = 2.0 / $x;
+ $fBym = self::besselY0($x);
+ $fBy = self::besselY1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fByp = $n * $fTox * $fBy - $fBym;
+ $fBym = $fBy;
+ $fBy = $fByp;
+ }
+
+ return $fBy;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
new file mode 100644
index 0000000..f3e6ed2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
@@ -0,0 +1,268 @@
+getMessage();
+ }
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
+ }
+
+ /**
+ * BITOR.
+ *
+ * Returns the bitwise OR of two integer values.
+ *
+ * Excel Function:
+ * BITOR(number1, number2)
+ *
+ * @param array|int $number1
+ * Or can be an array of values
+ * @param array|int $number2
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITOR($number1, $number2): array|string|int
+ {
+ if (is_array($number1) || is_array($number2)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+ }
+
+ try {
+ $number1 = self::validateBitwiseArgument($number1);
+ $number2 = self::validateBitwiseArgument($number2);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
+ }
+
+ /**
+ * BITXOR.
+ *
+ * Returns the bitwise XOR of two integer values.
+ *
+ * Excel Function:
+ * BITXOR(number1, number2)
+ *
+ * @param array|int $number1
+ * Or can be an array of values
+ * @param array|int $number2
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITXOR($number1, $number2): array|string|int
+ {
+ if (is_array($number1) || is_array($number2)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+ }
+
+ try {
+ $number1 = self::validateBitwiseArgument($number1);
+ $number2 = self::validateBitwiseArgument($number2);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $split1 = self::splitNumber($number1);
+ $split2 = self::splitNumber($number2);
+
+ return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
+ }
+
+ /**
+ * BITLSHIFT.
+ *
+ * Returns the number value shifted left by shift_amount bits.
+ *
+ * Excel Function:
+ * BITLSHIFT(number, shift_amount)
+ *
+ * @param array|int $number
+ * Or can be an array of values
+ * @param array|int $shiftAmount
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITLSHIFT($number, $shiftAmount): array|string|float
+ {
+ if (is_array($number) || is_array($shiftAmount)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
+ }
+
+ try {
+ $number = self::validateBitwiseArgument($number);
+ $shiftAmount = self::validateShiftAmount($shiftAmount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = floor($number * (2 ** $shiftAmount));
+ if ($result > 2 ** 48 - 1) {
+ return ExcelError::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * BITRSHIFT.
+ *
+ * Returns the number value shifted right by shift_amount bits.
+ *
+ * Excel Function:
+ * BITRSHIFT(number, shift_amount)
+ *
+ * @param array|int $number
+ * Or can be an array of values
+ * @param array|int $shiftAmount
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function BITRSHIFT($number, $shiftAmount): array|string|float
+ {
+ if (is_array($number) || is_array($shiftAmount)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
+ }
+
+ try {
+ $number = self::validateBitwiseArgument($number);
+ $shiftAmount = self::validateShiftAmount($shiftAmount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = floor($number / (2 ** $shiftAmount));
+ if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
+ return ExcelError::NAN();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validate arguments passed to the bitwise functions.
+ */
+ private static function validateBitwiseArgument(mixed $value): float
+ {
+ $value = self::nullFalseTrueToNumber($value);
+
+ if (is_numeric($value)) {
+ $value = (float) $value;
+ if ($value == floor($value)) {
+ if (($value > 2 ** 48 - 1) || ($value < 0)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return floor($value);
+ }
+
+ throw new Exception(ExcelError::NAN());
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Validate arguments passed to the bitwise functions.
+ *
+ * @return int
+ */
+ private static function validateShiftAmount(mixed $value)
+ {
+ $value = self::nullFalseTrueToNumber($value);
+
+ if (is_numeric($value)) {
+ if (abs($value) > 53) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return (int) $value;
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Many functions accept null/false/true argument treated as 0/0/1.
+ *
+ * @return mixed
+ */
+ private static function nullFalseTrueToNumber(mixed &$number)
+ {
+ if ($number === null) {
+ $number = 0;
+ } elseif (is_bool($number)) {
+ $number = (int) $number;
+ }
+
+ return $number;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
new file mode 100644
index 0000000..0a8c1f3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
@@ -0,0 +1,82 @@
+getMessage();
+ }
+
+ return (int) (abs($a - $b) < 1.0e-15);
+ }
+
+ /**
+ * GESTEP.
+ *
+ * Excel Function:
+ * GESTEP(number[,step])
+ *
+ * Returns 1 if number >= step; returns 0 (zero) otherwise
+ * Use this function to filter a set of values. For example, by summing several GESTEP
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @param array|float $number the value to test against step
+ * Or can be an array of values
+ * @param null|array|float $step The threshold value. If you omit a value for step, GESTEP uses zero.
+ * Or can be an array of values
+ *
+ * @return array|int|string (string in the event of an error)
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function GESTEP($number, $step = 0.0): array|string|int
+ {
+ if (is_array($number) || is_array($step)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);
+ }
+
+ try {
+ $number = EngineeringValidations::validateFloat($number);
+ $step = EngineeringValidations::validateFloat($step ?? 0.0);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (int) ($number >= $step);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
new file mode 100644
index 0000000..4ae0342
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
@@ -0,0 +1,121 @@
+getMessage();
+ }
+
+ if (($suffix === 'i') || ($suffix === 'j') || ($suffix === '')) {
+ $complex = new ComplexObject($realNumber, $imaginary, $suffix);
+
+ return (string) $complex;
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * IMAGINARY.
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMAGINARY(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the imaginary
+ * coefficient
+ * Or can be an array of values
+ *
+ * @return array|float|string (string if an error)
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMAGINARY($complexNumber): array|string|float
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return $complex->getImaginary();
+ }
+
+ /**
+ * IMREAL.
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMREAL(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the real coefficient
+ * Or can be an array of values
+ *
+ * @return array|float|string (string if an error)
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMREAL($complexNumber): array|string|float
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return $complex->getReal();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
new file mode 100644
index 0000000..5b0daf1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
@@ -0,0 +1,611 @@
+abs();
+ }
+
+ /**
+ * IMARGUMENT.
+ *
+ * Returns the argument theta of a complex number, i.e. the angle in radians from the real
+ * axis to the representation of the number in polar coordinates.
+ *
+ * Excel Function:
+ * IMARGUMENT(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the argument theta
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMARGUMENT($complexNumber)
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::DIV0();
+ }
+
+ return $complex->argument();
+ }
+
+ /**
+ * IMCONJUGATE.
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCONJUGATE(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the conjugate
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCONJUGATE($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->conjugate();
+ }
+
+ /**
+ * IMCOS.
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOS(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the cosine
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCOS($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->cos();
+ }
+
+ /**
+ * IMCOSH.
+ *
+ * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOSH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCOSH($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->cosh();
+ }
+
+ /**
+ * IMCOT.
+ *
+ * Returns the cotangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOT(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the cotangent
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCOT($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->cot();
+ }
+
+ /**
+ * IMCSC.
+ *
+ * Returns the cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSC(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the cosecant
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCSC($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->csc();
+ }
+
+ /**
+ * IMCSCH.
+ *
+ * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCSCH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMCSCH($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->csch();
+ }
+
+ /**
+ * IMSIN.
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSIN(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the sine
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSIN($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sin();
+ }
+
+ /**
+ * IMSINH.
+ *
+ * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSINH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic sine
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSINH($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sinh();
+ }
+
+ /**
+ * IMSEC.
+ *
+ * Returns the secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSEC(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the secant
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSEC($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sec();
+ }
+
+ /**
+ * IMSECH.
+ *
+ * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSECH(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the hyperbolic secant
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSECH($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->sech();
+ }
+
+ /**
+ * IMTAN.
+ *
+ * Returns the tangent of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMTAN(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the tangent
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMTAN($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->tan();
+ }
+
+ /**
+ * IMSQRT.
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSQRT(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the square root
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSQRT($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ $theta = self::IMARGUMENT($complexNumber);
+ if ($theta === ExcelError::DIV0()) {
+ return '0';
+ }
+
+ return (string) $complex->sqrt();
+ }
+
+ /**
+ * IMLN.
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLN(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the natural logarithm
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMLN($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->ln();
+ }
+
+ /**
+ * IMLOG10.
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG10(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the common logarithm
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMLOG10($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->log10();
+ }
+
+ /**
+ * IMLOG2.
+ *
+ * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG2(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the base-2 logarithm
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMLOG2($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->log2();
+ }
+
+ /**
+ * IMEXP.
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMEXP(complexNumber)
+ *
+ * @param array|string $complexNumber the complex number for which you want the exponential
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMEXP($complexNumber): array|string
+ {
+ if (is_array($complexNumber)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $complex->exp();
+ }
+
+ /**
+ * IMPOWER.
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * Excel Function:
+ * IMPOWER(complexNumber,realNumber)
+ *
+ * @param array|string $complexNumber the complex number you want to raise to a power
+ * Or can be an array of values
+ * @param array|float|int|string $realNumber the power to which you want to raise the complex number
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMPOWER($complexNumber, $realNumber): array|string
+ {
+ if (is_array($complexNumber) || is_array($realNumber)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
+ }
+
+ try {
+ $complex = new ComplexObject($complexNumber);
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ if (!is_numeric($realNumber)) {
+ return ExcelError::VALUE();
+ }
+
+ return (string) $complex->pow((float) $realNumber);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
new file mode 100644
index 0000000..bab5082
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
@@ -0,0 +1,130 @@
+divideby(new ComplexObject($complexDivisor));
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+ }
+
+ /**
+ * IMSUB.
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUB(complexNumber1,complexNumber2)
+ *
+ * @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
+ * Or can be an array of values
+ * @param array|string $complexNumber2 the complex number to subtract from complexNumber1
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function IMSUB($complexNumber1, $complexNumber2): array|string
+ {
+ if (is_array($complexNumber1) || is_array($complexNumber2)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
+ }
+
+ try {
+ return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+ }
+
+ /**
+ * IMSUM.
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUM(complexNumber[,complexNumber[,...]])
+ *
+ * @param string ...$complexNumbers Series of complex numbers to add
+ */
+ public static function IMSUM(...$complexNumbers): string
+ {
+ // Return value
+ $returnValue = new ComplexObject(0.0);
+ $aArgs = Functions::flattenArray($complexNumbers);
+
+ try {
+ // Loop through the arguments
+ foreach ($aArgs as $complex) {
+ $returnValue = $returnValue->add(new ComplexObject($complex));
+ }
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $returnValue;
+ }
+
+ /**
+ * IMPRODUCT.
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMPRODUCT(complexNumber[,complexNumber[,...]])
+ *
+ * @param string ...$complexNumbers Series of complex numbers to multiply
+ */
+ public static function IMPRODUCT(...$complexNumbers): string
+ {
+ // Return value
+ $returnValue = new ComplexObject(1.0);
+ $aArgs = Functions::flattenArray($complexNumbers);
+
+ try {
+ // Loop through the arguments
+ foreach ($aArgs as $complex) {
+ $returnValue = $returnValue->multiply(new ComplexObject($complex));
+ }
+ } catch (ComplexException) {
+ return ExcelError::NAN();
+ }
+
+ return (string) $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php
new file mode 100644
index 0000000..a926db6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php
@@ -0,0 +1,11 @@
+ 10) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return (int) $places;
+ }
+
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ /**
+ * Formats a number base string value with leading zeroes.
+ *
+ * @param string $value The "number" to pad
+ * @param ?int $places The length that we want to pad this value
+ *
+ * @return string The padded "number"
+ */
+ protected static function nbrConversionFormat(string $value, ?int $places): string
+ {
+ if ($places !== null) {
+ if (strlen($value) <= $places) {
+ return substr(str_pad($value, $places, '0', STR_PAD_LEFT), -10);
+ }
+
+ return ExcelError::NAN();
+ }
+
+ return substr($value, -10);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
new file mode 100644
index 0000000..f21b4d5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
@@ -0,0 +1,163 @@
+getMessage();
+ }
+
+ if (strlen($value) == 10 && $value[0] === '1') {
+ // Two's Complement
+ $value = substr($value, -9);
+
+ return '-' . (512 - bindec($value));
+ }
+
+ return (string) bindec($value);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return a binary value as hex.
+ *
+ * Excel Function:
+ * BIN2HEX(x[,places])
+ *
+ * @param array|string $value The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
+ * If places is negative, BIN2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toHex($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateBinary($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) == 10 && $value[0] === '1') {
+ $high2 = substr($value, 0, 2);
+ $low8 = substr($value, 2);
+ $xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
+
+ return $xarr[$high2] . strtoupper(substr('0' . dechex((int) bindec($low8)), -2));
+ }
+ $hexVal = (string) strtoupper(dechex((int) bindec($value)));
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return a binary value as octal.
+ *
+ * Excel Function:
+ * BIN2OCT(x[,places])
+ *
+ * @param array|string $value The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
+ * If places is negative, BIN2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toOctal($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateBinary($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) == 10 && $value[0] === '1') { // Two's Complement
+ return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
+ }
+ $octVal = (string) decoct((int) bindec($value));
+
+ return self::nbrConversionFormat($octVal, $places);
+ }
+
+ protected static function validateBinary(string $value): string
+ {
+ if ((strlen($value) > preg_match_all('/[01]/', $value)) || (strlen($value) > 10)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
new file mode 100644
index 0000000..14ee687
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
@@ -0,0 +1,213 @@
+ 511, DEC2BIN returns the #NUM! error
+ * value.
+ * If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If DEC2BIN requires more than places characters, it returns the #NUM!
+ * error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If places is zero or negative, DEC2BIN returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toBinary($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = (int) floor((float) $value);
+ if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
+ return ExcelError::NAN();
+ }
+
+ $r = decbin($value);
+ // Two's Complement
+ $r = substr($r, -10);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return a decimal value as hex.
+ *
+ * Excel Function:
+ * DEC2HEX(x[,places])
+ *
+ * @param array|string $value The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2HEX returns a 10-character (40-bit)
+ * hexadecimal number in which the most significant bit is the sign
+ * bit. The remaining 39 bits are magnitude bits. Negative numbers
+ * are represented using two's-complement notation.
+ * If number < -549,755,813,888 or if number > 549,755,813,887,
+ * DEC2HEX returns the #NUM! error value.
+ * If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If DEC2HEX requires more than places characters, it returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If places is zero or negative, DEC2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toHex($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = floor((float) $value);
+ if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
+ return ExcelError::NAN();
+ }
+ $r = strtoupper(dechex((int) $value));
+ $r = self::hex32bit($value, $r);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ public static function hex32bit(float $value, string $hexstr, bool $force = false): string
+ {
+ if (PHP_INT_SIZE === 4 || $force) {
+ if ($value >= 2 ** 32) {
+ $quotient = (int) ($value / (2 ** 32));
+
+ return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr);
+ }
+ if ($value < -(2 ** 32)) {
+ $quotient = 256 - (int) ceil((-$value) / (2 ** 32));
+
+ return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8));
+ }
+ if ($value < 0) {
+ return "FF$hexstr";
+ }
+ }
+
+ return $hexstr;
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return an decimal value as octal.
+ *
+ * Excel Function:
+ * DEC2OCT(x[,places])
+ *
+ * @param array|string $value The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2OCT returns a 10-character (30-bit)
+ * octal number in which the most significant bit is the sign bit.
+ * The remaining 29 bits are magnitude bits. Negative numbers are
+ * represented using two's-complement notation.
+ * If number < -536,870,912 or if number > 536,870,911, DEC2OCT
+ * returns the #NUM! error value.
+ * If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If DEC2OCT requires more than places characters, it returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If places is zero or negative, DEC2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toOctal($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateDecimal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $value = (int) floor((float) $value);
+ if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
+ return ExcelError::NAN();
+ }
+ $r = decoct($value);
+ $r = substr($r, -10);
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+ protected static function validateDecimal(string $value): string
+ {
+ if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
new file mode 100644
index 0000000..9d39acf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
@@ -0,0 +1,175 @@
+getMessage();
+ }
+
+ $dec = self::toDecimal($value);
+
+ return ConvertDecimal::toBinary($dec, $places);
+ }
+
+ /**
+ * toDecimal.
+ *
+ * Return a hex value as decimal.
+ *
+ * Excel Function:
+ * HEX2DEC(x)
+ *
+ * @param array|string $value The hexadecimal number you want to convert. This number cannot
+ * contain more than 10 characters (40 bits). The most significant
+ * bit of number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is not a valid hexadecimal number, HEX2DEC returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toDecimal($value)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateHex($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (strlen($value) > 10) {
+ return ExcelError::NAN();
+ }
+
+ $binX = '';
+ foreach (str_split($value) as $char) {
+ $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
+ }
+ if (strlen($binX) == 40 && $binX[0] == '1') {
+ for ($i = 0; $i < 40; ++$i) {
+ $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
+ }
+
+ return (string) ((bindec($binX) + 1) * -1);
+ }
+
+ return (string) bindec($binX);
+ }
+
+ /**
+ * toOctal.
+ *
+ * Return a hex value as octal.
+ *
+ * Excel Function:
+ * HEX2OCT(x[,places])
+ *
+ * @param array|string $value The hexadecimal number you want to convert. Number cannot
+ * contain more than 10 characters. The most significant bit of
+ * number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is negative, HEX2OCT ignores places and returns a
+ * 10-character octal number.
+ * If number is negative, it cannot be less than FFE0000000, and
+ * if number is positive, it cannot be greater than 1FFFFFFF.
+ * If number is not a valid hexadecimal number, HEX2OCT returns
+ * the #NUM! error value.
+ * If HEX2OCT requires more than places characters, it returns
+ * the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
+ * uses the minimum number of characters necessary. Places is
+ * useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2OCT returns the #VALUE! error
+ * value.
+ * If places is negative, HEX2OCT returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toOctal($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateHex($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $decimal = self::toDecimal($value);
+
+ return ConvertDecimal::toOctal($decimal, $places);
+ }
+
+ protected static function validateHex(string $value): string
+ {
+ if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
new file mode 100644
index 0000000..8436a63
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
@@ -0,0 +1,174 @@
+getMessage();
+ }
+
+ return ConvertDecimal::toBinary(self::toDecimal($value), $places);
+ }
+
+ /**
+ * toDecimal.
+ *
+ * Return an octal value as decimal.
+ *
+ * Excel Function:
+ * OCT2DEC(x)
+ *
+ * @param array|string $value The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is not a valid octal number, OCT2DEC returns the
+ * #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toDecimal($value)
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateOctal($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $binX = '';
+ foreach (str_split($value) as $char) {
+ $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
+ }
+ if (strlen($binX) == 30 && $binX[0] == '1') {
+ for ($i = 0; $i < 30; ++$i) {
+ $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
+ }
+
+ return (string) ((bindec($binX) + 1) * -1);
+ }
+
+ return (string) bindec($binX);
+ }
+
+ /**
+ * toHex.
+ *
+ * Return an octal value as hex.
+ *
+ * Excel Function:
+ * OCT2HEX(x[,places])
+ *
+ * @param array|string $value The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is negative, OCT2HEX ignores places and returns a
+ * 10-character hexadecimal number.
+ * If number is not a valid octal number, OCT2HEX returns the
+ * #NUM! error value.
+ * If OCT2HEX requires more than places characters, it returns
+ * the #NUM! error value.
+ * Or can be an array of values
+ * @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
+ * uses the minimum number of characters necessary. Places is useful
+ * for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
+ * If places is negative, OCT2HEX returns the #NUM! error value.
+ * Or can be an array of values
+ *
+ * @return array|string Result, or an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toHex($value, $places = null): array|string
+ {
+ if (is_array($value) || is_array($places)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+ }
+
+ try {
+ $value = self::validateValue($value);
+ $value = self::validateOctal($value);
+ $places = self::validatePlaces($places);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $hexVal = strtoupper(dechex((int) self::toDecimal($value)));
+ $hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF{$hexVal}" : $hexVal;
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+ protected static function validateOctal(string $value): string
+ {
+ $numDigits = (int) preg_match_all('/[01234567]/', $value);
+ if (strlen($value) > $numDigits || $numDigits > 10) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
new file mode 100644
index 0000000..2a846f0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
@@ -0,0 +1,684 @@
+ ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true],
+ 'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false],
+ 'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
+ 'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
+ 'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
+ 'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false],
+ 'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
+ 'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
+ 'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
+ 'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
+ 'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
+ 'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false],
+ 'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false],
+ 'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+ 'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+ 'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+ // Distance
+ 'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true],
+ 'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
+ 'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
+ 'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false],
+ 'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false],
+ 'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false],
+ 'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
+ 'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false],
+ 'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false],
+ 'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
+ 'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
+ 'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
+ 'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
+ 'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false],
+ 'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
+ // Time
+ 'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false],
+ 'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
+ 'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
+ 'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false],
+ 'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
+ 'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
+ 'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
+ 's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
+ // Pressure
+ 'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
+ 'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
+ 'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
+ 'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
+ 'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
+ 'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true],
+ 'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true],
+ // Force
+ 'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true],
+ 'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
+ 'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
+ 'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
+ 'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true],
+ // Energy
+ 'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true],
+ 'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true],
+ 'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
+ 'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
+ 'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
+ 'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
+ 'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
+ 'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
+ 'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
+ 'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
+ 'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
+ 'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
+ 'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
+ // Power
+ 'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
+ 'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
+ 'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
+ 'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
+ 'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false],
+ // Magnetism
+ 'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
+ 'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
+ // Temperature
+ 'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
+ 'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
+ 'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
+ 'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
+ 'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
+ 'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
+ 'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false],
+ 'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false],
+ // Volume
+ 'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
+ 'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
+ 'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
+ 'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
+ 'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false],
+ 'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
+ 'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
+ 'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false],
+ 'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
+ 'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
+ 'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
+ 'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false],
+ 'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
+ 'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
+ 'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
+ 'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
+ 'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
+ 'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false],
+ 'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false],
+ 'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
+ 'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
+ 'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
+ 'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
+ 'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
+ 'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
+ 'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
+ 'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
+ 'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
+ 'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
+ 'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
+ 'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
+ 'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
+ 'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
+ 'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
+ 'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
+ 'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
+ 'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
+ // Area
+ 'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true],
+ 'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false],
+ 'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
+ 'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
+ 'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
+ 'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true],
+ 'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
+ 'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
+ 'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
+ 'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
+ 'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
+ 'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
+ 'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
+ 'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
+ 'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false],
+ 'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
+ 'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
+ 'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
+ 'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
+ 'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
+ 'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
+ 'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
+ // Information
+ 'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true],
+ 'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true],
+ // Speed
+ 'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
+ 'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
+ 'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
+ 'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
+ 'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false],
+ 'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false],
+ 'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false],
+ ];
+
+ /**
+ * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @var mixed[]
+ */
+ private static $conversionMultipliers = [
+ 'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
+ 'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
+ 'E' => ['multiplier' => 1E18, 'name' => 'exa'],
+ 'P' => ['multiplier' => 1E15, 'name' => 'peta'],
+ 'T' => ['multiplier' => 1E12, 'name' => 'tera'],
+ 'G' => ['multiplier' => 1E9, 'name' => 'giga'],
+ 'M' => ['multiplier' => 1E6, 'name' => 'mega'],
+ 'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
+ 'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
+ 'e' => ['multiplier' => 1E1, 'name' => 'dekao'],
+ 'da' => ['multiplier' => 1E1, 'name' => 'dekao'],
+ 'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
+ 'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
+ 'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
+ 'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
+ 'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
+ 'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
+ 'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
+ 'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
+ 'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
+ 'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
+ ];
+
+ /**
+ * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @var mixed[]
+ */
+ private static $binaryConversionMultipliers = [
+ 'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
+ 'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'],
+ 'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'],
+ 'Pi' => ['multiplier' => 2 ** 50, 'name' => 'pebi'],
+ 'Ti' => ['multiplier' => 2 ** 40, 'name' => 'tebi'],
+ 'Gi' => ['multiplier' => 2 ** 30, 'name' => 'gibi'],
+ 'Mi' => ['multiplier' => 2 ** 20, 'name' => 'mebi'],
+ 'ki' => ['multiplier' => 2 ** 10, 'name' => 'kibi'],
+ ];
+
+ /**
+ * Details of the Units of measure conversion factors, organised by group.
+ *
+ * @var mixed[]
+ */
+ private static $unitConversions = [
+ // Conversion uses gram (g) as an intermediate unit
+ self::CATEGORY_WEIGHT_AND_MASS => [
+ 'g' => 1.0,
+ 'sg' => 6.85217658567918E-05,
+ 'lbm' => 2.20462262184878E-03,
+ 'u' => 6.02214179421676E+23,
+ 'ozm' => 3.52739619495804E-02,
+ 'grain' => 1.54323583529414E+01,
+ 'cwt' => 2.20462262184878E-05,
+ 'shweight' => 2.20462262184878E-05,
+ 'uk_cwt' => 1.96841305522212E-05,
+ 'lcwt' => 1.96841305522212E-05,
+ 'hweight' => 1.96841305522212E-05,
+ 'stone' => 1.57473044417770E-04,
+ 'ton' => 1.10231131092439E-06,
+ 'uk_ton' => 9.84206527611061E-07,
+ 'LTON' => 9.84206527611061E-07,
+ 'brton' => 9.84206527611061E-07,
+ ],
+ // Conversion uses meter (m) as an intermediate unit
+ self::CATEGORY_DISTANCE => [
+ 'm' => 1.0,
+ 'mi' => 6.21371192237334E-04,
+ 'Nmi' => 5.39956803455724E-04,
+ 'in' => 3.93700787401575E+01,
+ 'ft' => 3.28083989501312E+00,
+ 'yd' => 1.09361329833771E+00,
+ 'ang' => 1.0E+10,
+ 'ell' => 8.74890638670166E-01,
+ 'ly' => 1.05700083402462E-16,
+ 'parsec' => 3.24077928966473E-17,
+ 'pc' => 3.24077928966473E-17,
+ 'Pica' => 2.83464566929134E+03,
+ 'Picapt' => 2.83464566929134E+03,
+ 'pica' => 2.36220472440945E+02,
+ 'survey_mi' => 6.21369949494950E-04,
+ ],
+ // Conversion uses second (s) as an intermediate unit
+ self::CATEGORY_TIME => [
+ 'yr' => 3.16880878140289E-08,
+ 'day' => 1.15740740740741E-05,
+ 'd' => 1.15740740740741E-05,
+ 'hr' => 2.77777777777778E-04,
+ 'mn' => 1.66666666666667E-02,
+ 'min' => 1.66666666666667E-02,
+ 'sec' => 1.0,
+ 's' => 1.0,
+ ],
+ // Conversion uses Pascal (Pa) as an intermediate unit
+ self::CATEGORY_PRESSURE => [
+ 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923266716013E-06,
+ 'at' => 9.86923266716013E-06,
+ 'mmHg' => 7.50063755419211E-03,
+ 'psi' => 1.45037737730209E-04,
+ 'Torr' => 7.50061682704170E-03,
+ ],
+ // Conversion uses Newton (N) as an intermediate unit
+ self::CATEGORY_FORCE => [
+ 'N' => 1.0,
+ 'dyn' => 1.0E+5,
+ 'dy' => 1.0E+5,
+ 'lbf' => 2.24808923655339E-01,
+ 'pond' => 1.01971621297793E+02,
+ ],
+ // Conversion uses Joule (J) as an intermediate unit
+ self::CATEGORY_ENERGY => [
+ 'J' => 1.0,
+ 'e' => 9.99999519343231E+06,
+ 'c' => 2.39006249473467E-01,
+ 'cal' => 2.38846190642017E-01,
+ 'eV' => 6.24145700000000E+18,
+ 'ev' => 6.24145700000000E+18,
+ 'HPh' => 3.72506430801000E-07,
+ 'hh' => 3.72506430801000E-07,
+ 'Wh' => 2.77777916238711E-04,
+ 'wh' => 2.77777916238711E-04,
+ 'flb' => 2.37304222192651E+01,
+ 'BTU' => 9.47815067349015E-04,
+ 'btu' => 9.47815067349015E-04,
+ ],
+ // Conversion uses Horsepower (HP) as an intermediate unit
+ self::CATEGORY_POWER => [
+ 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45699871582270E+02,
+ 'w' => 7.45699871582270E+02,
+ 'PS' => 1.01386966542400E+00,
+ ],
+ // Conversion uses Tesla (T) as an intermediate unit
+ self::CATEGORY_MAGNETISM => [
+ 'T' => 1.0,
+ 'ga' => 10000.0,
+ ],
+ // Conversion uses litre (l) as an intermediate unit
+ self::CATEGORY_VOLUME => [
+ 'l' => 1.0,
+ 'L' => 1.0,
+ 'lt' => 1.0,
+ 'tsp' => 2.02884136211058E+02,
+ 'tspm' => 2.0E+02,
+ 'tbs' => 6.76280454036860E+01,
+ 'oz' => 3.38140227018430E+01,
+ 'cup' => 4.22675283773038E+00,
+ 'pt' => 2.11337641886519E+00,
+ 'us_pt' => 2.11337641886519E+00,
+ 'uk_pt' => 1.75975398639270E+00,
+ 'qt' => 1.05668820943259E+00,
+ 'uk_qt' => 8.79876993196351E-01,
+ 'gal' => 2.64172052358148E-01,
+ 'uk_gal' => 2.19969248299088E-01,
+ 'ang3' => 1.0E+27,
+ 'ang^3' => 1.0E+27,
+ 'barrel' => 6.28981077043211E-03,
+ 'bushel' => 2.83775932584017E-02,
+ 'in3' => 6.10237440947323E+01,
+ 'in^3' => 6.10237440947323E+01,
+ 'ft3' => 3.53146667214886E-02,
+ 'ft^3' => 3.53146667214886E-02,
+ 'ly3' => 1.18093498844171E-51,
+ 'ly^3' => 1.18093498844171E-51,
+ 'm3' => 1.0E-03,
+ 'm^3' => 1.0E-03,
+ 'mi3' => 2.39912758578928E-13,
+ 'mi^3' => 2.39912758578928E-13,
+ 'yd3' => 1.30795061931439E-03,
+ 'yd^3' => 1.30795061931439E-03,
+ 'Nmi3' => 1.57426214685811E-13,
+ 'Nmi^3' => 1.57426214685811E-13,
+ 'Pica3' => 2.27769904358706E+07,
+ 'Pica^3' => 2.27769904358706E+07,
+ 'Picapt3' => 2.27769904358706E+07,
+ 'Picapt^3' => 2.27769904358706E+07,
+ 'GRT' => 3.53146667214886E-04,
+ 'regton' => 3.53146667214886E-04,
+ 'MTON' => 8.82866668037215E-04,
+ ],
+ // Conversion uses hectare (ha) as an intermediate unit
+ self::CATEGORY_AREA => [
+ 'ha' => 1.0,
+ 'uk_acre' => 2.47105381467165E+00,
+ 'us_acre' => 2.47104393046628E+00,
+ 'ang2' => 1.0E+24,
+ 'ang^2' => 1.0E+24,
+ 'ar' => 1.0E+02,
+ 'ft2' => 1.07639104167097E+05,
+ 'ft^2' => 1.07639104167097E+05,
+ 'in2' => 1.55000310000620E+07,
+ 'in^2' => 1.55000310000620E+07,
+ 'ly2' => 1.11725076312873E-28,
+ 'ly^2' => 1.11725076312873E-28,
+ 'm2' => 1.0E+04,
+ 'm^2' => 1.0E+04,
+ 'Morgen' => 4.0E+00,
+ 'mi2' => 3.86102158542446E-03,
+ 'mi^2' => 3.86102158542446E-03,
+ 'Nmi2' => 2.91553349598123E-03,
+ 'Nmi^2' => 2.91553349598123E-03,
+ 'Pica2' => 8.03521607043214E+10,
+ 'Pica^2' => 8.03521607043214E+10,
+ 'Picapt2' => 8.03521607043214E+10,
+ 'Picapt^2' => 8.03521607043214E+10,
+ 'yd2' => 1.19599004630108E+04,
+ 'yd^2' => 1.19599004630108E+04,
+ ],
+ // Conversion uses bit (bit) as an intermediate unit
+ self::CATEGORY_INFORMATION => [
+ 'bit' => 1.0,
+ 'byte' => 0.125,
+ ],
+ // Conversion uses Meters per Second (m/s) as an intermediate unit
+ self::CATEGORY_SPEED => [
+ 'm/s' => 1.0,
+ 'm/sec' => 1.0,
+ 'm/h' => 3.60E+03,
+ 'm/hr' => 3.60E+03,
+ 'mph' => 2.23693629205440E+00,
+ 'admkn' => 1.94260256941567E+00,
+ 'kn' => 1.94384449244060E+00,
+ ],
+ ];
+
+ /**
+ * getConversionGroups
+ * Returns a list of the different conversion groups for UOM conversions.
+ */
+ public static function getConversionCategories(): array
+ {
+ $conversionGroups = [];
+ foreach (self::$conversionUnits as $conversionUnit) {
+ $conversionGroups[] = $conversionUnit['Group'];
+ }
+
+ return array_merge(array_unique($conversionGroups));
+ }
+
+ /**
+ * getConversionGroupUnits
+ * Returns an array of units of measure, for a specified conversion group, or for all groups.
+ *
+ * @param string $category The group whose units of measure you want to retrieve
+ */
+ public static function getConversionCategoryUnits($category = null): array
+ {
+ $conversionGroups = [];
+ foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
+ if (($category === null) || ($conversionGroup['Group'] == $category)) {
+ $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
+ }
+ }
+
+ return $conversionGroups;
+ }
+
+ /**
+ * getConversionGroupUnitDetails.
+ *
+ * @param string $category The group whose units of measure you want to retrieve
+ */
+ public static function getConversionCategoryUnitDetails($category = null): array
+ {
+ $conversionGroups = [];
+ foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
+ if (($category === null) || ($conversionGroup['Group'] == $category)) {
+ $conversionGroups[$conversionGroup['Group']][] = [
+ 'unit' => $conversionUnit,
+ 'description' => $conversionGroup['Unit Name'],
+ ];
+ }
+ }
+
+ return $conversionGroups;
+ }
+
+ /**
+ * getConversionMultipliers
+ * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
+ *
+ * @return mixed[]
+ */
+ public static function getConversionMultipliers()
+ {
+ return self::$conversionMultipliers;
+ }
+
+ /**
+ * getBinaryConversionMultipliers
+ * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
+ *
+ * @return mixed[]
+ */
+ public static function getBinaryConversionMultipliers()
+ {
+ return self::$binaryConversionMultipliers;
+ }
+
+ /**
+ * CONVERT.
+ *
+ * Converts a number from one measurement system to another.
+ * For example, CONVERT can translate a table of distances in miles to a table of distances
+ * in kilometers.
+ *
+ * Excel Function:
+ * CONVERT(value,fromUOM,toUOM)
+ *
+ * @param array|float|int|string $value the value in fromUOM to convert
+ * Or can be an array of values
+ * @param array|string $fromUOM the units for value
+ * Or can be an array of values
+ * @param array|string $toUOM the units for the result
+ * Or can be an array of values
+ *
+ * @return array|float|string Result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function CONVERT($value, $fromUOM, $toUOM)
+ {
+ if (is_array($value) || is_array($fromUOM) || is_array($toUOM)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $fromUOM, $toUOM);
+ }
+
+ if (!is_numeric($value)) {
+ return ExcelError::VALUE();
+ }
+
+ try {
+ [$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
+ [$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
+ } catch (Exception) {
+ return ExcelError::NA();
+ }
+
+ if ($fromCategory !== $toCategory) {
+ return ExcelError::NA();
+ }
+
+ // @var float $value
+ $value *= $fromMultiplier;
+
+ if (($fromUOM === $toUOM) && ($fromMultiplier === $toMultiplier)) {
+ // We've already factored $fromMultiplier into the value, so we need
+ // to reverse it again
+ return $value / $fromMultiplier;
+ } elseif ($fromUOM === $toUOM) {
+ return $value / $toMultiplier;
+ } elseif ($fromCategory === self::CATEGORY_TEMPERATURE) {
+ return self::convertTemperature($fromUOM, $toUOM, $value);
+ }
+
+ $baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]);
+
+ return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
+ }
+
+ private static function getUOMDetails(string $uom): array
+ {
+ if (isset(self::$conversionUnits[$uom])) {
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+
+ return [$uom, $unitCategory, 1.0];
+ }
+
+ // Check 1-character standard metric multiplier prefixes
+ $multiplierType = substr($uom, 0, 1);
+ $uom = substr($uom, 1);
+ if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
+ if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
+ throw new Exception('Prefix not allowed for UoM');
+ }
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+
+ return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
+ }
+
+ $multiplierType .= substr($uom, 0, 1);
+ $uom = substr($uom, 1);
+
+ // Check 2-character standard metric multiplier prefixes
+ if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
+ if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
+ throw new Exception('Prefix not allowed for UoM');
+ }
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+
+ return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
+ }
+
+ // Check 2-character binary multiplier prefixes
+ if (isset(self::$conversionUnits[$uom], self::$binaryConversionMultipliers[$multiplierType])) {
+ if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
+ throw new Exception('Prefix not allowed for UoM');
+ }
+ $unitCategory = self::$conversionUnits[$uom]['Group'];
+ if ($unitCategory !== 'Information') {
+ throw new Exception('Binary Prefix is only allowed for Information UoM');
+ }
+
+ return [$uom, $unitCategory, self::$binaryConversionMultipliers[$multiplierType]['multiplier']];
+ }
+
+ throw new Exception('UoM Not Found');
+ }
+
+ /**
+ * @param float|int $value
+ *
+ * @return float|int
+ */
+ protected static function convertTemperature(string $fromUOM, string $toUOM, $value)
+ {
+ $fromUOM = self::resolveTemperatureSynonyms($fromUOM);
+ $toUOM = self::resolveTemperatureSynonyms($toUOM);
+
+ if ($fromUOM === $toUOM) {
+ return $value;
+ }
+
+ // Convert to Kelvin
+ switch ($fromUOM) {
+ case 'F':
+ $value = ($value - 32) / 1.8 + 273.15;
+
+ break;
+ case 'C':
+ $value += 273.15;
+
+ break;
+ case 'Rank':
+ $value /= 1.8;
+
+ break;
+ case 'Reau':
+ $value = $value * 1.25 + 273.15;
+
+ break;
+ }
+
+ // Convert from Kelvin
+ switch ($toUOM) {
+ case 'F':
+ $value = ($value - 273.15) * 1.8 + 32.00;
+
+ break;
+ case 'C':
+ $value -= 273.15;
+
+ break;
+ case 'Rank':
+ $value *= 1.8;
+
+ break;
+ case 'Reau':
+ $value = ($value - 273.15) * 0.80000;
+
+ break;
+ }
+
+ return $value;
+ }
+
+ private static function resolveTemperatureSynonyms(string $uom): string
+ {
+ return match ($uom) {
+ 'fah' => 'F',
+ 'cel' => 'C',
+ 'kel' => 'K',
+ default => $uom,
+ };
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
new file mode 100644
index 0000000..7e5118f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
@@ -0,0 +1,27 @@
+ 2.2) {
+ return 1 - self::makeFloat(ErfC::ERFC($value));
+ }
+ $sum = $term = $value;
+ $xsqr = ($value * $value);
+ $j = 1;
+ do {
+ $term *= $xsqr / $j;
+ $sum -= $term / (2 * $j + 1);
+ ++$j;
+ $term *= $xsqr / $j;
+ $sum += $term / (2 * $j + 1);
+ ++$j;
+ if ($sum == 0.0) {
+ break;
+ }
+ } while (abs($term / $sum) > Functions::PRECISION);
+
+ return self::TWO_SQRT_PI * $sum;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
new file mode 100644
index 0000000..e133c4c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
@@ -0,0 +1,82 @@
+ Functions::PRECISION);
+
+ return self::ONE_SQRT_PI * exp(-$value * $value) * $q2;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php
new file mode 100644
index 0000000..ea893d6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php
@@ -0,0 +1,22 @@
+line = $line;
+ $e->file = $file;
+
+ throw $e;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php
new file mode 100644
index 0000000..6461961
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php
@@ -0,0 +1,24 @@
+getMessage();
+ }
+
+ $yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+ if (is_string($yearFracx)) {
+ return $yearFracx;
+ }
+ /** @var float */
+ $yearFrac = $yearFracx;
+
+ $amortiseCoeff = self::getAmortizationCoefficient($rate);
+
+ $rate *= $amortiseCoeff;
+ $fNRate = round($yearFrac * $rate * $cost, 0);
+ $cost -= $fNRate;
+ $fRest = $cost - $salvage;
+
+ for ($n = 0; $n < $period; ++$n) {
+ $fNRate = round($rate * $cost, 0);
+ $fRest -= $fNRate;
+
+ if ($fRest < 0.0) {
+ return match ($period - $n) {
+ 1 => round($cost * 0.5, 0),
+ default => 0.0,
+ };
+ }
+ $cost -= $fNRate;
+ }
+
+ return $fNRate;
+ }
+
+ /**
+ * AMORLINC.
+ *
+ * Returns the depreciation for each accounting period.
+ * This function is provided for the French accounting system. If an asset is purchased in
+ * the middle of the accounting period, the prorated depreciation is taken into account.
+ *
+ * Excel Function:
+ * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
+ *
+ * @param mixed $cost The cost of the asset as a float
+ * @param mixed $purchased Date of the purchase of the asset
+ * @param mixed $firstPeriod Date of the end of the first period
+ * @param mixed $salvage The salvage value at the end of the life of the asset
+ * @param mixed $period The period as a float
+ * @param mixed $rate Rate of depreciation as float
+ * @param mixed $basis Integer indicating the type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string (string containing the error type if there is an error)
+ */
+ public static function AMORLINC(
+ mixed $cost,
+ mixed $purchased,
+ mixed $firstPeriod,
+ mixed $salvage,
+ mixed $period,
+ mixed $rate,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ): string|float {
+ $cost = Functions::flattenSingleValue($cost);
+ $purchased = Functions::flattenSingleValue($purchased);
+ $firstPeriod = Functions::flattenSingleValue($firstPeriod);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $period = Functions::flattenSingleValue($period);
+ $rate = Functions::flattenSingleValue($rate);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $cost = FinancialValidations::validateFloat($cost);
+ $purchased = FinancialValidations::validateDate($purchased);
+ $firstPeriod = FinancialValidations::validateDate($firstPeriod);
+ $salvage = FinancialValidations::validateFloat($salvage);
+ $period = FinancialValidations::validateFloat($period);
+ $rate = FinancialValidations::validateFloat($rate);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $fOneRate = $cost * $rate;
+ $fCostDelta = $cost - $salvage;
+ // Note, quirky variation for leap years on the YEARFRAC for this function
+ $purchasedYear = DateTimeExcel\DateParts::year($purchased);
+ $yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+ if (is_string($yearFracx)) {
+ return $yearFracx;
+ }
+ /** @var float */
+ $yearFrac = $yearFracx;
+
+ if (
+ $basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ && $yearFrac < 1
+ && DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear))
+ ) {
+ $yearFrac *= 365 / 366;
+ }
+
+ $f0Rate = $yearFrac * $rate * $cost;
+ $nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
+
+ if ($period == 0) {
+ return $f0Rate;
+ } elseif ($period <= $nNumOfFullPeriods) {
+ return $fOneRate;
+ } elseif ($period == ($nNumOfFullPeriods + 1)) {
+ return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
+ }
+
+ return 0.0;
+ }
+
+ private static function getAmortizationCoefficient(float $rate): float
+ {
+ // The depreciation coefficients are:
+ // Life of assets (1/rate) Depreciation coefficient
+ // Less than 3 years 1
+ // Between 3 and 4 years 1.5
+ // Between 5 and 6 years 2
+ // More than 6 years 2.5
+ $fUsePer = 1.0 / $rate;
+
+ if ($fUsePer < 3.0) {
+ return 1.0;
+ } elseif ($fUsePer < 4.0) {
+ return 1.5;
+ } elseif ($fUsePer <= 6.0) {
+ return 2.0;
+ }
+
+ return 2.5;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
new file mode 100644
index 0000000..ae5a794
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
@@ -0,0 +1,44 @@
+getMessage();
+ }
+
+ return self::calculateFutureValue($rate, $numberOfPeriods, $payment, $presentValue, $type);
+ }
+
+ /**
+ * PV.
+ *
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $numberOfPeriods Number of periods as an integer
+ * @param mixed $payment Periodic payment (annuity)
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function presentValue(
+ mixed $rate,
+ mixed $numberOfPeriods,
+ mixed $payment = 0.0,
+ mixed $futureValue = 0.0,
+ mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ): string|float {
+ $rate = Functions::flattenSingleValue($rate);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($numberOfPeriods < 0) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculatePresentValue($rate, $numberOfPeriods, $payment, $futureValue, $type);
+ }
+
+ /**
+ * NPER.
+ *
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $payment Periodic payment (annuity)
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function periods(
+ mixed $rate,
+ mixed $payment,
+ mixed $presentValue,
+ mixed $futureValue = 0.0,
+ mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ) {
+ $rate = Functions::flattenSingleValue($rate);
+ $payment = Functions::flattenSingleValue($payment);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($payment == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculatePeriods($rate, $payment, $presentValue, $futureValue, $type);
+ }
+
+ private static function calculateFutureValue(
+ float $rate,
+ int $numberOfPeriods,
+ float $payment,
+ float $presentValue,
+ int $type
+ ): float {
+ if ($rate !== null && $rate != 0) {
+ return -$presentValue
+ * (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
+ / $rate;
+ }
+
+ return -$presentValue - $payment * $numberOfPeriods;
+ }
+
+ private static function calculatePresentValue(
+ float $rate,
+ int $numberOfPeriods,
+ float $payment,
+ float $futureValue,
+ int $type
+ ): float {
+ if ($rate != 0.0) {
+ return (-$payment * (1 + $rate * $type)
+ * (((1 + $rate) ** $numberOfPeriods - 1) / $rate) - $futureValue) / (1 + $rate) ** $numberOfPeriods;
+ }
+
+ return -$futureValue - $payment * $numberOfPeriods;
+ }
+
+ private static function calculatePeriods(
+ float $rate,
+ float $payment,
+ float $presentValue,
+ float $futureValue,
+ int $type
+ ): string|float {
+ if ($rate != 0.0) {
+ if ($presentValue == 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return log(($payment * (1 + $rate * $type) / $rate - $futureValue)
+ / ($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+ }
+
+ return (-$presentValue - $futureValue) / $payment;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
new file mode 100644
index 0000000..2cca27a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
@@ -0,0 +1,142 @@
+getMessage();
+ }
+
+ // Validate parameters
+ if ($start < 1 || $start > $end) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ $interest = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $ipmt = Interest::payment($rate, $per, $periods, $presentValue, 0, $type);
+ if (is_string($ipmt)) {
+ return $ipmt;
+ }
+
+ $interest += $ipmt;
+ }
+
+ return $interest;
+ }
+
+ /**
+ * CUMPRINC.
+ *
+ * Returns the cumulative principal paid on a loan between the start and end periods.
+ *
+ * Excel Function:
+ * CUMPRINC(rate,nper,pv,start,end[,type])
+ *
+ * @param mixed $rate The Interest rate
+ * @param mixed $periods The total number of payment periods as an integer
+ * @param mixed $presentValue Present Value
+ * @param mixed $start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param mixed $end the last period in the calculation
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ *
+ * @return float|string
+ */
+ public static function principal(
+ mixed $rate,
+ mixed $periods,
+ mixed $presentValue,
+ mixed $start,
+ mixed $end,
+ mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ): string|float|int {
+ $rate = Functions::flattenSingleValue($rate);
+ $periods = Functions::flattenSingleValue($periods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $start = Functions::flattenSingleValue($start);
+ $end = Functions::flattenSingleValue($end);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $periods = CashFlowValidations::validateInt($periods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $start = CashFlowValidations::validateInt($start);
+ $end = CashFlowValidations::validateInt($end);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($start < 1 || $start > $end) {
+ return ExcelError::VALUE();
+ }
+
+ // Calculate
+ $principal = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $ppmt = Payments::interestPayment($rate, $per, $periods, $presentValue, 0, $type);
+ if (is_string($ppmt)) {
+ return $ppmt;
+ }
+
+ $principal += $ppmt;
+ }
+
+ return $principal;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
new file mode 100644
index 0000000..833c573
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
@@ -0,0 +1,213 @@
+getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ $interestAndPrincipal = new InterestAndPrincipal(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue,
+ $type
+ );
+
+ return $interestAndPrincipal->interest();
+ }
+
+ /**
+ * ISPMT.
+ *
+ * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
+ *
+ * Excel Function:
+ * =ISPMT(interest_rate, period, number_payments, pv)
+ *
+ * @param mixed $interestRate is the interest rate for the investment
+ * @param mixed $period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
+ * @param mixed $numberOfPeriods is the number of payments for the annuity
+ * @param mixed $principleRemaining is the loan amount or present value of the payments
+ */
+ public static function schedulePayment(mixed $interestRate, mixed $period, mixed $numberOfPeriods, mixed $principleRemaining): string|float
+ {
+ $interestRate = Functions::flattenSingleValue($interestRate);
+ $period = Functions::flattenSingleValue($period);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $principleRemaining = Functions::flattenSingleValue($principleRemaining);
+
+ try {
+ $interestRate = CashFlowValidations::validateRate($interestRate);
+ $period = CashFlowValidations::validateInt($period);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $principleRemaining = CashFlowValidations::validateFloat($principleRemaining);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return ExcelError::NAN();
+ }
+
+ // Return value
+ $returnValue = 0;
+
+ // Calculate
+ $principlePayment = ($principleRemaining * 1.0) / ($numberOfPeriods * 1.0);
+ for ($i = 0; $i <= $period; ++$i) {
+ $returnValue = $interestRate * $principleRemaining * -1;
+ $principleRemaining -= $principlePayment;
+ // principle needs to be 0 after the last payment, don't let floating point screw it up
+ if ($i == $numberOfPeriods) {
+ $returnValue = 0.0;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * RATE.
+ *
+ * Returns the interest rate per period of an annuity.
+ * RATE is calculated by iteration and can have zero or more solutions.
+ * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
+ * RATE returns the #NUM! error value.
+ *
+ * Excel Function:
+ * RATE(nper,pmt,pv[,fv[,type[,guess]]])
+ *
+ * @param mixed $numberOfPeriods The total number of payment periods in an annuity
+ * @param mixed $payment The payment made each period and cannot change over the life of the annuity.
+ * Typically, pmt includes principal and interest but no other fees or taxes.
+ * @param mixed $presentValue The present value - the total amount that a series of future payments is worth now
+ * @param mixed $futureValue The future value, or a cash balance you want to attain after the last payment is made.
+ * If fv is omitted, it is assumed to be 0 (the future value of a loan,
+ * for example, is 0).
+ * @param mixed $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @param mixed $guess Your guess for what the rate will be.
+ * If you omit guess, it is assumed to be 10 percent.
+ */
+ public static function rate(
+ mixed $numberOfPeriods,
+ mixed $payment,
+ mixed $presentValue,
+ mixed $futureValue = 0.0,
+ mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD,
+ mixed $guess = 0.1
+ ): string|float {
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $payment = Functions::flattenSingleValue($payment);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+ $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
+
+ try {
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $payment = CashFlowValidations::validateFloat($payment);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ $guess = CashFlowValidations::validateFloat($guess);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $rate = $guess;
+ // rest of code adapted from python/numpy
+ $close = false;
+ $iter = 0;
+ while (!$close && $iter < self::FINANCIAL_MAX_ITERATIONS) {
+ $nextdiff = self::rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type);
+ if (!is_numeric($nextdiff)) {
+ break;
+ }
+ $rate1 = $rate - $nextdiff;
+ $close = abs($rate1 - $rate) < self::FINANCIAL_PRECISION;
+ ++$iter;
+ $rate = $rate1;
+ }
+
+ return $close ? $rate : ExcelError::NAN();
+ }
+
+ private static function rateNextGuess(float $rate, int $numberOfPeriods, float $payment, float $presentValue, float $futureValue, int $type): string|float
+ {
+ if ($rate == 0.0) {
+ return ExcelError::NAN();
+ }
+ $tt1 = ($rate + 1) ** $numberOfPeriods;
+ $tt2 = ($rate + 1) ** ($numberOfPeriods - 1);
+ $numerator = $futureValue + $tt1 * $presentValue + $payment * ($tt1 - 1) * ($rate * $type + 1) / $rate;
+ $denominator = $numberOfPeriods * $tt2 * $presentValue - $payment * ($tt1 - 1)
+ * ($rate * $type + 1) / ($rate * $rate) + $numberOfPeriods
+ * $payment * $tt2 * ($rate * $type + 1) / $rate + $payment * ($tt1 - 1) * $type / $rate;
+ if ($denominator == 0) {
+ return ExcelError::NAN();
+ }
+
+ return $numerator / $denominator;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
new file mode 100644
index 0000000..ea9abb9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
@@ -0,0 +1,44 @@
+interest = $interest;
+ $this->principal = $principal;
+ }
+
+ public function interest(): float
+ {
+ return $this->interest;
+ }
+
+ public function principal(): float
+ {
+ return $this->principal;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
new file mode 100644
index 0000000..41e88f9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ // Calculate
+ if ($interestRate != 0.0) {
+ return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods)
+ / (1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
+ }
+
+ return (-$presentValue - $futureValue) / $numberOfPeriods;
+ }
+
+ /**
+ * PPMT.
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments
+ * and a constant interest rate.
+ *
+ * @param mixed $interestRate Interest rate per period
+ * @param mixed $period Period for which we want to find the interest
+ * @param mixed $numberOfPeriods Number of periods
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function interestPayment(
+ mixed $interestRate,
+ mixed $period,
+ mixed $numberOfPeriods,
+ mixed $presentValue,
+ mixed $futureValue = 0,
+ mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+ ): string|float {
+ $interestRate = Functions::flattenSingleValue($interestRate);
+ $period = Functions::flattenSingleValue($period);
+ $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = ($futureValue === null) ? 0.0 : Functions::flattenSingleValue($futureValue);
+ $type = ($type === null) ? FinancialConstants::PAYMENT_END_OF_PERIOD : Functions::flattenSingleValue($type);
+
+ try {
+ $interestRate = CashFlowValidations::validateRate($interestRate);
+ $period = CashFlowValidations::validateInt($period);
+ $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ $type = CashFlowValidations::validatePeriodType($type);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($period <= 0 || $period > $numberOfPeriods) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ $interestAndPrincipal = new InterestAndPrincipal(
+ $interestRate,
+ $period,
+ $numberOfPeriods,
+ $presentValue,
+ $futureValue,
+ $type
+ );
+
+ return $interestAndPrincipal->principal();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
new file mode 100644
index 0000000..8a4f16e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
@@ -0,0 +1,107 @@
+getMessage();
+ }
+
+ return $principal;
+ }
+
+ /**
+ * PDURATION.
+ *
+ * Calculates the number of periods required for an investment to reach a specified value.
+ *
+ * @param mixed $rate Interest rate per period
+ * @param mixed $presentValue Present Value
+ * @param mixed $futureValue Future Value
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function periods(mixed $rate, mixed $presentValue, mixed $futureValue): string|float
+ {
+ $rate = Functions::flattenSingleValue($rate);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = Functions::flattenSingleValue($futureValue);
+
+ try {
+ $rate = CashFlowValidations::validateRate($rate);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($rate <= 0.0 || $presentValue <= 0.0 || $futureValue <= 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return (log($futureValue) - log($presentValue)) / log(1 + $rate);
+ }
+
+ /**
+ * RRI.
+ *
+ * Calculates the interest rate required for an investment to grow to a specified future value .
+ *
+ * @param float $periods The number of periods over which the investment is made
+ * @param float $presentValue Present Value
+ * @param float $futureValue Future Value
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function interestRate($periods = 0.0, $presentValue = 0.0, $futureValue = 0.0): string|float
+ {
+ $periods = Functions::flattenSingleValue($periods);
+ $presentValue = Functions::flattenSingleValue($presentValue);
+ $futureValue = Functions::flattenSingleValue($futureValue);
+
+ try {
+ $periods = CashFlowValidations::validateFloat($periods);
+ $presentValue = CashFlowValidations::validatePresentValue($presentValue);
+ $futureValue = CashFlowValidations::validateFutureValue($futureValue);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if ($periods <= 0.0 || $presentValue <= 0.0 || $futureValue < 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return ($futureValue / $presentValue) ** (1 / $periods) - 1;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
new file mode 100644
index 0000000..f2e537d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
@@ -0,0 +1,308 @@
+ 1;
+ $datesIsArray = count($dates) > 1;
+ if (!$valuesIsArray && !$datesIsArray) {
+ return ExcelError::NA();
+ }
+ if (count($values) != count($dates)) {
+ return ExcelError::NAN();
+ }
+
+ $datesCount = count($dates);
+ for ($i = 0; $i < $datesCount; ++$i) {
+ try {
+ $dates[$i] = DateTimeExcel\Helpers::getDateValue($dates[$i]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ return self::xirrPart2($values);
+ }
+
+ private static function xirrPart2(array &$values): string
+ {
+ $valCount = count($values);
+ $foundpos = false;
+ $foundneg = false;
+ for ($i = 0; $i < $valCount; ++$i) {
+ $fld = $values[$i];
+ if (!is_numeric($fld)) {
+ return ExcelError::VALUE();
+ } elseif ($fld > 0) {
+ $foundpos = true;
+ } elseif ($fld < 0) {
+ $foundneg = true;
+ }
+ }
+ if (!self::bothNegAndPos($foundneg, $foundpos)) {
+ return ExcelError::NAN();
+ }
+
+ return '';
+ }
+
+ private static function xirrPart3(array $values, array $dates, float $x1, float $x2): float|string
+ {
+ $f = self::xnpvOrdered($x1, $values, $dates, false);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ $rslt = ExcelError::VALUE();
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = (float) self::xnpvOrdered($x_mid, $values, $dates, false);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
+ $rslt = $x_mid;
+
+ break;
+ }
+ }
+
+ return $rslt;
+ }
+
+ private static function xirrBisection(array $values, array $dates, float $x1, float $x2): string|float
+ {
+ $rslt = ExcelError::NAN();
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $rslt = ExcelError::NAN();
+ $f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
+ $f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
+ if (!is_numeric($f1) || !is_numeric($f2)) {
+ break;
+ }
+ $f1 = (float) $f1;
+ $f2 = (float) $f2;
+ if (abs($f1) < self::FINANCIAL_PRECISION && abs($f2) < self::FINANCIAL_PRECISION) {
+ break;
+ }
+ if ($f1 * $f2 > 0) {
+ break;
+ }
+ $rslt = ($x1 + $x2) / 2;
+ $f3 = self::xnpvOrdered($rslt, $values, $dates, false, true);
+ if (!is_float($f3)) {
+ break;
+ }
+ if ($f3 * $f1 < 0) {
+ $x2 = $rslt;
+ } else {
+ $x1 = $rslt;
+ }
+ if (abs($f3) < self::FINANCIAL_PRECISION) {
+ break;
+ }
+ }
+
+ return $rslt;
+ }
+
+ /**
+ * @return float|string
+ */
+ private static function xnpvOrdered(mixed $rate, mixed $values, mixed $dates, bool $ordered = true, bool $capAtNegative1 = false)
+ {
+ $rate = Functions::flattenSingleValue($rate);
+ $values = Functions::flattenArray($values);
+ $dates = Functions::flattenArray($dates);
+ $valCount = count($values);
+
+ try {
+ self::validateXnpv($rate, $values, $dates);
+ if ($capAtNegative1 && $rate <= -1) {
+ $rate = -1.0 + 1.0E-10;
+ }
+ $date0 = DateTimeExcel\Helpers::getDateValue($dates[0]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $xnpv = 0.0;
+ for ($i = 0; $i < $valCount; ++$i) {
+ if (!is_numeric($values[$i])) {
+ return ExcelError::VALUE();
+ }
+
+ try {
+ $datei = DateTimeExcel\Helpers::getDateValue($dates[$i]);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ if ($date0 > $datei) {
+ $dif = $ordered ? ExcelError::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
+ } else {
+ $dif = Functions::scalar(DateTimeExcel\Difference::interval($date0, $datei, 'd'));
+ }
+ if (!is_numeric($dif)) {
+ return $dif;
+ }
+ if ($rate <= -1.0) {
+ $xnpv += -abs($values[$i]) / (-1 - $rate) ** ($dif / 365);
+ } else {
+ $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
+ }
+ }
+
+ return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
+ }
+
+ private static function validateXnpv(mixed $rate, array $values, array $dates): void
+ {
+ if (!is_numeric($rate)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ $valCount = count($values);
+ if ($valCount != count($dates)) {
+ throw new Exception(ExcelError::NAN());
+ }
+ if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
new file mode 100644
index 0000000..c5fffcd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
@@ -0,0 +1,159 @@
+ 0.0) {
+ return ExcelError::VALUE();
+ }
+
+ $f = self::presentValue($x1, $values);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::presentValue($x_mid, $values);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
+ return $x_mid;
+ }
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * MIRR.
+ *
+ * Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
+ * the cost of the investment and the interest received on reinvestment of cash.
+ *
+ * Excel Function:
+ * MIRR(values,finance_rate, reinvestment_rate)
+ *
+ * @param mixed $values An array or a reference to cells that contain a series of payments and
+ * income occurring at regular intervals.
+ * Payments are negative value, income is positive values.
+ * @param mixed $financeRate The interest rate you pay on the money used in the cash flows
+ * @param mixed $reinvestmentRate The interest rate you receive on the cash flows as you reinvest them
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function modifiedRate(mixed $values, mixed $financeRate, mixed $reinvestmentRate): string|float
+ {
+ if (!is_array($values)) {
+ return ExcelError::DIV0();
+ }
+ $values = Functions::flattenArray($values);
+ $financeRate = Functions::flattenSingleValue($financeRate);
+ $reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate);
+ $n = count($values);
+
+ $rr = 1.0 + $reinvestmentRate;
+ $fr = 1.0 + $financeRate;
+
+ $npvPos = $npvNeg = 0.0;
+ foreach ($values as $i => $v) {
+ if ($v >= 0) {
+ $npvPos += $v / $rr ** $i;
+ } else {
+ $npvNeg += $v / $fr ** $i;
+ }
+ }
+
+ if ($npvNeg === 0.0 || $npvPos === 0.0) {
+ return ExcelError::DIV0();
+ }
+
+ $mirr = ((-$npvPos * $rr ** $n)
+ / ($npvNeg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
+
+ return is_finite($mirr) ? $mirr : ExcelError::NAN();
+ }
+
+ /**
+ * NPV.
+ *
+ * Returns the Net Present Value of a cash flow series given a discount rate.
+ *
+ * @param array $args
+ *
+ * @return float
+ */
+ public static function presentValue(mixed $rate, ...$args): int|float
+ {
+ $returnValue = 0;
+
+ $rate = Functions::flattenSingleValue($rate);
+ $aArgs = Functions::flattenArray($args);
+
+ // Calculate
+ $countArgs = count($aArgs);
+ for ($i = 1; $i <= $countArgs; ++$i) {
+ // Is it a numeric value?
+ if (is_numeric($aArgs[$i - 1])) {
+ $returnValue += $aArgs[$i - 1] / (1 + $rate) ** $i;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php
new file mode 100644
index 0000000..17740b0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Constants.php
@@ -0,0 +1,19 @@
+getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+ if (is_string($daysPerYear)) {
+ return ExcelError::VALUE();
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+
+ if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) {
+ return abs((float) DateTimeExcel\Days::between($prev, $settlement));
+ }
+
+ return (float) DateTimeExcel\YearFrac::fraction($prev, $settlement, $basis) * $daysPerYear;
+ }
+
+ /**
+ * COUPDAYS.
+ *
+ * Returns the number of days in the coupon period that contains the settlement date.
+ *
+ * Excel Function:
+ * COUPDAYS(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function COUPDAYS(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $frequency,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ): string|int|float {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ switch ($basis) {
+ case FinancialConstants::BASIS_DAYS_PER_YEAR_365:
+ // Actual/365
+ return 365 / $frequency;
+ case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL:
+ // Actual/actual
+ if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) {
+ $daysPerYear = (int) Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+
+ return $daysPerYear / $frequency;
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+
+ return $next - $prev;
+ default:
+ // US (NASD) 30/360, Actual/360 or European 30/360
+ return 360 / $frequency;
+ }
+ }
+
+ /**
+ * COUPDAYSNC.
+ *
+ * Returns the number of days from the settlement date to the next coupon date.
+ *
+ * Excel Function:
+ * COUPDAYSNC(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int) .
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ */
+ public static function COUPDAYSNC(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $frequency,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ): string|float {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ $basis = FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ /** @var int */
+ $daysPerYear = Helpers::daysPerYear(Functions::Scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+
+ if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) {
+ $settlementDate = Date::excelToDateTimeObject($settlement);
+ $settlementEoM = Helpers::isLastDayOfMonth($settlementDate);
+ if ($settlementEoM) {
+ ++$settlement;
+ }
+ }
+
+ return (float) DateTimeExcel\YearFrac::fraction($settlement, $next, $basis) * $daysPerYear;
+ }
+
+ /**
+ * COUPNCD.
+ *
+ * Returns the next coupon date after the settlement date.
+ *
+ * Excel Function:
+ * COUPNCD(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Excel date/time serial value or error message
+ */
+ public static function COUPNCD(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $frequency,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ): string|float {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
+ }
+
+ /**
+ * COUPNUM.
+ *
+ * Returns the number of coupons payable between the settlement date and maturity date,
+ * rounded up to the nearest whole coupon.
+ *
+ * Excel Function:
+ * COUPNUM(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ */
+ public static function COUPNUM(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $frequency,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ): string|int {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction(
+ $settlement,
+ $maturity,
+ FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ );
+
+ return (int) ceil((float) $yearsBetweenSettlementAndMaturity * $frequency);
+ }
+
+ /**
+ * COUPPCD.
+ *
+ * Returns the previous coupon date before the settlement date.
+ *
+ * Excel Function:
+ * COUPPCD(settlement,maturity,frequency[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $frequency The number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * @param mixed $basis The type of day count to use (int).
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Excel date/time serial value or error message
+ */
+ public static function COUPPCD(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $frequency,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ): string|float {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $frequency = Functions::flattenSingleValue($frequency);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ self::validateCouponPeriod($settlement, $maturity);
+ $frequency = FinancialValidations::validateFrequency($frequency);
+ FinancialValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
+ }
+
+ private static function monthsDiff(DateTime $result, int $months, string $plusOrMinus, int $day, bool $lastDayFlag): void
+ {
+ $result->setDate((int) $result->format('Y'), (int) $result->format('m'), 1);
+ $result->modify("$plusOrMinus $months months");
+ $daysInMonth = (int) $result->format('t');
+ $result->setDate((int) $result->format('Y'), (int) $result->format('m'), $lastDayFlag ? $daysInMonth : min($day, $daysInMonth));
+ }
+
+ private static function couponFirstPeriodDate(float $settlement, float $maturity, int $frequency, bool $next): float
+ {
+ $months = 12 / $frequency;
+
+ $result = Date::excelToDateTimeObject($maturity);
+ $day = (int) $result->format('d');
+ $lastDayFlag = Helpers::isLastDayOfMonth($result);
+
+ while ($settlement < Date::PHPToExcel($result)) {
+ self::monthsDiff($result, $months, '-', $day, $lastDayFlag);
+ }
+ if ($next === true) {
+ self::monthsDiff($result, $months, '+', $day, $lastDayFlag);
+ }
+
+ return (float) Date::PHPToExcel($result);
+ }
+
+ private static function validateCouponPeriod(float $settlement, float $maturity): void
+ {
+ if ($settlement >= $maturity) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
new file mode 100644
index 0000000..a0f6f6b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
@@ -0,0 +1,270 @@
+getMessage();
+ }
+
+ if ($cost === self::$zeroPointZero) {
+ return 0.0;
+ }
+
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ // TODO Handle period value between 0 and 1 (e.g. 0.5)
+ $previousDepreciation = 0;
+ $depreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ if ($per == 1) {
+ $depreciation = $cost * $fixedDepreciationRate * $month / 12;
+ } elseif ($per == ($life + 1)) {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
+ } else {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
+ }
+ $previousDepreciation += $depreciation;
+ }
+
+ return $depreciation;
+ }
+
+ /**
+ * DDB.
+ *
+ * Returns the depreciation of an asset for a specified period using the
+ * double-declining balance method or some other method you specify.
+ *
+ * Excel Function:
+ * DDB(cost,salvage,life,period[,factor])
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation.
+ * (Sometimes called the salvage value of the asset)
+ * @param mixed $life Number of periods over which the asset is depreciated.
+ * (Sometimes called the useful life of the asset)
+ * @param mixed $period The period for which you want to calculate the
+ * depreciation. Period must use the same units as life.
+ * @param mixed $factor The rate at which the balance declines.
+ * If factor is omitted, it is assumed to be 2 (the
+ * double-declining balance method).
+ *
+ * @return float|string
+ */
+ public static function DDB(mixed $cost, mixed $salvage, mixed $life, mixed $period, mixed $factor = 2.0)
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+ $period = Functions::flattenSingleValue($period);
+ $factor = Functions::flattenSingleValue($factor);
+
+ try {
+ $cost = self::validateCost($cost);
+ $salvage = self::validateSalvage($salvage);
+ $life = self::validateLife($life);
+ $period = self::validatePeriod($period);
+ $factor = self::validateFactor($factor);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($period > $life) {
+ return ExcelError::NAN();
+ }
+
+ // Loop through each period calculating the depreciation
+ // TODO Handling for fractional $period values
+ $previousDepreciation = 0;
+ $depreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ $depreciation = min(
+ ($cost - $previousDepreciation) * ($factor / $life),
+ ($cost - $salvage - $previousDepreciation)
+ );
+ $previousDepreciation += $depreciation;
+ }
+
+ return $depreciation;
+ }
+
+ /**
+ * SLN.
+ *
+ * Returns the straight-line depreciation of an asset for one period
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation
+ * @param mixed $life Number of periods over which the asset is depreciated
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function SLN(mixed $cost, mixed $salvage, mixed $life): string|float
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+
+ try {
+ $cost = self::validateCost($cost, true);
+ $salvage = self::validateSalvage($salvage, true);
+ $life = self::validateLife($life, true);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($life === self::$zeroPointZero) {
+ return ExcelError::DIV0();
+ }
+
+ return ($cost - $salvage) / $life;
+ }
+
+ /**
+ * SYD.
+ *
+ * Returns the sum-of-years' digits depreciation of an asset for a specified period.
+ *
+ * @param mixed $cost Initial cost of the asset
+ * @param mixed $salvage Value at the end of the depreciation
+ * @param mixed $life Number of periods over which the asset is depreciated
+ * @param mixed $period Period
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function SYD(mixed $cost, mixed $salvage, mixed $life, mixed $period): string|float
+ {
+ $cost = Functions::flattenSingleValue($cost);
+ $salvage = Functions::flattenSingleValue($salvage);
+ $life = Functions::flattenSingleValue($life);
+ $period = Functions::flattenSingleValue($period);
+
+ try {
+ $cost = self::validateCost($cost, true);
+ $salvage = self::validateSalvage($salvage);
+ $life = self::validateLife($life);
+ $period = self::validatePeriod($period);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($period > $life) {
+ return ExcelError::NAN();
+ }
+
+ $syd = (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
+
+ return $syd;
+ }
+
+ private static function validateCost(mixed $cost, bool $negativeValueAllowed = false): float
+ {
+ $cost = FinancialValidations::validateFloat($cost);
+ if ($cost < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $cost;
+ }
+
+ private static function validateSalvage(mixed $salvage, bool $negativeValueAllowed = false): float
+ {
+ $salvage = FinancialValidations::validateFloat($salvage);
+ if ($salvage < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $salvage;
+ }
+
+ private static function validateLife(mixed $life, bool $negativeValueAllowed = false): float
+ {
+ $life = FinancialValidations::validateFloat($life);
+ if ($life < 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $life;
+ }
+
+ private static function validatePeriod(mixed $period, bool $negativeValueAllowed = false): float
+ {
+ $period = FinancialValidations::validateFloat($period);
+ if ($period <= 0.0 && $negativeValueAllowed === false) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $period;
+ }
+
+ private static function validateMonth(mixed $month): int
+ {
+ $month = FinancialValidations::validateInt($month);
+ if ($month < 1) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $month;
+ }
+
+ private static function validateFactor(mixed $factor): float
+ {
+ $factor = FinancialValidations::validateFloat($factor);
+ if ($factor <= 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $factor;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
new file mode 100644
index 0000000..b1535d4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
@@ -0,0 +1,128 @@
+getMessage();
+ }
+
+ // Additional parameter validations
+ if ($fraction < 0) {
+ return ExcelError::NAN();
+ }
+ if ($fraction == 0) {
+ return ExcelError::DIV0();
+ }
+
+ $dollars = ($fractionalDollar < 0) ? ceil($fractionalDollar) : floor($fractionalDollar);
+ $cents = fmod($fractionalDollar, 1.0);
+ $cents /= $fraction;
+ $cents *= 10 ** ceil(log10($fraction));
+
+ return $dollars + $cents;
+ }
+
+ /**
+ * DOLLARFR.
+ *
+ * Converts a dollar price expressed as a decimal number into a dollar price
+ * expressed as a fraction.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * Excel Function:
+ * DOLLARFR(decimal_dollar,fraction)
+ *
+ * @param mixed $decimalDollar Decimal Dollar
+ * Or can be an array of values
+ * @param mixed $fraction Fraction
+ * Or can be an array of values
+ */
+ public static function fractional(mixed $decimalDollar = null, mixed $fraction = 0): array|string|float
+ {
+ if (is_array($decimalDollar) || is_array($fraction)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $decimalDollar, $fraction);
+ }
+
+ try {
+ $decimalDollar = FinancialValidations::validateFloat(
+ Functions::flattenSingleValue($decimalDollar) ?? 0.0
+ );
+ $fraction = FinancialValidations::validateInt(Functions::flattenSingleValue($fraction));
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Additional parameter validations
+ if ($fraction < 0) {
+ return ExcelError::NAN();
+ }
+ if ($fraction == 0) {
+ return ExcelError::DIV0();
+ }
+
+ $dollars = ($decimalDollar < 0.0) ? ceil($decimalDollar) : floor($decimalDollar);
+ $cents = fmod($decimalDollar, 1);
+ $cents *= $fraction;
+ $cents *= 10 ** (-ceil(log10($fraction)));
+
+ return $dollars + $cents;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
new file mode 100644
index 0000000..e596fc3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
@@ -0,0 +1,122 @@
+ 4)) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $basis;
+ }
+
+ public static function validatePrice(mixed $price): float
+ {
+ $price = self::validateFloat($price);
+ if ($price < 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $price;
+ }
+
+ public static function validateParValue(mixed $parValue): float
+ {
+ $parValue = self::validateFloat($parValue);
+ if ($parValue < 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $parValue;
+ }
+
+ public static function validateYield(mixed $yield): float
+ {
+ $yield = self::validateFloat($yield);
+ if ($yield < 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $yield;
+ }
+
+ public static function validateDiscount(mixed $discount): float
+ {
+ $discount = self::validateFloat($discount);
+ if ($discount <= 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $discount;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
new file mode 100644
index 0000000..aa28712
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
@@ -0,0 +1,58 @@
+format('d') === $date->format('t');
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
new file mode 100644
index 0000000..2916df6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
@@ -0,0 +1,71 @@
+getMessage();
+ }
+
+ if ($nominalRate <= 0 || $periodsPerYear < 1) {
+ return ExcelError::NAN();
+ }
+
+ return ((1 + $nominalRate / $periodsPerYear) ** $periodsPerYear) - 1;
+ }
+
+ /**
+ * NOMINAL.
+ *
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
+ *
+ * @param mixed $effectiveRate Effective interest rate as a float
+ * @param mixed $periodsPerYear Integer number of compounding payments per year
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function nominal(mixed $effectiveRate = 0, mixed $periodsPerYear = 0): string|float
+ {
+ $effectiveRate = Functions::flattenSingleValue($effectiveRate);
+ $periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
+
+ try {
+ $effectiveRate = FinancialValidations::validateFloat($effectiveRate);
+ $periodsPerYear = FinancialValidations::validateInt($periodsPerYear);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($effectiveRate <= 0 || $periodsPerYear < 1) {
+ return ExcelError::NAN();
+ }
+
+ // Calculate
+ return $periodsPerYear * (($effectiveRate + 1) ** (1 / $periodsPerYear) - 1);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
new file mode 100644
index 0000000..eb57abf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
@@ -0,0 +1,151 @@
+getMessage();
+ }
+
+ $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenFirstInterestAndSettlement = Functions::scalar(YearFrac::fraction($firstInterest, $settlement, $basis));
+ if (!is_numeric($daysBetweenFirstInterestAndSettlement)) {
+ // return date error
+ return $daysBetweenFirstInterestAndSettlement;
+ }
+
+ return $parValue * $rate * $daysBetweenIssueAndSettlement;
+ }
+
+ /**
+ * ACCRINTM.
+ *
+ * Returns the accrued interest for a security that pays interest at maturity.
+ *
+ * Excel Function:
+ * ACCRINTM(issue,settlement,rate[,par[,basis]])
+ *
+ * @param mixed $issue The security's issue date
+ * @param mixed $settlement The security's settlement (or maturity) date
+ * @param mixed $rate The security's annual coupon rate
+ * @param mixed $parValue The security's par value.
+ * If you omit parValue, ACCRINT uses $1,000.
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function atMaturity(
+ mixed $issue,
+ mixed $settlement,
+ mixed $rate,
+ mixed $parValue = 1000,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $issue = Functions::flattenSingleValue($issue);
+ $settlement = Functions::flattenSingleValue($settlement);
+ $rate = Functions::flattenSingleValue($rate);
+ $parValue = ($parValue === null) ? 1000 : Functions::flattenSingleValue($parValue);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ SecurityValidations::validateSecurityPeriod($issue, $settlement);
+ $rate = SecurityValidations::validateRate($rate);
+ $parValue = SecurityValidations::validateParValue($parValue);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+
+ return $parValue * $rate * $daysBetweenIssueAndSettlement;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
new file mode 100644
index 0000000..b07b2c9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
@@ -0,0 +1,283 @@
+getMessage();
+ }
+
+ $dsc = (float) Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
+ $e = (float) Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
+ $n = (int) Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
+ $a = (float) Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
+
+ $baseYF = 1.0 + ($yield / $frequency);
+ $rfp = 100 * ($rate / $frequency);
+ $de = $dsc / $e;
+
+ $result = $redemption / $baseYF ** (--$n + $de);
+ for ($k = 0; $k <= $n; ++$k) {
+ $result += $rfp / ($baseYF ** ($k + $de));
+ }
+ $result -= $rfp * ($a / $e);
+
+ return $result;
+ }
+
+ /**
+ * PRICEDISC.
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $discount The security's discount rate
+ * @param mixed $redemption The security's redemption value per $100 face value
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function priceDiscounted(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $discount,
+ mixed $redemption,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $discount = Functions::flattenSingleValue($discount);
+ $redemption = Functions::flattenSingleValue($redemption);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $discount = SecurityValidations::validateDiscount($discount);
+ $redemption = SecurityValidations::validateRedemption($redemption);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * PRICEMAT.
+ *
+ * Returns the price per $100 face value of a security that pays interest at maturity.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the
+ * security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $issue The security's issue date
+ * @param mixed $rate The security's interest rate at date of issue
+ * @param mixed $yield The security's annual yield
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function priceAtMaturity(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $issue,
+ mixed $rate,
+ mixed $yield,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $issue = Functions::flattenSingleValue($issue);
+ $rate = Functions::flattenSingleValue($rate);
+ $yield = Functions::flattenSingleValue($yield);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $rate = SecurityValidations::validateRate($rate);
+ $yield = SecurityValidations::validateYield($yield);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100))
+ / (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield))
+ - (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
+ }
+
+ /**
+ * RECEIVED.
+ *
+ * Returns the amount received at maturity for a fully invested Security.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $investment The amount invested in the security
+ * @param mixed $discount The security's discount rate
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function received(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $investment,
+ mixed $discount,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $investment = Functions::flattenSingleValue($investment);
+ $discount = Functions::flattenSingleValue($discount);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $investment = SecurityValidations::validateFloat($investment);
+ $discount = SecurityValidations::validateDiscount($discount);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($investment <= 0) {
+ return ExcelError::NAN();
+ }
+ $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return Functions::scalar($daysBetweenSettlementAndMaturity);
+ }
+
+ return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
new file mode 100644
index 0000000..e0c7e35
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
@@ -0,0 +1,138 @@
+getMessage();
+ }
+
+ if ($price <= 0.0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
+ }
+
+ /**
+ * INTRATE.
+ *
+ * Returns the interest rate for a fully invested security.
+ *
+ * Excel Function:
+ * INTRATE(settlement,maturity,investment,redemption[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $investment the amount invested in the security
+ * @param mixed $redemption the amount to be received at maturity
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string
+ */
+ public static function interest(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $investment,
+ mixed $redemption,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $investment = Functions::flattenSingleValue($investment);
+ $redemption = Functions::flattenSingleValue($redemption);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $investment = SecurityValidations::validateFloat($investment);
+ $redemption = SecurityValidations::validateRedemption($redemption);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($investment <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
new file mode 100644
index 0000000..a0804cb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
@@ -0,0 +1,32 @@
+= $maturity) {
+ throw new Exception(ExcelError::NAN());
+ }
+ }
+
+ public static function validateRedemption(mixed $redemption): float
+ {
+ $redemption = self::validateFloat($redemption);
+ if ($redemption <= 0.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $redemption;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
new file mode 100644
index 0000000..a4c5a48
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
@@ -0,0 +1,153 @@
+getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * YIELDMAT.
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security
+ * is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed $issue The security's issue date
+ * @param mixed $rate The security's interest rate at date of issue
+ * @param mixed $price The security's price per $100 face value
+ * @param mixed $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function yieldAtMaturity(
+ mixed $settlement,
+ mixed $maturity,
+ mixed $issue,
+ mixed $rate,
+ mixed $price,
+ mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ ) {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $issue = Functions::flattenSingleValue($issue);
+ $rate = Functions::flattenSingleValue($rate);
+ $price = Functions::flattenSingleValue($price);
+ $basis = ($basis === null)
+ ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+ : Functions::flattenSingleValue($basis);
+
+ try {
+ $settlement = SecurityValidations::validateSettlementDate($settlement);
+ $maturity = SecurityValidations::validateMaturityDate($maturity);
+ SecurityValidations::validateSecurityPeriod($settlement, $maturity);
+ $issue = SecurityValidations::validateIssueDate($issue);
+ $rate = SecurityValidations::validateRate($rate);
+ $price = SecurityValidations::validatePrice($price);
+ $basis = SecurityValidations::validateBasis($basis);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate)
+ - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate)))
+ / (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate)))
+ * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
new file mode 100644
index 0000000..699efcc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
@@ -0,0 +1,146 @@
+getMessage();
+ }
+
+ if ($discount <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return ExcelError::NAN();
+ }
+
+ return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+ /**
+ * TBILLPRICE.
+ *
+ * Returns the price per $100 face value for a Treasury bill.
+ *
+ * @param mixed $settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date
+ * when the Treasury bill is traded to the buyer.
+ * @param mixed $maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param mixed $discount The Treasury bill's discount rate
+ *
+ * @return float|string Result, or a string containing an error
+ */
+ public static function price(mixed $settlement, mixed $maturity, mixed $discount): string|float
+ {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $discount = Functions::flattenSingleValue($discount);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ $discount = FinancialValidations::validateFloat($discount);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($discount <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return ExcelError::NAN();
+ }
+
+ $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
+ if ($price < 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return $price;
+ }
+
+ /**
+ * TBILLYIELD.
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed $settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when
+ * the Treasury bill is traded to the buyer.
+ * @param mixed $maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param float|string $price The Treasury bill's price per $100 face value
+ */
+ public static function yield(mixed $settlement, mixed $maturity, $price): string|float
+ {
+ $settlement = Functions::flattenSingleValue($settlement);
+ $maturity = Functions::flattenSingleValue($maturity);
+ $price = Functions::flattenSingleValue($price);
+
+ try {
+ $settlement = FinancialValidations::validateSettlementDate($settlement);
+ $maturity = FinancialValidations::validateMaturityDate($maturity);
+ $price = FinancialValidations::validatePrice($price);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $daysBetweenSettlementAndMaturity = $maturity - $settlement;
+ $daysPerYear = Helpers::daysPerYear(
+ Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
+ FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+ );
+
+ if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
+ return ExcelError::NAN();
+ }
+
+ return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
new file mode 100644
index 0000000..0be1ca8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
@@ -0,0 +1,618 @@
+<';
+ const OPERATORS_POSTFIX = '%';
+
+ /**
+ * Formula.
+ */
+ private string $formula;
+
+ /**
+ * Tokens.
+ *
+ * @var FormulaToken[]
+ */
+ private $tokens = [];
+
+ /**
+ * Create a new FormulaParser.
+ *
+ * @param ?string $formula Formula to parse
+ */
+ public function __construct($formula = '')
+ {
+ // Check parameters
+ if ($formula === null) {
+ throw new Exception('Invalid parameter passed: formula');
+ }
+
+ // Initialise values
+ $this->formula = trim($formula);
+ // Parse!
+ $this->parseToTokens();
+ }
+
+ /**
+ * Get Formula.
+ *
+ * @return string
+ */
+ public function getFormula()
+ {
+ return $this->formula;
+ }
+
+ /**
+ * Get Token.
+ *
+ * @param int $id Token id
+ */
+ public function getToken(int $id = 0): FormulaToken
+ {
+ if (isset($this->tokens[$id])) {
+ return $this->tokens[$id];
+ }
+
+ throw new Exception("Token with id $id does not exist.");
+ }
+
+ /**
+ * Get Token count.
+ */
+ public function getTokenCount(): int
+ {
+ return count($this->tokens);
+ }
+
+ /**
+ * Get Tokens.
+ *
+ * @return FormulaToken[]
+ */
+ public function getTokens()
+ {
+ return $this->tokens;
+ }
+
+ /**
+ * Parse to tokens.
+ */
+ private function parseToTokens(): void
+ {
+ // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
+ // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
+
+ // Check if the formula has a valid starting =
+ $formulaLength = strlen($this->formula);
+ if ($formulaLength < 2 || $this->formula[0] != '=') {
+ return;
+ }
+
+ // Helper variables
+ $tokens1 = $tokens2 = $stack = [];
+ $inString = $inPath = $inRange = $inError = false;
+ $nextToken = null;
+ //$token = $previousToken = null;
+
+ $index = 1;
+ $value = '';
+
+ $ERRORS = ['#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', '#N/A'];
+ $COMPARATORS_MULTI = ['>=', '<=', '<>'];
+
+ while ($index < $formulaLength) {
+ // state-dependent character evaluation (order is important)
+
+ // double-quoted strings
+ // embeds are doubled
+ // end marks token
+ if ($inString) {
+ if ($this->formula[$index] == self::QUOTE_DOUBLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->formula[$index + 1] == self::QUOTE_DOUBLE)) {
+ $value .= self::QUOTE_DOUBLE;
+ ++$index;
+ } else {
+ $inString = false;
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_TEXT);
+ $value = '';
+ }
+ } else {
+ $value .= $this->formula[$index];
+ }
+ ++$index;
+
+ continue;
+ }
+
+ // single-quoted strings (links)
+ // embeds are double
+ // end does not mark a token
+ if ($inPath) {
+ if ($this->formula[$index] == self::QUOTE_SINGLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->formula[$index + 1] == self::QUOTE_SINGLE)) {
+ $value .= self::QUOTE_SINGLE;
+ ++$index;
+ } else {
+ $inPath = false;
+ }
+ } else {
+ $value .= $this->formula[$index];
+ }
+ ++$index;
+
+ continue;
+ }
+
+ // bracked strings (R1C1 range index or linked workbook name)
+ // no embeds (changed to "()" by Excel)
+ // end does not mark a token
+ if ($inRange) {
+ if ($this->formula[$index] == self::BRACKET_CLOSE) {
+ $inRange = false;
+ }
+ $value .= $this->formula[$index];
+ ++$index;
+
+ continue;
+ }
+
+ // error values
+ // end marks a token, determined from absolute list of values
+ if ($inError) {
+ $value .= $this->formula[$index];
+ ++$index;
+ if (in_array($value, $ERRORS)) {
+ $inError = false;
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_ERROR);
+ $value = '';
+ }
+
+ continue;
+ }
+
+ // scientific notation check
+ if (str_contains(self::OPERATORS_SN, $this->formula[$index])) {
+ if (strlen($value) > 1) {
+ if (preg_match('/^[1-9]{1}(\\.\\d+)?E{1}$/', $this->formula[$index]) != 0) {
+ $value .= $this->formula[$index];
+ ++$index;
+
+ continue;
+ }
+ }
+ }
+
+ // independent character evaluation (order not important)
+
+ // establish state-dependent character evaluations
+ if ($this->formula[$index] == self::QUOTE_DOUBLE) {
+ if ($value !== '') {
+ // unexpected
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = '';
+ }
+ $inString = true;
+ ++$index;
+
+ continue;
+ }
+
+ if ($this->formula[$index] == self::QUOTE_SINGLE) {
+ if ($value !== '') {
+ // unexpected
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = '';
+ }
+ $inPath = true;
+ ++$index;
+
+ continue;
+ }
+
+ if ($this->formula[$index] == self::BRACKET_OPEN) {
+ $inRange = true;
+ $value .= self::BRACKET_OPEN;
+ ++$index;
+
+ continue;
+ }
+
+ if ($this->formula[$index] == self::ERROR_START) {
+ if ($value !== '') {
+ // unexpected
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = '';
+ }
+ $inError = true;
+ $value .= self::ERROR_START;
+ ++$index;
+
+ continue;
+ }
+
+ // mark start and end of arrays and array rows
+ if ($this->formula[$index] == self::BRACE_OPEN) {
+ if ($value !== '') {
+ // unexpected
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = '';
+ }
+
+ $tmp = new FormulaToken('ARRAY', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ $tmp = new FormulaToken('ARRAYROW', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+
+ continue;
+ }
+
+ if ($this->formula[$index] == self::SEMICOLON) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+
+ /** @var FormulaToken $tmp */
+ $tmp = array_pop($stack);
+ $tmp->setValue('');
+ $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = new FormulaToken(',', FormulaToken::TOKEN_TYPE_ARGUMENT);
+ $tokens1[] = $tmp;
+
+ $tmp = new FormulaToken('ARRAYROW', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+
+ continue;
+ }
+
+ if ($this->formula[$index] == self::BRACE_CLOSE) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+
+ /** @var FormulaToken $tmp */
+ $tmp = array_pop($stack);
+ $tmp->setValue('');
+ $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ /** @var FormulaToken $tmp */
+ $tmp = array_pop($stack);
+ $tmp->setValue('');
+ $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+
+ continue;
+ }
+
+ // trim white-space
+ if ($this->formula[$index] == self::WHITESPACE) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+ $tokens1[] = new FormulaToken('', FormulaToken::TOKEN_TYPE_WHITESPACE);
+ ++$index;
+ while (($this->formula[$index] == self::WHITESPACE) && ($index < $formulaLength)) {
+ ++$index;
+ }
+
+ continue;
+ }
+
+ // multi-character comparators
+ if (($index + 2) <= $formulaLength) {
+ if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+ $tokens1[] = new FormulaToken(substr($this->formula, $index, 2), FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ $index += 2;
+
+ continue;
+ }
+ }
+
+ // standard infix operators
+ if (str_contains(self::OPERATORS_INFIX, $this->formula[$index])) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+ $tokens1[] = new FormulaToken($this->formula[$index], FormulaToken::TOKEN_TYPE_OPERATORINFIX);
+ ++$index;
+
+ continue;
+ }
+
+ // standard postfix operators (only one)
+ if (str_contains(self::OPERATORS_POSTFIX, $this->formula[$index])) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+ $tokens1[] = new FormulaToken($this->formula[$index], FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
+ ++$index;
+
+ continue;
+ }
+
+ // start subexpression or function
+ if ($this->formula[$index] == self::PAREN_OPEN) {
+ if ($value !== '') {
+ $tmp = new FormulaToken($value, FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ $value = '';
+ } else {
+ $tmp = new FormulaToken('', FormulaToken::TOKEN_TYPE_SUBEXPRESSION, FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ }
+ ++$index;
+
+ continue;
+ }
+
+ // function, subexpression, or array parameters, or operand unions
+ if ($this->formula[$index] == self::COMMA) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+
+ /** @var FormulaToken $tmp */
+ $tmp = array_pop($stack);
+ $tmp->setValue('');
+ $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
+ $stack[] = $tmp;
+
+ if ($tmp->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
+ $tokens1[] = new FormulaToken(',', FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_UNION);
+ } else {
+ $tokens1[] = new FormulaToken(',', FormulaToken::TOKEN_TYPE_ARGUMENT);
+ }
+ ++$index;
+
+ continue;
+ }
+
+ // stop subexpression
+ if ($this->formula[$index] == self::PAREN_CLOSE) {
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = '';
+ }
+
+ /** @var FormulaToken $tmp */
+ $tmp = array_pop($stack);
+ $tmp->setValue('');
+ $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+
+ continue;
+ }
+
+ // token accumulation
+ $value .= $this->formula[$index];
+ ++$index;
+ }
+
+ // dump remaining accumulation
+ if ($value !== '') {
+ $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
+ }
+
+ // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
+ $tokenCount = count($tokens1);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens1[$i];
+ if (isset($tokens1[$i - 1])) {
+ $previousToken = $tokens1[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens1[$i + 1])) {
+ $nextToken = $tokens1[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if ($token->getTokenType() != FormulaToken::TOKEN_TYPE_WHITESPACE) {
+ $tokens2[] = $token;
+
+ continue;
+ }
+
+ if ($previousToken === null) {
+ continue;
+ }
+
+ if (
+ !(
+ (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
+ || (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
+ || ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+ )
+ ) {
+ continue;
+ }
+
+ if ($nextToken === null) {
+ continue;
+ }
+
+ if (
+ !(
+ (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START))
+ || (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START))
+ || ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+ )
+ ) {
+ continue;
+ }
+
+ $tokens2[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
+ }
+
+ // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
+ // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
+ $this->tokens = [];
+
+ $tokenCount = count($tokens2);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens2[$i];
+ if (isset($tokens2[$i - 1])) {
+ $previousToken = $tokens2[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+
+ if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') {
+ if ($i == 0) {
+ $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ } elseif (
+ (($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION)
+ && ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
+ || (($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION)
+ && ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
+ || ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX)
+ || ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ }
+
+ $this->tokens[] = $token;
+
+ continue;
+ }
+
+ if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '+') {
+ if ($i == 0) {
+ continue;
+ } elseif (
+ (($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION)
+ && ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
+ || (($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION)
+ && ($previousToken?->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP))
+ || ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX)
+ || ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+ ) {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ continue;
+ }
+
+ $this->tokens[] = $token;
+
+ continue;
+ }
+
+ if (
+ $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX
+ && $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
+ ) {
+ if (str_contains('<>=', substr($token->getValue(), 0, 1))) {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } elseif ($token->getValue() == '&') {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
+ } else {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
+ }
+
+ $this->tokens[] = $token;
+
+ continue;
+ }
+
+ if (
+ $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND
+ && $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
+ ) {
+ if (!is_numeric($token->getValue())) {
+ if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } else {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_RANGE);
+ }
+ } else {
+ $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_NUMBER);
+ }
+
+ $this->tokens[] = $token;
+
+ continue;
+ }
+
+ if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
+ if ($token->getValue() !== '') {
+ if (str_starts_with($token->getValue(), '@')) {
+ $token->setValue(substr($token->getValue(), 1));
+ }
+ }
+ }
+
+ $this->tokens[] = $token;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
new file mode 100644
index 0000000..68e5eea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
@@ -0,0 +1,150 @@
+value = $value;
+ $this->tokenType = $tokenType;
+ $this->tokenSubType = $tokenSubType;
+ }
+
+ /**
+ * Get Value.
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set Value.
+ *
+ * @param string $value
+ */
+ public function setValue($value): void
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * Get Token Type (represented by TOKEN_TYPE_*).
+ *
+ * @return string
+ */
+ public function getTokenType()
+ {
+ return $this->tokenType;
+ }
+
+ /**
+ * Set Token Type (represented by TOKEN_TYPE_*).
+ *
+ * @param string $value
+ */
+ public function setTokenType($value): void
+ {
+ $this->tokenType = $value;
+ }
+
+ /**
+ * Get Token SubType (represented by TOKEN_SUBTYPE_*).
+ *
+ * @return string
+ */
+ public function getTokenSubType()
+ {
+ return $this->tokenSubType;
+ }
+
+ /**
+ * Set Token SubType (represented by TOKEN_SUBTYPE_*).
+ *
+ * @param string $value
+ */
+ public function setTokenSubType($value): void
+ {
+ $this->tokenSubType = $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
new file mode 100644
index 0000000..9cde335
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
@@ -0,0 +1,667 @@
+ 0);
+ }
+
+ public static function isValue(mixed $idx): bool
+ {
+ return substr_count($idx, '.') === 0;
+ }
+
+ public static function isCellValue(mixed $idx): bool
+ {
+ return substr_count($idx, '.') > 1;
+ }
+
+ public static function ifCondition(mixed $condition): string
+ {
+ $condition = self::flattenSingleValue($condition);
+
+ if ($condition === '' || $condition === null) {
+ return '=""';
+ }
+ if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
+ $condition = self::operandSpecialHandling($condition);
+ if (is_bool($condition)) {
+ return '=' . ($condition ? 'TRUE' : 'FALSE');
+ } elseif (!is_numeric($condition)) {
+ if ($condition !== '""') { // Not an empty string
+ // Escape any quotes in the string value
+ $condition = (string) preg_replace('/"/ui', '""', $condition);
+ }
+ $condition = Calculation::wrapResult(strtoupper($condition));
+ }
+
+ return str_replace('""""', '""', '=' . $condition);
+ }
+ preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
+ [, $operator, $operand] = $matches;
+
+ $operand = self::operandSpecialHandling($operand);
+ if (is_numeric(trim($operand, '"'))) {
+ $operand = trim($operand, '"');
+ } elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
+ $operand = str_replace('"', '""', $operand);
+ $operand = Calculation::wrapResult(strtoupper($operand));
+ }
+
+ return str_replace('""""', '""', $operator . $operand);
+ }
+
+ /**
+ * @return mixed
+ */
+ private static function operandSpecialHandling(mixed $operand)
+ {
+ if (is_numeric($operand) || is_bool($operand)) {
+ return $operand;
+ } elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
+ return strtoupper($operand);
+ }
+
+ // Check for percentage
+ if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $operand)) {
+ return ((float) rtrim($operand, '%')) / 100;
+ }
+
+ // Check for dates
+ if (($dateValueOperand = Date::stringToExcel($operand)) !== false) {
+ return $dateValueOperand;
+ }
+
+ return $operand;
+ }
+
+ /**
+ * NULL.
+ *
+ * Returns the error value #NULL!
+ *
+ * @deprecated 1.23.0 Use the null() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::null()
+ *
+ * @return string #NULL!
+ */
+ public static function null(): string
+ {
+ return Information\ExcelError::null();
+ }
+
+ /**
+ * NaN.
+ *
+ * Returns the error value #NUM!
+ *
+ * @deprecated 1.23.0 Use the NAN() method in the Information\Error class instead
+ * @see Information\ExcelError::NAN()
+ *
+ * @return string #NUM!
+ */
+ public static function NAN(): string
+ {
+ return Information\ExcelError::NAN();
+ }
+
+ /**
+ * REF.
+ *
+ * Returns the error value #REF!
+ *
+ * @deprecated 1.23.0 Use the REF() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::REF()
+ *
+ * @return string #REF!
+ */
+ public static function REF(): string
+ {
+ return Information\ExcelError::REF();
+ }
+
+ /**
+ * NA.
+ *
+ * Excel Function:
+ * =NA()
+ *
+ * Returns the error value #N/A
+ * #N/A is the error value that means "no value is available."
+ *
+ * @deprecated 1.23.0 Use the NA() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::NA()
+ *
+ * @return string #N/A!
+ */
+ public static function NA(): string
+ {
+ return Information\ExcelError::NA();
+ }
+
+ /**
+ * VALUE.
+ *
+ * Returns the error value #VALUE!
+ *
+ * @deprecated 1.23.0 Use the VALUE() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::VALUE()
+ *
+ * @return string #VALUE!
+ */
+ public static function VALUE(): string
+ {
+ return Information\ExcelError::VALUE();
+ }
+
+ /**
+ * NAME.
+ *
+ * Returns the error value #NAME?
+ *
+ * @deprecated 1.23.0 Use the NAME() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::NAME()
+ *
+ * @return string #NAME?
+ */
+ public static function NAME(): string
+ {
+ return Information\ExcelError::NAME();
+ }
+
+ /**
+ * DIV0.
+ *
+ * @deprecated 1.23.0 Use the DIV0() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::DIV0()
+ *
+ * @return string #Not Yet Implemented
+ */
+ public static function DIV0(): string
+ {
+ return Information\ExcelError::DIV0();
+ }
+
+ /**
+ * ERROR_TYPE.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the type() method in the Information\ExcelError class instead
+ * @see Information\ExcelError::type()
+ *
+ * @return array|int|string
+ */
+ public static function errorType(mixed $value = '')
+ {
+ return Information\ExcelError::type($value);
+ }
+
+ /**
+ * IS_BLANK.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isBlank() method in the Information\Value class instead
+ * @see Information\Value::isBlank()
+ *
+ * @return array|bool
+ */
+ public static function isBlank(mixed $value = null)
+ {
+ return Information\Value::isBlank($value);
+ }
+
+ /**
+ * IS_ERR.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isErr() method in the Information\ErrorValue class instead
+ * @see Information\ErrorValue::isErr()
+ *
+ * @return array|bool
+ */
+ public static function isErr(mixed $value = '')
+ {
+ return Information\ErrorValue::isErr($value);
+ }
+
+ /**
+ * IS_ERROR.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isError() method in the Information\ErrorValue class instead
+ * @see Information\ErrorValue::isError()
+ *
+ * @return array|bool
+ */
+ public static function isError(mixed $value = '')
+ {
+ return Information\ErrorValue::isError($value);
+ }
+
+ /**
+ * IS_NA.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isNa() method in the Information\ErrorValue class instead
+ * @see Information\ErrorValue::isNa()
+ *
+ * @return array|bool
+ */
+ public static function isNa(mixed $value = '')
+ {
+ return Information\ErrorValue::isNa($value);
+ }
+
+ /**
+ * IS_EVEN.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isEven() method in the Information\Value class instead
+ * @see Information\Value::isEven()
+ *
+ * @return array|bool|string
+ */
+ public static function isEven(mixed $value = null)
+ {
+ return Information\Value::isEven($value);
+ }
+
+ /**
+ * IS_ODD.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isOdd() method in the Information\Value class instead
+ * @see Information\Value::isOdd()
+ *
+ * @return array|bool|string
+ */
+ public static function isOdd(mixed $value = null)
+ {
+ return Information\Value::isOdd($value);
+ }
+
+ /**
+ * IS_NUMBER.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isNumber() method in the Information\Value class instead
+ * @see Information\Value::isNumber()
+ *
+ * @return array|bool
+ */
+ public static function isNumber(mixed $value = null)
+ {
+ return Information\Value::isNumber($value);
+ }
+
+ /**
+ * IS_LOGICAL.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isLogical() method in the Information\Value class instead
+ * @see Information\Value::isLogical()
+ */
+ public static function isLogical(mixed $value = null): bool|array
+ {
+ return Information\Value::isLogical($value);
+ }
+
+ /**
+ * IS_TEXT.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isText() method in the Information\Value class instead
+ * @see Information\Value::isText()
+ *
+ * @return array|bool
+ */
+ public static function isText(mixed $value = null)
+ {
+ return Information\Value::isText($value);
+ }
+
+ /**
+ * IS_NONTEXT.
+ *
+ * @param mixed $value Value to check
+ *
+ * @deprecated 1.23.0 Use the isNonText() method in the Information\Value class instead
+ * @see Information\Value::isNonText()
+ *
+ * @return array|bool
+ */
+ public static function isNonText(mixed $value = null)
+ {
+ return Information\Value::isNonText($value);
+ }
+
+ /**
+ * N.
+ *
+ * Returns a value converted to a number
+ *
+ * @deprecated 1.23.0 Use the asNumber() method in the Information\Value class instead
+ * @see Information\Value::asNumber()
+ *
+ * @param null|mixed $value The value you want converted
+ *
+ * @return number|string N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number That number
+ * A date The serial number of that date
+ * TRUE 1
+ * FALSE 0
+ * An error value The error value
+ * Anything else 0
+ */
+ public static function n($value = null)
+ {
+ return Information\Value::asNumber($value);
+ }
+
+ /**
+ * TYPE.
+ *
+ * Returns a number that identifies the type of a value
+ *
+ * @deprecated 1.23.0 Use the type() method in the Information\Value class instead
+ * @see Information\Value::type()
+ *
+ * @param null|mixed $value The value you want tested
+ *
+ * @return int N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number 1
+ * Text 2
+ * Logical Value 4
+ * An error value 16
+ * Array or Matrix 64
+ */
+ public static function TYPE($value = null): int
+ {
+ return Information\Value::type($value);
+ }
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array.
+ *
+ * @param array|mixed $array Array to be flattened
+ *
+ * @return array Flattened array
+ */
+ public static function flattenArray($array): array
+ {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $flattened = [];
+ $stack = array_values($array);
+
+ while (!empty($stack)) {
+ $value = array_shift($stack);
+
+ if (is_array($value)) {
+ array_unshift($stack, ...array_values($value));
+ } else {
+ $flattened[] = $value;
+ }
+ }
+
+ return $flattened;
+ }
+
+ /**
+ * @return null|mixed
+ */
+ public static function scalar(mixed $value)
+ {
+ if (!is_array($value)) {
+ return $value;
+ }
+
+ do {
+ $value = array_pop($value);
+ } while (is_array($value));
+
+ return $value;
+ }
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
+ *
+ * @param array|mixed $array Array to be flattened
+ *
+ * @return array Flattened array
+ */
+ public static function flattenArrayIndexed($array)
+ {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = [];
+ foreach ($array as $k1 => $value) {
+ if (is_array($value)) {
+ foreach ($value as $k2 => $val) {
+ if (is_array($val)) {
+ foreach ($val as $k3 => $v) {
+ $arrayValues[$k1 . '.' . $k2 . '.' . $k3] = $v;
+ }
+ } else {
+ $arrayValues[$k1 . '.' . $k2] = $val;
+ }
+ }
+ } else {
+ $arrayValues[$k1] = $value;
+ }
+ }
+
+ return $arrayValues;
+ }
+
+ /**
+ * Convert an array to a single scalar value by extracting the first element.
+ *
+ * @param mixed $value Array or scalar value
+ *
+ * @return mixed
+ */
+ public static function flattenSingleValue(mixed $value = '')
+ {
+ while (is_array($value)) {
+ $value = array_shift($value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * ISFORMULA.
+ *
+ * @deprecated 1.23.0 Use the isFormula() method in the Information\Value class instead
+ * @see Information\Value::isFormula()
+ *
+ * @param mixed $cellReference The cell to check
+ * @param ?Cell $cell The current cell (containing this formula)
+ *
+ * @return array|bool|string
+ */
+ public static function isFormula(mixed $cellReference = '', ?Cell $cell = null)
+ {
+ return Information\Value::isFormula($cellReference, $cell);
+ }
+
+ public static function expandDefinedName(string $coordinate, Cell $cell): string
+ {
+ $worksheet = $cell->getWorksheet();
+ $spreadsheet = $worksheet->getParentOrThrow();
+ // Uppercase coordinate
+ $pCoordinatex = strtoupper($coordinate);
+ // Eliminate leading equal sign
+ $pCoordinatex = (string) preg_replace('/^=/', '', $pCoordinatex);
+ $defined = $spreadsheet->getDefinedName($pCoordinatex, $worksheet);
+ if ($defined !== null) {
+ $worksheet2 = $defined->getWorkSheet();
+ if (!$defined->isFormula() && $worksheet2 !== null) {
+ $coordinate = "'" . $worksheet2->getTitle() . "'!"
+ . (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
+ }
+ }
+
+ return $coordinate;
+ }
+
+ public static function trimTrailingRange(string $coordinate): string
+ {
+ return (string) preg_replace('/:[\\w\$]+$/', '', $coordinate);
+ }
+
+ public static function trimSheetFromCellReference(string $coordinate): string
+ {
+ if (str_contains($coordinate, '!')) {
+ $coordinate = substr($coordinate, strrpos($coordinate, '!') + 1);
+ }
+
+ return $coordinate;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
new file mode 100644
index 0000000..8c3d6c3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
@@ -0,0 +1,71 @@
+
+ */
+ public const ERROR_CODES = [
+ 'null' => '#NULL!', // 1
+ 'divisionbyzero' => '#DIV/0!', // 2
+ 'value' => '#VALUE!', // 3
+ 'reference' => '#REF!', // 4
+ 'name' => '#NAME?', // 5
+ 'num' => '#NUM!', // 6
+ 'na' => '#N/A', // 7
+ 'gettingdata' => '#GETTING_DATA', // 8
+ 'spill' => '#SPILL!', // 9
+ 'connect' => '#CONNECT!', //10
+ 'blocked' => '#BLOCKED!', //11
+ 'unknown' => '#UNKNOWN!', //12
+ 'field' => '#FIELD!', //13
+ 'calculation' => '#CALC!', //14
+ ];
+
+ /**
+ * List of error codes. Replaced by constant;
+ * previously it was public and updateable, allowing
+ * user to make inappropriate alterations.
+ *
+ * @deprecated 1.25.0 Use ERROR_CODES constant instead.
+ *
+ * @var array
+ */
+ public static $errorCodes = self::ERROR_CODES;
+
+ public static function throwError(mixed $value): string
+ {
+ return in_array($value, self::ERROR_CODES, true) ? $value : self::ERROR_CODES['value'];
+ }
+
+ /**
+ * ERROR_TYPE.
+ *
+ * @param mixed $value Value to check
+ */
+ public static function type(mixed $value = ''): array|int|string
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ $i = 1;
+ foreach (self::ERROR_CODES as $errorCode) {
+ if ($value === $errorCode) {
+ return $i;
+ }
+ ++$i;
+ }
+
+ return self::NA();
+ }
+
+ /**
+ * NULL.
+ *
+ * Returns the error value #NULL!
+ *
+ * @return string #NULL!
+ */
+ public static function null(): string
+ {
+ return self::ERROR_CODES['null'];
+ }
+
+ /**
+ * NaN.
+ *
+ * Returns the error value #NUM!
+ *
+ * @return string #NUM!
+ */
+ public static function NAN(): string
+ {
+ return self::ERROR_CODES['num'];
+ }
+
+ /**
+ * REF.
+ *
+ * Returns the error value #REF!
+ *
+ * @return string #REF!
+ */
+ public static function REF(): string
+ {
+ return self::ERROR_CODES['reference'];
+ }
+
+ /**
+ * NA.
+ *
+ * Excel Function:
+ * =NA()
+ *
+ * Returns the error value #N/A
+ * #N/A is the error value that means "no value is available."
+ *
+ * @return string #N/A!
+ */
+ public static function NA(): string
+ {
+ return self::ERROR_CODES['na'];
+ }
+
+ /**
+ * VALUE.
+ *
+ * Returns the error value #VALUE!
+ *
+ * @return string #VALUE!
+ */
+ public static function VALUE(): string
+ {
+ return self::ERROR_CODES['value'];
+ }
+
+ /**
+ * NAME.
+ *
+ * Returns the error value #NAME?
+ *
+ * @return string #NAME?
+ */
+ public static function NAME(): string
+ {
+ return self::ERROR_CODES['name'];
+ }
+
+ /**
+ * DIV0.
+ *
+ * @return string #DIV/0!
+ */
+ public static function DIV0(): string
+ {
+ return self::ERROR_CODES['divisionbyzero'];
+ }
+
+ /**
+ * CALC.
+ *
+ * @return string #CALC!
+ */
+ public static function CALC(): string
+ {
+ return self::ERROR_CODES['calculation'];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php
new file mode 100644
index 0000000..863102b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php
@@ -0,0 +1,326 @@
+getCoordinate()) {
+ return false;
+ }
+
+ $cellValue = Functions::trimTrailingRange($value);
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) {
+ [$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true);
+ if (!empty($worksheet) && $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheet) === null) {
+ return false;
+ }
+ [$column, $row] = Coordinate::indexesFromString($cellValue ?? '');
+ if ($column > 16384 || $row > 1048576) {
+ return false;
+ }
+
+ return true;
+ }
+
+ $namedRange = $cell->getWorksheet()->getParentOrThrow()->getNamedRange($value);
+
+ return $namedRange instanceof NamedRange;
+ }
+
+ /**
+ * IS_EVEN.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isEven(mixed $value = null): array|string|bool
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ if ($value === null) {
+ return ExcelError::NAME();
+ } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return ExcelError::VALUE();
+ }
+
+ return ((int) fmod($value, 2)) === 0;
+ }
+
+ /**
+ * IS_ODD.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isOdd(mixed $value = null): array|string|bool
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ if ($value === null) {
+ return ExcelError::NAME();
+ } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return ExcelError::VALUE();
+ }
+
+ return ((int) fmod($value, 2)) !== 0;
+ }
+
+ /**
+ * IS_NUMBER.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isNumber(mixed $value = null): array|bool
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ if (is_string($value)) {
+ return false;
+ }
+
+ return is_numeric($value);
+ }
+
+ /**
+ * IS_LOGICAL.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isLogical(mixed $value = null): array|bool
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ return is_bool($value);
+ }
+
+ /**
+ * IS_TEXT.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isText(mixed $value = null): array|bool
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ return is_string($value) && !ErrorValue::isError($value);
+ }
+
+ /**
+ * IS_NONTEXT.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|bool
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function isNonText(mixed $value = null): array|bool
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ return !self::isText($value);
+ }
+
+ /**
+ * ISFORMULA.
+ *
+ * @param mixed $cellReference The cell to check
+ * @param ?Cell $cell The current cell (containing this formula)
+ *
+ * @return array|bool|string
+ */
+ public static function isFormula(mixed $cellReference = '', ?Cell $cell = null)
+ {
+ if ($cell === null) {
+ return ExcelError::REF();
+ }
+
+ $fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
+
+ if (str_contains($cellReference, '!')) {
+ $cellReference = Functions::trimSheetFromCellReference($cellReference);
+ $cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
+ if (count($cellReferences) > 1) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $cellReferences, $cell);
+ }
+ }
+
+ $fullCellReference = Functions::trimTrailingRange($fullCellReference);
+
+ preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
+
+ $fullCellReference = $matches[6] . $matches[7];
+ $worksheetName = str_replace("''", "'", trim($matches[2], "'"));
+
+ $worksheet = (!empty($worksheetName))
+ ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
+ : $cell->getWorksheet();
+
+ return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
+ }
+
+ /**
+ * N.
+ *
+ * Returns a value converted to a number
+ *
+ * @param null|mixed $value The value you want converted
+ *
+ * @return number|string N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number That number value
+ * A date The Excel serialized number of that date
+ * TRUE 1
+ * FALSE 0
+ * An error value The error value
+ * Anything else 0
+ */
+ public static function asNumber($value = null)
+ {
+ while (is_array($value)) {
+ $value = array_shift($value);
+ }
+
+ switch (gettype($value)) {
+ case 'double':
+ case 'float':
+ case 'integer':
+ return $value;
+ case 'boolean':
+ return (int) $value;
+ case 'string':
+ // Errors
+ if (($value !== '') && ($value[0] == '#')) {
+ return $value;
+ }
+
+ break;
+ }
+
+ return 0;
+ }
+
+ /**
+ * TYPE.
+ *
+ * Returns a number that identifies the type of a value
+ *
+ * @param null|mixed $value The value you want tested
+ *
+ * @return int N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number 1
+ * Text 2
+ * Logical Value 4
+ * An error value 16
+ * Array or Matrix 64
+ */
+ public static function type($value = null): int
+ {
+ $value = Functions::flattenArrayIndexed($value);
+ if (is_array($value) && (count($value) > 1)) {
+ end($value);
+ $a = key($value);
+ // Range of cells is an error
+ if (Functions::isCellValue($a)) {
+ return 16;
+ // Test for Matrix
+ } elseif (Functions::isMatrixValue($a)) {
+ return 64;
+ }
+ } elseif (empty($value)) {
+ // Empty Cell
+ return 1;
+ }
+
+ $value = Functions::flattenSingleValue($value);
+ if (($value === null) || (is_float($value)) || (is_int($value))) {
+ return 1;
+ } elseif (is_bool($value)) {
+ return 4;
+ } elseif (is_array($value)) {
+ return 64;
+ } elseif (is_string($value)) {
+ // Errors
+ if (($value !== '') && ($value[0] == '#')) {
+ return 16;
+ }
+
+ return 2;
+ }
+
+ return 0;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
new file mode 100644
index 0000000..22c95e8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
@@ -0,0 +1,12 @@
+ 0) {
+ $targetValue = Functions::flattenSingleValue($arguments[0]);
+ $argc = count($arguments) - 1;
+ $switchCount = floor($argc / 2);
+ $hasDefaultClause = $argc % 2 !== 0;
+ $defaultClause = $argc % 2 === 0 ? null : $arguments[$argc];
+
+ $switchSatisfied = false;
+ if ($switchCount > 0) {
+ for ($index = 0; $index < $switchCount; ++$index) {
+ if ($targetValue == Functions::flattenSingleValue($arguments[$index * 2 + 1])) {
+ $result = $arguments[$index * 2 + 2];
+ $switchSatisfied = true;
+
+ break;
+ }
+ }
+ }
+
+ if ($switchSatisfied !== true) {
+ $result = $hasDefaultClause ? $defaultClause : ExcelError::NA();
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * IFERROR.
+ *
+ * Excel Function:
+ * =IFERROR(testValue,errorpart)
+ *
+ * @param mixed $testValue Value to check, is also the value returned when no error
+ * Or can be an array of values
+ * @param mixed $errorpart Value to return when testValue is an error condition
+ * Note that this can be an array value to be returned
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ * If an array of values is passed as the $testValue argument, then the returned result will also be
+ * an array with the same dimensions
+ */
+ public static function IFERROR(mixed $testValue = '', mixed $errorpart = '')
+ {
+ if (is_array($testValue)) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $errorpart);
+ }
+
+ $errorpart = $errorpart ?? '';
+ $testValue = $testValue ?? 0; // this is how Excel handles empty cell
+
+ return self::statementIf(ErrorValue::isError($testValue), $errorpart, $testValue);
+ }
+
+ /**
+ * IFNA.
+ *
+ * Excel Function:
+ * =IFNA(testValue,napart)
+ *
+ * @param mixed $testValue Value to check, is also the value returned when not an NA
+ * Or can be an array of values
+ * @param mixed $napart Value to return when testValue is an NA condition
+ * Note that this can be an array value to be returned
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ * If an array of values is passed as the $testValue argument, then the returned result will also be
+ * an array with the same dimensions
+ */
+ public static function IFNA(mixed $testValue = '', mixed $napart = '')
+ {
+ if (is_array($testValue)) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $napart);
+ }
+
+ $napart = $napart ?? '';
+ $testValue = $testValue ?? 0; // this is how Excel handles empty cell
+
+ return self::statementIf(ErrorValue::isNa($testValue), $napart, $testValue);
+ }
+
+ /**
+ * IFS.
+ *
+ * Excel Function:
+ * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
+ *
+ * testValue1 ... testValue_n
+ * Conditions to Evaluate
+ * returnIfTrue1 ... returnIfTrue_n
+ * Value returned if corresponding testValue (nth) was true
+ *
+ * @param mixed ...$arguments Statement arguments
+ * Note that this can be an array value to be returned
+ *
+ * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
+ */
+ public static function IFS(mixed ...$arguments)
+ {
+ $argumentCount = count($arguments);
+
+ if ($argumentCount % 2 != 0) {
+ return ExcelError::NA();
+ }
+ // We use instance of Exception as a falseValue in order to prevent string collision with value in cell
+ $falseValueException = new Exception();
+ for ($i = 0; $i < $argumentCount; $i += 2) {
+ $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
+ $returnIfTrue = ($arguments[$i + 1] === null) ? '' : $arguments[$i + 1];
+ $result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
+
+ if ($result !== $falseValueException) {
+ return $result;
+ }
+ }
+
+ return ExcelError::NA();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
new file mode 100644
index 0000000..51c8827
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
@@ -0,0 +1,166 @@
+ $trueValueCount === $count);
+ }
+
+ /**
+ * LOGICAL_OR.
+ *
+ * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
+ *
+ * Excel Function:
+ * =OR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @param mixed $args Data values
+ *
+ * @return bool|string the logical OR of the arguments
+ */
+ public static function logicalOr(mixed ...$args)
+ {
+ return self::countTrueValues($args, fn (int $trueValueCount): bool => $trueValueCount > 0);
+ }
+
+ /**
+ * LOGICAL_XOR.
+ *
+ * Returns the Exclusive Or logical operation for one or more supplied conditions.
+ * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
+ * and FALSE otherwise.
+ *
+ * Excel Function:
+ * =XOR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @param mixed $args Data values
+ *
+ * @return bool|string the logical XOR of the arguments
+ */
+ public static function logicalXor(mixed ...$args)
+ {
+ return self::countTrueValues($args, fn (int $trueValueCount): bool => $trueValueCount % 2 === 1);
+ }
+
+ /**
+ * NOT.
+ *
+ * Returns the boolean inverse of the argument.
+ *
+ * Excel Function:
+ * =NOT(logical)
+ *
+ * The argument must evaluate to a logical value such as TRUE or FALSE
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
+ * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+ * Or can be an array of values
+ *
+ * @return array|bool|string the boolean inverse of the argument
+ * If an array of values is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function NOT(mixed $logical = false): array|bool|string
+ {
+ if (is_array($logical)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $logical);
+ }
+
+ if (is_string($logical)) {
+ $logical = mb_strtoupper($logical, 'UTF-8');
+ if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
+ return false;
+ } elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
+ return true;
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ return !$logical;
+ }
+
+ /**
+ * @return bool|string
+ */
+ private static function countTrueValues(array $args, callable $func)
+ {
+ $trueValueCount = 0;
+ $count = 0;
+
+ $aArgs = Functions::flattenArrayIndexed($args);
+ foreach ($aArgs as $k => $arg) {
+ ++$count;
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $trueValueCount += $arg;
+ } elseif (is_string($arg)) {
+ $isLiteral = !Functions::isCellValue($k);
+ $arg = mb_strtoupper($arg, 'UTF-8');
+ if ($isLiteral && ($arg == 'TRUE' || $arg == Calculation::getTRUE())) {
+ ++$trueValueCount;
+ } elseif ($isLiteral && ($arg == 'FALSE' || $arg == Calculation::getFALSE())) {
+ //$trueValueCount += 0;
+ } else {
+ --$count;
+ }
+ } elseif (is_int($arg) || is_float($arg)) {
+ $trueValueCount += (int) ($arg != 0);
+ } else {
+ --$count;
+ }
+ }
+
+ return ($count === 0) ? ExcelError::VALUE() : $func($trueValueCount, $count);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
new file mode 100644
index 0000000..9b71cfb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
@@ -0,0 +1,398 @@
+ '') {
+ if (str_contains($sheetName, ' ') || str_contains($sheetName, '[')) {
+ $sheetName = "'{$sheetName}'";
+ }
+ $sheetName .= '!';
+ }
+
+ return $sheetName;
+ }
+
+ private static function formatAsA1(int $row, int $column, int $relativity, string $sheetName): string
+ {
+ $rowRelative = $columnRelative = '$';
+ if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $columnRelative = '';
+ }
+ if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $rowRelative = '';
+ }
+ $column = Coordinate::stringFromColumnIndex($column);
+
+ return "{$sheetName}{$columnRelative}{$column}{$rowRelative}{$row}";
+ }
+
+ private static function formatAsR1C1(int $row, int $column, int $relativity, string $sheetName): string
+ {
+ if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $column = "[{$column}]";
+ }
+ if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
+ $row = "[{$row}]";
+ }
+ [$rowChar, $colChar] = AddressHelper::getRowAndColumnChars();
+
+ return "{$sheetName}$rowChar{$row}$colChar{$column}";
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
new file mode 100644
index 0000000..2adc87c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
@@ -0,0 +1,252 @@
+getMessage();
+ }
+
+ // MATCH() is not case sensitive, so we convert lookup value to be lower cased if it's a string type.
+ if (is_string($lookupValue)) {
+ $lookupValue = StringHelper::strToLower($lookupValue);
+ }
+
+ $valueKey = match ($matchType) {
+ self::MATCHTYPE_LARGEST_VALUE => self::matchLargestValue($lookupArray, $lookupValue, $keySet),
+ self::MATCHTYPE_FIRST_VALUE => self::matchFirstValue($lookupArray, $lookupValue),
+ default => self::matchSmallestValue($lookupArray, $lookupValue),
+ };
+
+ if ($valueKey !== null) {
+ return ++$valueKey;
+ }
+
+ // Unsuccessful in finding a match, return #N/A error value
+ return ExcelError::NA();
+ }
+
+ private static function matchFirstValue(array $lookupArray, mixed $lookupValue): int|string|null
+ {
+ if (is_string($lookupValue)) {
+ $valueIsString = true;
+ $wildcard = WildcardMatch::wildcard($lookupValue);
+ } else {
+ $valueIsString = false;
+ $wildcard = '';
+ }
+
+ $valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if (
+ $valueIsString
+ && is_string($lookupArrayValue)
+ ) {
+ if (WildcardMatch::compare($lookupArrayValue, $wildcard)) {
+ return $i; // wildcard match
+ }
+ } else {
+ if ($lookupArrayValue === $lookupValue) {
+ return $i; // exact match
+ }
+ if (
+ $valueIsNumeric
+ && (is_float($lookupArrayValue) || is_int($lookupArrayValue))
+ && $lookupArrayValue == $lookupValue
+ ) {
+ return $i; // exact match
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return mixed
+ */
+ private static function matchLargestValue(array $lookupArray, mixed $lookupValue, array $keySet)
+ {
+ if (is_string($lookupValue)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $wildcard = WildcardMatch::wildcard($lookupValue);
+ foreach (array_reverse($lookupArray) as $i => $lookupArrayValue) {
+ if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
+ return $i;
+ }
+ }
+ } else {
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if ($lookupArrayValue === $lookupValue) {
+ return $keySet[$i];
+ }
+ }
+ }
+ }
+ $valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if ($valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue))) {
+ if ($lookupArrayValue <= $lookupValue) {
+ return array_search($i, $keySet);
+ }
+ }
+ $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
+ if ($typeMatch && ($lookupArrayValue <= $lookupValue)) {
+ return array_search($i, $keySet);
+ }
+ }
+
+ return null;
+ }
+
+ private static function matchSmallestValue(array $lookupArray, mixed $lookupValue): int|string|null
+ {
+ $valueKey = null;
+ if (is_string($lookupValue)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $wildcard = WildcardMatch::wildcard($lookupValue);
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
+ return $i;
+ }
+ }
+ }
+ }
+
+ $valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
+ // The basic algorithm is:
+ // Iterate and keep the highest match until the next element is smaller than the searched value.
+ // Return immediately if perfect match is found
+ foreach ($lookupArray as $i => $lookupArrayValue) {
+ $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
+ $bothNumeric = $valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue));
+
+ if ($lookupArrayValue === $lookupValue) {
+ // Another "special" case. If a perfect match is found,
+ // the algorithm gives up immediately
+ return $i;
+ }
+ if ($bothNumeric && $lookupValue == $lookupArrayValue) {
+ return $i; // exact match, as above
+ }
+ if (($typeMatch || $bothNumeric) && $lookupArrayValue >= $lookupValue) {
+ $valueKey = $i;
+ } elseif ($typeMatch && $lookupArrayValue < $lookupValue) {
+ //Excel algorithm gives up immediately if the first element is smaller than the searched value
+ break;
+ }
+ }
+
+ return $valueKey;
+ }
+
+ private static function validateLookupValue(mixed $lookupValue): void
+ {
+ // Lookup_value type has to be number, text, or logical values
+ if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
+ throw new Exception(ExcelError::NA());
+ }
+ }
+
+ private static function validateMatchType(mixed $matchType): int
+ {
+ // Match_type is 0, 1 or -1
+ // However Excel accepts other numeric values,
+ // including numeric strings and floats.
+ // It seems to just be interested in the sign.
+ if (!is_numeric($matchType)) {
+ throw new Exception(ExcelError::Value());
+ }
+ if ($matchType > 0) {
+ return self::MATCHTYPE_LARGEST_VALUE;
+ }
+ if ($matchType < 0) {
+ return self::MATCHTYPE_SMALLEST_VALUE;
+ }
+
+ return self::MATCHTYPE_FIRST_VALUE;
+ }
+
+ private static function validateLookupArray(array $lookupArray): void
+ {
+ // Lookup_array should not be empty
+ $lookupArraySize = count($lookupArray);
+ if ($lookupArraySize <= 0) {
+ throw new Exception(ExcelError::NA());
+ }
+ }
+
+ private static function prepareLookupArray(array $lookupArray, mixed $matchType): array
+ {
+ // Lookup_array should contain only number, text, or logical values, or empty (null) cells
+ foreach ($lookupArray as $i => $value) {
+ // check the type of the value
+ if ((!is_numeric($value)) && (!is_string($value)) && (!is_bool($value)) && ($value !== null)) {
+ throw new Exception(ExcelError::NA());
+ }
+ // Convert strings to lowercase for case-insensitive testing
+ if (is_string($value)) {
+ $lookupArray[$i] = StringHelper::strToLower($value);
+ }
+ if (
+ ($value === null)
+ && (($matchType == self::MATCHTYPE_LARGEST_VALUE) || ($matchType == self::MATCHTYPE_SMALLEST_VALUE))
+ ) {
+ unset($lookupArray[$i]);
+ }
+ }
+
+ return $lookupArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
new file mode 100644
index 0000000..920b8e7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
@@ -0,0 +1,75 @@
+ (bool) $matchArray[$index],
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ private static function filterByColumn(array $lookupArray, array $matchArray): array
+ {
+ $lookupArray = Matrix::transpose($lookupArray);
+
+ if (count($matchArray) === 1) {
+ $matchArray = array_pop($matchArray);
+ }
+
+ array_walk(
+ $matchArray,
+ function (&$value): void {
+ $value = [$value];
+ }
+ );
+
+ $result = self::filterByRow($lookupArray, $matchArray);
+
+ return Matrix::transpose($result);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
new file mode 100644
index 0000000..e4990ce
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
@@ -0,0 +1,43 @@
+getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
+ : $cell->getWorksheet();
+
+ if (
+ $worksheet === null
+ || !$worksheet->cellExists($cellReference)
+ || !$worksheet->getCell($cellReference)->isFormula()
+ ) {
+ return ExcelError::NA();
+ }
+
+ return $worksheet->getCell($cellReference)->getValue();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
new file mode 100644
index 0000000..9e09e3d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
@@ -0,0 +1,121 @@
+getMessage();
+ }
+
+ $f = array_keys($lookupArray);
+ $firstRow = reset($f);
+ if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray))) {
+ return ExcelError::REF();
+ }
+
+ $firstkey = $f[0] - 1;
+ $returnColumn = $firstkey + $indexNumber;
+ $firstColumn = array_shift($f) ?? 1;
+ $rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
+
+ if ($rowNumber !== null) {
+ // otherwise return the appropriate value
+ return $lookupArray[$returnColumn][Coordinate::stringFromColumnIndex($rowNumber)];
+ }
+
+ return ExcelError::NA();
+ }
+
+ /**
+ * @param mixed $lookupValue The value that you want to match in lookup_array
+ * @param int|string $column
+ */
+ private static function hLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
+ {
+ $lookupLower = StringHelper::strToLower((string) $lookupValue);
+
+ $rowNumber = null;
+ foreach ($lookupArray[$column] as $rowKey => $rowData) {
+ // break if we have passed possible keys
+ $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
+ $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
+ $cellDataLower = StringHelper::strToLower((string) $rowData);
+
+ if (
+ $notExactMatch
+ && (($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
+ ) {
+ break;
+ }
+
+ $rowNumber = self::checkMatch(
+ $bothNumeric,
+ $bothNotNumeric,
+ $notExactMatch,
+ Coordinate::columnIndexFromString($rowKey),
+ $cellDataLower,
+ $lookupLower,
+ $rowNumber
+ );
+ }
+
+ return $rowNumber;
+ }
+
+ private static function convertLiteralArray(array $lookupArray): array
+ {
+ if (array_key_exists(0, $lookupArray)) {
+ $lookupArray2 = [];
+ $row = 0;
+ foreach ($lookupArray as $arrayVal) {
+ ++$row;
+ if (!is_array($arrayVal)) {
+ $arrayVal = [$arrayVal];
+ }
+ $arrayVal2 = [];
+ foreach ($arrayVal as $key2 => $val2) {
+ $index = Coordinate::stringFromColumnIndex($key2 + 1);
+ $arrayVal2[$index] = $val2;
+ }
+ $lookupArray2[$row] = $arrayVal2;
+ }
+ $lookupArray = $lookupArray2;
+ }
+
+ return $lookupArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
new file mode 100644
index 0000000..191144b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
@@ -0,0 +1,74 @@
+getWorkSheet();
+ $sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle();
+ $value = (string) preg_replace('/^=/', '', $namedRange->getValue());
+ self::adjustSheetTitle($sheetTitle, $value);
+ $cellAddress1 = $sheetTitle . $value;
+ $cellAddress = $cellAddress1;
+ $a1 = self::CELLADDRESS_USE_A1;
+ }
+ if (str_contains($cellAddress, ':')) {
+ [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
+ }
+ $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1, $baseRow, $baseCol);
+
+ return [$cellAddress1, $cellAddress2, $cellAddress];
+ }
+
+ public static function extractWorksheet(string $cellAddress, Cell $cell): array
+ {
+ $sheetName = '';
+ if (str_contains($cellAddress, '!')) {
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $sheetName = trim($sheetName, "'");
+ }
+
+ $worksheet = ($sheetName !== '')
+ ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($sheetName)
+ : $cell->getWorksheet();
+
+ return [$cellAddress, $worksheet, $sheetName];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
new file mode 100644
index 0000000..a199d5b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
@@ -0,0 +1,41 @@
+getHyperlink()->setUrl($linkURL);
+ $cell->getHyperlink()->setTooltip($displayName);
+
+ return $displayName;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
new file mode 100644
index 0000000..aa480ab
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
@@ -0,0 +1,130 @@
+getCoordinate());
+
+ try {
+ $a1 = self::a1Format($a1fmt);
+ $cellAddress = self::validateAddress($cellAddress);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ [$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+ $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+ } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+ $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+ }
+
+ try {
+ [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName, $baseRow, $baseCol);
+ } catch (Exception) {
+ return ExcelError::REF();
+ }
+
+ if (
+ (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches))
+ || (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
+ ) {
+ return ExcelError::REF();
+ }
+
+ return self::extractRequiredCells($worksheet, $cellAddress);
+ }
+
+ /**
+ * Extract range values.
+ *
+ * @return array Array of values in range if range contains more than one element.
+ * Otherwise, a single value is returned.
+ */
+ private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
+ {
+ return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
+ ->extractCellRange($cellAddress, $worksheet, false);
+ }
+
+ private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
+ {
+ // Being lazy, we're only checking a single row/column to get the max
+ if (ctype_digit($start) && $start <= 1048576) {
+ // Max 16,384 columns for Excel2007
+ $endColRef = ($worksheet !== null) ? $worksheet->getHighestDataColumn((int) $start) : AddressRange::MAX_COLUMN;
+
+ return "A{$start}:{$endColRef}{$end}";
+ } elseif (ctype_alpha($start) && strlen($start) <= 3) {
+ // Max 1,048,576 rows for Excel2007
+ $endRowRef = ($worksheet !== null) ? $worksheet->getHighestDataRow($start) : AddressRange::MAX_ROW;
+
+ return "{$start}1:{$end}{$endRowRef}";
+ }
+
+ return "{$start}:{$end}";
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
new file mode 100644
index 0000000..147c982
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
@@ -0,0 +1,106 @@
+ 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
+ $lookupVector = LookupRef\Matrix::transpose($lookupVector);
+ $lookupRows = self::rowCount($lookupVector);
+ $lookupColumns = self::columnCount($lookupVector);
+ }
+
+ $resultVector = self::verifyResultVector($resultVector ?? $lookupVector);
+
+ if ($lookupRows === 2 && !$hasResultVector) {
+ $resultVector = array_pop($lookupVector);
+ $lookupVector = array_shift($lookupVector);
+ }
+
+ if ($lookupColumns !== 2) {
+ $lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
+ }
+
+ return VLookup::lookup($lookupValue, $lookupVector, 2);
+ }
+
+ private static function verifyLookupValues(array $lookupVector, array $resultVector): array
+ {
+ foreach ($lookupVector as &$value) {
+ if (is_array($value)) {
+ $k = array_keys($value);
+ $key1 = $key2 = array_shift($k);
+ ++$key2;
+ $dataValue1 = $value[$key1];
+ } else {
+ $key1 = 0;
+ $key2 = 1;
+ $dataValue1 = $value;
+ }
+
+ $dataValue2 = array_shift($resultVector);
+ if (is_array($dataValue2)) {
+ $dataValue2 = array_shift($dataValue2);
+ }
+ $value = [$key1 => $dataValue1, $key2 => $dataValue2];
+ }
+ unset($value);
+
+ return $lookupVector;
+ }
+
+ private static function verifyResultVector(array $resultVector): array
+ {
+ $resultRows = self::rowCount($resultVector);
+ $resultColumns = self::columnCount($resultVector);
+
+ // we correctly orient our results
+ if ($resultRows === 1 && $resultColumns > 1) {
+ $resultVector = LookupRef\Matrix::transpose($resultVector);
+ }
+
+ return $resultVector;
+ }
+
+ private static function rowCount(array $dataArray): int
+ {
+ return count($dataArray);
+ }
+
+ private static function columnCount(array $dataArray): int
+ {
+ $rowKeys = array_keys($dataArray);
+ $row = array_shift($rowKeys);
+
+ return count($dataArray[$row]);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
new file mode 100644
index 0000000..7d21cce
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
@@ -0,0 +1,64 @@
+ 1
+ && (count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
+ }
+
+ /**
+ * TRANSPOSE.
+ *
+ * @param array|mixed $matrixData A matrix of values
+ */
+ public static function transpose($matrixData): array
+ {
+ $returnMatrix = [];
+ if (!is_array($matrixData)) {
+ $matrixData = [[$matrixData]];
+ }
+
+ $column = 0;
+ foreach ($matrixData as $matrixRow) {
+ $row = 0;
+ foreach ($matrixRow as $matrixCell) {
+ $returnMatrix[$row][$column] = $matrixCell;
+ ++$row;
+ }
+ ++$column;
+ }
+
+ return $returnMatrix;
+ }
+
+ /**
+ * INDEX.
+ *
+ * Uses an index to choose a value from a reference or array
+ *
+ * Excel Function:
+ * =INDEX(range_array, row_num, [column_num], [area_num])
+ *
+ * @param mixed $matrix A range of cells or an array constant
+ * @param mixed $rowNum The row in the array or range from which to return a value.
+ * If row_num is omitted, column_num is required.
+ * Or can be an array of values
+ * @param mixed $columnNum The column in the array or range from which to return a value.
+ * If column_num is omitted, row_num is required.
+ * Or can be an array of values
+ *
+ * TODO Provide support for area_num, currently not supported
+ *
+ * @return mixed the value of a specified cell or array of cells
+ * If an array of values is passed as the $rowNum and/or $columnNum arguments, then the returned result
+ * will also be an array with the same dimensions
+ */
+ public static function index(mixed $matrix, mixed $rowNum = 0, mixed $columnNum = null)
+ {
+ if (is_array($rowNum) || is_array($columnNum)) {
+ return self::evaluateArrayArgumentsSubsetFrom([self::class, __FUNCTION__], 1, $matrix, $rowNum, $columnNum);
+ }
+
+ $rowNum = $rowNum ?? 0;
+ $originalColumnNum = $columnNum;
+ $columnNum = $columnNum ?? 0;
+
+ try {
+ $rowNum = LookupRefValidations::validatePositiveInt($rowNum);
+ $columnNum = LookupRefValidations::validatePositiveInt($columnNum);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (!is_array($matrix) || ($rowNum > count($matrix))) {
+ return ExcelError::REF();
+ }
+
+ $rowKeys = array_keys($matrix);
+ $columnKeys = @array_keys($matrix[$rowKeys[0]]);
+
+ if ($columnNum > count($columnKeys)) {
+ return ExcelError::REF();
+ }
+ if ($originalColumnNum === null && 1 < count($columnKeys)) {
+ return ExcelError::REF();
+ }
+
+ if ($columnNum === 0) {
+ return self::extractRowValue($matrix, $rowKeys, $rowNum);
+ }
+
+ $columnNum = $columnKeys[--$columnNum];
+ if ($rowNum === 0) {
+ return array_map(
+ fn ($value): array => [$value],
+ array_column($matrix, $columnNum)
+ );
+ }
+ $rowNum = $rowKeys[--$rowNum];
+
+ return $matrix[$rowNum][$columnNum];
+ }
+
+ /** @return mixed */
+ private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum)
+ {
+ if ($rowNum === 0) {
+ return $matrix;
+ }
+
+ $rowNum = $rowKeys[--$rowNum];
+ $row = $matrix[$rowNum];
+ if (is_array($row)) {
+ return [$rowNum => $row];
+ }
+
+ return $row;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
new file mode 100644
index 0000000..bb15e19
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
@@ -0,0 +1,148 @@
+getParent() : null)
+ ->extractCellRange($cellAddress, $worksheet, false);
+ }
+
+ private static function extractWorksheet(?string $cellAddress, Cell $cell): array
+ {
+ $cellAddress = self::assessCellAddress($cellAddress ?? '', $cell);
+
+ $sheetName = '';
+ if (str_contains($cellAddress, '!')) {
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $sheetName = trim($sheetName, "'");
+ }
+
+ $worksheet = ($sheetName !== '')
+ ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($sheetName)
+ : $cell->getWorksheet();
+
+ return [$cellAddress, $worksheet];
+ }
+
+ private static function assessCellAddress(string $cellAddress, Cell $cell): string
+ {
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $cellAddress) !== false) {
+ $cellAddress = Functions::expandDefinedName($cellAddress, $cell);
+ }
+
+ return $cellAddress;
+ }
+
+ private static function adjustEndCellColumnForWidth(string $endCellColumn, mixed $width, int $startCellColumn, mixed $columns): int
+ {
+ $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
+ if (($width !== null) && (!is_object($width))) {
+ $endCellColumn = $startCellColumn + (int) $width - 1;
+ } else {
+ $endCellColumn += (int) $columns;
+ }
+
+ return $endCellColumn;
+ }
+
+ private static function adustEndCellRowForHeight(mixed $height, int $startCellRow, mixed $rows, mixed $endCellRow): int
+ {
+ if (($height !== null) && (!is_object($height))) {
+ $endCellRow = $startCellRow + (int) $height - 1;
+ } else {
+ $endCellRow += (int) $rows;
+ }
+
+ return $endCellRow;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
new file mode 100644
index 0000000..ea3ce44
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
@@ -0,0 +1,210 @@
+getColumn()) : 1;
+ }
+
+ /**
+ * COLUMN.
+ *
+ * Returns the column number of the given cell reference
+ * If the cell reference is a range of cells, COLUMN returns the column numbers of each column
+ * in the reference as a horizontal array.
+ * If cell reference is omitted, and the function is being called through the calculation engine,
+ * then it is assumed to be the reference of the cell in which the COLUMN function appears;
+ * otherwise this function returns 1.
+ *
+ * Excel Function:
+ * =COLUMN([cellAddress])
+ *
+ * @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
+ *
+ * @return int|int[]
+ */
+ public static function COLUMN($cellAddress = null, ?Cell $cell = null): int|array
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return self::cellColumn($cell);
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $columnKey => $value) {
+ $columnKey = (string) preg_replace('/[^a-z]/i', '', $columnKey);
+
+ return Coordinate::columnIndexFromString($columnKey);
+ }
+
+ return self::cellColumn($cell);
+ }
+
+ $cellAddress = $cellAddress ?? '';
+ if ($cell != null) {
+ [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
+ }
+ [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $cellAddress ??= '';
+
+ if (str_contains($cellAddress, ':')) {
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
+ $startAddress = (string) preg_replace('/[^a-z]/i', '', $startAddress);
+ $endAddress = (string) preg_replace('/[^a-z]/i', '', $endAddress);
+
+ return range(
+ Coordinate::columnIndexFromString($startAddress),
+ Coordinate::columnIndexFromString($endAddress)
+ );
+ }
+
+ $cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
+
+ return Coordinate::columnIndexFromString($cellAddress);
+ }
+
+ /**
+ * COLUMNS.
+ *
+ * Returns the number of columns in an array or reference.
+ *
+ * Excel Function:
+ * =COLUMNS(cellAddress)
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of columns
+ *
+ * @return int|string The number of columns in cellAddress, or a string if arguments are invalid
+ */
+ public static function COLUMNS($cellAddress = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return 1;
+ }
+ if (!is_array($cellAddress)) {
+ return ExcelError::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $rows;
+ }
+
+ return $columns;
+ }
+
+ private static function cellRow(?Cell $cell): int
+ {
+ return ($cell !== null) ? $cell->getRow() : 1;
+ }
+
+ /**
+ * ROW.
+ *
+ * Returns the row number of the given cell reference
+ * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
+ * as a vertical array.
+ * If cell reference is omitted, and the function is being called through the calculation engine,
+ * then it is assumed to be the reference of the cell in which the ROW function appears;
+ * otherwise this function returns 1.
+ *
+ * Excel Function:
+ * =ROW([cellAddress])
+ *
+ * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
+ *
+ * @return int|mixed[]
+ */
+ public static function ROW($cellAddress = null, ?Cell $cell = null): int|array
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return self::cellRow($cell);
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $rowKey => $rowValue) {
+ foreach ($rowValue as $columnKey => $cellValue) {
+ return (int) preg_replace('/\D/', '', $rowKey);
+ }
+ }
+
+ return self::cellRow($cell);
+ }
+
+ $cellAddress = $cellAddress ?? '';
+ if ($cell !== null) {
+ [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
+ }
+ [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ $cellAddress ??= '';
+ if (str_contains($cellAddress, ':')) {
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
+ $startAddress = (int) (string) preg_replace('/\D/', '', $startAddress);
+ $endAddress = (int) (string) preg_replace('/\D/', '', $endAddress);
+
+ return array_map(
+ fn ($value): array => [$value],
+ range($startAddress, $endAddress)
+ );
+ }
+ [$cellAddress] = explode(':', $cellAddress);
+
+ return (int) preg_replace('/\D/', '', $cellAddress);
+ }
+
+ /**
+ * ROWS.
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * Excel Function:
+ * =ROWS(cellAddress)
+ *
+ * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
+ * for which you want the number of rows
+ *
+ * @return int|string The number of rows in cellAddress, or a string if arguments are invalid
+ */
+ public static function ROWS($cellAddress = null)
+ {
+ if (self::cellAddressNullOrWhitespace($cellAddress)) {
+ return 1;
+ }
+ if (!is_array($cellAddress)) {
+ return ExcelError::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $columns;
+ }
+
+ return $rows;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
new file mode 100644
index 0000000..ae74267
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
@@ -0,0 +1,51 @@
+ $entryCount)) {
+ return ExcelError::VALUE();
+ }
+
+ if (is_array($chooseArgs[$chosenEntry])) {
+ return Functions::flattenArray($chooseArgs[$chosenEntry]);
+ }
+
+ return $chooseArgs[$chosenEntry];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
new file mode 100644
index 0000000..a428174
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
@@ -0,0 +1,312 @@
+getMessage();
+ }
+
+ // We want a simple, enumrated array of arrays where we can reference column by its index number.
+ $sortArray = array_values(array_map('array_values', $sortArray));
+
+ return ($byColumn === true)
+ ? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
+ : self::sortByRow($sortArray, $sortIndex, $sortOrder);
+ }
+
+ /**
+ * SORTBY
+ * The SORTBY function sorts the contents of a range or array based on the values in a corresponding range or array.
+ * The returned array is the same shape as the provided array argument.
+ * Both $sortIndex and $sortOrder can be arrays, to provide multi-level sorting.
+ *
+ * @param mixed $sortArray The range of cells being sorted
+ * @param mixed $args
+ * At least one additional argument must be provided, The vector or range to sort on
+ * After that, arguments are passed as pairs:
+ * sort order: ascending or descending
+ * Ascending = 1 (self::ORDER_ASCENDING)
+ * Descending = -1 (self::ORDER_DESCENDING)
+ * additional arrays or ranges for multi-level sorting
+ *
+ * @return mixed The sorted values from the sort range
+ */
+ public static function sortBy(mixed $sortArray, mixed ...$args)
+ {
+ if (!is_array($sortArray)) {
+ // Scalars are always returned "as is"
+ return $sortArray;
+ }
+
+ $sortArray = self::enumerateArrayKeys($sortArray);
+
+ $lookupArraySize = count($sortArray);
+ $argumentCount = count($args);
+
+ try {
+ $sortBy = $sortOrder = [];
+ for ($i = 0; $i < $argumentCount; $i += 2) {
+ $sortBy[] = self::validateSortVector($args[$i], $lookupArraySize);
+ $sortOrder[] = self::validateSortOrder($args[$i + 1] ?? self::ORDER_ASCENDING);
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::processSortBy($sortArray, $sortBy, $sortOrder);
+ }
+
+ private static function enumerateArrayKeys(array $sortArray): array
+ {
+ array_walk(
+ $sortArray,
+ function (&$columns): void {
+ if (is_array($columns)) {
+ $columns = array_values($columns);
+ }
+ }
+ );
+
+ return array_values($sortArray);
+ }
+
+ private static function validateScalarArgumentsForSort(mixed &$sortIndex, mixed &$sortOrder, int $sortArraySize): void
+ {
+ if (is_array($sortIndex) || is_array($sortOrder)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $sortIndex = self::validatePositiveInt($sortIndex, false);
+
+ if ($sortIndex > $sortArraySize) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $sortOrder = self::validateSortOrder($sortOrder);
+ }
+
+ private static function validateSortVector(mixed $sortVector, int $sortArraySize): array
+ {
+ if (!is_array($sortVector)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ // It doesn't matter if it's a row or a column vectors, it works either way
+ $sortVector = Functions::flattenArray($sortVector);
+ if (count($sortVector) !== $sortArraySize) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $sortVector;
+ }
+
+ private static function validateSortOrder(mixed $sortOrder): int
+ {
+ $sortOrder = self::validateInt($sortOrder);
+ if (($sortOrder == self::ORDER_ASCENDING || $sortOrder === self::ORDER_DESCENDING) === false) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $sortOrder;
+ }
+
+ /**
+ * @param array $sortIndex
+ */
+ private static function validateArrayArgumentsForSort(&$sortIndex, mixed &$sortOrder, int $sortArraySize): void
+ {
+ // It doesn't matter if they're row or column vectors, it works either way
+ $sortIndex = Functions::flattenArray($sortIndex);
+ $sortOrder = Functions::flattenArray($sortOrder);
+
+ if (
+ count($sortOrder) === 0 || count($sortOrder) > $sortArraySize
+ || (count($sortOrder) > count($sortIndex))
+ ) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ if (count($sortIndex) > count($sortOrder)) {
+ // If $sortOrder has fewer elements than $sortIndex, then the last order element is repeated.
+ $sortOrder = array_merge(
+ $sortOrder,
+ array_fill(0, count($sortIndex) - count($sortOrder), array_pop($sortOrder))
+ );
+ }
+
+ foreach ($sortIndex as $key => &$value) {
+ self::validateScalarArgumentsForSort($value, $sortOrder[$key], $sortArraySize);
+ }
+ }
+
+ private static function prepareSortVectorValues(array $sortVector): array
+ {
+ // Strings should be sorted case-insensitive; with booleans converted to locale-strings
+ return array_map(
+ function ($value) {
+ if (is_bool($value)) {
+ return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
+ } elseif (is_string($value)) {
+ return StringHelper::strToLower($value);
+ }
+
+ return $value;
+ },
+ $sortVector
+ );
+ }
+
+ /**
+ * @param array[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function processSortBy(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArguments = [];
+ $sortData = [];
+ foreach ($sortIndex as $index => $sortValues) {
+ $sortData[] = $sortValues;
+ $sortArguments[] = self::prepareSortVectorValues($sortValues);
+ $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+ }
+
+ $sortVector = self::executeVectorSortQuery($sortData, $sortArguments);
+
+ return self::sortLookupArrayFromVector($sortArray, $sortVector);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortVector = self::buildVectorForSort($sortArray, $sortIndex, $sortOrder);
+
+ return self::sortLookupArrayFromVector($sortArray, $sortVector);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArray = Matrix::transpose($sortArray);
+ $result = self::sortByRow($sortArray, $sortIndex, $sortOrder);
+
+ return Matrix::transpose($result);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArguments = [];
+ $sortData = [];
+ foreach ($sortIndex as $index => $sortIndexValue) {
+ $sortValues = array_column($sortArray, $sortIndexValue - 1);
+ $sortData[] = $sortValues;
+ $sortArguments[] = self::prepareSortVectorValues($sortValues);
+ $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+ }
+
+ $sortData = self::executeVectorSortQuery($sortData, $sortArguments);
+
+ return $sortData;
+ }
+
+ private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
+ {
+ $sortData = Matrix::transpose($sortData);
+
+ // We need to set an index that can be retained, as array_multisort doesn't maintain numeric keys.
+ $sortDataIndexed = [];
+ foreach ($sortData as $key => $value) {
+ $sortDataIndexed[Coordinate::stringFromColumnIndex($key + 1)] = $value;
+ }
+ unset($sortData);
+
+ $sortArguments[] = &$sortDataIndexed;
+
+ array_multisort(...$sortArguments);
+
+ // After the sort, we restore the numeric keys that will now be in the correct, sorted order
+ $sortedData = [];
+ foreach (array_keys($sortDataIndexed) as $key) {
+ $sortedData[] = Coordinate::columnIndexFromString($key) - 1;
+ }
+
+ return $sortedData;
+ }
+
+ private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
+ {
+ // Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
+ $sortedArray = [];
+ foreach ($sortVector as $index) {
+ $sortedArray[] = $sortArray[$index];
+ }
+
+ return $sortedArray;
+
+// uksort(
+// $lookupArray,
+// function (int $a, int $b) use (array $sortVector) {
+// return $sortVector[$a] <=> $sortVector[$b];
+// }
+// );
+//
+// return $lookupArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
new file mode 100644
index 0000000..fa048b7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
@@ -0,0 +1,137 @@
+ count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
+ // We're looking at a full column check (multiple rows)
+ $transpose = Matrix::transpose($lookupVector);
+ $result = self::uniqueByRow($transpose, $exactlyOnce);
+
+ return (is_array($result)) ? Matrix::transpose($result) : $result;
+ }
+
+ $result = self::countValuesCaseInsensitive($flattenedLookupVector);
+
+ if ($exactlyOnce === true) {
+ $result = self::exactlyOnceFilter($result);
+ }
+
+ if (count($result) === 0) {
+ return ExcelError::CALC();
+ }
+
+ $result = array_keys($result);
+
+ return $result;
+ }
+
+ private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
+ {
+ $caseInsensitiveCounts = array_count_values(
+ array_map(
+ fn (string $value): string => StringHelper::strToUpper($value),
+ $caseSensitiveLookupValues
+ )
+ );
+
+ $caseSensitiveCounts = [];
+ foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
+ if (is_numeric($caseInsensitiveKey)) {
+ $caseSensitiveCounts[$caseInsensitiveKey] = $count;
+ } else {
+ foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
+ if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
+ $caseSensitiveCounts[$caseSensitiveValue] = $count;
+
+ break;
+ }
+ }
+ }
+ }
+
+ return $caseSensitiveCounts;
+ }
+
+ private static function exactlyOnceFilter(array $values): array
+ {
+ return array_filter(
+ $values,
+ fn ($value): bool => $value === 1
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
new file mode 100644
index 0000000..a0e375d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
@@ -0,0 +1,117 @@
+getMessage();
+ }
+
+ $f = array_keys($lookupArray);
+ $firstRow = array_pop($f);
+ if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
+ return ExcelError::REF();
+ }
+ $columnKeys = array_keys($lookupArray[$firstRow]);
+ $returnColumn = $columnKeys[--$indexNumber];
+ $firstColumn = array_shift($columnKeys) ?? 1;
+
+ if (!$notExactMatch) {
+ /** @var callable */
+ $callable = [self::class, 'vlookupSort'];
+ uasort($lookupArray, $callable);
+ }
+
+ $rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
+
+ if ($rowNumber !== null) {
+ // return the appropriate value
+ return $lookupArray[$rowNumber][$returnColumn];
+ }
+
+ return ExcelError::NA();
+ }
+
+ private static function vlookupSort(array $a, array $b): int
+ {
+ reset($a);
+ $firstColumn = key($a);
+ $aLower = StringHelper::strToLower((string) $a[$firstColumn]);
+ $bLower = StringHelper::strToLower((string) $b[$firstColumn]);
+
+ if ($aLower == $bLower) {
+ return 0;
+ }
+
+ return ($aLower < $bLower) ? -1 : 1;
+ }
+
+ /**
+ * @param mixed $lookupValue The value that you want to match in lookup_array
+ * @param int|string $column
+ */
+ private static function vLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
+ {
+ $lookupLower = StringHelper::strToLower((string) $lookupValue);
+
+ $rowNumber = null;
+ foreach ($lookupArray as $rowKey => $rowData) {
+ $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
+ $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
+ $cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
+
+ // break if we have passed possible keys
+ if (
+ $notExactMatch
+ && (($bothNumeric && ($rowData[$column] > $lookupValue))
+ || ($bothNotNumeric && ($cellDataLower > $lookupLower)))
+ ) {
+ break;
+ }
+
+ $rowNumber = self::checkMatch(
+ $bothNumeric,
+ $bothNotNumeric,
+ $notExactMatch,
+ $rowKey,
+ $cellDataLower,
+ $lookupLower,
+ $rowNumber
+ );
+ }
+
+ return $rowNumber;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
new file mode 100644
index 0000000..58a56a0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
@@ -0,0 +1,1440 @@
+getMessage();
+ }
+
+ return abs($number);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
new file mode 100644
index 0000000..e7de7aa
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
@@ -0,0 +1,63 @@
+getMessage();
+ }
+
+ return rad2deg($number);
+ }
+
+ /**
+ * RADIANS.
+ *
+ * Returns the result of builtin function deg2rad after validating args.
+ *
+ * @param mixed $number Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string Rounded number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function toRadians(mixed $number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return deg2rad($number);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
new file mode 100644
index 0000000..3c32476
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
@@ -0,0 +1,97 @@
+ 1000,
+ 'D' => 500,
+ 'C' => 100,
+ 'L' => 50,
+ 'X' => 10,
+ 'V' => 5,
+ 'I' => 1,
+ ];
+
+ /**
+ * Recursively calculate the arabic value of a roman numeral.
+ *
+ * @param int $sum
+ * @param int $subtract
+ *
+ * @return int
+ */
+ private static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
+ {
+ $numeral = array_shift($roman);
+ if (!isset(self::ROMAN_LOOKUP[$numeral])) {
+ throw new Exception('Invalid character detected');
+ }
+
+ $arabic = self::ROMAN_LOOKUP[$numeral];
+ if (count($roman) > 0 && isset(self::ROMAN_LOOKUP[$roman[0]]) && $arabic < self::ROMAN_LOOKUP[$roman[0]]) {
+ $subtract += $arabic;
+ } else {
+ $sum += ($arabic - $subtract);
+ $subtract = 0;
+ }
+
+ if (count($roman) > 0) {
+ self::calculateArabic($roman, $sum, $subtract);
+ }
+
+ return $sum;
+ }
+
+ /**
+ * ARABIC.
+ *
+ * Converts a Roman numeral to an Arabic numeral.
+ *
+ * Excel Function:
+ * ARABIC(text)
+ *
+ * @param mixed $roman Should be a string, or can be an array of strings
+ *
+ * @return array|int|string the arabic numberal contrived from the roman numeral
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function evaluate(mixed $roman)
+ {
+ if (is_array($roman)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $roman);
+ }
+
+ // An empty string should return 0
+ $roman = substr(trim(strtoupper((string) $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) {
+ return ExcelError::VALUE(); // Invalid character detected
+ }
+
+ if ($negativeNumber) {
+ $arabic *= -1; // The number should be negative
+ }
+
+ return $arabic;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
new file mode 100644
index 0000000..7eda72c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
@@ -0,0 +1,65 @@
+getMessage();
+ }
+
+ return self::calculate($number, $radix, $minLength);
+ }
+
+ private static function calculate(float $number, int $radix, mixed $minLength): string
+ {
+ if ($minLength === null || is_numeric($minLength)) {
+ if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
+ return ExcelError::NAN(); // Numeric range constraints
+ }
+
+ $outcome = strtoupper((string) base_convert("$number", 10, $radix));
+ if ($minLength !== null) {
+ $outcome = str_pad($outcome, (int) $minLength, '0', STR_PAD_LEFT); // String padding
+ }
+
+ return $outcome;
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
new file mode 100644
index 0000000..365ec2e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
@@ -0,0 +1,165 @@
+getMessage();
+ }
+
+ return self::argumentsOk((float) $number, (float) $significance);
+ }
+
+ /**
+ * CEILING.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * CEILING.MATH(number[,significance[,mode]])
+ *
+ * @param mixed $number Number to round
+ * Or can be an array of values
+ * @param mixed $significance Significance
+ * Or can be an array of values
+ * @param array|int $mode direction to round negative numbers
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function math(mixed $number, mixed $significance = null, $mode = 0): array|string|float
+ {
+ if (is_array($number) || is_array($significance) || is_array($mode)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
+ $mode = Helpers::validateNumericNullSubstitution($mode, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (empty($significance * $number)) {
+ return 0.0;
+ }
+ if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
+ return floor($number / $significance) * $significance;
+ }
+
+ return ceil($number / $significance) * $significance;
+ }
+
+ /**
+ * CEILING.PRECISE.
+ *
+ * Rounds number up, away from zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * CEILING.PRECISE(number[,significance])
+ *
+ * @param mixed $number the number you want to round
+ * Or can be an array of values
+ * @param array|float $significance the multiple to which you want to round
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function precise(mixed $number, $significance = 1): array|string|float
+ {
+ if (is_array($number) || is_array($significance)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (!$significance) {
+ return 0.0;
+ }
+ $result = $number / abs($significance);
+
+ return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
+ }
+
+ /**
+ * Let CEILINGMATH complexity pass Scrutinizer.
+ */
+ private static function ceilingMathTest(float $significance, float $number, int $mode): bool
+ {
+ return ($significance < 0) || ($number < 0 && !empty($mode));
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ */
+ private static function argumentsOk(float $number, float $significance): float|string
+ {
+ if (empty($number * $significance)) {
+ return 0.0;
+ }
+ if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ private static function floorCheck1Arg(): void
+ {
+ $compatibility = Functions::getCompatibilityMode();
+ if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
+ throw new Exception('Excel requires 2 arguments for CEILING');
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
new file mode 100644
index 0000000..170b4b2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
@@ -0,0 +1,91 @@
+getMessage();
+ }
+
+ return round(Factorial::fact($numObjs) / Factorial::fact($numObjs - $numInSet)) / Factorial::fact($numInSet); // @phpstan-ignore-line
+ }
+
+ /**
+ * COMBINA.
+ *
+ * Returns the number of combinations for a given number of items. Use COMBIN to
+ * determine the total possible number of groups for a given number of items.
+ *
+ * Excel Function:
+ * COMBINA(numObjs,numInSet)
+ *
+ * @param mixed $numObjs Number of different objects, or can be an array of numbers
+ * @param mixed $numInSet Number of objects in each combination, or can be an array of numbers
+ *
+ * @return array|float|int|string Number of combinations, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function withRepetition(mixed $numObjs, mixed $numInSet): array|int|string|float
+ {
+ if (is_array($numObjs) || is_array($numInSet)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
+ }
+
+ try {
+ $numObjs = Helpers::validateNumericNullSubstitution($numObjs, null);
+ $numInSet = Helpers::validateNumericNullSubstitution($numInSet, null);
+ Helpers::validateNotNegative($numInSet);
+ Helpers::validateNotNegative($numObjs);
+ $numObjs = (int) $numObjs;
+ $numInSet = (int) $numInSet;
+ // Microsoft documentation says following is true, but Excel
+ // does not enforce this restriction.
+ //Helpers::validateNotNegative($numObjs - $numInSet);
+ if ($numObjs === 0) {
+ Helpers::validateNotNegative(-$numInSet);
+
+ return 1;
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return round(
+ Factorial::fact($numObjs + $numInSet - 1) / Factorial::fact($numObjs - 1) // @phpstan-ignore-line
+ ) / Factorial::fact($numInSet);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
new file mode 100644
index 0000000..ea67d5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
@@ -0,0 +1,37 @@
+getMessage();
+ }
+
+ return exp($number);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
new file mode 100644
index 0000000..02143b0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
@@ -0,0 +1,126 @@
+getMessage();
+ }
+
+ $factLoop = floor($factVal);
+ if ($factVal > $factLoop) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return Statistical\Distributions\Gamma::gammaValue($factVal + 1);
+ }
+ }
+
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ }
+
+ return $factorial;
+ }
+
+ /**
+ * FACTDOUBLE.
+ *
+ * Returns the double factorial of a number.
+ *
+ * Excel Function:
+ * FACTDOUBLE(factVal)
+ *
+ * @param array|float $factVal Factorial Value, or can be an array of numbers
+ *
+ * @return array|float|int|string Double Factorial, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function factDouble($factVal): array|string|float|int
+ {
+ if (is_array($factVal)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $factVal);
+ }
+
+ try {
+ $factVal = Helpers::validateNumericNullSubstitution($factVal, 0);
+ Helpers::validateNotNegative($factVal);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $factLoop = floor($factVal);
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop;
+ $factLoop -= 2;
+ }
+
+ return $factorial;
+ }
+
+ /**
+ * MULTINOMIAL.
+ *
+ * Returns the ratio of the factorial of a sum of values to the product of factorials.
+ *
+ * @param mixed[] $args An array of mixed values for the Data Series
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function multinomial(...$args): string|int|float
+ {
+ $summer = 0;
+ $divisor = 1;
+
+ try {
+ // Loop through arguments
+ foreach (Functions::flattenArray($args) as $argx) {
+ $arg = Helpers::validateNumericNullSubstitution($argx, null);
+ Helpers::validateNotNegative($arg);
+ $arg = (int) $arg;
+ $summer += $arg;
+ $divisor *= self::fact($arg);
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $summer = self::fact($summer);
+
+ return is_numeric($summer) ? ($summer / $divisor) : ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
new file mode 100644
index 0000000..83cf051
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
@@ -0,0 +1,191 @@
+getMessage();
+ }
+
+ return self::argumentsOk((float) $number, (float) $significance);
+ }
+
+ /**
+ * FLOOR.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.MATH(number[,significance[,mode]])
+ *
+ * @param mixed $number Number to round
+ * Or can be an array of values
+ * @param mixed $significance Significance
+ * Or can be an array of values
+ * @param mixed $mode direction to round negative numbers
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function math(mixed $number, mixed $significance = null, mixed $mode = 0)
+ {
+ if (is_array($number) || is_array($significance) || is_array($mode)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
+ $mode = Helpers::validateNumericNullSubstitution($mode, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::argsOk((float) $number, (float) $significance, (int) $mode);
+ }
+
+ /**
+ * FLOOR.PRECISE.
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.PRECISE(number[,significance])
+ *
+ * @param array|float $number Number to round
+ * Or can be an array of values
+ * @param array|float $significance Significance
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function precise($number, $significance = 1)
+ {
+ if (is_array($number) || is_array($significance)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $significance = Helpers::validateNumericNullSubstitution($significance, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::argumentsOkPrecise((float) $number, (float) $significance);
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ */
+ private static function argumentsOkPrecise(float $number, float $significance): string|float
+ {
+ if ($significance == 0.0) {
+ return ExcelError::DIV0();
+ }
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ return floor($number / abs($significance)) * abs($significance);
+ }
+
+ /**
+ * Avoid Scrutinizer complexity problems.
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ private static function argsOk(float $number, float $significance, int $mode): string|float
+ {
+ if (!$significance) {
+ return ExcelError::DIV0();
+ }
+ if (!$number) {
+ return 0.0;
+ }
+ if (self::floorMathTest($number, $significance, $mode)) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return floor($number / $significance) * $significance;
+ }
+
+ /**
+ * Let FLOORMATH complexity pass Scrutinizer.
+ */
+ private static function floorMathTest(float $number, float $significance, int $mode): bool
+ {
+ return Helpers::returnSign($significance) == -1 || (Helpers::returnSign($number) == -1 && !empty($mode));
+ }
+
+ /**
+ * Avoid Scrutinizer problems concerning complexity.
+ */
+ private static function argumentsOk(float $number, float $significance): string|float
+ {
+ if ($significance == 0.0) {
+ return ExcelError::DIV0();
+ }
+ if ($number == 0.0) {
+ return 0.0;
+ }
+ if (Helpers::returnSign($significance) == 1) {
+ return floor($number / $significance) * $significance;
+ }
+ if (Helpers::returnSign($number) == -1 && Helpers::returnSign($significance) == -1) {
+ return floor($number / $significance) * $significance;
+ }
+
+ return ExcelError::NAN();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
new file mode 100644
index 0000000..6641ac6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
@@ -0,0 +1,70 @@
+getMessage();
+ }
+
+ if (count($arrayArgs) <= 0) {
+ return ExcelError::VALUE();
+ }
+ $gcd = (int) array_pop($arrayArgs);
+ do {
+ $gcd = self::evaluateGCD($gcd, (int) array_pop($arrayArgs));
+ } while (!empty($arrayArgs));
+
+ return $gcd;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
new file mode 100644
index 0000000..28b9711
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
@@ -0,0 +1,125 @@
+= 0.
+ *
+ * @param float|int $number
+ */
+ public static function validateNotNegative($number, ?string $except = null): void
+ {
+ if ($number >= 0) {
+ return;
+ }
+
+ throw new Exception($except ?? ExcelError::NAN());
+ }
+
+ /**
+ * Confirm number > 0.
+ *
+ * @param float|int $number
+ */
+ public static function validatePositive($number, ?string $except = null): void
+ {
+ if ($number > 0) {
+ return;
+ }
+
+ throw new Exception($except ?? ExcelError::NAN());
+ }
+
+ /**
+ * Confirm number != 0.
+ *
+ * @param float|int $number
+ */
+ public static function validateNotZero($number): void
+ {
+ if ($number) {
+ return;
+ }
+
+ throw new Exception(ExcelError::DIV0());
+ }
+
+ public static function returnSign(float $number): int
+ {
+ return $number ? (($number > 0) ? 1 : -1) : 0;
+ }
+
+ public static function getEven(float $number): float
+ {
+ $significance = 2 * self::returnSign($number);
+
+ return $significance ? (ceil($number / $significance) * $significance) : 0;
+ }
+
+ /**
+ * Return NAN or value depending on argument.
+ *
+ * @param float $result Number
+ *
+ * @return float|string
+ */
+ public static function numberOrNan($result)
+ {
+ return is_nan($result) ? ExcelError::NAN() : $result;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
new file mode 100644
index 0000000..76bbced
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
@@ -0,0 +1,40 @@
+getMessage();
+ }
+
+ return (int) floor($number);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
new file mode 100644
index 0000000..979b6df
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
@@ -0,0 +1,111 @@
+ 1; --$i) {
+ if (($value % $i) == 0) {
+ $factorArray = array_merge($factorArray, self::factors($value / $i));
+ $factorArray = array_merge($factorArray, self::factors($i));
+ if ($i <= sqrt($value)) {
+ break;
+ }
+ }
+ }
+ if (!empty($factorArray)) {
+ rsort($factorArray);
+
+ return $factorArray;
+ }
+
+ return [(int) $value];
+ }
+
+ /**
+ * LCM.
+ *
+ * Returns the lowest common multiplier of a series of numbers
+ * The least common multiple is the smallest positive integer that is a multiple
+ * of all integer arguments number1, number2, and so on. Use LCM to add fractions
+ * with different denominators.
+ *
+ * Excel Function:
+ * LCM(number1[,number2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return int|string Lowest Common Multiplier, or a string containing an error
+ */
+ public static function evaluate(mixed ...$args): int|string
+ {
+ try {
+ $arrayArgs = [];
+ $anyZeros = 0;
+ $anyNonNulls = 0;
+ foreach (Functions::flattenArray($args) as $value1) {
+ $anyNonNulls += (int) ($value1 !== null);
+ $value = Helpers::validateNumericNullSubstitution($value1, 1);
+ Helpers::validateNotNegative($value);
+ $arrayArgs[] = (int) $value;
+ $anyZeros += (int) !((bool) $value);
+ }
+ self::testNonNulls($anyNonNulls);
+ if ($anyZeros) {
+ return 0;
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $returnValue = 1;
+ $allPoweredFactors = [];
+ // Loop through arguments
+ foreach ($arrayArgs as $value) {
+ $myFactors = self::factors(floor($value));
+ $myCountedFactors = array_count_values($myFactors);
+ $myPoweredFactors = [];
+ foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
+ $myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
+ }
+ self::processPoweredFactors($allPoweredFactors, $myPoweredFactors);
+ }
+ foreach ($allPoweredFactors as $allPoweredFactor) {
+ $returnValue *= (int) $allPoweredFactor;
+ }
+
+ return $returnValue;
+ }
+
+ private static function processPoweredFactors(array &$allPoweredFactors, array &$myPoweredFactors): void
+ {
+ foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
+ if (isset($allPoweredFactors[$myPoweredValue])) {
+ if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ } else {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ }
+ }
+
+ private static function testNonNulls(int $anyNonNulls): void
+ {
+ if (!$anyNonNulls) {
+ throw new Exception(ExcelError::VALUE());
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
new file mode 100644
index 0000000..3de0a2b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
@@ -0,0 +1,102 @@
+getMessage();
+ }
+
+ return log($number, $base);
+ }
+
+ /**
+ * LOG10.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @param mixed $number Should be numeric
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded number
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function base10(mixed $number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ Helpers::validatePositive($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return log10($number);
+ }
+
+ /**
+ * LN.
+ *
+ * Returns the result of builtin function log after validating args.
+ *
+ * @param mixed $number Should be numeric
+ * Or can be an array of values
+ *
+ * @return array|float|string Rounded number
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function natural(mixed $number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ Helpers::validatePositive($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return log($number);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
new file mode 100644
index 0000000..fad0108
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
@@ -0,0 +1,179 @@
+getMessage();
+ }
+
+ if ($step === 0) {
+ return array_chunk(
+ array_fill(0, $rows * $columns, $start),
+ max($columns, 1)
+ );
+ }
+
+ return array_chunk(
+ range($start, $start + (($rows * $columns - 1) * $step), $step),
+ max($columns, 1)
+ );
+ }
+
+ /**
+ * MDETERM.
+ *
+ * Returns the matrix determinant of an array.
+ *
+ * Excel Function:
+ * MDETERM(array)
+ *
+ * @param mixed $matrixValues A matrix of values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function determinant(mixed $matrixValues)
+ {
+ try {
+ $matrix = self::getMatrix($matrixValues);
+
+ return $matrix->determinant();
+ } catch (MatrixException) {
+ return ExcelError::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MINVERSE.
+ *
+ * Returns the inverse matrix for the matrix stored in an array.
+ *
+ * Excel Function:
+ * MINVERSE(array)
+ *
+ * @param mixed $matrixValues A matrix of values
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function inverse(mixed $matrixValues): array|string
+ {
+ try {
+ $matrix = self::getMatrix($matrixValues);
+
+ return $matrix->inverse()->toArray();
+ } catch (MatrixDiv0Exception) {
+ return ExcelError::NAN();
+ } catch (MatrixException) {
+ return ExcelError::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MMULT.
+ *
+ * @param mixed $matrixData1 A matrix of values
+ * @param mixed $matrixData2 A matrix of values
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function multiply(mixed $matrixData1, mixed $matrixData2): array|string
+ {
+ try {
+ $matrixA = self::getMatrix($matrixData1);
+ $matrixB = self::getMatrix($matrixData2);
+
+ return $matrixA->multiply($matrixB)->toArray();
+ } catch (MatrixException) {
+ return ExcelError::VALUE();
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+
+ /**
+ * MUnit.
+ *
+ * @param mixed $dimension Number of rows and columns
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function identity(mixed $dimension)
+ {
+ try {
+ $dimension = (int) Helpers::validateNumericNullBool($dimension);
+ Helpers::validatePositive($dimension, ExcelError::VALUE());
+ $matrix = Builder::createIdentityMatrix($dimension, 0)->toArray();
+
+ return $matrix;
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
new file mode 100644
index 0000000..5a90731
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
@@ -0,0 +1,158 @@
+getMessage();
+ }
+
+ if (($dividend < 0.0) && ($divisor > 0.0)) {
+ return $divisor - fmod(abs($dividend), $divisor);
+ }
+ if (($dividend > 0.0) && ($divisor < 0.0)) {
+ return $divisor + fmod($dividend, abs($divisor));
+ }
+
+ return fmod($dividend, $divisor);
+ }
+
+ /**
+ * POWER.
+ *
+ * Computes x raised to the power y.
+ *
+ * @param array|float|int $x
+ * Or can be an array of values
+ * @param array|float|int $y
+ * Or can be an array of values
+ *
+ * @return array|float|int|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function power($x, $y)
+ {
+ if (is_array($x) || is_array($y)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $y);
+ }
+
+ try {
+ $x = Helpers::validateNumericNullBool($x);
+ $y = Helpers::validateNumericNullBool($y);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Validate parameters
+ if (!$x && !$y) {
+ return ExcelError::NAN();
+ }
+ if (!$x && $y < 0.0) {
+ return ExcelError::DIV0();
+ }
+
+ // Return
+ $result = $x ** $y;
+
+ return Helpers::numberOrNan($result);
+ }
+
+ /**
+ * PRODUCT.
+ *
+ * PRODUCT returns the product of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * PRODUCT(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ */
+ public static function product(mixed ...$args): string|float
+ {
+ $args = array_filter(
+ Functions::flattenArray($args),
+ fn ($value): bool => $value !== null
+ );
+
+ // Return value
+ $returnValue = (count($args) === 0) ? 0.0 : 1.0;
+
+ // Loop through arguments
+ foreach ($args as $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ $returnValue *= $arg;
+ } else {
+ return ExcelError::throwError($arg);
+ }
+ }
+
+ return (float) $returnValue;
+ }
+
+ /**
+ * QUOTIENT.
+ *
+ * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
+ * and denominator is the divisor.
+ *
+ * Excel Function:
+ * QUOTIENT(value1,value2)
+ *
+ * @param mixed $numerator Expect float|int
+ * Or can be an array of values
+ * @param mixed $denominator Expect float|int
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function quotient(mixed $numerator, mixed $denominator): array|string|int
+ {
+ if (is_array($numerator) || is_array($denominator)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $numerator, $denominator);
+ }
+
+ try {
+ $numerator = Helpers::validateNumericNullSubstitution($numerator, 0);
+ $denominator = Helpers::validateNumericNullSubstitution($denominator, 0);
+ Helpers::validateNotZero($denominator);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (int) ($numerator / $denominator);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
new file mode 100644
index 0000000..055872b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
@@ -0,0 +1,99 @@
+getMessage();
+ }
+
+ return mt_rand($min, $max);
+ }
+
+ /**
+ * RANDARRAY.
+ *
+ * Generates a list of sequential numbers in an array.
+ *
+ * Excel Function:
+ * RANDARRAY([rows],[columns],[start],[step])
+ *
+ * @param mixed $rows the number of rows to return, defaults to 1
+ * @param mixed $columns the number of columns to return, defaults to 1
+ * @param mixed $min the minimum number to be returned, defaults to 0
+ * @param mixed $max the maximum number to be returned, defaults to 1
+ * @param bool $wholeNumber the type of numbers to return:
+ * False - Decimal numbers to 15 decimal places. (default)
+ * True - Whole (integer) numbers
+ *
+ * @return array|string The resulting array, or a string containing an error
+ */
+ public static function randArray(mixed $rows = 1, mixed $columns = 1, mixed $min = 0, mixed $max = 1, $wholeNumber = false): string|array
+ {
+ try {
+ $rows = (int) Helpers::validateNumericNullSubstitution($rows, 1);
+ Helpers::validatePositive($rows);
+ $columns = (int) Helpers::validateNumericNullSubstitution($columns, 1);
+ Helpers::validatePositive($columns);
+ $min = Helpers::validateNumericNullSubstitution($min, 1);
+ $max = Helpers::validateNumericNullSubstitution($max, 1);
+
+ if ($max <= $min) {
+ return ExcelError::VALUE();
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return array_chunk(
+ array_map(
+ function () use ($min, $max, $wholeNumber): int|float {
+ return $wholeNumber
+ ? mt_rand((int) $min, (int) $max)
+ : (mt_rand() / mt_getrandmax()) * ($max - $min) + $min;
+ },
+ array_fill(0, $rows * $columns, $min)
+ ),
+ max($columns, 1)
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
new file mode 100644
index 0000000..7c6f8e7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
@@ -0,0 +1,846 @@
+ ['VL'],
+ 46 => ['VLI'],
+ 47 => ['VLII'],
+ 48 => ['VLIII'],
+ 49 => ['VLIV', 'IL'],
+ 95 => ['VC'],
+ 96 => ['VCI'],
+ 97 => ['VCII'],
+ 98 => ['VCIII'],
+ 99 => ['VCIV', 'IC'],
+ 145 => ['CVL'],
+ 146 => ['CVLI'],
+ 147 => ['CVLII'],
+ 148 => ['CVLIII'],
+ 149 => ['CVLIV', 'CIL'],
+ 195 => ['CVC'],
+ 196 => ['CVCI'],
+ 197 => ['CVCII'],
+ 198 => ['CVCIII'],
+ 199 => ['CVCIV', 'CIC'],
+ 245 => ['CCVL'],
+ 246 => ['CCVLI'],
+ 247 => ['CCVLII'],
+ 248 => ['CCVLIII'],
+ 249 => ['CCVLIV', 'CCIL'],
+ 295 => ['CCVC'],
+ 296 => ['CCVCI'],
+ 297 => ['CCVCII'],
+ 298 => ['CCVCIII'],
+ 299 => ['CCVCIV', 'CCIC'],
+ 345 => ['CCCVL'],
+ 346 => ['CCCVLI'],
+ 347 => ['CCCVLII'],
+ 348 => ['CCCVLIII'],
+ 349 => ['CCCVLIV', 'CCCIL'],
+ 395 => ['CCCVC'],
+ 396 => ['CCCVCI'],
+ 397 => ['CCCVCII'],
+ 398 => ['CCCVCIII'],
+ 399 => ['CCCVCIV', 'CCCIC'],
+ 445 => ['CDVL'],
+ 446 => ['CDVLI'],
+ 447 => ['CDVLII'],
+ 448 => ['CDVLIII'],
+ 449 => ['CDVLIV', 'CDIL'],
+ 450 => ['LD'],
+ 451 => ['LDI'],
+ 452 => ['LDII'],
+ 453 => ['LDIII'],
+ 454 => ['LDIV'],
+ 455 => ['LDV'],
+ 456 => ['LDVI'],
+ 457 => ['LDVII'],
+ 458 => ['LDVIII'],
+ 459 => ['LDIX'],
+ 460 => ['LDX'],
+ 461 => ['LDXI'],
+ 462 => ['LDXII'],
+ 463 => ['LDXIII'],
+ 464 => ['LDXIV'],
+ 465 => ['LDXV'],
+ 466 => ['LDXVI'],
+ 467 => ['LDXVII'],
+ 468 => ['LDXVIII'],
+ 469 => ['LDXIX'],
+ 470 => ['LDXX'],
+ 471 => ['LDXXI'],
+ 472 => ['LDXXII'],
+ 473 => ['LDXXIII'],
+ 474 => ['LDXXIV'],
+ 475 => ['LDXXV'],
+ 476 => ['LDXXVI'],
+ 477 => ['LDXXVII'],
+ 478 => ['LDXXVIII'],
+ 479 => ['LDXXIX'],
+ 480 => ['LDXXX'],
+ 481 => ['LDXXXI'],
+ 482 => ['LDXXXII'],
+ 483 => ['LDXXXIII'],
+ 484 => ['LDXXXIV'],
+ 485 => ['LDXXXV'],
+ 486 => ['LDXXXVI'],
+ 487 => ['LDXXXVII'],
+ 488 => ['LDXXXVIII'],
+ 489 => ['LDXXXIX'],
+ 490 => ['LDXL', 'XD'],
+ 491 => ['LDXLI', 'XDI'],
+ 492 => ['LDXLII', 'XDII'],
+ 493 => ['LDXLIII', 'XDIII'],
+ 494 => ['LDXLIV', 'XDIV'],
+ 495 => ['LDVL', 'XDV', 'VD'],
+ 496 => ['LDVLI', 'XDVI', 'VDI'],
+ 497 => ['LDVLII', 'XDVII', 'VDII'],
+ 498 => ['LDVLIII', 'XDVIII', 'VDIII'],
+ 499 => ['LDVLIV', 'XDIX', 'VDIV', 'ID'],
+ 545 => ['DVL'],
+ 546 => ['DVLI'],
+ 547 => ['DVLII'],
+ 548 => ['DVLIII'],
+ 549 => ['DVLIV', 'DIL'],
+ 595 => ['DVC'],
+ 596 => ['DVCI'],
+ 597 => ['DVCII'],
+ 598 => ['DVCIII'],
+ 599 => ['DVCIV', 'DIC'],
+ 645 => ['DCVL'],
+ 646 => ['DCVLI'],
+ 647 => ['DCVLII'],
+ 648 => ['DCVLIII'],
+ 649 => ['DCVLIV', 'DCIL'],
+ 695 => ['DCVC'],
+ 696 => ['DCVCI'],
+ 697 => ['DCVCII'],
+ 698 => ['DCVCIII'],
+ 699 => ['DCVCIV', 'DCIC'],
+ 745 => ['DCCVL'],
+ 746 => ['DCCVLI'],
+ 747 => ['DCCVLII'],
+ 748 => ['DCCVLIII'],
+ 749 => ['DCCVLIV', 'DCCIL'],
+ 795 => ['DCCVC'],
+ 796 => ['DCCVCI'],
+ 797 => ['DCCVCII'],
+ 798 => ['DCCVCIII'],
+ 799 => ['DCCVCIV', 'DCCIC'],
+ 845 => ['DCCCVL'],
+ 846 => ['DCCCVLI'],
+ 847 => ['DCCCVLII'],
+ 848 => ['DCCCVLIII'],
+ 849 => ['DCCCVLIV', 'DCCCIL'],
+ 895 => ['DCCCVC'],
+ 896 => ['DCCCVCI'],
+ 897 => ['DCCCVCII'],
+ 898 => ['DCCCVCIII'],
+ 899 => ['DCCCVCIV', 'DCCCIC'],
+ 945 => ['CMVL'],
+ 946 => ['CMVLI'],
+ 947 => ['CMVLII'],
+ 948 => ['CMVLIII'],
+ 949 => ['CMVLIV', 'CMIL'],
+ 950 => ['LM'],
+ 951 => ['LMI'],
+ 952 => ['LMII'],
+ 953 => ['LMIII'],
+ 954 => ['LMIV'],
+ 955 => ['LMV'],
+ 956 => ['LMVI'],
+ 957 => ['LMVII'],
+ 958 => ['LMVIII'],
+ 959 => ['LMIX'],
+ 960 => ['LMX'],
+ 961 => ['LMXI'],
+ 962 => ['LMXII'],
+ 963 => ['LMXIII'],
+ 964 => ['LMXIV'],
+ 965 => ['LMXV'],
+ 966 => ['LMXVI'],
+ 967 => ['LMXVII'],
+ 968 => ['LMXVIII'],
+ 969 => ['LMXIX'],
+ 970 => ['LMXX'],
+ 971 => ['LMXXI'],
+ 972 => ['LMXXII'],
+ 973 => ['LMXXIII'],
+ 974 => ['LMXXIV'],
+ 975 => ['LMXXV'],
+ 976 => ['LMXXVI'],
+ 977 => ['LMXXVII'],
+ 978 => ['LMXXVIII'],
+ 979 => ['LMXXIX'],
+ 980 => ['LMXXX'],
+ 981 => ['LMXXXI'],
+ 982 => ['LMXXXII'],
+ 983 => ['LMXXXIII'],
+ 984 => ['LMXXXIV'],
+ 985 => ['LMXXXV'],
+ 986 => ['LMXXXVI'],
+ 987 => ['LMXXXVII'],
+ 988 => ['LMXXXVIII'],
+ 989 => ['LMXXXIX'],
+ 990 => ['LMXL', 'XM'],
+ 991 => ['LMXLI', 'XMI'],
+ 992 => ['LMXLII', 'XMII'],
+ 993 => ['LMXLIII', 'XMIII'],
+ 994 => ['LMXLIV', 'XMIV'],
+ 995 => ['LMVL', 'XMV', 'VM'],
+ 996 => ['LMVLI', 'XMVI', 'VMI'],
+ 997 => ['LMVLII', 'XMVII', 'VMII'],
+ 998 => ['LMVLIII', 'XMVIII', 'VMIII'],
+ 999 => ['LMVLIV', 'XMIX', 'VMIV', 'IM'],
+ 1045 => ['MVL'],
+ 1046 => ['MVLI'],
+ 1047 => ['MVLII'],
+ 1048 => ['MVLIII'],
+ 1049 => ['MVLIV', 'MIL'],
+ 1095 => ['MVC'],
+ 1096 => ['MVCI'],
+ 1097 => ['MVCII'],
+ 1098 => ['MVCIII'],
+ 1099 => ['MVCIV', 'MIC'],
+ 1145 => ['MCVL'],
+ 1146 => ['MCVLI'],
+ 1147 => ['MCVLII'],
+ 1148 => ['MCVLIII'],
+ 1149 => ['MCVLIV', 'MCIL'],
+ 1195 => ['MCVC'],
+ 1196 => ['MCVCI'],
+ 1197 => ['MCVCII'],
+ 1198 => ['MCVCIII'],
+ 1199 => ['MCVCIV', 'MCIC'],
+ 1245 => ['MCCVL'],
+ 1246 => ['MCCVLI'],
+ 1247 => ['MCCVLII'],
+ 1248 => ['MCCVLIII'],
+ 1249 => ['MCCVLIV', 'MCCIL'],
+ 1295 => ['MCCVC'],
+ 1296 => ['MCCVCI'],
+ 1297 => ['MCCVCII'],
+ 1298 => ['MCCVCIII'],
+ 1299 => ['MCCVCIV', 'MCCIC'],
+ 1345 => ['MCCCVL'],
+ 1346 => ['MCCCVLI'],
+ 1347 => ['MCCCVLII'],
+ 1348 => ['MCCCVLIII'],
+ 1349 => ['MCCCVLIV', 'MCCCIL'],
+ 1395 => ['MCCCVC'],
+ 1396 => ['MCCCVCI'],
+ 1397 => ['MCCCVCII'],
+ 1398 => ['MCCCVCIII'],
+ 1399 => ['MCCCVCIV', 'MCCCIC'],
+ 1445 => ['MCDVL'],
+ 1446 => ['MCDVLI'],
+ 1447 => ['MCDVLII'],
+ 1448 => ['MCDVLIII'],
+ 1449 => ['MCDVLIV', 'MCDIL'],
+ 1450 => ['MLD'],
+ 1451 => ['MLDI'],
+ 1452 => ['MLDII'],
+ 1453 => ['MLDIII'],
+ 1454 => ['MLDIV'],
+ 1455 => ['MLDV'],
+ 1456 => ['MLDVI'],
+ 1457 => ['MLDVII'],
+ 1458 => ['MLDVIII'],
+ 1459 => ['MLDIX'],
+ 1460 => ['MLDX'],
+ 1461 => ['MLDXI'],
+ 1462 => ['MLDXII'],
+ 1463 => ['MLDXIII'],
+ 1464 => ['MLDXIV'],
+ 1465 => ['MLDXV'],
+ 1466 => ['MLDXVI'],
+ 1467 => ['MLDXVII'],
+ 1468 => ['MLDXVIII'],
+ 1469 => ['MLDXIX'],
+ 1470 => ['MLDXX'],
+ 1471 => ['MLDXXI'],
+ 1472 => ['MLDXXII'],
+ 1473 => ['MLDXXIII'],
+ 1474 => ['MLDXXIV'],
+ 1475 => ['MLDXXV'],
+ 1476 => ['MLDXXVI'],
+ 1477 => ['MLDXXVII'],
+ 1478 => ['MLDXXVIII'],
+ 1479 => ['MLDXXIX'],
+ 1480 => ['MLDXXX'],
+ 1481 => ['MLDXXXI'],
+ 1482 => ['MLDXXXII'],
+ 1483 => ['MLDXXXIII'],
+ 1484 => ['MLDXXXIV'],
+ 1485 => ['MLDXXXV'],
+ 1486 => ['MLDXXXVI'],
+ 1487 => ['MLDXXXVII'],
+ 1488 => ['MLDXXXVIII'],
+ 1489 => ['MLDXXXIX'],
+ 1490 => ['MLDXL', 'MXD'],
+ 1491 => ['MLDXLI', 'MXDI'],
+ 1492 => ['MLDXLII', 'MXDII'],
+ 1493 => ['MLDXLIII', 'MXDIII'],
+ 1494 => ['MLDXLIV', 'MXDIV'],
+ 1495 => ['MLDVL', 'MXDV', 'MVD'],
+ 1496 => ['MLDVLI', 'MXDVI', 'MVDI'],
+ 1497 => ['MLDVLII', 'MXDVII', 'MVDII'],
+ 1498 => ['MLDVLIII', 'MXDVIII', 'MVDIII'],
+ 1499 => ['MLDVLIV', 'MXDIX', 'MVDIV', 'MID'],
+ 1545 => ['MDVL'],
+ 1546 => ['MDVLI'],
+ 1547 => ['MDVLII'],
+ 1548 => ['MDVLIII'],
+ 1549 => ['MDVLIV', 'MDIL'],
+ 1595 => ['MDVC'],
+ 1596 => ['MDVCI'],
+ 1597 => ['MDVCII'],
+ 1598 => ['MDVCIII'],
+ 1599 => ['MDVCIV', 'MDIC'],
+ 1645 => ['MDCVL'],
+ 1646 => ['MDCVLI'],
+ 1647 => ['MDCVLII'],
+ 1648 => ['MDCVLIII'],
+ 1649 => ['MDCVLIV', 'MDCIL'],
+ 1695 => ['MDCVC'],
+ 1696 => ['MDCVCI'],
+ 1697 => ['MDCVCII'],
+ 1698 => ['MDCVCIII'],
+ 1699 => ['MDCVCIV', 'MDCIC'],
+ 1745 => ['MDCCVL'],
+ 1746 => ['MDCCVLI'],
+ 1747 => ['MDCCVLII'],
+ 1748 => ['MDCCVLIII'],
+ 1749 => ['MDCCVLIV', 'MDCCIL'],
+ 1795 => ['MDCCVC'],
+ 1796 => ['MDCCVCI'],
+ 1797 => ['MDCCVCII'],
+ 1798 => ['MDCCVCIII'],
+ 1799 => ['MDCCVCIV', 'MDCCIC'],
+ 1845 => ['MDCCCVL'],
+ 1846 => ['MDCCCVLI'],
+ 1847 => ['MDCCCVLII'],
+ 1848 => ['MDCCCVLIII'],
+ 1849 => ['MDCCCVLIV', 'MDCCCIL'],
+ 1895 => ['MDCCCVC'],
+ 1896 => ['MDCCCVCI'],
+ 1897 => ['MDCCCVCII'],
+ 1898 => ['MDCCCVCIII'],
+ 1899 => ['MDCCCVCIV', 'MDCCCIC'],
+ 1945 => ['MCMVL'],
+ 1946 => ['MCMVLI'],
+ 1947 => ['MCMVLII'],
+ 1948 => ['MCMVLIII'],
+ 1949 => ['MCMVLIV', 'MCMIL'],
+ 1950 => ['MLM'],
+ 1951 => ['MLMI'],
+ 1952 => ['MLMII'],
+ 1953 => ['MLMIII'],
+ 1954 => ['MLMIV'],
+ 1955 => ['MLMV'],
+ 1956 => ['MLMVI'],
+ 1957 => ['MLMVII'],
+ 1958 => ['MLMVIII'],
+ 1959 => ['MLMIX'],
+ 1960 => ['MLMX'],
+ 1961 => ['MLMXI'],
+ 1962 => ['MLMXII'],
+ 1963 => ['MLMXIII'],
+ 1964 => ['MLMXIV'],
+ 1965 => ['MLMXV'],
+ 1966 => ['MLMXVI'],
+ 1967 => ['MLMXVII'],
+ 1968 => ['MLMXVIII'],
+ 1969 => ['MLMXIX'],
+ 1970 => ['MLMXX'],
+ 1971 => ['MLMXXI'],
+ 1972 => ['MLMXXII'],
+ 1973 => ['MLMXXIII'],
+ 1974 => ['MLMXXIV'],
+ 1975 => ['MLMXXV'],
+ 1976 => ['MLMXXVI'],
+ 1977 => ['MLMXXVII'],
+ 1978 => ['MLMXXVIII'],
+ 1979 => ['MLMXXIX'],
+ 1980 => ['MLMXXX'],
+ 1981 => ['MLMXXXI'],
+ 1982 => ['MLMXXXII'],
+ 1983 => ['MLMXXXIII'],
+ 1984 => ['MLMXXXIV'],
+ 1985 => ['MLMXXXV'],
+ 1986 => ['MLMXXXVI'],
+ 1987 => ['MLMXXXVII'],
+ 1988 => ['MLMXXXVIII'],
+ 1989 => ['MLMXXXIX'],
+ 1990 => ['MLMXL', 'MXM'],
+ 1991 => ['MLMXLI', 'MXMI'],
+ 1992 => ['MLMXLII', 'MXMII'],
+ 1993 => ['MLMXLIII', 'MXMIII'],
+ 1994 => ['MLMXLIV', 'MXMIV'],
+ 1995 => ['MLMVL', 'MXMV', 'MVM'],
+ 1996 => ['MLMVLI', 'MXMVI', 'MVMI'],
+ 1997 => ['MLMVLII', 'MXMVII', 'MVMII'],
+ 1998 => ['MLMVLIII', 'MXMVIII', 'MVMIII'],
+ 1999 => ['MLMVLIV', 'MXMIX', 'MVMIV', 'MIM'],
+ 2045 => ['MMVL'],
+ 2046 => ['MMVLI'],
+ 2047 => ['MMVLII'],
+ 2048 => ['MMVLIII'],
+ 2049 => ['MMVLIV', 'MMIL'],
+ 2095 => ['MMVC'],
+ 2096 => ['MMVCI'],
+ 2097 => ['MMVCII'],
+ 2098 => ['MMVCIII'],
+ 2099 => ['MMVCIV', 'MMIC'],
+ 2145 => ['MMCVL'],
+ 2146 => ['MMCVLI'],
+ 2147 => ['MMCVLII'],
+ 2148 => ['MMCVLIII'],
+ 2149 => ['MMCVLIV', 'MMCIL'],
+ 2195 => ['MMCVC'],
+ 2196 => ['MMCVCI'],
+ 2197 => ['MMCVCII'],
+ 2198 => ['MMCVCIII'],
+ 2199 => ['MMCVCIV', 'MMCIC'],
+ 2245 => ['MMCCVL'],
+ 2246 => ['MMCCVLI'],
+ 2247 => ['MMCCVLII'],
+ 2248 => ['MMCCVLIII'],
+ 2249 => ['MMCCVLIV', 'MMCCIL'],
+ 2295 => ['MMCCVC'],
+ 2296 => ['MMCCVCI'],
+ 2297 => ['MMCCVCII'],
+ 2298 => ['MMCCVCIII'],
+ 2299 => ['MMCCVCIV', 'MMCCIC'],
+ 2345 => ['MMCCCVL'],
+ 2346 => ['MMCCCVLI'],
+ 2347 => ['MMCCCVLII'],
+ 2348 => ['MMCCCVLIII'],
+ 2349 => ['MMCCCVLIV', 'MMCCCIL'],
+ 2395 => ['MMCCCVC'],
+ 2396 => ['MMCCCVCI'],
+ 2397 => ['MMCCCVCII'],
+ 2398 => ['MMCCCVCIII'],
+ 2399 => ['MMCCCVCIV', 'MMCCCIC'],
+ 2445 => ['MMCDVL'],
+ 2446 => ['MMCDVLI'],
+ 2447 => ['MMCDVLII'],
+ 2448 => ['MMCDVLIII'],
+ 2449 => ['MMCDVLIV', 'MMCDIL'],
+ 2450 => ['MMLD'],
+ 2451 => ['MMLDI'],
+ 2452 => ['MMLDII'],
+ 2453 => ['MMLDIII'],
+ 2454 => ['MMLDIV'],
+ 2455 => ['MMLDV'],
+ 2456 => ['MMLDVI'],
+ 2457 => ['MMLDVII'],
+ 2458 => ['MMLDVIII'],
+ 2459 => ['MMLDIX'],
+ 2460 => ['MMLDX'],
+ 2461 => ['MMLDXI'],
+ 2462 => ['MMLDXII'],
+ 2463 => ['MMLDXIII'],
+ 2464 => ['MMLDXIV'],
+ 2465 => ['MMLDXV'],
+ 2466 => ['MMLDXVI'],
+ 2467 => ['MMLDXVII'],
+ 2468 => ['MMLDXVIII'],
+ 2469 => ['MMLDXIX'],
+ 2470 => ['MMLDXX'],
+ 2471 => ['MMLDXXI'],
+ 2472 => ['MMLDXXII'],
+ 2473 => ['MMLDXXIII'],
+ 2474 => ['MMLDXXIV'],
+ 2475 => ['MMLDXXV'],
+ 2476 => ['MMLDXXVI'],
+ 2477 => ['MMLDXXVII'],
+ 2478 => ['MMLDXXVIII'],
+ 2479 => ['MMLDXXIX'],
+ 2480 => ['MMLDXXX'],
+ 2481 => ['MMLDXXXI'],
+ 2482 => ['MMLDXXXII'],
+ 2483 => ['MMLDXXXIII'],
+ 2484 => ['MMLDXXXIV'],
+ 2485 => ['MMLDXXXV'],
+ 2486 => ['MMLDXXXVI'],
+ 2487 => ['MMLDXXXVII'],
+ 2488 => ['MMLDXXXVIII'],
+ 2489 => ['MMLDXXXIX'],
+ 2490 => ['MMLDXL', 'MMXD'],
+ 2491 => ['MMLDXLI', 'MMXDI'],
+ 2492 => ['MMLDXLII', 'MMXDII'],
+ 2493 => ['MMLDXLIII', 'MMXDIII'],
+ 2494 => ['MMLDXLIV', 'MMXDIV'],
+ 2495 => ['MMLDVL', 'MMXDV', 'MMVD'],
+ 2496 => ['MMLDVLI', 'MMXDVI', 'MMVDI'],
+ 2497 => ['MMLDVLII', 'MMXDVII', 'MMVDII'],
+ 2498 => ['MMLDVLIII', 'MMXDVIII', 'MMVDIII'],
+ 2499 => ['MMLDVLIV', 'MMXDIX', 'MMVDIV', 'MMID'],
+ 2545 => ['MMDVL'],
+ 2546 => ['MMDVLI'],
+ 2547 => ['MMDVLII'],
+ 2548 => ['MMDVLIII'],
+ 2549 => ['MMDVLIV', 'MMDIL'],
+ 2595 => ['MMDVC'],
+ 2596 => ['MMDVCI'],
+ 2597 => ['MMDVCII'],
+ 2598 => ['MMDVCIII'],
+ 2599 => ['MMDVCIV', 'MMDIC'],
+ 2645 => ['MMDCVL'],
+ 2646 => ['MMDCVLI'],
+ 2647 => ['MMDCVLII'],
+ 2648 => ['MMDCVLIII'],
+ 2649 => ['MMDCVLIV', 'MMDCIL'],
+ 2695 => ['MMDCVC'],
+ 2696 => ['MMDCVCI'],
+ 2697 => ['MMDCVCII'],
+ 2698 => ['MMDCVCIII'],
+ 2699 => ['MMDCVCIV', 'MMDCIC'],
+ 2745 => ['MMDCCVL'],
+ 2746 => ['MMDCCVLI'],
+ 2747 => ['MMDCCVLII'],
+ 2748 => ['MMDCCVLIII'],
+ 2749 => ['MMDCCVLIV', 'MMDCCIL'],
+ 2795 => ['MMDCCVC'],
+ 2796 => ['MMDCCVCI'],
+ 2797 => ['MMDCCVCII'],
+ 2798 => ['MMDCCVCIII'],
+ 2799 => ['MMDCCVCIV', 'MMDCCIC'],
+ 2845 => ['MMDCCCVL'],
+ 2846 => ['MMDCCCVLI'],
+ 2847 => ['MMDCCCVLII'],
+ 2848 => ['MMDCCCVLIII'],
+ 2849 => ['MMDCCCVLIV', 'MMDCCCIL'],
+ 2895 => ['MMDCCCVC'],
+ 2896 => ['MMDCCCVCI'],
+ 2897 => ['MMDCCCVCII'],
+ 2898 => ['MMDCCCVCIII'],
+ 2899 => ['MMDCCCVCIV', 'MMDCCCIC'],
+ 2945 => ['MMCMVL'],
+ 2946 => ['MMCMVLI'],
+ 2947 => ['MMCMVLII'],
+ 2948 => ['MMCMVLIII'],
+ 2949 => ['MMCMVLIV', 'MMCMIL'],
+ 2950 => ['MMLM'],
+ 2951 => ['MMLMI'],
+ 2952 => ['MMLMII'],
+ 2953 => ['MMLMIII'],
+ 2954 => ['MMLMIV'],
+ 2955 => ['MMLMV'],
+ 2956 => ['MMLMVI'],
+ 2957 => ['MMLMVII'],
+ 2958 => ['MMLMVIII'],
+ 2959 => ['MMLMIX'],
+ 2960 => ['MMLMX'],
+ 2961 => ['MMLMXI'],
+ 2962 => ['MMLMXII'],
+ 2963 => ['MMLMXIII'],
+ 2964 => ['MMLMXIV'],
+ 2965 => ['MMLMXV'],
+ 2966 => ['MMLMXVI'],
+ 2967 => ['MMLMXVII'],
+ 2968 => ['MMLMXVIII'],
+ 2969 => ['MMLMXIX'],
+ 2970 => ['MMLMXX'],
+ 2971 => ['MMLMXXI'],
+ 2972 => ['MMLMXXII'],
+ 2973 => ['MMLMXXIII'],
+ 2974 => ['MMLMXXIV'],
+ 2975 => ['MMLMXXV'],
+ 2976 => ['MMLMXXVI'],
+ 2977 => ['MMLMXXVII'],
+ 2978 => ['MMLMXXVIII'],
+ 2979 => ['MMLMXXIX'],
+ 2980 => ['MMLMXXX'],
+ 2981 => ['MMLMXXXI'],
+ 2982 => ['MMLMXXXII'],
+ 2983 => ['MMLMXXXIII'],
+ 2984 => ['MMLMXXXIV'],
+ 2985 => ['MMLMXXXV'],
+ 2986 => ['MMLMXXXVI'],
+ 2987 => ['MMLMXXXVII'],
+ 2988 => ['MMLMXXXVIII'],
+ 2989 => ['MMLMXXXIX'],
+ 2990 => ['MMLMXL', 'MMXM'],
+ 2991 => ['MMLMXLI', 'MMXMI'],
+ 2992 => ['MMLMXLII', 'MMXMII'],
+ 2993 => ['MMLMXLIII', 'MMXMIII'],
+ 2994 => ['MMLMXLIV', 'MMXMIV'],
+ 2995 => ['MMLMVL', 'MMXMV', 'MMVM'],
+ 2996 => ['MMLMVLI', 'MMXMVI', 'MMVMI'],
+ 2997 => ['MMLMVLII', 'MMXMVII', 'MMVMII'],
+ 2998 => ['MMLMVLIII', 'MMXMVIII', 'MMVMIII'],
+ 2999 => ['MMLMVLIV', 'MMXMIX', 'MMVMIV', 'MMIM'],
+ 3045 => ['MMMVL'],
+ 3046 => ['MMMVLI'],
+ 3047 => ['MMMVLII'],
+ 3048 => ['MMMVLIII'],
+ 3049 => ['MMMVLIV', 'MMMIL'],
+ 3095 => ['MMMVC'],
+ 3096 => ['MMMVCI'],
+ 3097 => ['MMMVCII'],
+ 3098 => ['MMMVCIII'],
+ 3099 => ['MMMVCIV', 'MMMIC'],
+ 3145 => ['MMMCVL'],
+ 3146 => ['MMMCVLI'],
+ 3147 => ['MMMCVLII'],
+ 3148 => ['MMMCVLIII'],
+ 3149 => ['MMMCVLIV', 'MMMCIL'],
+ 3195 => ['MMMCVC'],
+ 3196 => ['MMMCVCI'],
+ 3197 => ['MMMCVCII'],
+ 3198 => ['MMMCVCIII'],
+ 3199 => ['MMMCVCIV', 'MMMCIC'],
+ 3245 => ['MMMCCVL'],
+ 3246 => ['MMMCCVLI'],
+ 3247 => ['MMMCCVLII'],
+ 3248 => ['MMMCCVLIII'],
+ 3249 => ['MMMCCVLIV', 'MMMCCIL'],
+ 3295 => ['MMMCCVC'],
+ 3296 => ['MMMCCVCI'],
+ 3297 => ['MMMCCVCII'],
+ 3298 => ['MMMCCVCIII'],
+ 3299 => ['MMMCCVCIV', 'MMMCCIC'],
+ 3345 => ['MMMCCCVL'],
+ 3346 => ['MMMCCCVLI'],
+ 3347 => ['MMMCCCVLII'],
+ 3348 => ['MMMCCCVLIII'],
+ 3349 => ['MMMCCCVLIV', 'MMMCCCIL'],
+ 3395 => ['MMMCCCVC'],
+ 3396 => ['MMMCCCVCI'],
+ 3397 => ['MMMCCCVCII'],
+ 3398 => ['MMMCCCVCIII'],
+ 3399 => ['MMMCCCVCIV', 'MMMCCCIC'],
+ 3445 => ['MMMCDVL'],
+ 3446 => ['MMMCDVLI'],
+ 3447 => ['MMMCDVLII'],
+ 3448 => ['MMMCDVLIII'],
+ 3449 => ['MMMCDVLIV', 'MMMCDIL'],
+ 3450 => ['MMMLD'],
+ 3451 => ['MMMLDI'],
+ 3452 => ['MMMLDII'],
+ 3453 => ['MMMLDIII'],
+ 3454 => ['MMMLDIV'],
+ 3455 => ['MMMLDV'],
+ 3456 => ['MMMLDVI'],
+ 3457 => ['MMMLDVII'],
+ 3458 => ['MMMLDVIII'],
+ 3459 => ['MMMLDIX'],
+ 3460 => ['MMMLDX'],
+ 3461 => ['MMMLDXI'],
+ 3462 => ['MMMLDXII'],
+ 3463 => ['MMMLDXIII'],
+ 3464 => ['MMMLDXIV'],
+ 3465 => ['MMMLDXV'],
+ 3466 => ['MMMLDXVI'],
+ 3467 => ['MMMLDXVII'],
+ 3468 => ['MMMLDXVIII'],
+ 3469 => ['MMMLDXIX'],
+ 3470 => ['MMMLDXX'],
+ 3471 => ['MMMLDXXI'],
+ 3472 => ['MMMLDXXII'],
+ 3473 => ['MMMLDXXIII'],
+ 3474 => ['MMMLDXXIV'],
+ 3475 => ['MMMLDXXV'],
+ 3476 => ['MMMLDXXVI'],
+ 3477 => ['MMMLDXXVII'],
+ 3478 => ['MMMLDXXVIII'],
+ 3479 => ['MMMLDXXIX'],
+ 3480 => ['MMMLDXXX'],
+ 3481 => ['MMMLDXXXI'],
+ 3482 => ['MMMLDXXXII'],
+ 3483 => ['MMMLDXXXIII'],
+ 3484 => ['MMMLDXXXIV'],
+ 3485 => ['MMMLDXXXV'],
+ 3486 => ['MMMLDXXXVI'],
+ 3487 => ['MMMLDXXXVII'],
+ 3488 => ['MMMLDXXXVIII'],
+ 3489 => ['MMMLDXXXIX'],
+ 3490 => ['MMMLDXL', 'MMMXD'],
+ 3491 => ['MMMLDXLI', 'MMMXDI'],
+ 3492 => ['MMMLDXLII', 'MMMXDII'],
+ 3493 => ['MMMLDXLIII', 'MMMXDIII'],
+ 3494 => ['MMMLDXLIV', 'MMMXDIV'],
+ 3495 => ['MMMLDVL', 'MMMXDV', 'MMMVD'],
+ 3496 => ['MMMLDVLI', 'MMMXDVI', 'MMMVDI'],
+ 3497 => ['MMMLDVLII', 'MMMXDVII', 'MMMVDII'],
+ 3498 => ['MMMLDVLIII', 'MMMXDVIII', 'MMMVDIII'],
+ 3499 => ['MMMLDVLIV', 'MMMXDIX', 'MMMVDIV', 'MMMID'],
+ 3545 => ['MMMDVL'],
+ 3546 => ['MMMDVLI'],
+ 3547 => ['MMMDVLII'],
+ 3548 => ['MMMDVLIII'],
+ 3549 => ['MMMDVLIV', 'MMMDIL'],
+ 3595 => ['MMMDVC'],
+ 3596 => ['MMMDVCI'],
+ 3597 => ['MMMDVCII'],
+ 3598 => ['MMMDVCIII'],
+ 3599 => ['MMMDVCIV', 'MMMDIC'],
+ 3645 => ['MMMDCVL'],
+ 3646 => ['MMMDCVLI'],
+ 3647 => ['MMMDCVLII'],
+ 3648 => ['MMMDCVLIII'],
+ 3649 => ['MMMDCVLIV', 'MMMDCIL'],
+ 3695 => ['MMMDCVC'],
+ 3696 => ['MMMDCVCI'],
+ 3697 => ['MMMDCVCII'],
+ 3698 => ['MMMDCVCIII'],
+ 3699 => ['MMMDCVCIV', 'MMMDCIC'],
+ 3745 => ['MMMDCCVL'],
+ 3746 => ['MMMDCCVLI'],
+ 3747 => ['MMMDCCVLII'],
+ 3748 => ['MMMDCCVLIII'],
+ 3749 => ['MMMDCCVLIV', 'MMMDCCIL'],
+ 3795 => ['MMMDCCVC'],
+ 3796 => ['MMMDCCVCI'],
+ 3797 => ['MMMDCCVCII'],
+ 3798 => ['MMMDCCVCIII'],
+ 3799 => ['MMMDCCVCIV', 'MMMDCCIC'],
+ 3845 => ['MMMDCCCVL'],
+ 3846 => ['MMMDCCCVLI'],
+ 3847 => ['MMMDCCCVLII'],
+ 3848 => ['MMMDCCCVLIII'],
+ 3849 => ['MMMDCCCVLIV', 'MMMDCCCIL'],
+ 3895 => ['MMMDCCCVC'],
+ 3896 => ['MMMDCCCVCI'],
+ 3897 => ['MMMDCCCVCII'],
+ 3898 => ['MMMDCCCVCIII'],
+ 3899 => ['MMMDCCCVCIV', 'MMMDCCCIC'],
+ 3945 => ['MMMCMVL'],
+ 3946 => ['MMMCMVLI'],
+ 3947 => ['MMMCMVLII'],
+ 3948 => ['MMMCMVLIII'],
+ 3949 => ['MMMCMVLIV', 'MMMCMIL'],
+ 3950 => ['MMMLM'],
+ 3951 => ['MMMLMI'],
+ 3952 => ['MMMLMII'],
+ 3953 => ['MMMLMIII'],
+ 3954 => ['MMMLMIV'],
+ 3955 => ['MMMLMV'],
+ 3956 => ['MMMLMVI'],
+ 3957 => ['MMMLMVII'],
+ 3958 => ['MMMLMVIII'],
+ 3959 => ['MMMLMIX'],
+ 3960 => ['MMMLMX'],
+ 3961 => ['MMMLMXI'],
+ 3962 => ['MMMLMXII'],
+ 3963 => ['MMMLMXIII'],
+ 3964 => ['MMMLMXIV'],
+ 3965 => ['MMMLMXV'],
+ 3966 => ['MMMLMXVI'],
+ 3967 => ['MMMLMXVII'],
+ 3968 => ['MMMLMXVIII'],
+ 3969 => ['MMMLMXIX'],
+ 3970 => ['MMMLMXX'],
+ 3971 => ['MMMLMXXI'],
+ 3972 => ['MMMLMXXII'],
+ 3973 => ['MMMLMXXIII'],
+ 3974 => ['MMMLMXXIV'],
+ 3975 => ['MMMLMXXV'],
+ 3976 => ['MMMLMXXVI'],
+ 3977 => ['MMMLMXXVII'],
+ 3978 => ['MMMLMXXVIII'],
+ 3979 => ['MMMLMXXIX'],
+ 3980 => ['MMMLMXXX'],
+ 3981 => ['MMMLMXXXI'],
+ 3982 => ['MMMLMXXXII'],
+ 3983 => ['MMMLMXXXIII'],
+ 3984 => ['MMMLMXXXIV'],
+ 3985 => ['MMMLMXXXV'],
+ 3986 => ['MMMLMXXXVI'],
+ 3987 => ['MMMLMXXXVII'],
+ 3988 => ['MMMLMXXXVIII'],
+ 3989 => ['MMMLMXXXIX'],
+ 3990 => ['MMMLMXL', 'MMMXM'],
+ 3991 => ['MMMLMXLI', 'MMMXMI'],
+ 3992 => ['MMMLMXLII', 'MMMXMII'],
+ 3993 => ['MMMLMXLIII', 'MMMXMIII'],
+ 3994 => ['MMMLMXLIV', 'MMMXMIV'],
+ 3995 => ['MMMLMVL', 'MMMXMV', 'MMMVM'],
+ 3996 => ['MMMLMVLI', 'MMMXMVI', 'MMMVMI'],
+ 3997 => ['MMMLMVLII', 'MMMXMVII', 'MMMVMII'],
+ 3998 => ['MMMLMVLIII', 'MMMXMVIII', 'MMMVMIII'],
+ 3999 => ['MMMLMVLIV', 'MMMXMIX', 'MMMVMIV', 'MMMIM'],
+ ];
+
+ private const THOUSANDS = ['', 'M', 'MM', 'MMM'];
+ private const HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
+ private const TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
+ private const ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
+ const MAX_ROMAN_VALUE = 3999;
+ const MAX_ROMAN_STYLE = 4;
+
+ private static function valueOk(int $aValue, int $style): string
+ {
+ $origValue = $aValue;
+ $m = \intdiv($aValue, 1000);
+ $aValue %= 1000;
+ $c = \intdiv($aValue, 100);
+ $aValue %= 100;
+ $t = \intdiv($aValue, 10);
+ $aValue %= 10;
+ $result = self::THOUSANDS[$m] . self::HUNDREDS[$c] . self::TENS[$t] . self::ONES[$aValue];
+ if ($style > 0) {
+ if (array_key_exists($origValue, self::VALUES)) {
+ $arr = self::VALUES[$origValue];
+ $idx = min($style, count($arr)) - 1;
+ $result = $arr[$idx];
+ }
+ }
+
+ return $result;
+ }
+
+ private static function styleOk(int $aValue, int $style): string
+ {
+ return ($aValue < 0 || $aValue > self::MAX_ROMAN_VALUE) ? ExcelError::VALUE() : self::valueOk($aValue, $style);
+ }
+
+ public static function calculateRoman(int $aValue, int $style): string
+ {
+ return ($style < 0 || $style > self::MAX_ROMAN_STYLE) ? ExcelError::VALUE() : self::styleOk($aValue, $style);
+ }
+
+ /**
+ * ROMAN.
+ *
+ * Converts a number to Roman numeral
+ *
+ * @param mixed $aValue Number to convert
+ * Or can be an array of numbers
+ * @param mixed $style Number indicating one of five possible forms
+ * Or can be an array of styles
+ *
+ * @return array|string Roman numeral, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function evaluate(mixed $aValue, mixed $style = 0): array|string
+ {
+ if (is_array($aValue) || is_array($style)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $aValue, $style);
+ }
+
+ try {
+ $aValue = Helpers::validateNumericNullBool($aValue);
+ if (is_bool($style)) {
+ $style = $style ? 0 : 4;
+ }
+ $style = Helpers::validateNumericNullSubstitution($style, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::calculateRoman((int) $aValue, (int) $style);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
new file mode 100644
index 0000000..21e4a34
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
@@ -0,0 +1,218 @@
+getMessage();
+ }
+
+ return round($number, (int) $precision);
+ }
+
+ /**
+ * ROUNDUP.
+ *
+ * Rounds a number up to a specified number of decimal places
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ * @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function up($number, $digits): array|string|float
+ {
+ if (is_array($number) || is_array($digits)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ if ($number < 0.0) {
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+ }
+
+ return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+ }
+
+ /**
+ * ROUNDDOWN.
+ *
+ * Rounds a number down to a specified number of decimal places
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ * @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function down($number, $digits): array|string|float
+ {
+ if (is_array($number) || is_array($digits)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ $digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0.0) {
+ return 0.0;
+ }
+
+ if ($number < 0.0) {
+ return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+ }
+
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+ }
+
+ /**
+ * MROUND.
+ *
+ * Rounds a number to the nearest multiple of a specified value
+ *
+ * @param mixed $number Expect float. Number to round, or can be an array of numbers
+ * @param mixed $multiple Expect int. Multiple to which you want to round, or can be an array of numbers.
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function multiple(mixed $number, mixed $multiple): array|string|int|float
+ {
+ if (is_array($number) || is_array($multiple)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $multiple);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullSubstitution($number, 0);
+ $multiple = Helpers::validateNumericNullSubstitution($multiple, null);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($number == 0 || $multiple == 0) {
+ return 0;
+ }
+ if ((Helpers::returnSign($number)) == (Helpers::returnSign($multiple))) {
+ $multiplier = 1 / $multiple;
+
+ return round($number * $multiplier) / $multiplier;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * EVEN.
+ *
+ * Returns number rounded up to the nearest even integer.
+ * You can use this function for processing items that come in twos. For example,
+ * a packing crate accepts rows of one or two items. The crate is full when
+ * the number of items, rounded up to the nearest two, matches the crate's
+ * capacity.
+ *
+ * Excel Function:
+ * EVEN(number)
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function even($number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::getEven($number);
+ }
+
+ /**
+ * ODD.
+ *
+ * Returns number rounded up to the nearest odd integer.
+ *
+ * @param array|float $number Number to round, or can be an array of numbers
+ *
+ * @return array|float|string Rounded Number, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function odd($number): array|string|int|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $significance = Helpers::returnSign($number);
+ if ($significance == 0) {
+ return 1;
+ }
+
+ $result = ceil($number / $significance) * $significance;
+ if ($result == Helpers::getEven($result)) {
+ $result += $significance;
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
new file mode 100644
index 0000000..e2da113
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
@@ -0,0 +1,53 @@
+getMessage();
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
new file mode 100644
index 0000000..86a5509
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
@@ -0,0 +1,38 @@
+getMessage();
+ }
+
+ return Helpers::returnSign($number);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
new file mode 100644
index 0000000..18289f7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return Helpers::numberOrNan(sqrt($number));
+ }
+
+ /**
+ * SQRTPI.
+ *
+ * Returns the square root of (number * pi).
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string Square Root of Number * Pi, or a string containing an error
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function pi($number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullSubstitution($number, 0);
+ Helpers::validateNotNegative($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return sqrt($number * M_PI);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
new file mode 100644
index 0000000..20a1080
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
@@ -0,0 +1,127 @@
+getWorksheet()->getRowDimension($row)->getVisible();
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ protected static function filterFormulaArgs(mixed $cellReference, mixed $args): array
+ {
+ return array_filter(
+ $args,
+ function ($index) use ($cellReference): bool {
+ $explodeArray = explode('.', $index);
+ $row = $explodeArray[1] ?? '';
+ $column = $explodeArray[2] ?? '';
+ $retVal = true;
+ if ($cellReference->getWorksheet()->cellExists($column . $row)) {
+ //take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
+ $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
+ $cellFormula = !preg_match(
+ '/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i',
+ $cellReference->getWorksheet()->getCell($column . $row)->getValue() ?? ''
+ );
+
+ $retVal = !$isFormula || $cellFormula;
+ }
+
+ return $retVal;
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ private const CALL_FUNCTIONS = [
+ 1 => [Statistical\Averages::class, 'average'], // 1 and 101
+ [Statistical\Counts::class, 'COUNT'], // 2 and 102
+ [Statistical\Counts::class, 'COUNTA'], // 3 and 103
+ [Statistical\Maximum::class, 'max'], // 4 and 104
+ [Statistical\Minimum::class, 'min'], // 5 and 105
+ [Operations::class, 'product'], // 6 and 106
+ [Statistical\StandardDeviations::class, 'STDEV'], // 7 and 107
+ [Statistical\StandardDeviations::class, 'STDEVP'], // 8 and 108
+ [Sum::class, 'sumIgnoringStrings'], // 9 and 109
+ [Statistical\Variances::class, 'VAR'], // 10 and 110
+ [Statistical\Variances::class, 'VARP'], // 111 and 111
+ ];
+
+ /**
+ * SUBTOTAL.
+ *
+ * Returns a subtotal in a list or database.
+ *
+ * @param mixed $functionType
+ * A number 1 to 11 that specifies which function to
+ * use in calculating subtotals within a range
+ * list
+ * Numbers 101 to 111 shadow the functions of 1 to 11
+ * but ignore any values in the range that are
+ * in hidden rows
+ * @param mixed[] $args A mixed data series of values
+ *
+ * @return float|string
+ */
+ public static function evaluate(mixed $functionType, ...$args)
+ {
+ $cellReference = array_pop($args);
+ $bArgs = Functions::flattenArrayIndexed($args);
+ $aArgs = [];
+ // int keys must come before string keys for PHP 8.0+
+ // Otherwise, PHP thinks positional args follow keyword
+ // in the subsequent call to call_user_func_array.
+ // Fortunately, order of args is unimportant to Subtotal.
+ foreach ($bArgs as $key => $value) {
+ if (is_int($key)) {
+ $aArgs[$key] = $value;
+ }
+ }
+ foreach ($bArgs as $key => $value) {
+ if (!is_int($key)) {
+ $aArgs[$key] = $value;
+ }
+ }
+
+ try {
+ $subtotal = (int) Helpers::validateNumericNullBool($functionType);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ // Calculate
+ if ($subtotal > 100) {
+ $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
+ $subtotal -= 100;
+ }
+
+ $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
+ if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) {
+ /** @var callable */
+ $call = self::CALL_FUNCTIONS[$subtotal];
+
+ return call_user_func_array($call, $aArgs);
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
new file mode 100644
index 0000000..7fc6090
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
@@ -0,0 +1,114 @@
+ $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ $returnValue += $arg;
+ } elseif (is_bool($arg)) {
+ $returnValue += (int) $arg;
+ } elseif (ErrorValue::isError($arg)) {
+ return $arg;
+ } elseif ($arg !== null && !Functions::isCellValue($k)) {
+ // ignore non-numerics from cell, but fail as literals (except null)
+ return ExcelError::VALUE();
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * SUMPRODUCT.
+ *
+ * Excel Function:
+ * SUMPRODUCT(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|int|string The result, or a string containing an error
+ */
+ public static function product(mixed ...$args): string|int|float
+ {
+ $arrayList = $args;
+
+ $wrkArray = Functions::flattenArray(array_shift($arrayList));
+ $wrkCellCount = count($wrkArray);
+
+ for ($i = 0; $i < $wrkCellCount; ++$i) {
+ if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
+ $wrkArray[$i] = 0;
+ }
+ }
+
+ foreach ($arrayList as $matrixData) {
+ $array2 = Functions::flattenArray($matrixData);
+ $count = count($array2);
+ if ($wrkCellCount != $count) {
+ return ExcelError::VALUE();
+ }
+
+ foreach ($array2 as $i => $val) {
+ if ((!is_numeric($val)) || (is_string($val))) {
+ $val = 0;
+ }
+ $wrkArray[$i] *= $val;
+ }
+ }
+
+ return array_sum($wrkArray);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
new file mode 100644
index 0000000..1e5b5d5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
@@ -0,0 +1,141 @@
+getMessage();
+ }
+
+ return $returnValue;
+ }
+
+ private static function getCount(array $array1, array $array2): int
+ {
+ $count = count($array1);
+ if ($count !== count($array2)) {
+ throw new Exception(ExcelError::NA());
+ }
+
+ return $count;
+ }
+
+ /**
+ * These functions accept only numeric arguments, not even strings which are numeric.
+ */
+ private static function numericNotString(mixed $item): bool
+ {
+ return is_numeric($item) && !is_string($item);
+ }
+
+ /**
+ * SUMX2MY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXSquaredMinusYSquared($matrixData1, $matrixData2): string|int|float
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+
+ /**
+ * SUMX2PY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXSquaredPlusYSquared($matrixData1, $matrixData2): string|int|float
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+
+ /**
+ * SUMXMY2.
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ *
+ * @return float|string
+ */
+ public static function sumXMinusYSquared($matrixData1, $matrixData2): string|int|float
+ {
+ try {
+ $array1 = Functions::flattenArray($matrixData1);
+ $array2 = Functions::flattenArray($matrixData2);
+ $count = self::getCount($array1, $array2);
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
+ $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
+ }
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
new file mode 100644
index 0000000..845b6c1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, sin($angle));
+ }
+
+ /**
+ * CSCH.
+ *
+ * Returns the hyperbolic cosecant of an angle.
+ *
+ * @param array|float $angle Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic cosecant of the angle
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function csch($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, sinh($angle));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
new file mode 100644
index 0000000..733e3d6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ return cos($number);
+ }
+
+ /**
+ * COSH.
+ *
+ * Returns the result of builtin function cosh after validating args.
+ *
+ * @param mixed $number Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string hyperbolic cosine
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function cosh(mixed $number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return cosh($number);
+ }
+
+ /**
+ * ACOS.
+ *
+ * Returns the arccosine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arccosine of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acos($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(acos($number));
+ }
+
+ /**
+ * ACOSH.
+ *
+ * Returns the arc inverse hyperbolic cosine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The inverse hyperbolic cosine of the number, or an error string
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acosh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(acosh($number));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
new file mode 100644
index 0000000..861159a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
@@ -0,0 +1,118 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(cos($angle), sin($angle));
+ }
+
+ /**
+ * COTH.
+ *
+ * Returns the hyperbolic cotangent of an angle.
+ *
+ * @param array|float $angle Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic cotangent of the angle
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function coth($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, tanh($angle));
+ }
+
+ /**
+ * ACOT.
+ *
+ * Returns the arccotangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arccotangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acot($number): array|string|float
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (M_PI / 2) - atan($number);
+ }
+
+ /**
+ * ACOTH.
+ *
+ * Returns the hyperbolic arccotangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic arccotangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function acoth($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $result = ($number === 1) ? NAN : (log(($number + 1) / ($number - 1)) / 2);
+
+ return Helpers::numberOrNan($result);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
new file mode 100644
index 0000000..2d26e5d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, cos($angle));
+ }
+
+ /**
+ * SECH.
+ *
+ * Returns the hyperbolic secant of an angle.
+ *
+ * @param array|float $angle Number, or can be an array of numbers
+ *
+ * @return array|float|string The hyperbolic secant of the angle
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function sech($angle)
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::verySmallDenominator(1.0, cosh($angle));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
new file mode 100644
index 0000000..924466e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
@@ -0,0 +1,116 @@
+getMessage();
+ }
+
+ return sin($angle);
+ }
+
+ /**
+ * SINH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @param mixed $angle Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string hyperbolic sine
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function sinh(mixed $angle): array|string|float
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return sinh($angle);
+ }
+
+ /**
+ * ASIN.
+ *
+ * Returns the arcsine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arcsine of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function asin($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(asin($number));
+ }
+
+ /**
+ * ASINH.
+ *
+ * Returns the inverse hyperbolic sine of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The inverse hyperbolic sine of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function asinh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(asinh($number));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
new file mode 100644
index 0000000..74feb7a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
@@ -0,0 +1,161 @@
+getMessage();
+ }
+
+ return Helpers::verySmallDenominator(sin($angle), cos($angle));
+ }
+
+ /**
+ * TANH.
+ *
+ * Returns the result of builtin function sinh after validating args.
+ *
+ * @param mixed $angle Should be numeric, or can be an array of numbers
+ *
+ * @return array|float|string hyperbolic tangent
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function tanh(mixed $angle): array|string|float
+ {
+ if (is_array($angle)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
+ }
+
+ try {
+ $angle = Helpers::validateNumericNullBool($angle);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return tanh($angle);
+ }
+
+ /**
+ * ATAN.
+ *
+ * Returns the arctangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The arctangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function atan($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(atan($number));
+ }
+
+ /**
+ * ATANH.
+ *
+ * Returns the inverse hyperbolic tangent of a number.
+ *
+ * @param array|float $number Number, or can be an array of numbers
+ *
+ * @return array|float|string The inverse hyperbolic tangent of the number
+ * If an array of numbers is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function atanh($number)
+ {
+ if (is_array($number)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
+ }
+
+ try {
+ $number = Helpers::validateNumericNullBool($number);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return Helpers::numberOrNan(atanh($number));
+ }
+
+ /**
+ * ATAN2.
+ *
+ * This function calculates the arc tangent of the two variables x and y. It is similar to
+ * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
+ * to determine the quadrant of the result.
+ * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
+ * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
+ * -pi and pi, excluding -pi.
+ *
+ * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
+ * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
+ *
+ * Excel Function:
+ * ATAN2(xCoordinate,yCoordinate)
+ *
+ * @param mixed $xCoordinate should be float, the x-coordinate of the point, or can be an array of numbers
+ * @param mixed $yCoordinate should be float, the y-coordinate of the point, or can be an array of numbers
+ *
+ * @return array|float|string
+ * The inverse tangent of the specified x- and y-coordinates, or a string containing an error
+ * If an array of numbers is passed as one of the arguments, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function atan2(mixed $xCoordinate, mixed $yCoordinate): array|string|float
+ {
+ if (is_array($xCoordinate) || is_array($yCoordinate)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $xCoordinate, $yCoordinate);
+ }
+
+ try {
+ $xCoordinate = Helpers::validateNumericNullBool($xCoordinate);
+ $yCoordinate = Helpers::validateNumericNullBool($yCoordinate);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($xCoordinate == 0) && ($yCoordinate == 0)) {
+ return ExcelError::DIV0();
+ }
+
+ return atan2($yCoordinate, $xCoordinate);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
new file mode 100644
index 0000000..943e209
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
@@ -0,0 +1,50 @@
+getMessage();
+ }
+
+ $digits = floor($digits);
+
+ // Truncate
+ $adjust = 10 ** $digits;
+
+ if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
+ return $value;
+ }
+
+ return ((int) ($value * $adjust)) / $adjust;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
new file mode 100644
index 0000000..5d85db2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
@@ -0,0 +1,1713 @@
+ $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return ExcelError::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += abs($arg - $aMean);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount === 0) {
+ return ExcelError::DIV0();
+ }
+
+ return $returnValue / $aCount;
+ }
+
+ /**
+ * AVERAGE.
+ *
+ * Returns the average (arithmetic mean) of the arguments
+ *
+ * Excel Function:
+ * AVERAGE(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function average(mixed ...$args): string|int|float
+ {
+ $returnValue = $aCount = 0;
+
+ // Loop through arguments
+ foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return ExcelError::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += $arg;
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ }
+
+ return ExcelError::DIV0();
+ }
+
+ /**
+ * AVERAGEA.
+ *
+ * Returns the average of its arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * AVERAGEA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function averageA(mixed ...$args): string|int|float
+ {
+ $returnValue = null;
+
+ $aCount = 0;
+ // Loop through arguments
+ foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
+ if (is_numeric($arg)) {
+ // do nothing
+ } elseif (is_bool($arg)) {
+ $arg = (int) $arg;
+ } elseif (!Functions::isMatrixValue($k)) {
+ $arg = 0;
+ } else {
+ return ExcelError::VALUE();
+ }
+ $returnValue += $arg;
+ ++$aCount;
+ }
+
+ if ($aCount > 0) {
+ return $returnValue / $aCount;
+ }
+
+ return ExcelError::DIV0();
+ }
+
+ /**
+ * MEDIAN.
+ *
+ * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
+ *
+ * Excel Function:
+ * MEDIAN(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function median(mixed ...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ $returnValue = ExcelError::NAN();
+
+ $aArgs = self::filterArguments($aArgs);
+ $valueCount = count($aArgs);
+ if ($valueCount > 0) {
+ sort($aArgs, SORT_NUMERIC);
+ $valueCount = $valueCount / 2;
+ if ($valueCount == floor($valueCount)) {
+ $returnValue = ($aArgs[$valueCount--] + $aArgs[$valueCount]) / 2;
+ } else {
+ $valueCount = floor($valueCount);
+ $returnValue = $aArgs[$valueCount];
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * MODE.
+ *
+ * Returns the most frequently occurring, or repetitive, value in an array or range of data
+ *
+ * Excel Function:
+ * MODE(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function mode(mixed ...$args)
+ {
+ $returnValue = ExcelError::NA();
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ $aArgs = self::filterArguments($aArgs);
+
+ if (!empty($aArgs)) {
+ return self::modeCalc($aArgs);
+ }
+
+ return $returnValue;
+ }
+
+ protected static function filterArguments(array $args): array
+ {
+ return array_filter(
+ $args,
+ function ($value): bool {
+ // Is it a numeric value?
+ return is_numeric($value) && (!is_string($value));
+ }
+ );
+ }
+
+ /**
+ * Special variant of array_count_values that isn't limited to strings and integers,
+ * but can work with floating point numbers as values.
+ *
+ * @return float|string
+ */
+ private static function modeCalc(array $data)
+ {
+ $frequencyArray = [];
+ $index = 0;
+ $maxfreq = 0;
+ $maxfreqkey = '';
+ $maxfreqdatum = '';
+ foreach ($data as $datum) {
+ $found = false;
+ ++$index;
+ foreach ($frequencyArray as $key => $value) {
+ if ((string) $value['value'] == (string) $datum) {
+ ++$frequencyArray[$key]['frequency'];
+ $freq = $frequencyArray[$key]['frequency'];
+ if ($freq > $maxfreq) {
+ $maxfreq = $freq;
+ $maxfreqkey = $key;
+ $maxfreqdatum = $datum;
+ } elseif ($freq == $maxfreq) {
+ if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) {
+ $maxfreqkey = $key;
+ $maxfreqdatum = $datum;
+ }
+ }
+ $found = true;
+
+ break;
+ }
+ }
+
+ if ($found === false) {
+ $frequencyArray[] = [
+ 'value' => $datum,
+ 'frequency' => 1,
+ 'index' => $index,
+ ];
+ }
+ }
+
+ if ($maxfreq <= 1) {
+ return ExcelError::NA();
+ }
+
+ return $maxfreqdatum;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
new file mode 100644
index 0000000..a05d7c7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
@@ -0,0 +1,132 @@
+ 0)) {
+ $aCount = Counts::COUNT($aArgs);
+ if (Minimum::min($aArgs) > 0) {
+ return $aMean ** (1 / $aCount);
+ }
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * HARMEAN.
+ *
+ * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
+ * arithmetic mean of reciprocals.
+ *
+ * Excel Function:
+ * HARMEAN(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string
+ */
+ public static function harmonic(mixed ...$args): string|float|int
+ {
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ if (Minimum::min($aArgs) < 0) {
+ return ExcelError::NAN();
+ }
+
+ $returnValue = 0;
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if ($arg <= 0) {
+ return ExcelError::NAN();
+ }
+ $returnValue += (1 / $arg);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount > 0) {
+ return 1 / ($returnValue / $aCount);
+ }
+
+ return ExcelError::NA();
+ }
+
+ /**
+ * TRIMMEAN.
+ *
+ * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
+ * taken by excluding a percentage of data points from the top and bottom tails
+ * of a data set.
+ *
+ * Excel Function:
+ * TRIMEAN(value1[,value2[, ...]], $discard)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string
+ */
+ public static function trim(mixed ...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ // Calculate
+ $percent = array_pop($aArgs);
+
+ if ((is_numeric($percent)) && (!is_string($percent))) {
+ if (($percent < 0) || ($percent > 1)) {
+ return ExcelError::NAN();
+ }
+
+ $mArgs = [];
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ $discard = floor(Counts::COUNT($mArgs) * $percent / 2);
+ sort($mArgs);
+
+ for ($i = 0; $i < $discard; ++$i) {
+ array_pop($mArgs);
+ array_shift($mArgs);
+ }
+
+ return Averages::average($mArgs);
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
new file mode 100644
index 0000000..02c2811
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
@@ -0,0 +1,307 @@
+ $value !== null && $value !== ''
+ );
+
+ $range = array_merge([[self::CONDITION_COLUMN_NAME]], array_chunk($range, 1));
+ $condition = array_merge([[self::CONDITION_COLUMN_NAME]], [[$condition]]);
+
+ return DCount::evaluate($range, null, $condition, false);
+ }
+
+ /**
+ * COUNTIFS.
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
+ *
+ * @param mixed $args Pairs of Ranges and Criteria
+ *
+ * @return int|string
+ */
+ public static function COUNTIFS(mixed ...$args)
+ {
+ if (empty($args)) {
+ return 0;
+ } elseif (count($args) === 2) {
+ return self::COUNTIF(...$args);
+ }
+
+ $database = self::buildDatabase(...$args);
+ $conditions = self::buildConditionSet(...$args);
+
+ return DCount::evaluate($database, null, $conditions, false);
+ }
+
+ /**
+ * MAXIFS.
+ *
+ * Returns the maximum value within a range of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
+ *
+ * @param mixed $args Pairs of Ranges and Criteria
+ *
+ * @return null|float|string
+ */
+ public static function MAXIFS(mixed ...$args)
+ {
+ if (empty($args)) {
+ return 0.0;
+ }
+
+ $conditions = self::buildConditionSetForValueRange(...$args);
+ $database = self::buildDatabaseWithValueRange(...$args);
+
+ return DMax::evaluate($database, self::VALUE_COLUMN_NAME, $conditions, false);
+ }
+
+ /**
+ * MINIFS.
+ *
+ * Returns the minimum value within a range of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
+ *
+ * @param mixed $args Pairs of Ranges and Criteria
+ *
+ * @return null|float|string
+ */
+ public static function MINIFS(mixed ...$args)
+ {
+ if (empty($args)) {
+ return 0.0;
+ }
+
+ $conditions = self::buildConditionSetForValueRange(...$args);
+ $database = self::buildDatabaseWithValueRange(...$args);
+
+ return DMin::evaluate($database, self::VALUE_COLUMN_NAME, $conditions, false);
+ }
+
+ /**
+ * SUMIF.
+ *
+ * Totals the values of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * SUMIF(range, criteria, [sum_range])
+ *
+ * @param array $range Data values
+ *
+ * @return null|float|string
+ */
+ public static function SUMIF(array $range, mixed $condition, array $sumRange = [])
+ {
+ $database = self::databaseFromRangeAndValue($range, $sumRange);
+ $condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
+
+ return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $condition);
+ }
+
+ /**
+ * SUMIFS.
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * SUMIFS(average_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
+ *
+ * @param mixed $args Pairs of Ranges and Criteria
+ *
+ * @return null|float|string
+ */
+ public static function SUMIFS(mixed ...$args)
+ {
+ if (empty($args)) {
+ return 0.0;
+ } elseif (count($args) === 3) {
+ return self::SUMIF($args[1], $args[2], $args[0]);
+ }
+
+ $conditions = self::buildConditionSetForValueRange(...$args);
+ $database = self::buildDatabaseWithValueRange(...$args);
+
+ return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
+ }
+
+ /** @param array $args */
+ private static function buildConditionSet(...$args): array
+ {
+ $conditions = self::buildConditions(1, ...$args);
+
+ return array_map(null, ...$conditions);
+ }
+
+ /** @param array $args */
+ private static function buildConditionSetForValueRange(...$args): array
+ {
+ $conditions = self::buildConditions(2, ...$args);
+
+ if (count($conditions) === 1) {
+ return array_map(
+ fn ($value): array => [$value],
+ $conditions[0]
+ );
+ }
+
+ return array_map(null, ...$conditions);
+ }
+
+ /** @param array $args */
+ private static function buildConditions(int $startOffset, ...$args): array
+ {
+ $conditions = [];
+
+ $pairCount = 1;
+ $argumentCount = count($args);
+ for ($argument = $startOffset; $argument < $argumentCount; $argument += 2) {
+ $conditions[] = array_merge([sprintf(self::CONDITIONAL_COLUMN_NAME, $pairCount)], [$args[$argument]]);
+ ++$pairCount;
+ }
+
+ return $conditions;
+ }
+
+ /** @param array $args */
+ private static function buildDatabase(...$args): array
+ {
+ $database = [];
+
+ return self::buildDataSet(0, $database, ...$args);
+ }
+
+ /** @param array $args */
+ private static function buildDatabaseWithValueRange(...$args): array
+ {
+ $database = [];
+ $database[] = array_merge(
+ [self::VALUE_COLUMN_NAME],
+ Functions::flattenArray($args[0])
+ );
+
+ return self::buildDataSet(1, $database, ...$args);
+ }
+
+ /** @param array $args */
+ private static function buildDataSet(int $startOffset, array $database, ...$args): array
+ {
+ $pairCount = 1;
+ $argumentCount = count($args);
+ for ($argument = $startOffset; $argument < $argumentCount; $argument += 2) {
+ $database[] = array_merge(
+ [sprintf(self::CONDITIONAL_COLUMN_NAME, $pairCount)],
+ Functions::flattenArray($args[$argument])
+ );
+ ++$pairCount;
+ }
+
+ return array_map(null, ...$database);
+ }
+
+ private static function databaseFromRangeAndValue(array $range, array $valueRange = []): array
+ {
+ $range = Functions::flattenArray($range);
+
+ $valueRange = Functions::flattenArray($valueRange);
+ if (empty($valueRange)) {
+ $valueRange = $range;
+ }
+
+ $database = array_map(null, array_merge([self::CONDITION_COLUMN_NAME], $range), array_merge([self::VALUE_COLUMN_NAME], $valueRange));
+
+ return $database;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php
new file mode 100644
index 0000000..ae4f61e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php
@@ -0,0 +1,52 @@
+getMessage();
+ }
+
+ if (($alpha <= 0) || ($alpha >= 1) || ($stdDev <= 0) || ($size < 1)) {
+ return ExcelError::NAN();
+ }
+ /** @var float */
+ $temp = Distributions\StandardNormal::inverse(1 - $alpha / 2);
+
+ return Functions::scalar($temp * $stdDev / sqrt($size));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
new file mode 100644
index 0000000..20ed634
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
@@ -0,0 +1,96 @@
+ $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if (self::isAcceptedCountable($arg, $k, true)) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * COUNTA.
+ *
+ * Counts the number of cells that are not empty within the list of arguments
+ *
+ * Excel Function:
+ * COUNTA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ */
+ public static function COUNTA(mixed ...$args): int
+ {
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ foreach ($aArgs as $k => $arg) {
+ // Nulls are counted if literals, but not if cell values
+ if ($arg !== null || (!Functions::isCellValue($k))) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * COUNTBLANK.
+ *
+ * Counts the number of empty cells within the list of arguments
+ *
+ * Excel Function:
+ * COUNTBLANK(value1[,value2[, ...]])
+ *
+ * @param mixed $range Data values
+ */
+ public static function COUNTBLANK(mixed $range): int
+ {
+ if ($range === null) {
+ return 1;
+ }
+ if (!is_array($range) || array_key_exists(0, $range)) {
+ throw new CalcException('Must specify range of cells, not any kind of literal');
+ }
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($range);
+ foreach ($aArgs as $arg) {
+ // Is it a blank cell?
+ if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
+ ++$returnValue;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
new file mode 100644
index 0000000..bc6b19c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
@@ -0,0 +1,140 @@
+ $arg) {
+ // Is it a numeric value?
+ if (
+ (is_bool($arg))
+ && ((!Functions::isCellValue($k))
+ || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
+ ) {
+ $arg = (int) $arg;
+ }
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += ($arg - $aMean) ** 2;
+ ++$aCount;
+ }
+ }
+
+ return $aCount === 0 ? ExcelError::VALUE() : $returnValue;
+ }
+
+ /**
+ * KURT.
+ *
+ * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
+ * or flatness of a distribution compared with the normal distribution. Positive
+ * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
+ * relatively flat distribution.
+ *
+ * @param array ...$args Data Series
+ *
+ * @return float|string
+ */
+ public static function kurtosis(...$args): string|int|float
+ {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $mean = Averages::average($aArgs);
+ if (!is_numeric($mean)) {
+ return ExcelError::DIV0();
+ }
+ $stdDev = (float) StandardDeviations::STDEV($aArgs);
+
+ if ($stdDev > 0) {
+ $count = $summer = 0;
+
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += (($arg - $mean) / $stdDev) ** 4;
+ ++$count;
+ }
+ }
+ }
+
+ if ($count > 3) {
+ return $summer * ($count * ($count + 1)
+ / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2
+ / (($count - 2) * ($count - 3)));
+ }
+ }
+
+ return ExcelError::DIV0();
+ }
+
+ /**
+ * SKEW.
+ *
+ * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
+ * of a distribution around its mean. Positive skewness indicates a distribution with an
+ * asymmetric tail extending toward more positive values. Negative skewness indicates a
+ * distribution with an asymmetric tail extending toward more negative values.
+ *
+ * @param array ...$args Data Series
+ *
+ * @return float|int|string The result, or a string containing an error
+ */
+ public static function skew(...$args): string|int|float
+ {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $mean = Averages::average($aArgs);
+ if (!is_numeric($mean)) {
+ return ExcelError::DIV0();
+ }
+ $stdDev = StandardDeviations::STDEV($aArgs);
+ if ($stdDev === 0.0 || is_string($stdDev)) {
+ return ExcelError::DIV0();
+ }
+
+ $count = $summer = 0;
+ // Loop through arguments
+ foreach ($aArgs as $k => $arg) {
+ if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
+ } elseif (!is_numeric($arg)) {
+ return ExcelError::VALUE();
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summer += (($arg - $mean) / $stdDev) ** 3;
+ ++$count;
+ }
+ }
+ }
+
+ if ($count > 2) {
+ return $summer * ($count / (($count - 1) * ($count - 2)));
+ }
+
+ return ExcelError::DIV0();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
new file mode 100644
index 0000000..d99ee60
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
@@ -0,0 +1,283 @@
+getMessage();
+ }
+
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
+ return ExcelError::NAN();
+ }
+
+ $value -= $rMin;
+ $value /= ($rMax - $rMin);
+
+ return self::incompleteBeta($value, $alpha, $beta);
+ }
+
+ /**
+ * BETAINV.
+ *
+ * Returns the inverse of the Beta distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $alpha Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $beta Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $rMin Minimum value as a float
+ * Or can be an array of values
+ * @param mixed $rMax Maximum value as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse(mixed $probability, mixed $alpha, mixed $beta, mixed $rMin = 0.0, mixed $rMax = 1.0): array|string|float
+ {
+ if (is_array($probability) || is_array($alpha) || is_array($beta) || is_array($rMin) || is_array($rMax)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta, $rMin, $rMax);
+ }
+
+ $rMin = $rMin ?? 0.0;
+ $rMax = $rMax ?? 1.0;
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ $beta = DistributionValidations::validateFloat($beta);
+ $rMax = DistributionValidations::validateFloat($rMax);
+ $rMin = DistributionValidations::validateFloat($rMin);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($rMin > $rMax) {
+ $tmp = $rMin;
+ $rMin = $rMax;
+ $rMax = $tmp;
+ }
+ if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateInverse($probability, $alpha, $beta, $rMin, $rMax);
+ }
+
+ private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax): string|float
+ {
+ $a = 0;
+ $b = 2;
+ $guess = ($a + $b) / 2;
+
+ $i = 0;
+ while ((($b - $a) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
+ $guess = ($a + $b) / 2;
+ $result = self::distribution($guess, $alpha, $beta);
+ if (($result === $probability) || ($result === 0.0)) {
+ $b = $a;
+ } elseif ($result > $probability) {
+ $b = $guess;
+ } else {
+ $a = $guess;
+ }
+ }
+
+ if ($i === self::MAX_ITERATIONS) {
+ return ExcelError::NA();
+ }
+
+ return round($rMin + $guess * ($rMax - $rMin), 12);
+ }
+
+ /**
+ * Incomplete beta function.
+ *
+ * @author Jaco van Kooten
+ * @author Paul Meagher
+ *
+ * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
+ *
+ * @param float $x require 0<=x<=1
+ * @param float $p require p>0
+ * @param float $q require q>0
+ *
+ * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
+ */
+ public static function incompleteBeta(float $x, float $p, float $q): float
+ {
+ if ($x <= 0.0) {
+ return 0.0;
+ } elseif ($x >= 1.0) {
+ return 1.0;
+ } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
+ return 0.0;
+ }
+
+ $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
+ if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
+ return $beta_gam * self::betaFraction($x, $p, $q) / $p;
+ }
+
+ return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
+ }
+
+ // Function cache for logBeta function
+ /** @var float */
+ private static $logBetaCacheP = 0.0;
+
+ /** @var float */
+ private static $logBetaCacheQ = 0.0;
+
+ /** @var float */
+ private static $logBetaCacheResult = 0.0;
+
+ /**
+ * The natural logarithm of the beta function.
+ *
+ * @param float $p require p>0
+ * @param float $q require q>0
+ *
+ * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ *
+ * @author Jaco van Kooten
+ */
+ private static function logBeta(float $p, float $q): float
+ {
+ if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
+ self::$logBetaCacheP = $p;
+ self::$logBetaCacheQ = $q;
+ if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
+ self::$logBetaCacheResult = 0.0;
+ } else {
+ self::$logBetaCacheResult = Gamma::logGamma($p) + Gamma::logGamma($q) - Gamma::logGamma($p + $q);
+ }
+ }
+
+ return self::$logBetaCacheResult;
+ }
+
+ /**
+ * Evaluates of continued fraction part of incomplete beta function.
+ * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
+ *
+ * @author Jaco van Kooten
+ */
+ private static function betaFraction(float $x, float $p, float $q): float
+ {
+ $c = 1.0;
+ $sum_pq = $p + $q;
+ $p_plus = $p + 1.0;
+ $p_minus = $p - 1.0;
+ $h = 1.0 - $sum_pq * $x / $p_plus;
+ if (abs($h) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $frac = $h;
+ $m = 1;
+ $delta = 0.0;
+ while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
+ $m2 = 2 * $m;
+ // even index for d
+ $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < self::XMININ) {
+ $c = self::XMININ;
+ }
+ $frac *= $h * $c;
+ // odd index for d
+ $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < self::XMININ) {
+ $h = self::XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < self::XMININ) {
+ $c = self::XMININ;
+ }
+ $delta = $h * $c;
+ $frac *= $delta;
+ ++$m;
+ }
+
+ return $frac;
+ }
+
+ /*
+ private static function betaValue(float $a, float $b): float
+ {
+ return (Gamma::gammaValue($a) * Gamma::gammaValue($b)) /
+ Gamma::gammaValue($a + $b);
+ }
+
+ private static function regularizedIncompleteBeta(float $value, float $a, float $b): float
+ {
+ return self::incompleteBeta($value, $a, $b) / self::betaValue($a, $b);
+ }
+ */
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
new file mode 100644
index 0000000..a4abb06
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
@@ -0,0 +1,234 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($value > $trials)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ return self::calculateCumulativeBinomial($value, $trials, $probability);
+ }
+ /** @var float */
+ $comb = Combinations::withoutRepetition($trials, $value);
+
+ return $comb * $probability ** $value
+ * (1 - $probability) ** ($trials - $value);
+ }
+
+ /**
+ * BINOM.DIST.RANGE.
+ *
+ * Returns returns the Binomial Distribution probability for the number of successes from a specified number
+ * of trials falling into a specified range.
+ *
+ * @param mixed $trials Integer number of trials
+ * Or can be an array of values
+ * @param mixed $probability Probability of success on each trial as a float
+ * Or can be an array of values
+ * @param mixed $successes The integer number of successes in trials
+ * Or can be an array of values
+ * @param mixed $limit Upper limit for successes in trials as null, or an integer
+ * If null, then this will indicate the same as the number of Successes
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function range(mixed $trials, mixed $probability, mixed $successes, mixed $limit = null): array|string|float|int
+ {
+ if (is_array($trials) || is_array($probability) || is_array($successes) || is_array($limit)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $successes, $limit);
+ }
+
+ $limit = $limit ?? $successes;
+
+ try {
+ $trials = DistributionValidations::validateInt($trials);
+ $probability = DistributionValidations::validateProbability($probability);
+ $successes = DistributionValidations::validateInt($successes);
+ $limit = DistributionValidations::validateInt($limit);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($successes < 0) || ($successes > $trials)) {
+ return ExcelError::NAN();
+ }
+ if (($limit < 0) || ($limit > $trials) || $limit < $successes) {
+ return ExcelError::NAN();
+ }
+
+ $summer = 0;
+ for ($i = $successes; $i <= $limit; ++$i) {
+ /** @var float */
+ $comb = Combinations::withoutRepetition($trials, $i);
+ $summer += $comb * $probability ** $i
+ * (1 - $probability) ** ($trials - $i);
+ }
+
+ return $summer;
+ }
+
+ /**
+ * NEGBINOMDIST.
+ *
+ * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
+ * there will be number_f failures before the number_s-th success, when the constant
+ * probability of a success is probability_s. This function is similar to the binomial
+ * distribution, except that the number of successes is fixed, and the number of trials is
+ * variable. Like the binomial, trials are assumed to be independent.
+ *
+ * @param mixed $failures Number of Failures as an integer
+ * Or can be an array of values
+ * @param mixed $successes Threshold number of Successes as an integer
+ * Or can be an array of values
+ * @param mixed $probability Probability of success on each trial as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ *
+ * TODO Add support for the cumulative flag not present for NEGBINOMDIST, but introduced for NEGBINOM.DIST
+ * The cumulative default should be false to reflect the behaviour of NEGBINOMDIST
+ */
+ public static function negative(mixed $failures, mixed $successes, mixed $probability): array|string|float
+ {
+ if (is_array($failures) || is_array($successes) || is_array($probability)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $failures, $successes, $probability);
+ }
+
+ try {
+ $failures = DistributionValidations::validateInt($failures);
+ $successes = DistributionValidations::validateInt($successes);
+ $probability = DistributionValidations::validateProbability($probability);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($failures < 0) || ($successes < 1)) {
+ return ExcelError::NAN();
+ }
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ if (($failures + $successes - 1) <= 0) {
+ return ExcelError::NAN();
+ }
+ }
+ /** @var float */
+ $comb = Combinations::withoutRepetition($failures + $successes - 1, $successes - 1);
+
+ return $comb
+ * ($probability ** $successes) * ((1 - $probability) ** $failures);
+ }
+
+ /**
+ * BINOM.INV.
+ *
+ * Returns the smallest value for which the cumulative binomial distribution is greater
+ * than or equal to a criterion value
+ *
+ * @param mixed $trials number of Bernoulli trials as an integer
+ * Or can be an array of values
+ * @param mixed $probability probability of a success on each trial as a float
+ * Or can be an array of values
+ * @param mixed $alpha criterion value as a float
+ * Or can be an array of values
+ *
+ * @return array|int|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse(mixed $trials, mixed $probability, mixed $alpha): array|string|int
+ {
+ if (is_array($trials) || is_array($probability) || is_array($alpha)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $alpha);
+ }
+
+ try {
+ $trials = DistributionValidations::validateInt($trials);
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($trials < 0) {
+ return ExcelError::NAN();
+ } elseif (($alpha < 0.0) || ($alpha > 1.0)) {
+ return ExcelError::NAN();
+ }
+
+ $successes = 0;
+ while ($successes <= $trials) {
+ $result = self::calculateCumulativeBinomial($successes, $trials, $probability);
+ if ($result >= $alpha) {
+ break;
+ }
+ ++$successes;
+ }
+
+ return $successes;
+ }
+
+ private static function calculateCumulativeBinomial(int $value, int $trials, float $probability): float|int
+ {
+ $summer = 0;
+ for ($i = 0; $i <= $value; ++$i) {
+ /** @var float */
+ $comb = Combinations::withoutRepetition($trials, $i);
+ $summer += $comb * $probability ** $i
+ * (1 - $probability) ** ($trials - $i);
+ }
+
+ return $summer;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
new file mode 100644
index 0000000..c3cc80b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
@@ -0,0 +1,337 @@
+getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+ if ($value < 0) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2));
+ }
+
+ /**
+ * CHIDIST.
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $value Float value for which we want the probability
+ * Or can be an array of values
+ * @param mixed $degrees Integer degrees of freedom
+ * Or can be an array of values
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function distributionLeftTail(mixed $value, mixed $degrees, mixed $cumulative): array|string|int|float
+ {
+ if (is_array($value) || is_array($degrees) || is_array($cumulative)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $cumulative);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $degrees = DistributionValidations::validateInt($degrees);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+ if ($value < 0) {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
+ return 1;
+ }
+
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative === true) {
+ $temp = self::distributionRightTail($value, $degrees);
+
+ return 1 - (is_numeric($temp) ? $temp : 0);
+ }
+
+ return ($value ** (($degrees / 2) - 1) * exp(-$value / 2))
+ / ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
+ }
+
+ /**
+ * CHIINV.
+ *
+ * Returns the inverse of the right-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $degrees Integer degrees of freedom
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverseRightTail(mixed $probability, mixed $degrees)
+ {
+ if (is_array($probability) || is_array($degrees)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+
+ $callback = function ($value) use ($degrees): float {
+ return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2)
+ / Gamma::gammaValue($degrees / 2));
+ };
+
+ $newtonRaphson = new NewtonRaphson($callback);
+
+ return $newtonRaphson->execute($probability);
+ }
+
+ /**
+ * CHIINV.
+ *
+ * Returns the inverse of the left-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $degrees Integer degrees of freedom
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverseLeftTail(mixed $probability, mixed $degrees): array|string|float
+ {
+ if (is_array($probability) || is_array($degrees)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees < 1) {
+ return ExcelError::NAN();
+ }
+
+ return self::inverseLeftTailCalculation($probability, $degrees);
+ }
+
+ /**
+ * CHITEST.
+ *
+ * Uses the chi-square test to calculate the probability that the differences between two supplied data sets
+ * (of observed and expected frequencies), are likely to be simply due to sampling error,
+ * or if they are likely to be real.
+ *
+ * @param mixed $actual an array of observed frequencies
+ * @param mixed $expected an array of expected frequencies
+ *
+ * @return float|string
+ */
+ public static function test(mixed $actual, mixed $expected)
+ {
+ $rows = count($actual);
+ $actual = Functions::flattenArray($actual);
+ $expected = Functions::flattenArray($expected);
+ $columns = intdiv(count($actual), $rows);
+
+ $countActuals = count($actual);
+ $countExpected = count($expected);
+ if ($countActuals !== $countExpected || $countActuals === 1) {
+ return ExcelError::NAN();
+ }
+
+ $result = 0.0;
+ for ($i = 0; $i < $countActuals; ++$i) {
+ if ($expected[$i] == 0.0) {
+ return ExcelError::DIV0();
+ } elseif ($expected[$i] < 0.0) {
+ return ExcelError::NAN();
+ }
+ $result += (($actual[$i] - $expected[$i]) ** 2) / $expected[$i];
+ }
+
+ $degrees = self::degrees($rows, $columns);
+
+ $result = Functions::scalar(self::distributionRightTail($result, $degrees));
+
+ return $result;
+ }
+
+ protected static function degrees(int $rows, int $columns): int
+ {
+ if ($rows === 1) {
+ return $columns - 1;
+ } elseif ($columns === 1) {
+ return $rows - 1;
+ }
+
+ return ($columns - 1) * ($rows - 1);
+ }
+
+ private static function inverseLeftTailCalculation(float $probability, int $degrees): float
+ {
+ // bracket the root
+ $min = 0;
+ $sd = sqrt(2.0 * $degrees);
+ $max = 2 * $sd;
+ $s = -1;
+
+ while ($s * self::pchisq($max, $degrees) > $probability * $s) {
+ $min = $max;
+ $max += 2 * $sd;
+ }
+
+ // Find root using bisection
+ $chi2 = 0.5 * ($min + $max);
+
+ while (($max - $min) > self::EPS * $chi2) {
+ if ($s * self::pchisq($chi2, $degrees) > $probability * $s) {
+ $min = $chi2;
+ } else {
+ $max = $chi2;
+ }
+ $chi2 = 0.5 * ($min + $max);
+ }
+
+ return $chi2;
+ }
+
+ private static function pchisq(float $chi2, int $degrees): float
+ {
+ return self::gammp($degrees, 0.5 * $chi2);
+ }
+
+ private static function gammp(int $n, float $x): float
+ {
+ if ($x < 0.5 * $n + 1) {
+ return self::gser($n, $x);
+ }
+
+ return 1 - self::gcf($n, $x);
+ }
+
+ // Return the incomplete gamma function P(n/2,x) evaluated by
+ // series representation. Algorithm from numerical recipe.
+ // Assume that n is a positive integer and x>0, won't check arguments.
+ // Relative error controlled by the eps parameter
+ private static function gser(int $n, float $x): float
+ {
+ /** @var float */
+ $gln = Gamma::ln($n / 2);
+ $a = 0.5 * $n;
+ $ap = $a;
+ $sum = 1.0 / $a;
+ $del = $sum;
+ for ($i = 1; $i < 101; ++$i) {
+ ++$ap;
+ $del = $del * $x / $ap;
+ $sum += $del;
+ if ($del < $sum * self::EPS) {
+ break;
+ }
+ }
+
+ return $sum * exp(-$x + $a * log($x) - $gln);
+ }
+
+ // Return the incomplete gamma function Q(n/2,x) evaluated by
+ // its continued fraction representation. Algorithm from numerical recipe.
+ // Assume that n is a postive integer and x>0, won't check arguments.
+ // Relative error controlled by the eps parameter
+ private static function gcf(int $n, float $x): float
+ {
+ /** @var float */
+ $gln = Gamma::ln($n / 2);
+ $a = 0.5 * $n;
+ $b = $x + 1 - $a;
+ $fpmin = 1.e-300;
+ $c = 1 / $fpmin;
+ $d = 1 / $b;
+ $h = $d;
+ for ($i = 1; $i < 101; ++$i) {
+ $an = -$i * ($i - $a);
+ $b += 2;
+ $d = $an * $d + $b;
+ if (abs($d) < $fpmin) {
+ $d = $fpmin;
+ }
+ $c = $b + $an / $c;
+ if (abs($c) < $fpmin) {
+ $c = $fpmin;
+ }
+ $d = 1 / $d;
+ $del = $d * $c;
+ $h = $h * $del;
+ if (abs($del - 1) < self::EPS) {
+ break;
+ }
+ }
+
+ return $h * exp(-$x + $a * log($x) - $gln);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
new file mode 100644
index 0000000..61c62f6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
@@ -0,0 +1,21 @@
+ 1.0) {
+ throw new Exception(ExcelError::NAN());
+ }
+
+ return $probability;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
new file mode 100644
index 0000000..ba56848
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
@@ -0,0 +1,55 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($lambda < 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative === true) {
+ return 1 - exp(0 - $value * $lambda);
+ }
+
+ return $lambda * exp(0 - $value * $lambda);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
new file mode 100644
index 0000000..2b2bd43
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
@@ -0,0 +1,64 @@
+getMessage();
+ }
+
+ if ($value < 0 || $u < 1 || $v < 1) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ $adjustedValue = ($u * $value) / ($u * $value + $v);
+
+ return Beta::incompleteBeta($adjustedValue, $u / 2, $v / 2);
+ }
+
+ return (Gamma::gammaValue(($v + $u) / 2)
+ / (Gamma::gammaValue($u / 2) * Gamma::gammaValue($v / 2)))
+ * (($u / $v) ** ($u / 2))
+ * (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
new file mode 100644
index 0000000..2f494bc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
@@ -0,0 +1,74 @@
+getMessage();
+ }
+
+ if (($value <= -1) || ($value >= 1)) {
+ return ExcelError::NAN();
+ }
+
+ return 0.5 * log((1 + $value) / (1 - $value));
+ }
+
+ /**
+ * FISHERINV.
+ *
+ * Returns the inverse of the Fisher transformation. Use this transformation when
+ * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
+ * FISHERINV(y) = x.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse(mixed $probability): array|string|float
+ {
+ if (is_array($probability)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $probability);
+ }
+
+ try {
+ DistributionValidations::validateFloat($probability);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return (exp(2 * $probability) - 1) / (exp(2 * $probability) + 1);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
new file mode 100644
index 0000000..58986c1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
@@ -0,0 +1,151 @@
+getMessage();
+ }
+
+ if ((((int) $value) == ((float) $value)) && $value <= 0.0) {
+ return ExcelError::NAN();
+ }
+
+ return self::gammaValue($value);
+ }
+
+ /**
+ * GAMMADIST.
+ *
+ * Returns the gamma distribution.
+ *
+ * @param mixed $value Float Value at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $a Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $b Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function distribution(mixed $value, mixed $a, mixed $b, mixed $cumulative)
+ {
+ if (is_array($value) || is_array($a) || is_array($b) || is_array($cumulative)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $a, $b, $cumulative);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $a = DistributionValidations::validateFloat($a);
+ $b = DistributionValidations::validateFloat($b);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($value < 0) || ($a <= 0) || ($b <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateDistribution($value, $a, $b, $cumulative);
+ }
+
+ /**
+ * GAMMAINV.
+ *
+ * Returns the inverse of the Gamma distribution.
+ *
+ * @param mixed $probability Float probability at which you want to evaluate the distribution
+ * Or can be an array of values
+ * @param mixed $alpha Parameter to the distribution as a float
+ * Or can be an array of values
+ * @param mixed $beta Parameter to the distribution as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse(mixed $probability, mixed $alpha, mixed $beta)
+ {
+ if (is_array($probability) || is_array($alpha) || is_array($beta)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $alpha = DistributionValidations::validateFloat($alpha);
+ $beta = DistributionValidations::validateFloat($beta);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($alpha <= 0.0) || ($beta <= 0.0)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateInverse($probability, $alpha, $beta);
+ }
+
+ /**
+ * GAMMALN.
+ *
+ * Returns the natural logarithm of the gamma function.
+ *
+ * @param mixed $value Float Value at which you want to evaluate the distribution
+ * Or can be an array of values
+ *
+ * @return array|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function ln(mixed $value): array|string|float
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($value <= 0) {
+ return ExcelError::NAN();
+ }
+
+ return log(self::gammaValue($value));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
new file mode 100644
index 0000000..f199bbf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
@@ -0,0 +1,390 @@
+ Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = self::calculateDistribution($x, $alpha, $beta, true);
+ if (!is_float($result)) {
+ return ExcelError::NA();
+ }
+ $error = $result - $probability;
+
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+
+ $pdf = self::calculateDistribution($x, $alpha, $beta, false);
+ // Avoid division by zero
+ if (!is_float($pdf)) {
+ return ExcelError::NA();
+ }
+ if ($pdf !== 0.0) {
+ $dx = $error / $pdf;
+ $xNew = $x - $dx;
+ }
+
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+
+ if ($i === self::MAX_ITERATIONS) {
+ return ExcelError::NA();
+ }
+
+ return $x;
+ }
+
+ //
+ // Implementation of the incomplete Gamma function
+ //
+ public static function incompleteGamma(float $a, float $x): float
+ {
+ static $max = 32;
+ $summer = 0;
+ for ($n = 0; $n <= $max; ++$n) {
+ $divisor = $a;
+ for ($i = 1; $i <= $n; ++$i) {
+ $divisor *= ($a + $i);
+ }
+ $summer += ($x ** $n / $divisor);
+ }
+
+ return $x ** $a * exp(0 - $x) * $summer;
+ }
+
+ //
+ // Implementation of the Gamma function
+ //
+ public static function gammaValue(float $value): float
+ {
+ if ($value == 0.0) {
+ return 0;
+ }
+
+ static $p0 = 1.000000000190015;
+ static $p = [
+ 1 => 76.18009172947146,
+ 2 => -86.50532032941677,
+ 3 => 24.01409824083091,
+ 4 => -1.231739572450155,
+ 5 => 1.208650973866179e-3,
+ 6 => -5.395239384953e-6,
+ ];
+
+ $y = $x = $value;
+ $tmp = $x + 5.5;
+ $tmp -= ($x + 0.5) * log($tmp);
+
+ $summer = $p0;
+ for ($j = 1; $j <= 6; ++$j) {
+ $summer += ($p[$j] / ++$y);
+ }
+
+ return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
+ }
+
+ private const LG_D1 = -0.5772156649015328605195174;
+
+ private const LG_D2 = 0.4227843350984671393993777;
+
+ private const LG_D4 = 1.791759469228055000094023;
+
+ private const LG_P1 = [
+ 4.945235359296727046734888,
+ 201.8112620856775083915565,
+ 2290.838373831346393026739,
+ 11319.67205903380828685045,
+ 28557.24635671635335736389,
+ 38484.96228443793359990269,
+ 26377.48787624195437963534,
+ 7225.813979700288197698961,
+ ];
+
+ private const LG_P2 = [
+ 4.974607845568932035012064,
+ 542.4138599891070494101986,
+ 15506.93864978364947665077,
+ 184793.2904445632425417223,
+ 1088204.76946882876749847,
+ 3338152.967987029735917223,
+ 5106661.678927352456275255,
+ 3074109.054850539556250927,
+ ];
+
+ private const LG_P4 = [
+ 14745.02166059939948905062,
+ 2426813.369486704502836312,
+ 121475557.4045093227939592,
+ 2663432449.630976949898078,
+ 29403789566.34553899906876,
+ 170266573776.5398868392998,
+ 492612579337.743088758812,
+ 560625185622.3951465078242,
+ ];
+
+ private const LG_Q1 = [
+ 67.48212550303777196073036,
+ 1113.332393857199323513008,
+ 7738.757056935398733233834,
+ 27639.87074403340708898585,
+ 54993.10206226157329794414,
+ 61611.22180066002127833352,
+ 36351.27591501940507276287,
+ 8785.536302431013170870835,
+ ];
+
+ private const LG_Q2 = [
+ 183.0328399370592604055942,
+ 7765.049321445005871323047,
+ 133190.3827966074194402448,
+ 1136705.821321969608938755,
+ 5267964.117437946917577538,
+ 13467014.54311101692290052,
+ 17827365.30353274213975932,
+ 9533095.591844353613395747,
+ ];
+
+ private const LG_Q4 = [
+ 2690.530175870899333379843,
+ 639388.5654300092398984238,
+ 41355999.30241388052042842,
+ 1120872109.61614794137657,
+ 14886137286.78813811542398,
+ 101680358627.2438228077304,
+ 341747634550.7377132798597,
+ 446315818741.9713286462081,
+ ];
+
+ private const LG_C = [
+ -0.001910444077728,
+ 8.4171387781295e-4,
+ -5.952379913043012e-4,
+ 7.93650793500350248e-4,
+ -0.002777777777777681622553,
+ 0.08333333333333333331554247,
+ 0.0057083835261,
+ ];
+
+ // Rough estimate of the fourth root of logGamma_xBig
+ private const LG_FRTBIG = 2.25e76;
+
+ private const PNT68 = 0.6796875;
+
+ // Function cache for logGamma
+ /** @var float */
+ private static $logGammaCacheResult = 0.0;
+
+ /** @var float */
+ private static $logGammaCacheX = 0.0;
+
+ /**
+ * logGamma function.
+ *
+ * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
+ *
+ * The natural logarithm of the gamma function.
+ * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
+ * Applied Mathematics Division
+ * Argonne National Laboratory
+ * Argonne, IL 60439
+ *
+ * References:
+ *
+ * W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
+ * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
+ * K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
+ * Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
+ *
+ *
+ *
+ * From the original documentation:
+ *
+ *
+ * This routine calculates the LOG(GAMMA) function for a positive real argument X.
+ * Computation is based on an algorithm outlined in references 1 and 2.
+ * The program uses rational functions that theoretically approximate LOG(GAMMA)
+ * to at least 18 significant decimal digits. The approximation for X > 12 is from
+ * reference 3, while approximations for X < 12.0 are similar to those in reference
+ * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
+ * the compiler, the intrinsic functions, and proper selection of the
+ * machine-dependent constants.
+ *
+ *
+ * Error returns:
+ * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
+ * The computation is believed to be free of underflow and overflow.
+ *
+ *
+ * @version 1.1
+ *
+ * @author Jaco van Kooten
+ *
+ * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
+ */
+ public static function logGamma(float $x): float
+ {
+ if ($x == self::$logGammaCacheX) {
+ return self::$logGammaCacheResult;
+ }
+
+ $y = $x;
+ if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
+ if ($y <= self::EPS) {
+ $res = -log($y);
+ } elseif ($y <= 1.5) {
+ $res = self::logGamma1($y);
+ } elseif ($y <= 4.0) {
+ $res = self::logGamma2($y);
+ } elseif ($y <= 12.0) {
+ $res = self::logGamma3($y);
+ } else {
+ $res = self::logGamma4($y);
+ }
+ } else {
+ // --------------------------
+ // Return for bad arguments
+ // --------------------------
+ $res = self::MAX_VALUE;
+ }
+
+ // ------------------------------
+ // Final adjustments and return
+ // ------------------------------
+ self::$logGammaCacheX = $x;
+ self::$logGammaCacheResult = $res;
+
+ return $res;
+ }
+
+ private static function logGamma1(float $y): float
+ {
+ // ---------------------
+ // EPS .LT. X .LE. 1.5
+ // ---------------------
+ if ($y < self::PNT68) {
+ $corr = -log($y);
+ $xm1 = $y;
+ } else {
+ $corr = 0.0;
+ $xm1 = $y - 1.0;
+ }
+
+ $xden = 1.0;
+ $xnum = 0.0;
+ if ($y <= 0.5 || $y >= self::PNT68) {
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm1 + self::LG_P1[$i];
+ $xden = $xden * $xm1 + self::LG_Q1[$i];
+ }
+
+ return $corr + $xm1 * (self::LG_D1 + $xm1 * ($xnum / $xden));
+ }
+
+ $xm2 = $y - 1.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + self::LG_P2[$i];
+ $xden = $xden * $xm2 + self::LG_Q2[$i];
+ }
+
+ return $corr + $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
+ }
+
+ private static function logGamma2(float $y): float
+ {
+ // ---------------------
+ // 1.5 .LT. X .LE. 4.0
+ // ---------------------
+ $xm2 = $y - 2.0;
+ $xden = 1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm2 + self::LG_P2[$i];
+ $xden = $xden * $xm2 + self::LG_Q2[$i];
+ }
+
+ return $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
+ }
+
+ protected static function logGamma3(float $y): float
+ {
+ // ----------------------
+ // 4.0 .LT. X .LE. 12.0
+ // ----------------------
+ $xm4 = $y - 4.0;
+ $xden = -1.0;
+ $xnum = 0.0;
+ for ($i = 0; $i < 8; ++$i) {
+ $xnum = $xnum * $xm4 + self::LG_P4[$i];
+ $xden = $xden * $xm4 + self::LG_Q4[$i];
+ }
+
+ return self::LG_D4 + $xm4 * ($xnum / $xden);
+ }
+
+ protected static function logGamma4(float $y): float
+ {
+ // ---------------------------------
+ // Evaluate for argument .GE. 12.0
+ // ---------------------------------
+ $res = 0.0;
+ if ($y <= self::LG_FRTBIG) {
+ $res = self::LG_C[6];
+ $ysq = $y * $y;
+ for ($i = 0; $i < 6; ++$i) {
+ $res = $res / $ysq + self::LG_C[$i];
+ }
+ $res /= $y;
+ $corr = log($y);
+ $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
+ $res += $y * ($corr - 1.0);
+ }
+
+ return $res;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
new file mode 100644
index 0000000..6e072c5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
@@ -0,0 +1,76 @@
+getMessage();
+ }
+
+ if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
+ return ExcelError::NAN();
+ }
+ if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
+ return ExcelError::NAN();
+ }
+ if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
+ return ExcelError::NAN();
+ }
+
+ $successesPopulationAndSample = (float) Combinations::withoutRepetition($populationSuccesses, $sampleSuccesses);
+ $numbersPopulationAndSample = (float) Combinations::withoutRepetition($populationNumber, $sampleNumber);
+ $adjustedPopulationAndSample = (float) Combinations::withoutRepetition(
+ $populationNumber - $populationSuccesses,
+ $sampleNumber - $sampleSuccesses
+ );
+
+ return $successesPopulationAndSample * $adjustedPopulationAndSample / $numbersPopulationAndSample;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
new file mode 100644
index 0000000..3fbdf0f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
@@ -0,0 +1,139 @@
+getMessage();
+ }
+
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ return StandardNormal::cumulative((log($value) - $mean) / $stdDev);
+ }
+
+ /**
+ * LOGNORM.DIST.
+ *
+ * Returns the lognormal distribution of x, where ln(x) is normally distributed
+ * with parameters mean and standard_dev.
+ *
+ * @param mixed $value Float value for which we want the probability
+ * Or can be an array of values
+ * @param mixed $mean Mean value as a float
+ * Or can be an array of values
+ * @param mixed $stdDev Standard Deviation as a float
+ * Or can be an array of values
+ * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function distribution(mixed $value, mixed $mean, mixed $stdDev, mixed $cumulative = false)
+ {
+ if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
+ }
+
+ try {
+ $value = DistributionValidations::validateFloat($value);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ $cumulative = DistributionValidations::validateBool($cumulative);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if (($value <= 0) || ($stdDev <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative === true) {
+ return StandardNormal::distribution((log($value) - $mean) / $stdDev, true);
+ }
+
+ return (1 / (sqrt(2 * M_PI) * $stdDev * $value))
+ * exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
+ }
+
+ /**
+ * LOGINV.
+ *
+ * Returns the inverse of the lognormal cumulative distribution
+ *
+ * @param mixed $probability Float probability for which we want the value
+ * Or can be an array of values
+ * @param mixed $mean Mean Value as a float
+ * Or can be an array of values
+ * @param mixed $stdDev Standard Deviation as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ *
+ * @TODO Try implementing P J Acklam's refinement algorithm for greater
+ * accuracy if I can get my head round the mathematics
+ * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
+ */
+ public static function inverse(mixed $probability, mixed $mean, mixed $stdDev): array|string|float
+ {
+ if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($stdDev <= 0) {
+ return ExcelError::NAN();
+ }
+ /** @var float */
+ $inverse = StandardNormal::inverse($probability);
+
+ return exp($mean + $stdDev * $inverse);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
new file mode 100644
index 0000000..d1727f4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
@@ -0,0 +1,65 @@
+callback = $callback;
+ }
+
+ /** @return float|string */
+ public function execute(float $probability): string|int|float
+ {
+ $xLo = 100;
+ $xHi = 0;
+
+ $dx = 1;
+ $x = $xNew = 1;
+ $i = 0;
+
+ while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
+ // Apply Newton-Raphson step
+ $result = call_user_func($this->callback, $x);
+ $error = $result - $probability;
+
+ if ($error == 0.0) {
+ $dx = 0;
+ } elseif ($error < 0.0) {
+ $xLo = $x;
+ } else {
+ $xHi = $x;
+ }
+
+ // Avoid division by zero
+ if ($result != 0.0) {
+ $dx = $error / $result;
+ $xNew = $x - $dx;
+ }
+
+ // If the NR fails to converge (which for example may be the
+ // case if the initial guess is too rough) we apply a bisection
+ // step to determine a more narrow interval around the root.
+ if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
+ $xNew = ($xLo + $xHi) / 2;
+ $dx = $xNew - $x;
+ }
+ $x = $xNew;
+ }
+
+ if ($i == self::MAX_ITERATIONS) {
+ return ExcelError::NA();
+ }
+
+ return $x;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
new file mode 100644
index 0000000..8d08d57
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
@@ -0,0 +1,180 @@
+getMessage();
+ }
+
+ if ($stdDev < 0) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ return 0.5 * (1 + Engineering\Erf::erfValue(($value - $mean) / ($stdDev * sqrt(2))));
+ }
+
+ return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev))));
+ }
+
+ /**
+ * NORMINV.
+ *
+ * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
+ *
+ * @param mixed $probability Float probability for which we want the value
+ * Or can be an array of values
+ * @param mixed $mean Mean Value as a float
+ * Or can be an array of values
+ * @param mixed $stdDev Standard Deviation as a float
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse(mixed $probability, mixed $mean, mixed $stdDev): array|string|float
+ {
+ if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $mean = DistributionValidations::validateFloat($mean);
+ $stdDev = DistributionValidations::validateFloat($stdDev);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($stdDev < 0) {
+ return ExcelError::NAN();
+ }
+
+ return (self::inverseNcdf($probability) * $stdDev) + $mean;
+ }
+
+ /*
+ * inverse_ncdf.php
+ * -------------------
+ * begin : Friday, January 16, 2004
+ * copyright : (C) 2004 Michael Nickerson
+ * email : nickersonm@yahoo.com
+ *
+ */
+ private static function inverseNcdf(float $p): float
+ {
+ // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
+ // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
+ // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
+ // I have not checked the accuracy of this implementation. Be aware that PHP
+ // will truncate the coeficcients to 14 digits.
+
+ // You have permission to use and distribute this function freely for
+ // whatever purpose you want, but please show common courtesy and give credit
+ // where credit is due.
+
+ // Input paramater is $p - probability - where 0 < p < 1.
+
+ // Coefficients in rational approximations
+ static $a = [
+ 1 => -3.969683028665376e+01,
+ 2 => 2.209460984245205e+02,
+ 3 => -2.759285104469687e+02,
+ 4 => 1.383577518672690e+02,
+ 5 => -3.066479806614716e+01,
+ 6 => 2.506628277459239e+00,
+ ];
+
+ static $b = [
+ 1 => -5.447609879822406e+01,
+ 2 => 1.615858368580409e+02,
+ 3 => -1.556989798598866e+02,
+ 4 => 6.680131188771972e+01,
+ 5 => -1.328068155288572e+01,
+ ];
+
+ static $c = [
+ 1 => -7.784894002430293e-03,
+ 2 => -3.223964580411365e-01,
+ 3 => -2.400758277161838e+00,
+ 4 => -2.549732539343734e+00,
+ 5 => 4.374664141464968e+00,
+ 6 => 2.938163982698783e+00,
+ ];
+
+ static $d = [
+ 1 => 7.784695709041462e-03,
+ 2 => 3.224671290700398e-01,
+ 3 => 2.445134137142996e+00,
+ 4 => 3.754408661907416e+00,
+ ];
+
+ // Define lower and upper region break-points.
+ $p_low = 0.02425; //Use lower region approx. below this
+ $p_high = 1 - $p_low; //Use upper region approx. above this
+
+ if (0 < $p && $p < $p_low) {
+ // Rational approximation for lower region.
+ $q = sqrt(-2 * log($p));
+
+ return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6])
+ / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ } elseif ($p_high < $p && $p < 1) {
+ // Rational approximation for upper region.
+ $q = sqrt(-2 * log(1 - $p));
+
+ return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6])
+ / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+ }
+
+ // Rational approximation for central region.
+ $q = $p - 0.5;
+ $r = $q * $q;
+
+ return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q
+ / ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
new file mode 100644
index 0000000..b5727f7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
@@ -0,0 +1,66 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($mean < 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ $summer = 0;
+ $floor = floor($value);
+ for ($i = 0; $i <= $floor; ++$i) {
+ /** @var float */
+ $fact = MathTrig\Factorial::fact($i);
+ $summer += $mean ** $i / $fact;
+ }
+
+ return exp(0 - $mean) * $summer;
+ }
+ /** @var float */
+ $fact = MathTrig\Factorial::fact($value);
+
+ return (exp(0 - $mean) * $mean ** $value) / $fact;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
new file mode 100644
index 0000000..b7995d4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
@@ -0,0 +1,159 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
+ return ExcelError::NAN();
+ }
+
+ return self::calculateDistribution($value, $degrees, $tails);
+ }
+
+ /**
+ * TINV.
+ *
+ * Returns the one-tailed probability of the chi-squared distribution.
+ *
+ * @param mixed $probability Float probability for the function
+ * Or can be an array of values
+ * @param mixed $degrees Integer value for degrees of freedom
+ * Or can be an array of values
+ *
+ * @return array|float|string The result, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function inverse(mixed $probability, mixed $degrees)
+ {
+ if (is_array($probability) || is_array($degrees)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
+ }
+
+ try {
+ $probability = DistributionValidations::validateProbability($probability);
+ $degrees = DistributionValidations::validateInt($degrees);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($degrees <= 0) {
+ return ExcelError::NAN();
+ }
+
+ $callback = fn ($value) => self::distribution($value, $degrees, 2);
+
+ $newtonRaphson = new NewtonRaphson($callback);
+
+ return $newtonRaphson->execute($probability);
+ }
+
+ private static function calculateDistribution(float $value, int $degrees, int $tails): float
+ {
+ // tdist, which finds the probability that corresponds to a given value
+ // of t with k degrees of freedom. This algorithm is translated from a
+ // pascal function on p81 of "Statistical Computing in Pascal" by D
+ // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
+ // London). The above Pascal algorithm is itself a translation of the
+ // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
+ // Laboratory as reported in (among other places) "Applied Statistics
+ // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
+ // Horwood Ltd.; W. Sussex, England).
+ $tterm = $degrees;
+ $ttheta = atan2($value, sqrt($tterm));
+ $tc = cos($ttheta);
+ $ts = sin($ttheta);
+
+ if (($degrees % 2) === 1) {
+ $ti = 3;
+ $tterm = $tc;
+ } else {
+ $ti = 2;
+ $tterm = 1;
+ }
+
+ $tsum = $tterm;
+ while ($ti < $degrees) {
+ $tterm *= $tc * $tc * ($ti - 1) / $ti;
+ $tsum += $tterm;
+ $ti += 2;
+ }
+
+ $tsum *= $ts;
+ if (($degrees % 2) == 1) {
+ $tsum = Functions::M_2DIVPI * ($tsum + $ttheta);
+ }
+
+ $tValue = 0.5 * (1 + $tsum);
+ if ($tails == 1) {
+ return 1 - abs($tValue);
+ }
+
+ return 1 - abs((1 - $tValue) - $tValue);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
new file mode 100644
index 0000000..2f20b62
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
@@ -0,0 +1,57 @@
+getMessage();
+ }
+
+ if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
+ return ExcelError::NAN();
+ }
+
+ if ($cumulative) {
+ return 1 - exp(0 - ($value / $beta) ** $alpha);
+ }
+
+ return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
new file mode 100644
index 0000000..ba5482f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
@@ -0,0 +1,20 @@
+ $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * MAXA.
+ *
+ * Returns the greatest value in a list of arguments, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * MAXA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float
+ */
+ public static function maxA(mixed ...$args)
+ {
+ $returnValue = null;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ foreach ($aArgs as $arg) {
+ if (ErrorValue::isError($arg)) {
+ $returnValue = $arg;
+
+ break;
+ }
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ if (($returnValue === null) || ($arg > $returnValue)) {
+ $returnValue = $arg;
+ }
+ }
+ }
+
+ if ($returnValue === null) {
+ return 0;
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
new file mode 100644
index 0000000..7ec013f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
@@ -0,0 +1,89 @@
+getMessage();
+ }
+
+ if (($entry < 0) || ($entry > 1)) {
+ return ExcelError::NAN();
+ }
+
+ $mArgs = self::percentileFilterValues($aArgs);
+ $mValueCount = count($mArgs);
+ if ($mValueCount > 0) {
+ sort($mArgs);
+ $count = Counts::COUNT($mArgs);
+ $index = $entry * ($count - 1);
+ $iBase = floor($index);
+ if ($index == $iBase) {
+ return $mArgs[$index];
+ }
+ $iNext = $iBase + 1;
+ $iProportion = $index - $iBase;
+
+ return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
+ }
+
+ return ExcelError::NAN();
+ }
+
+ /**
+ * PERCENTRANK.
+ *
+ * Returns the rank of a value in a data set as a percentage of the data set.
+ * Note that the returned rank is simply rounded to the appropriate significant digits,
+ * rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return
+ * 0.667 rather than 0.666
+ *
+ * @param mixed $valueSet An array of (float) values, or a reference to, a list of numbers
+ * @param mixed $value The number whose rank you want to find
+ * @param mixed $significance The (integer) number of significant digits for the returned percentage value
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function PERCENTRANK(mixed $valueSet, mixed $value, mixed $significance = 3): string|float
+ {
+ $valueSet = Functions::flattenArray($valueSet);
+ $value = Functions::flattenSingleValue($value);
+ $significance = ($significance === null) ? 3 : Functions::flattenSingleValue($significance);
+
+ try {
+ $value = StatisticalValidations::validateFloat($value);
+ $significance = StatisticalValidations::validateInt($significance);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $valueSet = self::rankFilterValues($valueSet);
+ $valueCount = count($valueSet);
+ if ($valueCount == 0) {
+ return ExcelError::NA();
+ }
+ sort($valueSet, SORT_NUMERIC);
+
+ $valueAdjustor = $valueCount - 1;
+ if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
+ return ExcelError::NA();
+ }
+
+ $pos = array_search($value, $valueSet);
+ if ($pos === false) {
+ $pos = 0;
+ $testValue = $valueSet[0];
+ while ($testValue < $value) {
+ $testValue = $valueSet[++$pos];
+ }
+ --$pos;
+ $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
+ }
+
+ return round(((float) $pos) / $valueAdjustor, $significance);
+ }
+
+ /**
+ * QUARTILE.
+ *
+ * Returns the quartile of a data set.
+ *
+ * Excel Function:
+ * QUARTILE(value1[,value2[, ...]],entry)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function QUARTILE(mixed ...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+ $entry = array_pop($aArgs);
+
+ try {
+ $entry = StatisticalValidations::validateFloat($entry);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $entry = floor($entry);
+ $entry /= 4;
+ if (($entry < 0) || ($entry > 1)) {
+ return ExcelError::NAN();
+ }
+
+ return self::PERCENTILE($aArgs, $entry);
+ }
+
+ /**
+ * RANK.
+ *
+ * Returns the rank of a number in a list of numbers.
+ *
+ * @param mixed $value The number whose rank you want to find
+ * @param mixed $valueSet An array of float values, or a reference to, a list of numbers
+ * @param mixed $order Order to sort the values in the value set
+ *
+ * @return float|string The result, or a string containing an error (0 = Descending, 1 = Ascending)
+ */
+ public static function RANK(mixed $value, mixed $valueSet, mixed $order = self::RANK_SORT_DESCENDING)
+ {
+ $value = Functions::flattenSingleValue($value);
+ $valueSet = Functions::flattenArray($valueSet);
+ $order = ($order === null) ? self::RANK_SORT_DESCENDING : Functions::flattenSingleValue($order);
+
+ try {
+ $value = StatisticalValidations::validateFloat($value);
+ $order = StatisticalValidations::validateInt($order);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $valueSet = self::rankFilterValues($valueSet);
+ if ($order === self::RANK_SORT_DESCENDING) {
+ rsort($valueSet, SORT_NUMERIC);
+ } else {
+ sort($valueSet, SORT_NUMERIC);
+ }
+
+ $pos = array_search($value, $valueSet);
+ if ($pos === false) {
+ return ExcelError::NA();
+ }
+
+ return ++$pos;
+ }
+
+ protected static function percentileFilterValues(array $dataSet): array
+ {
+ return array_filter(
+ $dataSet,
+ fn ($value): bool => is_numeric($value) && !is_string($value)
+ );
+ }
+
+ protected static function rankFilterValues(array $dataSet): array
+ {
+ return array_filter(
+ $dataSet,
+ fn ($value): bool => is_numeric($value)
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
new file mode 100644
index 0000000..41f641f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
@@ -0,0 +1,99 @@
+getMessage();
+ }
+
+ if ($numObjs < $numInSet) {
+ return ExcelError::NAN();
+ }
+ $result1 = MathTrig\Factorial::fact($numObjs);
+ if (is_string($result1)) {
+ return $result1;
+ }
+ $result2 = MathTrig\Factorial::fact($numObjs - $numInSet);
+ if (is_string($result2)) {
+ return $result2;
+ }
+ // phpstan thinks result1 and result2 can be arrays; they can't.
+ $result = round($result1 / $result2); // @phpstan-ignore-line
+
+ return IntOrFloat::evaluate($result);
+ }
+
+ /**
+ * PERMUTATIONA.
+ *
+ * Returns the number of permutations for a given number of objects (with repetitions)
+ * that can be selected from the total objects.
+ *
+ * @param mixed $numObjs Integer number of different objects
+ * Or can be an array of values
+ * @param mixed $numInSet Integer number of objects in each permutation
+ * Or can be an array of values
+ *
+ * @return array|float|int|string Number of permutations, or a string containing an error
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function PERMUTATIONA(mixed $numObjs, mixed $numInSet)
+ {
+ if (is_array($numObjs) || is_array($numInSet)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
+ }
+
+ try {
+ $numObjs = StatisticalValidations::validateInt($numObjs);
+ $numInSet = StatisticalValidations::validateInt($numInSet);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ if ($numObjs < 0 || $numInSet < 0) {
+ return ExcelError::NAN();
+ }
+
+ $result = $numObjs ** $numInSet;
+
+ return IntOrFloat::evaluate($result);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
new file mode 100644
index 0000000..71594bd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
@@ -0,0 +1,97 @@
+= $count) {
+ return ExcelError::NAN();
+ }
+ rsort($mArgs);
+
+ return $mArgs[$entry];
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * SMALL.
+ *
+ * Returns the nth smallest value in a data set. You can use this function to
+ * select a value based on its relative standing.
+ *
+ * Excel Function:
+ * SMALL(value1[,value2[, ...]],entry)
+ *
+ * @param mixed $args Data values
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function small(mixed ...$args)
+ {
+ $aArgs = Functions::flattenArray($args);
+
+ $entry = array_pop($aArgs);
+
+ if ((is_numeric($entry)) && (!is_string($entry))) {
+ $entry = (int) floor($entry);
+
+ $mArgs = self::filter($aArgs);
+ $count = Counts::COUNT($mArgs);
+ --$entry;
+ if ($count === 0 || $entry < 0 || $entry >= $count) {
+ return ExcelError::NAN();
+ }
+ sort($mArgs);
+
+ return $mArgs[$entry];
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * @param mixed[] $args Data values
+ */
+ protected static function filter(array $args): array
+ {
+ $mArgs = [];
+
+ foreach ($args as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $mArgs[] = $arg;
+ }
+ }
+
+ return $mArgs;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
new file mode 100644
index 0000000..095e9cf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
@@ -0,0 +1,95 @@
+getMessage();
+ }
+
+ if ($stdDev <= 0) {
+ return ExcelError::NAN();
+ }
+
+ return ($value - $mean) / $stdDev;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
new file mode 100644
index 0000000..59ad7ee
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
@@ -0,0 +1,36 @@
+ $value) {
+ if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
+ unset($array1[$key], $array2[$key]);
+ }
+ }
+ }
+
+ /**
+ * @param mixed $array1 should be array, but scalar is made into one
+ * @param mixed $array2 should be array, but scalar is made into one
+ */
+ private static function checkTrendArrays(mixed &$array1, mixed &$array2): void
+ {
+ if (!is_array($array1)) {
+ $array1 = [$array1];
+ }
+ if (!is_array($array2)) {
+ $array2 = [$array2];
+ }
+
+ $array1 = Functions::flattenArray($array1);
+ $array2 = Functions::flattenArray($array2);
+
+ self::filterTrendValues($array1, $array2);
+ self::filterTrendValues($array2, $array1);
+
+ // Reset the array indexes
+ $array1 = array_merge($array1);
+ $array2 = array_merge($array2);
+ }
+
+ protected static function validateTrendArrays(array $yValues, array $xValues): void
+ {
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
+ throw new Exception(ExcelError::NA());
+ } elseif ($yValueCount === 1) {
+ throw new Exception(ExcelError::DIV0());
+ }
+ }
+
+ /**
+ * CORREL.
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param mixed $yValues array of mixed Data Series Y
+ * @param null|mixed $xValues array of mixed Data Series X
+ *
+ * @return float|string
+ */
+ public static function CORREL(mixed $yValues, $xValues = null)
+ {
+ if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
+ return ExcelError::VALUE();
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getCorrelation();
+ }
+
+ /**
+ * COVAR.
+ *
+ * Returns covariance, the average of the products of deviations for each data point pair.
+ *
+ * @param mixed[] $yValues array of mixed Data Series Y
+ * @param mixed[] $xValues array of mixed Data Series X
+ *
+ * @return float|string
+ */
+ public static function COVAR(array $yValues, array $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getCovariance();
+ }
+
+ /**
+ * FORECAST.
+ *
+ * Calculates, or predicts, a future value by using existing values.
+ * The predicted value is a y-value for a given x-value.
+ *
+ * @param mixed $xValue Float value of X for which we want to find Y
+ * Or can be an array of values
+ * @param mixed[] $yValues array of mixed Data Series Y
+ * @param mixed[] $xValues array of mixed Data Series X
+ *
+ * @return array|bool|float|string
+ * If an array of numbers is passed as an argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function FORECAST(mixed $xValue, array $yValues, array $xValues)
+ {
+ if (is_array($xValue)) {
+ return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $xValue, $yValues, $xValues);
+ }
+
+ try {
+ $xValue = StatisticalValidations::validateFloat($xValue);
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getValueOfYForX($xValue);
+ }
+
+ /**
+ * GROWTH.
+ *
+ * Returns values along a predicted exponential Trend
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ * @param mixed[] $newValues Values of X for which we want to find Y
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ *
+ * @return float[]
+ */
+ public static function GROWTH($yValues, $xValues = [], $newValues = [], mixed $const = true)
+ {
+ $yValues = Functions::flattenArray($yValues);
+ $xValues = Functions::flattenArray($xValues);
+ $newValues = Functions::flattenArray($newValues);
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+
+ $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
+ if (empty($newValues)) {
+ $newValues = $bestFitExponential->getXValues();
+ }
+
+ $returnArray = [];
+ foreach ($newValues as $xValue) {
+ $returnArray[0][] = [$bestFitExponential->getValueOfYForX($xValue)];
+ }
+
+ return $returnArray; //* @phpstan-ignore-line
+ }
+
+ /**
+ * INTERCEPT.
+ *
+ * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string
+ */
+ public static function INTERCEPT(array $yValues, array $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getIntersect();
+ }
+
+ /**
+ * LINEST.
+ *
+ * Calculates the statistics for a line by using the "least squares" method to calculate a straight line
+ * that best fits your data, and then returns an array that describes the line.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param null|mixed[] $xValues Data Series X
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function LINEST(array $yValues, $xValues = null, mixed $const = true, mixed $stats = false): string|array
+ {
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+ $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
+ if ($xValues === null) {
+ $xValues = $yValues;
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
+
+ if ($stats === true) {
+ return [
+ [
+ $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect(),
+ ],
+ [
+ $bestFitLinear->getSlopeSE(),
+ ($const === false) ? ExcelError::NA() : $bestFitLinear->getIntersectSE(),
+ ],
+ [
+ $bestFitLinear->getGoodnessOfFit(),
+ $bestFitLinear->getStdevOfResiduals(),
+ ],
+ [
+ $bestFitLinear->getF(),
+ $bestFitLinear->getDFResiduals(),
+ ],
+ [
+ $bestFitLinear->getSSRegression(),
+ $bestFitLinear->getSSResiduals(),
+ ],
+ ];
+ }
+
+ return [
+ $bestFitLinear->getSlope(),
+ $bestFitLinear->getIntersect(),
+ ];
+ }
+
+ /**
+ * LOGEST.
+ *
+ * Calculates an exponential curve that best fits the X and Y data series,
+ * and then returns an array that describes the line.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param null|mixed[] $xValues Data Series X
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
+ *
+ * @return array|string The result, or a string containing an error
+ */
+ public static function LOGEST(array $yValues, $xValues = null, mixed $const = true, mixed $stats = false): string|array
+ {
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+ $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
+ if ($xValues === null) {
+ $xValues = $yValues;
+ }
+
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ foreach ($yValues as $value) {
+ if ($value < 0.0) {
+ return ExcelError::NAN();
+ }
+ }
+
+ $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
+
+ if ($stats === true) {
+ return [
+ [
+ $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect(),
+ ],
+ [
+ $bestFitExponential->getSlopeSE(),
+ ($const === false) ? ExcelError::NA() : $bestFitExponential->getIntersectSE(),
+ ],
+ [
+ $bestFitExponential->getGoodnessOfFit(),
+ $bestFitExponential->getStdevOfResiduals(),
+ ],
+ [
+ $bestFitExponential->getF(),
+ $bestFitExponential->getDFResiduals(),
+ ],
+ [
+ $bestFitExponential->getSSRegression(),
+ $bestFitExponential->getSSResiduals(),
+ ],
+ ];
+ }
+
+ return [
+ $bestFitExponential->getSlope(),
+ $bestFitExponential->getIntersect(),
+ ];
+ }
+
+ /**
+ * RSQ.
+ *
+ * Returns the square of the Pearson product moment correlation coefficient through data points
+ * in known_y's and known_x's.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function RSQ(array $yValues, array $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getGoodnessOfFit();
+ }
+
+ /**
+ * SLOPE.
+ *
+ * Returns the slope of the linear regression line through data points in known_y's and known_x's.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string The result, or a string containing an error
+ */
+ public static function SLOPE(array $yValues, array $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getSlope();
+ }
+
+ /**
+ * STEYX.
+ *
+ * Returns the standard error of the predicted y-value for each x in the regression.
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ *
+ * @return float|string
+ */
+ public static function STEYX(array $yValues, array $xValues)
+ {
+ try {
+ self::checkTrendArrays($yValues, $xValues);
+ self::validateTrendArrays($yValues, $xValues);
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
+
+ return $bestFitLinear->getStdevOfResiduals();
+ }
+
+ /**
+ * TREND.
+ *
+ * Returns values along a linear Trend
+ *
+ * @param mixed[] $yValues Data Series Y
+ * @param mixed[] $xValues Data Series X
+ * @param mixed[] $newValues Values of X for which we want to find Y
+ * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
+ *
+ * @return float[]
+ */
+ public static function TREND($yValues, $xValues = [], $newValues = [], mixed $const = true)
+ {
+ $yValues = Functions::flattenArray($yValues);
+ $xValues = Functions::flattenArray($xValues);
+ $newValues = Functions::flattenArray($newValues);
+ $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
+
+ $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
+ if (empty($newValues)) {
+ $newValues = $bestFitLinear->getXValues();
+ }
+
+ $returnArray = [];
+ foreach ($newValues as $xValue) {
+ $returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
+ }
+
+ return $returnArray; //* @phpstan-ignore-line
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
new file mode 100644
index 0000000..883f6c9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
@@ -0,0 +1,34 @@
+ 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARA.
+ *
+ * Estimates variance based on a sample, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARA(mixed ...$args): string|float
+ {
+ $returnValue = ExcelError::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) && (Functions::isValue($k))) {
+ return ExcelError::VALUE();
+ } elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 1) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * ($aCount - 1));
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARP.
+ *
+ * Calculates variance based on the entire population
+ *
+ * Excel Function:
+ * VARP(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARP(mixed ...$args): float|string
+ {
+ // Return value
+ $returnValue = ExcelError::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArray($args);
+ $aCount = 0;
+ foreach ($aArgs as $arg) {
+ $arg = self::datatypeAdjustmentBooleans($arg);
+
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * $aCount);
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * VARPA.
+ *
+ * Calculates variance based on the entire population, including numbers, text, and logical values
+ *
+ * Excel Function:
+ * VARPA(value1[,value2[, ...]])
+ *
+ * @param mixed ...$args Data values
+ *
+ * @return float|string (string if result is an error)
+ */
+ public static function VARPA(mixed ...$args): string|float
+ {
+ $returnValue = ExcelError::DIV0();
+
+ $summerA = $summerB = 0.0;
+
+ // Loop through arguments
+ $aArgs = Functions::flattenArrayIndexed($args);
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ if ((is_string($arg)) && (Functions::isValue($k))) {
+ return ExcelError::VALUE();
+ } elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
+ } else {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $arg = self::datatypeAdjustmentAllowStrings($arg);
+ $summerA += ($arg * $arg);
+ $summerB += $arg;
+ ++$aCount;
+ }
+ }
+ }
+
+ if ($aCount > 0) {
+ $summerA *= $aCount;
+ $summerB *= $summerB;
+
+ return ($summerA - $summerB) / ($aCount * $aCount);
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
new file mode 100644
index 0000000..94edad8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
@@ -0,0 +1,421 @@
+ 255) {
+ return ExcelError::VALUE();
+ }
+ $result = iconv('UCS-4LE', 'UTF-8', pack('V', $character));
+
+ return ($result === false) ? '' : $result;
+ }
+
+ /**
+ * CODE.
+ *
+ * @param mixed $characters String character to convert to its ASCII value
+ * Or can be an array of values
+ *
+ * @return array|int|string A string if arguments are invalid
+ * If an array of values is passed as the argument, then the returned result will also be an array
+ * with the same dimensions
+ */
+ public static function code(mixed $characters): array|string|int
+ {
+ if (is_array($characters)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $characters);
+ }
+
+ $characters = Helpers::extractString($characters);
+ if ($characters === '') {
+ return ExcelError::VALUE();
+ }
+
+ $character = $characters;
+ if (mb_strlen($characters, 'UTF-8') > 1) {
+ $character = mb_substr($characters, 0, 1, 'UTF-8');
+ }
+
+ return self::unicodeToOrd($character);
+ }
+
+ private static function unicodeToOrd(string $character): int
+ {
+ $retVal = 0;
+ $iconv = iconv('UTF-8', 'UCS-4LE', $character);
+ if ($iconv !== false) {
+ $result = unpack('V', $iconv);
+ if (is_array($result) && isset($result[1])) {
+ $retVal = $result[1];
+ }
+ }
+
+ return $retVal;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
new file mode 100644
index 0000000..c2281d4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
@@ -0,0 +1,137 @@
+ DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::CALC();
+
+ break;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * TEXTJOIN.
+ *
+ * @param mixed $delimiter The delimter to use between the joined arguments
+ * Or can be an array of values
+ * @param mixed $ignoreEmpty true/false Flag indicating whether empty arguments should be skipped
+ * Or can be an array of values
+ * @param mixed $args The values to join
+ *
+ * @return array|string The joined string
+ * If an array of values is passed for the $delimiter or $ignoreEmpty arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function TEXTJOIN(mixed $delimiter = '', mixed $ignoreEmpty = true, mixed ...$args): array|string
+ {
+ if (is_array($delimiter) || is_array($ignoreEmpty)) {
+ return self::evaluateArrayArgumentsSubset(
+ [self::class, __FUNCTION__],
+ 2,
+ $delimiter,
+ $ignoreEmpty,
+ ...$args
+ );
+ }
+
+ $delimiter ??= '';
+ $ignoreEmpty ??= true;
+ $aArgs = Functions::flattenArray($args);
+ $returnValue = self::evaluateTextJoinArray($ignoreEmpty, $aArgs);
+
+ $returnValue ??= implode($delimiter, $aArgs);
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::CALC();
+ }
+
+ return $returnValue;
+ }
+
+ private static function evaluateTextJoinArray(bool $ignoreEmpty, array &$aArgs): ?string
+ {
+ foreach ($aArgs as $key => &$arg) {
+ $value = Helpers::extractString($arg);
+ if (ErrorValue::isError($value)) {
+ return $value;
+ }
+
+ if ($ignoreEmpty === true && ((is_string($arg) && trim($arg) === '') || $arg === null)) {
+ unset($aArgs[$key]);
+ } elseif (is_bool($arg)) {
+ $arg = Helpers::convertBooleanValue($arg);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * REPT.
+ *
+ * Returns the result of builtin function round after validating args.
+ *
+ * @param mixed $stringValue The value to repeat
+ * Or can be an array of values
+ * @param mixed $repeatCount The number of times the string value should be repeated
+ * Or can be an array of values
+ *
+ * @return array|string The repeated string
+ * If an array of values is passed for the $stringValue or $repeatCount arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function builtinREPT(mixed $stringValue, mixed $repeatCount): array|string
+ {
+ if (is_array($stringValue) || is_array($repeatCount)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $stringValue, $repeatCount);
+ }
+
+ $stringValue = Helpers::extractString($stringValue);
+
+ if (!is_numeric($repeatCount) || $repeatCount < 0) {
+ $returnValue = ExcelError::VALUE();
+ } elseif (ErrorValue::isError($stringValue)) {
+ $returnValue = $stringValue;
+ } else {
+ $returnValue = str_repeat($stringValue, (int) $repeatCount);
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::VALUE(); // note VALUE not CALC
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
new file mode 100644
index 0000000..275b239
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
@@ -0,0 +1,276 @@
+getMessage();
+ }
+
+ return mb_substr($value, 0, $chars, 'UTF-8');
+ }
+
+ /**
+ * MID.
+ *
+ * @param mixed $value String value from which to extract characters
+ * Or can be an array of values
+ * @param mixed $start Integer offset of the first character that we want to extract
+ * Or can be an array of values
+ * @param mixed $chars The number of characters to extract (as an integer)
+ * Or can be an array of values
+ *
+ * @return array|string The joined string
+ * If an array of values is passed for the $value, $start or $chars arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function mid(mixed $value, mixed $start, mixed $chars): array|string
+ {
+ if (is_array($value) || is_array($start) || is_array($chars)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $start, $chars);
+ }
+
+ try {
+ $value = Helpers::extractString($value);
+ $start = Helpers::extractInt($start, 1);
+ $chars = Helpers::extractInt($chars, 0);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ return mb_substr($value, --$start, $chars, 'UTF-8');
+ }
+
+ /**
+ * RIGHT.
+ *
+ * @param mixed $value String value from which to extract characters
+ * Or can be an array of values
+ * @param mixed $chars The number of characters to extract (as an integer)
+ * Or can be an array of values
+ *
+ * @return array|string The joined string
+ * If an array of values is passed for the $value or $chars arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function right(mixed $value, mixed $chars = 1): array|string
+ {
+ if (is_array($value) || is_array($chars)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $chars);
+ }
+
+ try {
+ $value = Helpers::extractString($value);
+ $chars = Helpers::extractInt($chars, 0, 1);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
+ }
+
+ /**
+ * TEXTBEFORE.
+ *
+ * @param mixed $text the text that you're searching
+ * Or can be an array of values
+ * @param null|array|string $delimiter the text that marks the point before which you want to extract
+ * Multiple delimiters can be passed as an array of string values
+ * @param mixed $instance The instance of the delimiter after which you want to extract the text.
+ * By default, this is the first instance (1).
+ * A negative value means start searching from the end of the text string.
+ * Or can be an array of values
+ * @param mixed $matchMode Determines whether the match is case-sensitive or not.
+ * 0 - Case-sensitive
+ * 1 - Case-insensitive
+ * Or can be an array of values
+ * @param mixed $matchEnd Treats the end of text as a delimiter.
+ * 0 - Don't match the delimiter against the end of the text.
+ * 1 - Match the delimiter against the end of the text.
+ * Or can be an array of values
+ * @param mixed $ifNotFound value to return if no match is found
+ * The default is a #N/A Error
+ * Or can be an array of values
+ *
+ * @return array|string the string extracted from text before the delimiter; or the $ifNotFound value
+ * If an array of values is passed for any of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function before(mixed $text, $delimiter, mixed $instance = 1, mixed $matchMode = 0, mixed $matchEnd = 0, mixed $ifNotFound = '#N/A'): array|string
+ {
+ if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
+ return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ }
+
+ $text = Helpers::extractString($text ?? '');
+ $instance = (int) $instance;
+ $matchMode = (int) $matchMode;
+ $matchEnd = (int) $matchEnd;
+
+ $split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ if (is_string($split)) {
+ return $split;
+ }
+ if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
+ return ($instance > 0) ? '' : $text;
+ }
+
+ // Adjustment for a match as the first element of the split
+ $flags = self::matchFlags($matchMode);
+ $delimiter = self::buildDelimiter($delimiter);
+ $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
+ $oddReverseAdjustment = count($split) % 2;
+
+ $split = ($instance < 0)
+ ? array_slice($split, 0, max(count($split) - (abs($instance) * 2 - 1) - $adjust - $oddReverseAdjustment, 0))
+ : array_slice($split, 0, $instance * 2 - 1 - $adjust);
+
+ return implode('', $split);
+ }
+
+ /**
+ * TEXTAFTER.
+ *
+ * @param mixed $text the text that you're searching
+ * @param null|array|string $delimiter the text that marks the point before which you want to extract
+ * Multiple delimiters can be passed as an array of string values
+ * @param mixed $instance The instance of the delimiter after which you want to extract the text.
+ * By default, this is the first instance (1).
+ * A negative value means start searching from the end of the text string.
+ * Or can be an array of values
+ * @param mixed $matchMode Determines whether the match is case-sensitive or not.
+ * 0 - Case-sensitive
+ * 1 - Case-insensitive
+ * Or can be an array of values
+ * @param mixed $matchEnd Treats the end of text as a delimiter.
+ * 0 - Don't match the delimiter against the end of the text.
+ * 1 - Match the delimiter against the end of the text.
+ * Or can be an array of values
+ * @param mixed $ifNotFound value to return if no match is found
+ * The default is a #N/A Error
+ * Or can be an array of values
+ *
+ * @return array|string the string extracted from text before the delimiter; or the $ifNotFound value
+ * If an array of values is passed for any of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function after(mixed $text, $delimiter, mixed $instance = 1, mixed $matchMode = 0, mixed $matchEnd = 0, mixed $ifNotFound = '#N/A'): array|string
+ {
+ if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
+ return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ }
+
+ $text = Helpers::extractString($text ?? '');
+ $instance = (int) $instance;
+ $matchMode = (int) $matchMode;
+ $matchEnd = (int) $matchEnd;
+
+ $split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
+ if (is_string($split)) {
+ return $split;
+ }
+ if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
+ return ($instance < 0) ? '' : $text;
+ }
+
+ // Adjustment for a match as the first element of the split
+ $flags = self::matchFlags($matchMode);
+ $delimiter = self::buildDelimiter($delimiter);
+ $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
+ $oddReverseAdjustment = count($split) % 2;
+
+ $split = ($instance < 0)
+ ? array_slice($split, count($split) - ((int) abs($instance + 1) * 2) - $adjust - $oddReverseAdjustment)
+ : array_slice($split, $instance * 2 - $adjust);
+
+ return implode('', $split);
+ }
+
+ /**
+ * @param null|array|string $delimiter
+ * @param int $matchEnd
+ *
+ * @return array|string
+ */
+ private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, int $matchMode, $matchEnd, mixed $ifNotFound)
+ {
+ $flags = self::matchFlags($matchMode);
+ $delimiter = self::buildDelimiter($delimiter);
+
+ if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) {
+ return $ifNotFound;
+ }
+
+ $split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+ if ($split === false) {
+ return ExcelError::NA();
+ }
+
+ if ($instance === 0 || abs($instance) > StringHelper::countCharacters($text)) {
+ return ExcelError::VALUE();
+ }
+
+ if ($matchEnd === 0 && (abs($instance) > floor(count($split) / 2))) {
+ return ExcelError::NA();
+ } elseif ($matchEnd !== 0 && (abs($instance) - 1 > ceil(count($split) / 2))) {
+ return ExcelError::NA();
+ }
+
+ return $split;
+ }
+
+ /**
+ * @param null|array|string $delimiter the text that marks the point before which you want to extract
+ * Multiple delimiters can be passed as an array of string values
+ */
+ private static function buildDelimiter($delimiter): string
+ {
+ if (is_array($delimiter)) {
+ $delimiter = Functions::flattenArray($delimiter);
+ $quotedDelimiters = array_map(
+ fn ($delimiter): string => preg_quote($delimiter ?? '', '/'),
+ $delimiter
+ );
+ $delimiters = implode('|', $quotedDelimiters);
+
+ return '(' . $delimiters . ')';
+ }
+
+ return '(' . preg_quote($delimiter ?? '', '/') . ')';
+ }
+
+ private static function matchFlags(int $matchMode): string
+ {
+ return ($matchMode === 0) ? 'mu' : 'miu';
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
new file mode 100644
index 0000000..845df24
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
@@ -0,0 +1,314 @@
+getMessage();
+ }
+
+ $mask = '$#,##0';
+ if ($decimals > 0) {
+ $mask .= '.' . str_repeat('0', $decimals);
+ } else {
+ $round = 10 ** abs($decimals);
+ if ($value < 0) {
+ $round = 0 - $round;
+ }
+ $value = MathTrig\Round::multiple($value, $round);
+ }
+ $mask = "{$mask};-{$mask}";
+
+ return NumberFormat::toFormattedString($value, $mask);
+ }
+
+ /**
+ * FIXED.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $decimals Integer value for the number of decimal places that should be formatted
+ * Or can be an array of values
+ * @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function FIXEDFORMAT(mixed $value, mixed $decimals = 2, mixed $noCommas = false): array|string
+ {
+ if (is_array($value) || is_array($decimals) || is_array($noCommas)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals, $noCommas);
+ }
+
+ try {
+ $value = Helpers::extractFloat($value);
+ $decimals = Helpers::extractInt($decimals, -100, 0, true);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ $valueResult = round($value, $decimals);
+ if ($decimals < 0) {
+ $decimals = 0;
+ }
+ if ($noCommas === false) {
+ $valueResult = number_format(
+ $valueResult,
+ $decimals,
+ StringHelper::getDecimalSeparator(),
+ StringHelper::getThousandsSeparator()
+ );
+ }
+
+ return (string) $valueResult;
+ }
+
+ /**
+ * TEXT.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $format A string with the Format mask that should be used
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function TEXTFORMAT(mixed $value, mixed $format): array|string
+ {
+ if (is_array($value) || is_array($format)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
+ }
+
+ $value = Helpers::extractString($value);
+ $format = Helpers::extractString($format);
+
+ if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) {
+ $value = DateTimeExcel\DateValue::fromString($value) + DateTimeExcel\TimeValue::fromString($value);
+ }
+
+ return (string) NumberFormat::toFormattedString($value, $format);
+ }
+
+ /**
+ * @param mixed $value Value to check
+ *
+ * @return mixed
+ */
+ private static function convertValue(mixed $value, bool $spacesMeanZero = false)
+ {
+ $value = $value ?? 0;
+ if (is_bool($value)) {
+ if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
+ $value = (int) $value;
+ } else {
+ throw new CalcExp(ExcelError::VALUE());
+ }
+ }
+ if (is_string($value)) {
+ $value = trim($value);
+ if ($spacesMeanZero && $value === '') {
+ $value = 0;
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * VALUE.
+ *
+ * @param mixed $value Value to check
+ * Or can be an array of values
+ *
+ * @return array|DateTimeInterface|float|int|string A string if arguments are invalid
+ * If an array of values is passed for the argument, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function VALUE(mixed $value = '')
+ {
+ if (is_array($value)) {
+ return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+ }
+
+ try {
+ $value = self::convertValue($value);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+ if (!is_numeric($value)) {
+ $numberValue = str_replace(
+ StringHelper::getThousandsSeparator(),
+ '',
+ trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
+ );
+ if ($numberValue === '') {
+ return ExcelError::VALUE();
+ }
+ if (is_numeric($numberValue)) {
+ return (float) $numberValue;
+ }
+
+ $dateSetting = Functions::getReturnDateType();
+ Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
+
+ if (str_contains($value, ':')) {
+ $timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
+ if ($timeValue !== ExcelError::VALUE()) {
+ Functions::setReturnDateType($dateSetting);
+
+ return $timeValue;
+ }
+ }
+ $dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value));
+ if ($dateValue !== ExcelError::VALUE()) {
+ Functions::setReturnDateType($dateSetting);
+
+ return $dateValue;
+ }
+ Functions::setReturnDateType($dateSetting);
+
+ return ExcelError::VALUE();
+ }
+
+ return (float) $value;
+ }
+
+ /**
+ * TEXT.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function valueToText(mixed $value, mixed $format = false): array|string
+ {
+ if (is_array($value) || is_array($format)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
+ }
+
+ $format = (bool) $format;
+
+ if (is_object($value) && $value instanceof RichText) {
+ $value = $value->getPlainText();
+ }
+ if (is_string($value)) {
+ $value = ($format === true) ? Calculation::wrapResult($value) : $value;
+ $value = str_replace("\n", '', $value);
+ } elseif (is_bool($value)) {
+ $value = Calculation::getLocaleBoolean($value ? 'TRUE' : 'FALSE');
+ }
+
+ return (string) $value;
+ }
+
+ private static function getDecimalSeparator(mixed $decimalSeparator): string
+ {
+ return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
+ }
+
+ private static function getGroupSeparator(mixed $groupSeparator): string
+ {
+ return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
+ }
+
+ /**
+ * NUMBERVALUE.
+ *
+ * @param mixed $value The value to format
+ * Or can be an array of values
+ * @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
+ * Or can be an array of values
+ * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
+ * Or can be an array of values
+ */
+ public static function NUMBERVALUE(mixed $value = '', mixed $decimalSeparator = null, mixed $groupSeparator = null): array|string|float
+ {
+ if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
+ }
+
+ try {
+ $value = self::convertValue($value, true);
+ $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
+ $groupSeparator = self::getGroupSeparator($groupSeparator);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ if (!is_numeric($value)) {
+ $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator, '/') . '/', $value, $matches, PREG_OFFSET_CAPTURE);
+ if ($decimalPositions > 1) {
+ return ExcelError::VALUE();
+ }
+ $decimalOffset = array_pop($matches[0])[1] ?? null;
+ if ($decimalOffset === null || strpos($value, $groupSeparator, $decimalOffset) !== false) {
+ return ExcelError::VALUE();
+ }
+
+ $value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
+
+ // Handle the special case of trailing % signs
+ $percentageString = rtrim($value, '%');
+ if (!is_numeric($percentageString)) {
+ return ExcelError::VALUE();
+ }
+
+ $percentageAdjustment = strlen($value) - strlen($percentageString);
+ if ($percentageAdjustment) {
+ $value = (float) $percentageString;
+ $value /= 10 ** ($percentageAdjustment * 2);
+ }
+ }
+
+ return is_array($value) ? ExcelError::VALUE() : (float) $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
new file mode 100644
index 0000000..15b0467
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
@@ -0,0 +1,82 @@
+getMessage();
+ }
+ $returnValue = $left . $newText . $right;
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::VALUE();
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * SUBSTITUTE.
+ *
+ * @param mixed $text The text string value to modify
+ * Or can be an array of values
+ * @param mixed $fromText The string value that we want to replace in $text
+ * Or can be an array of values
+ * @param mixed $toText The string value that we want to replace with in $text
+ * Or can be an array of values
+ * @param mixed $instance Integer instance Number for the occurrence of frmText to change
+ * Or can be an array of values
+ *
+ * @return array|string
+ * If an array of values is passed for either of the arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function substitute(mixed $text = '', mixed $fromText = '', mixed $toText = '', mixed $instance = null): array|string
+ {
+ if (is_array($text) || is_array($fromText) || is_array($toText) || is_array($instance)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $text, $fromText, $toText, $instance);
+ }
+
+ try {
+ $text = Helpers::extractString($text, true);
+ $fromText = Helpers::extractString($fromText, true);
+ $toText = Helpers::extractString($toText, true);
+ if ($instance === null) {
+ $returnValue = str_replace($fromText, $toText, $text);
+ } else {
+ if (is_bool($instance)) {
+ if ($instance === false || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
+ return ExcelError::Value();
+ }
+ $instance = 1;
+ }
+ $instance = Helpers::extractInt($instance, 1, 0, true);
+ $returnValue = self::executeSubstitution($text, $fromText, $toText, $instance);
+ }
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+ if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
+ $returnValue = ExcelError::VALUE();
+ }
+
+ return $returnValue;
+ }
+
+ private static function executeSubstitution(string $text, string $fromText, string $toText, int $instance): string
+ {
+ $pos = -1;
+ while ($instance > 0) {
+ $pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
+ if ($pos === false) {
+ return $text;
+ }
+ --$instance;
+ }
+
+ return Functions::scalar(self::REPLACE($text, ++$pos, StringHelper::countCharacters($fromText), $toText));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
new file mode 100644
index 0000000..ad83f1a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
@@ -0,0 +1,97 @@
+getMessage();
+ }
+
+ if (StringHelper::countCharacters($haystack) >= $offset) {
+ if (StringHelper::countCharacters($needle) === 0) {
+ return $offset;
+ }
+
+ $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+
+ return ExcelError::VALUE();
+ }
+
+ /**
+ * SEARCH (case insensitive search).
+ *
+ * @param mixed $needle The string to look for
+ * Or can be an array of values
+ * @param mixed $haystack The string in which to look
+ * Or can be an array of values
+ * @param mixed $offset Integer offset within $haystack to start searching from
+ * Or can be an array of values
+ *
+ * @return array|int|string The offset where the first occurrence of needle was found in the haystack
+ * If an array of values is passed for the $value or $chars arguments, then the returned result
+ * will also be an array with matching dimensions
+ */
+ public static function insensitive(mixed $needle, mixed $haystack, mixed $offset = 1): array|string|int
+ {
+ if (is_array($needle) || is_array($haystack) || is_array($offset)) {
+ return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
+ }
+
+ try {
+ $needle = Helpers::extractString($needle);
+ $haystack = Helpers::extractString($haystack);
+ $offset = Helpers::extractInt($offset, 1, 0, true);
+ } catch (CalcExp $e) {
+ return $e->getMessage();
+ }
+
+ if (StringHelper::countCharacters($haystack) >= $offset) {
+ if (StringHelper::countCharacters($needle) === 0) {
+ return $offset;
+ }
+
+ $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
+ if ($pos !== false) {
+ return ++$pos;
+ }
+ }
+
+ return ExcelError::VALUE();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
new file mode 100644
index 0000000..cca613f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
@@ -0,0 +1,236 @@
+ $row !== ''
+ ));
+ }
+
+ if ($columnDelimiter !== null) {
+ $delimiter = self::buildDelimiter($columnDelimiter);
+ array_walk(
+ $rows,
+ function (&$row) use ($delimiter, $flags, $ignoreEmpty): void {
+ $row = ($delimiter === '()')
+ ? [$row]
+ : preg_split("/{$delimiter}/{$flags}", $row);
+ /** @var array $row */
+ if ($ignoreEmpty === true) {
+ $row = array_values(array_filter(
+ $row,
+ fn ($value): bool => $value !== ''
+ ));
+ }
+ }
+ );
+ if ($ignoreEmpty === true) {
+ $rows = array_values(array_filter(
+ $rows,
+ fn ($row): bool => $row !== [] && $row !== ['']
+ ));
+ }
+ }
+
+ return self::applyPadding($rows, $padding);
+ }
+
+ private static function applyPadding(array $rows, mixed $padding): array
+ {
+ $columnCount = array_reduce(
+ $rows,
+ fn (int $counter, array $row): int => max($counter, count($row)),
+ 0
+ );
+
+ return array_map(
+ function (array $row) use ($columnCount, $padding): array {
+ return (count($row) < $columnCount)
+ ? array_merge($row, array_fill(0, $columnCount - count($row), $padding))
+ : $row;
+ },
+ $rows
+ );
+ }
+
+ /**
+ * @param null|array|string $delimiter the text that marks the point before which you want to split
+ * Multiple delimiters can be passed as an array of string values
+ */
+ private static function buildDelimiter($delimiter): string
+ {
+ $valueSet = Functions::flattenArray($delimiter);
+
+ if (is_array($delimiter) && count($valueSet) > 1) {
+ $quotedDelimiters = array_map(
+ fn ($delimiter): string => preg_quote($delimiter ?? '', '/'),
+ $valueSet
+ );
+ $delimiters = implode('|', $quotedDelimiters);
+
+ return '(' . $delimiters . ')';
+ }
+
+ return '(' . preg_quote(Functions::flattenSingleValue($delimiter), '/') . ')';
+ }
+
+ private static function matchFlags(bool $matchMode): string
+ {
+ return ($matchMode === true) ? 'miu' : 'mu';
+ }
+
+ public static function fromArray(array $array, int $format = 0): string
+ {
+ $result = [];
+ foreach ($array as $row) {
+ $cells = [];
+ foreach ($row as $cellValue) {
+ $value = ($format === 1) ? self::formatValueMode1($cellValue) : self::formatValueMode0($cellValue);
+ $cells[] = $value;
+ }
+ $result[] = implode(($format === 1) ? ',' : ', ', $cells);
+ }
+
+ $result = implode(($format === 1) ? ';' : ', ', $result);
+
+ return ($format === 1) ? '{' . $result . '}' : $result;
+ }
+
+ private static function formatValueMode0(mixed $cellValue): string
+ {
+ if (is_bool($cellValue)) {
+ return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
+ }
+
+ return (string) $cellValue;
+ }
+
+ private static function formatValueMode1(mixed $cellValue): string
+ {
+ if (is_string($cellValue) && ErrorValue::isError($cellValue) === false) {
+ return Calculation::FORMULA_STRING_QUOTE . $cellValue . Calculation::FORMULA_STRING_QUOTE;
+ } elseif (is_bool($cellValue)) {
+ return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
+ }
+
+ return (string) $cellValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
new file mode 100644
index 0000000..24a54bf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
@@ -0,0 +1,52 @@
+branchPruner = $branchPruner;
+ }
+
+ /**
+ * Return the number of entries on the stack.
+ */
+ public function count(): int
+ {
+ return $this->count;
+ }
+
+ /**
+ * Push a new entry onto the stack.
+ */
+ public function push(string $type, mixed $value, ?string $reference = null): void
+ {
+ $stackItem = $this->getStackItem($type, $value, $reference);
+ $this->stack[$this->count++] = $stackItem;
+
+ if ($type === 'Function') {
+ $localeFunction = Calculation::localeFunc($value);
+ if ($localeFunction != $value) {
+ $this->stack[($this->count - 1)]['localeValue'] = $localeFunction;
+ }
+ }
+ }
+
+ public function pushStackItem(array $stackItem): void
+ {
+ $this->stack[$this->count++] = $stackItem;
+ }
+
+ public function getStackItem(string $type, mixed $value, ?string $reference = null): array
+ {
+ $stackItem = [
+ 'type' => $type,
+ 'value' => $value,
+ 'reference' => $reference,
+ ];
+
+ // will store the result under this alias
+ $storeKey = $this->branchPruner->currentCondition();
+ if (isset($storeKey) || $reference === 'NULL') {
+ $stackItem['storeKey'] = $storeKey;
+ }
+
+ // will only run computation if the matching store key is true
+ $onlyIf = $this->branchPruner->currentOnlyIf();
+ if (isset($onlyIf) || $reference === 'NULL') {
+ $stackItem['onlyIf'] = $onlyIf;
+ }
+
+ // will only run computation if the matching store key is false
+ $onlyIfNot = $this->branchPruner->currentOnlyIfNot();
+ if (isset($onlyIfNot) || $reference === 'NULL') {
+ $stackItem['onlyIfNot'] = $onlyIfNot;
+ }
+
+ return $stackItem;
+ }
+
+ /**
+ * Pop the last entry from the stack.
+ */
+ public function pop(): ?array
+ {
+ if ($this->count > 0) {
+ return $this->stack[--$this->count];
+ }
+
+ return null;
+ }
+
+ /**
+ * Return an entry from the stack without removing it.
+ */
+ public function last(int $n = 1): ?array
+ {
+ if ($this->count - $n < 0) {
+ return null;
+ }
+
+ return $this->stack[$this->count - $n];
+ }
+
+ /**
+ * Clear the stack.
+ */
+ public function clear(): void
+ {
+ $this->stack = [];
+ $this->count = 0;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php
new file mode 100644
index 0000000..f4dd40e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php
@@ -0,0 +1,30 @@
+ 2048) {
+ return ExcelError::VALUE(); // Invalid URL length
+ }
+
+ if (!preg_match('/^http[s]?:\/\//', $url)) {
+ return ExcelError::VALUE(); // Invalid protocol
+ }
+
+ // Get results from the the webservice
+ $client = Settings::getHttpClient();
+ $requestFactory = Settings::getRequestFactory();
+ $request = $requestFactory->createRequest('GET', $url);
+
+ try {
+ $response = $client->sendRequest($request);
+ } catch (ClientExceptionInterface) {
+ return ExcelError::VALUE(); // cURL error
+ }
+
+ if ($response->getStatusCode() != 200) {
+ return ExcelError::VALUE(); // cURL error
+ }
+
+ $output = $response->getBody()->getContents();
+ if (strlen($output) > 32767) {
+ return ExcelError::VALUE(); // Output not a string or too long
+ }
+
+ return $output;
+ }
+
+ /**
+ * URLENCODE.
+ *
+ * Returns data from a web service on the Internet or Intranet.
+ *
+ * Excel Function:
+ * urlEncode(text)
+ *
+ * @return string the url encoded output
+ */
+ public static function urlEncode(mixed $text): string|array
+ {
+ if (!is_string($text)) {
+ return ExcelError::VALUE();
+ }
+
+ return str_replace('+', '%20', urlencode($text));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx
new file mode 100644
index 0000000..080d5e7
Binary files /dev/null and b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx differ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config
new file mode 100644
index 0000000..689203c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config
@@ -0,0 +1,24 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## български (Bulgarian)
+##
+############################################################
+
+ArgumentSeparator = ;
+##
+## (For future use)
+##
+currencySymbol = лв
+
+##
+## Error Codes
+##
+NULL = #ПРАЗНО!
+DIV0 = #ДЕЛ/0!
+VALUE = #СТОЙНОСТ!
+REF = #РЕФ!
+NAME = #ИМЕ?
+NUM = #ЧИСЛО!
+NA = #Н/Д
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions
new file mode 100644
index 0000000..ec56433
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions
@@ -0,0 +1,409 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## български (Bulgarian)
+##
+############################################################
+
+
+##
+## Функции Куб (Cube Functions)
+##
+CUBEKPIMEMBER = КУБЭЛЕМЕНТКИП
+CUBEMEMBER = КУБЭЛЕМЕНТ
+CUBEMEMBERPROPERTY = КУБСВОЙСТВОЭЛЕМЕНТА
+CUBERANKEDMEMBER = КУБПОРЭЛЕМЕНТ
+CUBESET = КУБМНОЖ
+CUBESETCOUNT = КУБЧИСЛОЭЛМНОЖ
+CUBEVALUE = КУБЗНАЧЕНИЕ
+
+##
+## Функции для работы с базами данных (Database Functions)
+##
+DAVERAGE = ДСРЗНАЧ
+DCOUNT = БСЧЁТ
+DCOUNTA = БСЧЁТА
+DGET = БИЗВЛЕЧЬ
+DMAX = ДМАКС
+DMIN = ДМИН
+DPRODUCT = БДПРОИЗВЕД
+DSTDEV = ДСТАНДОТКЛ
+DSTDEVP = ДСТАНДОТКЛП
+DSUM = БДСУММ
+DVAR = БДДИСП
+DVARP = БДДИСПП
+
+##
+## Функции даты и времени (Date & Time Functions)
+##
+DATE = ДАТА
+DATEVALUE = ДАТАЗНАЧ
+DAY = ДЕНЬ
+DAYS360 = ДНЕЙ360
+EDATE = ДАТАМЕС
+EOMONTH = КОНМЕСЯЦА
+HOUR = ЧАС
+MINUTE = МИНУТЫ
+MONTH = МЕСЯЦ
+NETWORKDAYS = ЧИСТРАБДНИ
+NOW = ТДАТА
+SECOND = СЕКУНДЫ
+TIME = ВРЕМЯ
+TIMEVALUE = ВРЕМЗНАЧ
+TODAY = СЕГОДНЯ
+WEEKDAY = ДЕНЬНЕД
+WEEKNUM = НОМНЕДЕЛИ
+WORKDAY = РАБДЕНЬ
+YEAR = ГОД
+YEARFRAC = ДОЛЯГОДА
+
+##
+## Инженерные функции (Engineering Functions)
+##
+BESSELI = БЕССЕЛЬ.I
+BESSELJ = БЕССЕЛЬ.J
+BESSELK = БЕССЕЛЬ.K
+BESSELY = БЕССЕЛЬ.Y
+BIN2DEC = ДВ.В.ДЕС
+BIN2HEX = ДВ.В.ШЕСТН
+BIN2OCT = ДВ.В.ВОСЬМ
+COMPLEX = КОМПЛЕКСН
+CONVERT = ПРЕОБР
+DEC2BIN = ДЕС.В.ДВ
+DEC2HEX = ДЕС.В.ШЕСТН
+DEC2OCT = ДЕС.В.ВОСЬМ
+DELTA = ДЕЛЬТА
+ERF = ФОШ
+ERFC = ДФОШ
+GESTEP = ПОРОГ
+HEX2BIN = ШЕСТН.В.ДВ
+HEX2DEC = ШЕСТН.В.ДЕС
+HEX2OCT = ШЕСТН.В.ВОСЬМ
+IMABS = МНИМ.ABS
+IMAGINARY = МНИМ.ЧАСТЬ
+IMARGUMENT = МНИМ.АРГУМЕНТ
+IMCONJUGATE = МНИМ.СОПРЯЖ
+IMCOS = МНИМ.COS
+IMDIV = МНИМ.ДЕЛ
+IMEXP = МНИМ.EXP
+IMLN = МНИМ.LN
+IMLOG10 = МНИМ.LOG10
+IMLOG2 = МНИМ.LOG2
+IMPOWER = МНИМ.СТЕПЕНЬ
+IMPRODUCT = МНИМ.ПРОИЗВЕД
+IMREAL = МНИМ.ВЕЩ
+IMSIN = МНИМ.SIN
+IMSQRT = МНИМ.КОРЕНЬ
+IMSUB = МНИМ.РАЗН
+IMSUM = МНИМ.СУММ
+OCT2BIN = ВОСЬМ.В.ДВ
+OCT2DEC = ВОСЬМ.В.ДЕС
+OCT2HEX = ВОСЬМ.В.ШЕСТН
+
+##
+## Финансовые функции (Financial Functions)
+##
+ACCRINT = НАКОПДОХОД
+ACCRINTM = НАКОПДОХОДПОГАШ
+AMORDEGRC = АМОРУМ
+AMORLINC = АМОРУВ
+COUPDAYBS = ДНЕЙКУПОНДО
+COUPDAYS = ДНЕЙКУПОН
+COUPDAYSNC = ДНЕЙКУПОНПОСЛЕ
+COUPNCD = ДАТАКУПОНПОСЛЕ
+COUPNUM = ЧИСЛКУПОН
+COUPPCD = ДАТАКУПОНДО
+CUMIPMT = ОБЩПЛАТ
+CUMPRINC = ОБЩДОХОД
+DB = ФУО
+DDB = ДДОБ
+DISC = СКИДКА
+DOLLARDE = РУБЛЬ.ДЕС
+DOLLARFR = РУБЛЬ.ДРОБЬ
+DURATION = ДЛИТ
+EFFECT = ЭФФЕКТ
+FV = БС
+FVSCHEDULE = БЗРАСПИС
+INTRATE = ИНОРМА
+IPMT = ПРПЛТ
+IRR = ВСД
+ISPMT = ПРОЦПЛАТ
+MDURATION = МДЛИТ
+MIRR = МВСД
+NOMINAL = НОМИНАЛ
+NPER = КПЕР
+NPV = ЧПС
+ODDFPRICE = ЦЕНАПЕРВНЕРЕГ
+ODDFYIELD = ДОХОДПЕРВНЕРЕГ
+ODDLPRICE = ЦЕНАПОСЛНЕРЕГ
+ODDLYIELD = ДОХОДПОСЛНЕРЕГ
+PMT = ПЛТ
+PPMT = ОСПЛТ
+PRICE = ЦЕНА
+PRICEDISC = ЦЕНАСКИДКА
+PRICEMAT = ЦЕНАПОГАШ
+PV = ПС
+RATE = СТАВКА
+RECEIVED = ПОЛУЧЕНО
+SLN = АПЛ
+SYD = АСЧ
+TBILLEQ = РАВНОКЧЕК
+TBILLPRICE = ЦЕНАКЧЕК
+TBILLYIELD = ДОХОДКЧЕК
+VDB = ПУО
+XIRR = ЧИСТВНДОХ
+XNPV = ЧИСТНЗ
+YIELD = ДОХОД
+YIELDDISC = ДОХОДСКИДКА
+YIELDMAT = ДОХОДПОГАШ
+
+##
+## Информационные функции (Information Functions)
+##
+CELL = ЯЧЕЙКА
+ERROR.TYPE = ТИП.ОШИБКИ
+INFO = ИНФОРМ
+ISBLANK = ЕПУСТО
+ISERR = ЕОШ
+ISERROR = ЕОШИБКА
+ISEVEN = ЕЧЁТН
+ISLOGICAL = ЕЛОГИЧ
+ISNA = ЕНД
+ISNONTEXT = ЕНЕТЕКСТ
+ISNUMBER = ЕЧИСЛО
+ISODD = ЕНЕЧЁТ
+ISREF = ЕССЫЛКА
+ISTEXT = ЕТЕКСТ
+N = Ч
+NA = НД
+TYPE = ТИП
+
+##
+## Логические функции (Logical Functions)
+##
+AND = И
+FALSE = ЛОЖЬ
+IF = ЕСЛИ
+IFERROR = ЕСЛИОШИБКА
+NOT = НЕ
+OR = ИЛИ
+TRUE = ИСТИНА
+
+##
+## Функции ссылки и поиска (Lookup & Reference Functions)
+##
+ADDRESS = АДРЕС
+AREAS = ОБЛАСТИ
+CHOOSE = ВЫБОР
+COLUMN = СТОЛБЕЦ
+COLUMNS = ЧИСЛСТОЛБ
+GETPIVOTDATA = ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ
+HLOOKUP = ГПР
+HYPERLINK = ГИПЕРССЫЛКА
+INDEX = ИНДЕКС
+INDIRECT = ДВССЫЛ
+LOOKUP = ПРОСМОТР
+MATCH = ПОИСКПОЗ
+OFFSET = СМЕЩ
+ROW = СТРОКА
+ROWS = ЧСТРОК
+RTD = ДРВ
+TRANSPOSE = ТРАНСП
+VLOOKUP = ВПР
+
+##
+## Математические и тригонометрические функции (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ASIN = ASIN
+ASINH = ASINH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+COMBIN = ЧИСЛКОМБ
+COS = COS
+COSH = COSH
+DEGREES = ГРАДУСЫ
+EVEN = ЧЁТН
+EXP = EXP
+FACT = ФАКТР
+FACTDOUBLE = ДВФАКТР
+GCD = НОД
+INT = ЦЕЛОЕ
+LCM = НОК
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = МОПРЕД
+MINVERSE = МОБР
+MMULT = МУМНОЖ
+MOD = ОСТАТ
+MROUND = ОКРУГЛТ
+MULTINOMIAL = МУЛЬТИНОМ
+ODD = НЕЧЁТ
+PI = ПИ
+POWER = СТЕПЕНЬ
+PRODUCT = ПРОИЗВЕД
+QUOTIENT = ЧАСТНОЕ
+RADIANS = РАДИАНЫ
+RAND = СЛЧИС
+RANDBETWEEN = СЛУЧМЕЖДУ
+ROMAN = РИМСКОЕ
+ROUND = ОКРУГЛ
+ROUNDDOWN = ОКРУГЛВНИЗ
+ROUNDUP = ОКРУГЛВВЕРХ
+SERIESSUM = РЯД.СУММ
+SIGN = ЗНАК
+SIN = SIN
+SINH = SINH
+SQRT = КОРЕНЬ
+SQRTPI = КОРЕНЬПИ
+SUBTOTAL = ПРОМЕЖУТОЧНЫЕ.ИТОГИ
+SUM = СУММ
+SUMIF = СУММЕСЛИ
+SUMIFS = СУММЕСЛИМН
+SUMPRODUCT = СУММПРОИЗВ
+SUMSQ = СУММКВ
+SUMX2MY2 = СУММРАЗНКВ
+SUMX2PY2 = СУММСУММКВ
+SUMXMY2 = СУММКВРАЗН
+TAN = TAN
+TANH = TANH
+TRUNC = ОТБР
+
+##
+## Статистические функции (Statistical Functions)
+##
+AVEDEV = СРОТКЛ
+AVERAGE = СРЗНАЧ
+AVERAGEA = СРЗНАЧА
+AVERAGEIF = СРЗНАЧЕСЛИ
+AVERAGEIFS = СРЗНАЧЕСЛИМН
+CORREL = КОРРЕЛ
+COUNT = СЧЁТ
+COUNTA = СЧЁТЗ
+COUNTBLANK = СЧИТАТЬПУСТОТЫ
+COUNTIF = СЧЁТЕСЛИ
+COUNTIFS = СЧЁТЕСЛИМН
+DEVSQ = КВАДРОТКЛ
+FISHER = ФИШЕР
+FISHERINV = ФИШЕРОБР
+FREQUENCY = ЧАСТОТА
+GAMMALN = ГАММАНЛОГ
+GEOMEAN = СРГЕОМ
+GROWTH = РОСТ
+HARMEAN = СРГАРМ
+INTERCEPT = ОТРЕЗОК
+KURT = ЭКСЦЕСС
+LARGE = НАИБОЛЬШИЙ
+LINEST = ЛИНЕЙН
+LOGEST = ЛГРФПРИБЛ
+MAX = МАКС
+MAXA = МАКСА
+MEDIAN = МЕДИАНА
+MIN = МИН
+MINA = МИНА
+PEARSON = ПИРСОН
+PERMUT = ПЕРЕСТ
+PROB = ВЕРОЯТНОСТЬ
+RSQ = КВПИРСОН
+SKEW = СКОС
+SLOPE = НАКЛОН
+SMALL = НАИМЕНЬШИЙ
+STANDARDIZE = НОРМАЛИЗАЦИЯ
+STDEVA = СТАНДОТКЛОНА
+STDEVPA = СТАНДОТКЛОНПА
+STEYX = СТОШYX
+TREND = ТЕНДЕНЦИЯ
+TRIMMEAN = УРЕЗСРЕДНЕЕ
+VARA = ДИСПА
+VARPA = ДИСПРА
+
+##
+## Текстовые функции (Text Functions)
+##
+ASC = ASC
+BAHTTEXT = БАТТЕКСТ
+CHAR = СИМВОЛ
+CLEAN = ПЕЧСИМВ
+CODE = КОДСИМВ
+DOLLAR = РУБЛЬ
+EXACT = СОВПАД
+FIND = НАЙТИ
+FINDB = НАЙТИБ
+FIXED = ФИКСИРОВАННЫЙ
+LEFT = ЛЕВСИМВ
+LEFTB = ЛЕВБ
+LEN = ДЛСТР
+LENB = ДЛИНБ
+LOWER = СТРОЧН
+MID = ПСТР
+MIDB = ПСТРБ
+PHONETIC = PHONETIC
+PROPER = ПРОПНАЧ
+REPLACE = ЗАМЕНИТЬ
+REPLACEB = ЗАМЕНИТЬБ
+REPT = ПОВТОР
+RIGHT = ПРАВСИМВ
+RIGHTB = ПРАВБ
+SEARCH = ПОИСК
+SEARCHB = ПОИСКБ
+SUBSTITUTE = ПОДСТАВИТЬ
+T = Т
+TEXT = ТЕКСТ
+TRIM = СЖПРОБЕЛЫ
+UPPER = ПРОПИСН
+VALUE = ЗНАЧЕН
+
+##
+## (Web Functions)
+##
+
+##
+## (Compatibility Functions)
+##
+BETADIST = БЕТАРАСП
+BETAINV = БЕТАОБР
+BINOMDIST = БИНОМРАСП
+CEILING = ОКРВВЕРХ
+CHIDIST = ХИ2РАСП
+CHIINV = ХИ2ОБР
+CHITEST = ХИ2ТЕСТ
+CONCATENATE = СЦЕПИТЬ
+CONFIDENCE = ДОВЕРИТ
+COVAR = КОВАР
+CRITBINOM = КРИТБИНОМ
+EXPONDIST = ЭКСПРАСП
+FDIST = FРАСП
+FINV = FРАСПОБР
+FLOOR = ОКРВНИЗ
+FORECAST = ПРЕДСКАЗ
+FTEST = ФТЕСТ
+GAMMADIST = ГАММАРАСП
+GAMMAINV = ГАММАОБР
+HYPGEOMDIST = ГИПЕРГЕОМЕТ
+LOGINV = ЛОГНОРМОБР
+LOGNORMDIST = ЛОГНОРМРАСП
+MODE = МОДА
+NEGBINOMDIST = ОТРБИНОМРАСП
+NORMDIST = НОРМРАСП
+NORMINV = НОРМОБР
+NORMSDIST = НОРМСТРАСП
+NORMSINV = НОРМСТОБР
+PERCENTILE = ПЕРСЕНТИЛЬ
+PERCENTRANK = ПРОЦЕНТРАНГ
+POISSON = ПУАССОН
+QUARTILE = КВАРТИЛЬ
+RANK = РАНГ
+STDEV = СТАНДОТКЛОН
+STDEVP = СТАНДОТКЛОНП
+TDIST = СТЬЮДРАСП
+TINV = СТЬЮДРАСПОБР
+TTEST = ТТЕСТ
+VAR = ДИСП
+VARP = ДИСПР
+WEIBULL = ВЕЙБУЛЛ
+ZTEST = ZТЕСТ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config
new file mode 100644
index 0000000..49f40fc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Ceština (Czech)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL
+DIV0 = #DĚLENÍ_NULOU!
+VALUE = #HODNOTA!
+REF = #ODKAZ!
+NAME = #NÁZEV?
+NUM = #ČÍSLO!
+NA = #NENÍ_K_DISPOZICI
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions
new file mode 100644
index 0000000..49c4945
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions
@@ -0,0 +1,520 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Ceština (Czech)
+##
+############################################################
+
+
+##
+## Funkce pro práci s datovými krychlemi (Cube Functions)
+##
+CUBEKPIMEMBER = CUBEKPIMEMBER
+CUBEMEMBER = CUBEMEMBER
+CUBEMEMBERPROPERTY = CUBEMEMBERPROPERTY
+CUBERANKEDMEMBER = CUBERANKEDMEMBER
+CUBESET = CUBESET
+CUBESETCOUNT = CUBESETCOUNT
+CUBEVALUE = CUBEVALUE
+
+##
+## Funkce databáze (Database Functions)
+##
+DAVERAGE = DPRŮMĚR
+DCOUNT = DPOČET
+DCOUNTA = DPOČET2
+DGET = DZÍSKAT
+DMAX = DMAX
+DMIN = DMIN
+DPRODUCT = DSOUČIN
+DSTDEV = DSMODCH.VÝBĚR
+DSTDEVP = DSMODCH
+DSUM = DSUMA
+DVAR = DVAR.VÝBĚR
+DVARP = DVAR
+
+##
+## Funkce data a času (Date & Time Functions)
+##
+DATE = DATUM
+DATEVALUE = DATUMHODN
+DAY = DEN
+DAYS = DAYS
+DAYS360 = ROK360
+EDATE = EDATE
+EOMONTH = EOMONTH
+HOUR = HODINA
+ISOWEEKNUM = ISOWEEKNUM
+MINUTE = MINUTA
+MONTH = MĚSÍC
+NETWORKDAYS = NETWORKDAYS
+NETWORKDAYS.INTL = NETWORKDAYS.INTL
+NOW = NYNÍ
+SECOND = SEKUNDA
+TIME = ČAS
+TIMEVALUE = ČASHODN
+TODAY = DNES
+WEEKDAY = DENTÝDNE
+WEEKNUM = WEEKNUM
+WORKDAY = WORKDAY
+WORKDAY.INTL = WORKDAY.INTL
+YEAR = ROK
+YEARFRAC = YEARFRAC
+
+##
+## Inženýrské funkce (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BIN2DEC
+BIN2HEX = BIN2HEX
+BIN2OCT = BIN2OCT
+BITAND = BITAND
+BITLSHIFT = BITLSHIFT
+BITOR = BITOR
+BITRSHIFT = BITRSHIFT
+BITXOR = BITXOR
+COMPLEX = COMPLEX
+CONVERT = CONVERT
+DEC2BIN = DEC2BIN
+DEC2HEX = DEC2HEX
+DEC2OCT = DEC2OCT
+DELTA = DELTA
+ERF = ERF
+ERF.PRECISE = ERF.PRECISE
+ERFC = ERFC
+ERFC.PRECISE = ERFC.PRECISE
+GESTEP = GESTEP
+HEX2BIN = HEX2BIN
+HEX2DEC = HEX2DEC
+HEX2OCT = HEX2OCT
+IMABS = IMABS
+IMAGINARY = IMAGINARY
+IMARGUMENT = IMARGUMENT
+IMCONJUGATE = IMCONJUGATE
+IMCOS = IMCOS
+IMCOSH = IMCOSH
+IMCOT = IMCOT
+IMCSC = IMCSC
+IMCSCH = IMCSCH
+IMDIV = IMDIV
+IMEXP = IMEXP
+IMLN = IMLN
+IMLOG10 = IMLOG10
+IMLOG2 = IMLOG2
+IMPOWER = IMPOWER
+IMPRODUCT = IMPRODUCT
+IMREAL = IMREAL
+IMSEC = IMSEC
+IMSECH = IMSECH
+IMSIN = IMSIN
+IMSINH = IMSINH
+IMSQRT = IMSQRT
+IMSUB = IMSUB
+IMSUM = IMSUM
+IMTAN = IMTAN
+OCT2BIN = OCT2BIN
+OCT2DEC = OCT2DEC
+OCT2HEX = OCT2HEX
+
+##
+## Finanční funkce (Financial Functions)
+##
+ACCRINT = ACCRINT
+ACCRINTM = ACCRINTM
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = COUPDAYBS
+COUPDAYS = COUPDAYS
+COUPDAYSNC = COUPDAYSNC
+COUPNCD = COUPNCD
+COUPNUM = COUPNUM
+COUPPCD = COUPPCD
+CUMIPMT = CUMIPMT
+CUMPRINC = CUMPRINC
+DB = ODPIS.ZRYCH
+DDB = ODPIS.ZRYCH2
+DISC = DISC
+DOLLARDE = DOLLARDE
+DOLLARFR = DOLLARFR
+DURATION = DURATION
+EFFECT = EFFECT
+FV = BUDHODNOTA
+FVSCHEDULE = FVSCHEDULE
+INTRATE = INTRATE
+IPMT = PLATBA.ÚROK
+IRR = MÍRA.VÝNOSNOSTI
+ISPMT = ISPMT
+MDURATION = MDURATION
+MIRR = MOD.MÍRA.VÝNOSNOSTI
+NOMINAL = NOMINAL
+NPER = POČET.OBDOBÍ
+NPV = ČISTÁ.SOUČHODNOTA
+ODDFPRICE = ODDFPRICE
+ODDFYIELD = ODDFYIELD
+ODDLPRICE = ODDLPRICE
+ODDLYIELD = ODDLYIELD
+PDURATION = PDURATION
+PMT = PLATBA
+PPMT = PLATBA.ZÁKLAD
+PRICE = PRICE
+PRICEDISC = PRICEDISC
+PRICEMAT = PRICEMAT
+PV = SOUČHODNOTA
+RATE = ÚROKOVÁ.MÍRA
+RECEIVED = RECEIVED
+RRI = RRI
+SLN = ODPIS.LIN
+SYD = ODPIS.NELIN
+TBILLEQ = TBILLEQ
+TBILLPRICE = TBILLPRICE
+TBILLYIELD = TBILLYIELD
+VDB = ODPIS.ZA.INT
+XIRR = XIRR
+XNPV = XNPV
+YIELD = YIELD
+YIELDDISC = YIELDDISC
+YIELDMAT = YIELDMAT
+
+##
+## Informační funkce (Information Functions)
+##
+CELL = POLÍČKO
+ERROR.TYPE = CHYBA.TYP
+INFO = O.PROSTŘEDÍ
+ISBLANK = JE.PRÁZDNÉ
+ISERR = JE.CHYBA
+ISERROR = JE.CHYBHODN
+ISEVEN = ISEVEN
+ISFORMULA = ISFORMULA
+ISLOGICAL = JE.LOGHODN
+ISNA = JE.NEDEF
+ISNONTEXT = JE.NETEXT
+ISNUMBER = JE.ČISLO
+ISODD = ISODD
+ISREF = JE.ODKAZ
+ISTEXT = JE.TEXT
+N = N
+NA = NEDEF
+SHEET = SHEET
+SHEETS = SHEETS
+TYPE = TYP
+
+##
+## Logické funkce (Logical Functions)
+##
+AND = A
+FALSE = NEPRAVDA
+IF = KDYŽ
+IFERROR = IFERROR
+IFNA = IFNA
+IFS = IFS
+NOT = NE
+OR = NEBO
+SWITCH = SWITCH
+TRUE = PRAVDA
+XOR = XOR
+
+##
+## Vyhledávací funkce a funkce pro odkazy (Lookup & Reference Functions)
+##
+ADDRESS = ODKAZ
+AREAS = POČET.BLOKŮ
+CHOOSE = ZVOLIT
+COLUMN = SLOUPEC
+COLUMNS = SLOUPCE
+FORMULATEXT = FORMULATEXT
+GETPIVOTDATA = ZÍSKATKONTDATA
+HLOOKUP = VVYHLEDAT
+HYPERLINK = HYPERTEXTOVÝ.ODKAZ
+INDEX = INDEX
+INDIRECT = NEPŘÍMÝ.ODKAZ
+LOOKUP = VYHLEDAT
+MATCH = POZVYHLEDAT
+OFFSET = POSUN
+ROW = ŘÁDEK
+ROWS = ŘÁDKY
+RTD = RTD
+TRANSPOSE = TRANSPOZICE
+VLOOKUP = SVYHLEDAT
+
+##
+## Matematické a trigonometrické funkce (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ARCCOS
+ACOSH = ARCCOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = AGGREGATE
+ARABIC = ARABIC
+ASIN = ARCSIN
+ASINH = ARCSINH
+ATAN = ARCTG
+ATAN2 = ARCTG2
+ATANH = ARCTGH
+BASE = BASE
+CEILING.MATH = CEILING.MATH
+COMBIN = KOMBINACE
+COMBINA = COMBINA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DECIMAL
+DEGREES = DEGREES
+EVEN = ZAOKROUHLIT.NA.SUDÉ
+EXP = EXP
+FACT = FAKTORIÁL
+FACTDOUBLE = FACTDOUBLE
+FLOOR.MATH = FLOOR.MATH
+GCD = GCD
+INT = CELÁ.ČÁST
+LCM = LCM
+LN = LN
+LOG = LOGZ
+LOG10 = LOG
+MDETERM = DETERMINANT
+MINVERSE = INVERZE
+MMULT = SOUČIN.MATIC
+MOD = MOD
+MROUND = MROUND
+MULTINOMIAL = MULTINOMIAL
+MUNIT = MUNIT
+ODD = ZAOKROUHLIT.NA.LICHÉ
+PI = PI
+POWER = POWER
+PRODUCT = SOUČIN
+QUOTIENT = QUOTIENT
+RADIANS = RADIANS
+RAND = NÁHČÍSLO
+RANDBETWEEN = RANDBETWEEN
+ROMAN = ROMAN
+ROUND = ZAOKROUHLIT
+ROUNDDOWN = ROUNDDOWN
+ROUNDUP = ROUNDUP
+SEC = SEC
+SECH = SECH
+SERIESSUM = SERIESSUM
+SIGN = SIGN
+SIN = SIN
+SINH = SINH
+SQRT = ODMOCNINA
+SQRTPI = SQRTPI
+SUBTOTAL = SUBTOTAL
+SUM = SUMA
+SUMIF = SUMIF
+SUMIFS = SUMIFS
+SUMPRODUCT = SOUČIN.SKALÁRNÍ
+SUMSQ = SUMA.ČTVERCŮ
+SUMX2MY2 = SUMX2MY2
+SUMX2PY2 = SUMX2PY2
+SUMXMY2 = SUMXMY2
+TAN = TG
+TANH = TGH
+TRUNC = USEKNOUT
+
+##
+## Statistické funkce (Statistical Functions)
+##
+AVEDEV = PRŮMODCHYLKA
+AVERAGE = PRŮMĚR
+AVERAGEA = AVERAGEA
+AVERAGEIF = AVERAGEIF
+AVERAGEIFS = AVERAGEIFS
+BETA.DIST = BETA.DIST
+BETA.INV = BETA.INV
+BINOM.DIST = BINOM.DIST
+BINOM.DIST.RANGE = BINOM.DIST.RANGE
+BINOM.INV = BINOM.INV
+CHISQ.DIST = CHISQ.DIST
+CHISQ.DIST.RT = CHISQ.DIST.RT
+CHISQ.INV = CHISQ.INV
+CHISQ.INV.RT = CHISQ.INV.RT
+CHISQ.TEST = CHISQ.TEST
+CONFIDENCE.NORM = CONFIDENCE.NORM
+CONFIDENCE.T = CONFIDENCE.T
+CORREL = CORREL
+COUNT = POČET
+COUNTA = POČET2
+COUNTBLANK = COUNTBLANK
+COUNTIF = COUNTIF
+COUNTIFS = COUNTIFS
+COVARIANCE.P = COVARIANCE.P
+COVARIANCE.S = COVARIANCE.S
+DEVSQ = DEVSQ
+EXPON.DIST = EXPON.DIST
+F.DIST = F.DIST
+F.DIST.RT = F.DIST.RT
+F.INV = F.INV
+F.INV.RT = F.INV.RT
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = FORECAST.ETS
+FORECAST.ETS.CONFINT = FORECAST.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = FORECAST.ETS.SEASONALITY
+FORECAST.ETS.STAT = FORECAST.ETS.STAT
+FORECAST.LINEAR = FORECAST.LINEAR
+FREQUENCY = ČETNOSTI
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.DIST
+GAMMA.INV = GAMMA.INV
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.PRECISE
+GAUSS = GAUSS
+GEOMEAN = GEOMEAN
+GROWTH = LOGLINTREND
+HARMEAN = HARMEAN
+HYPGEOM.DIST = HYPGEOM.DIST
+INTERCEPT = INTERCEPT
+KURT = KURT
+LARGE = LARGE
+LINEST = LINREGRESE
+LOGEST = LOGLINREGRESE
+LOGNORM.DIST = LOGNORM.DIST
+LOGNORM.INV = LOGNORM.INV
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAXIFS
+MEDIAN = MEDIAN
+MIN = MIN
+MINA = MINA
+MINIFS = MINIFS
+MODE.MULT = MODE.MULT
+MODE.SNGL = MODE.SNGL
+NEGBINOM.DIST = NEGBINOM.DIST
+NORM.DIST = NORM.DIST
+NORM.INV = NORM.INV
+NORM.S.DIST = NORM.S.DIST
+NORM.S.INV = NORM.S.INV
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTIL.EXC
+PERCENTILE.INC = PERCENTIL.INC
+PERCENTRANK.EXC = PERCENTRANK.EXC
+PERCENTRANK.INC = PERCENTRANK.INC
+PERMUT = PERMUTACE
+PERMUTATIONA = PERMUTATIONA
+PHI = PHI
+POISSON.DIST = POISSON.DIST
+PROB = PROB
+QUARTILE.EXC = QUARTIL.EXC
+QUARTILE.INC = QUARTIL.INC
+RANK.AVG = RANK.AVG
+RANK.EQ = RANK.EQ
+RSQ = RKQ
+SKEW = SKEW
+SKEW.P = SKEW.P
+SLOPE = SLOPE
+SMALL = SMALL
+STANDARDIZE = STANDARDIZE
+STDEV.P = SMODCH.P
+STDEV.S = SMODCH.VÝBĚR.S
+STDEVA = STDEVA
+STDEVPA = STDEVPA
+STEYX = STEYX
+T.DIST = T.DIST
+T.DIST.2T = T.DIST.2T
+T.DIST.RT = T.DIST.RT
+T.INV = T.INV
+T.INV.2T = T.INV.2T
+T.TEST = T.TEST
+TREND = LINTREND
+TRIMMEAN = TRIMMEAN
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = WEIBULL.DIST
+Z.TEST = Z.TEST
+
+##
+## Textové funkce (Text Functions)
+##
+BAHTTEXT = BAHTTEXT
+CHAR = ZNAK
+CLEAN = VYČISTIT
+CODE = KÓD
+CONCAT = CONCAT
+DOLLAR = KČ
+EXACT = STEJNÉ
+FIND = NAJÍT
+FIXED = ZAOKROUHLIT.NA.TEXT
+LEFT = ZLEVA
+LEN = DÉLKA
+LOWER = MALÁ
+MID = ČÁST
+NUMBERVALUE = NUMBERVALUE
+PHONETIC = ZVUKOVÉ
+PROPER = VELKÁ2
+REPLACE = NAHRADIT
+REPT = OPAKOVAT
+RIGHT = ZPRAVA
+SEARCH = HLEDAT
+SUBSTITUTE = DOSADIT
+T = T
+TEXT = HODNOTA.NA.TEXT
+TEXTJOIN = TEXTJOIN
+TRIM = PROČISTIT
+UNICHAR = UNICHAR
+UNICODE = UNICODE
+UPPER = VELKÁ
+VALUE = HODNOTA
+
+##
+## Webové funkce (Web Functions)
+##
+ENCODEURL = ENCODEURL
+FILTERXML = FILTERXML
+WEBSERVICE = WEBSERVICE
+
+##
+## Funkce pro kompatibilitu (Compatibility Functions)
+##
+BETADIST = BETADIST
+BETAINV = BETAINV
+BINOMDIST = BINOMDIST
+CEILING = ZAOKR.NAHORU
+CHIDIST = CHIDIST
+CHIINV = CHIINV
+CHITEST = CHITEST
+CONCATENATE = CONCATENATE
+CONFIDENCE = CONFIDENCE
+COVAR = COVAR
+CRITBINOM = CRITBINOM
+EXPONDIST = EXPONDIST
+FDIST = FDIST
+FINV = FINV
+FLOOR = ZAOKR.DOLŮ
+FORECAST = FORECAST
+FTEST = FTEST
+GAMMADIST = GAMMADIST
+GAMMAINV = GAMMAINV
+HYPGEOMDIST = HYPGEOMDIST
+LOGINV = LOGINV
+LOGNORMDIST = LOGNORMDIST
+MODE = MODE
+NEGBINOMDIST = NEGBINOMDIST
+NORMDIST = NORMDIST
+NORMINV = NORMINV
+NORMSDIST = NORMSDIST
+NORMSINV = NORMSINV
+PERCENTILE = PERCENTIL
+PERCENTRANK = PERCENTRANK
+POISSON = POISSON
+QUARTILE = QUARTIL
+RANK = RANK
+STDEV = SMODCH.VÝBĚR
+STDEVP = SMODCH
+TDIST = TDIST
+TINV = TINV
+TTEST = TTEST
+VAR = VAR.VÝBĚR
+VARP = VAR
+WEIBULL = WEIBULL
+ZTEST = ZTEST
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config
new file mode 100644
index 0000000..284b249
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Dansk (Danish)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #NUL!
+DIV0
+VALUE = #VÆRDI!
+REF = #REFERENCE!
+NAME = #NAVN?
+NUM
+NA = #I/T
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions
new file mode 100644
index 0000000..03b68c9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions
@@ -0,0 +1,538 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Dansk (Danish)
+##
+############################################################
+
+
+##
+## Kubefunktioner (Cube Functions)
+##
+CUBEKPIMEMBER = KUBE.KPI.MEDLEM
+CUBEMEMBER = KUBEMEDLEM
+CUBEMEMBERPROPERTY = KUBEMEDLEM.EGENSKAB
+CUBERANKEDMEMBER = KUBERANGERET.MEDLEM
+CUBESET = KUBESÆT
+CUBESETCOUNT = KUBESÆT.ANTAL
+CUBEVALUE = KUBEVÆRDI
+
+##
+## Databasefunktioner (Database Functions)
+##
+DAVERAGE = DMIDDEL
+DCOUNT = DTÆL
+DCOUNTA = DTÆLV
+DGET = DHENT
+DMAX = DMAKS
+DMIN = DMIN
+DPRODUCT = DPRODUKT
+DSTDEV = DSTDAFV
+DSTDEVP = DSTDAFVP
+DSUM = DSUM
+DVAR = DVARIANS
+DVARP = DVARIANSP
+
+##
+## Dato- og klokkeslætfunktioner (Date & Time Functions)
+##
+DATE = DATO
+DATEDIF = DATO.FORSKEL
+DATESTRING = DATOSTRENG
+DATEVALUE = DATOVÆRDI
+DAY = DAG
+DAYS = DAGE
+DAYS360 = DAGE360
+EDATE = EDATO
+EOMONTH = SLUT.PÅ.MÅNED
+HOUR = TIME
+ISOWEEKNUM = ISOUGE.NR
+MINUTE = MINUT
+MONTH = MÅNED
+NETWORKDAYS = ANTAL.ARBEJDSDAGE
+NETWORKDAYS.INTL = ANTAL.ARBEJDSDAGE.INTL
+NOW = NU
+SECOND = SEKUND
+THAIDAYOFWEEK = THAILANDSKUGEDAG
+THAIMONTHOFYEAR = THAILANDSKMÅNED
+THAIYEAR = THAILANDSKÅR
+TIME = TID
+TIMEVALUE = TIDSVÆRDI
+TODAY = IDAG
+WEEKDAY = UGEDAG
+WEEKNUM = UGE.NR
+WORKDAY = ARBEJDSDAG
+WORKDAY.INTL = ARBEJDSDAG.INTL
+YEAR = ÅR
+YEARFRAC = ÅR.BRØK
+
+##
+## Tekniske funktioner (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BIN.TIL.DEC
+BIN2HEX = BIN.TIL.HEX
+BIN2OCT = BIN.TIL.OKT
+BITAND = BITOG
+BITLSHIFT = BITLSKIFT
+BITOR = BITELLER
+BITRSHIFT = BITRSKIFT
+BITXOR = BITXELLER
+COMPLEX = KOMPLEKS
+CONVERT = KONVERTER
+DEC2BIN = DEC.TIL.BIN
+DEC2HEX = DEC.TIL.HEX
+DEC2OCT = DEC.TIL.OKT
+DELTA = DELTA
+ERF = FEJLFUNK
+ERF.PRECISE = ERF.PRECISE
+ERFC = FEJLFUNK.KOMP
+ERFC.PRECISE = ERFC.PRECISE
+GESTEP = GETRIN
+HEX2BIN = HEX.TIL.BIN
+HEX2DEC = HEX.TIL.DEC
+HEX2OCT = HEX.TIL.OKT
+IMABS = IMAGABS
+IMAGINARY = IMAGINÆR
+IMARGUMENT = IMAGARGUMENT
+IMCONJUGATE = IMAGKONJUGERE
+IMCOS = IMAGCOS
+IMCOSH = IMAGCOSH
+IMCOT = IMAGCOT
+IMCSC = IMAGCSC
+IMCSCH = IMAGCSCH
+IMDIV = IMAGDIV
+IMEXP = IMAGEKSP
+IMLN = IMAGLN
+IMLOG10 = IMAGLOG10
+IMLOG2 = IMAGLOG2
+IMPOWER = IMAGPOTENS
+IMPRODUCT = IMAGPRODUKT
+IMREAL = IMAGREELT
+IMSEC = IMAGSEC
+IMSECH = IMAGSECH
+IMSIN = IMAGSIN
+IMSINH = IMAGSINH
+IMSQRT = IMAGKVROD
+IMSUB = IMAGSUB
+IMSUM = IMAGSUM
+IMTAN = IMAGTAN
+OCT2BIN = OKT.TIL.BIN
+OCT2DEC = OKT.TIL.DEC
+OCT2HEX = OKT.TIL.HEX
+
+##
+## Finansielle funktioner (Financial Functions)
+##
+ACCRINT = PÅLØBRENTE
+ACCRINTM = PÅLØBRENTE.UDLØB
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = KUPONDAGE.SA
+COUPDAYS = KUPONDAGE.A
+COUPDAYSNC = KUPONDAGE.ANK
+COUPNCD = KUPONDAG.NÆSTE
+COUPNUM = KUPONBETALINGER
+COUPPCD = KUPONDAG.FORRIGE
+CUMIPMT = AKKUM.RENTE
+CUMPRINC = AKKUM.HOVEDSTOL
+DB = DB
+DDB = DSA
+DISC = DISKONTO
+DOLLARDE = KR.DECIMAL
+DOLLARFR = KR.BRØK
+DURATION = VARIGHED
+EFFECT = EFFEKTIV.RENTE
+FV = FV
+FVSCHEDULE = FVTABEL
+INTRATE = RENTEFOD
+IPMT = R.YDELSE
+IRR = IA
+ISPMT = ISPMT
+MDURATION = MVARIGHED
+MIRR = MIA
+NOMINAL = NOMINEL
+NPER = NPER
+NPV = NUTIDSVÆRDI
+ODDFPRICE = ULIGE.KURS.PÅLYDENDE
+ODDFYIELD = ULIGE.FØRSTE.AFKAST
+ODDLPRICE = ULIGE.SIDSTE.KURS
+ODDLYIELD = ULIGE.SIDSTE.AFKAST
+PDURATION = PVARIGHED
+PMT = YDELSE
+PPMT = H.YDELSE
+PRICE = KURS
+PRICEDISC = KURS.DISKONTO
+PRICEMAT = KURS.UDLØB
+PV = NV
+RATE = RENTE
+RECEIVED = MODTAGET.VED.UDLØB
+RRI = RRI
+SLN = LA
+SYD = ÅRSAFSKRIVNING
+TBILLEQ = STATSOBLIGATION
+TBILLPRICE = STATSOBLIGATION.KURS
+TBILLYIELD = STATSOBLIGATION.AFKAST
+VDB = VSA
+XIRR = INTERN.RENTE
+XNPV = NETTO.NUTIDSVÆRDI
+YIELD = AFKAST
+YIELDDISC = AFKAST.DISKONTO
+YIELDMAT = AFKAST.UDLØBSDATO
+
+##
+## Informationsfunktioner (Information Functions)
+##
+CELL = CELLE
+ERROR.TYPE = FEJLTYPE
+INFO = INFO
+ISBLANK = ER.TOM
+ISERR = ER.FJL
+ISERROR = ER.FEJL
+ISEVEN = ER.LIGE
+ISFORMULA = ER.FORMEL
+ISLOGICAL = ER.LOGISK
+ISNA = ER.IKKE.TILGÆNGELIG
+ISNONTEXT = ER.IKKE.TEKST
+ISNUMBER = ER.TAL
+ISODD = ER.ULIGE
+ISREF = ER.REFERENCE
+ISTEXT = ER.TEKST
+N = TAL
+NA = IKKE.TILGÆNGELIG
+SHEET = ARK
+SHEETS = ARK.FLERE
+TYPE = VÆRDITYPE
+
+##
+## Logiske funktioner (Logical Functions)
+##
+AND = OG
+FALSE = FALSK
+IF = HVIS
+IFERROR = HVIS.FEJL
+IFNA = HVISIT
+IFS = HVISER
+NOT = IKKE
+OR = ELLER
+SWITCH = SKIFT
+TRUE = SAND
+XOR = XELLER
+
+##
+## Opslags- og referencefunktioner (Lookup & Reference Functions)
+##
+ADDRESS = ADRESSE
+AREAS = OMRÅDER
+CHOOSE = VÆLG
+COLUMN = KOLONNE
+COLUMNS = KOLONNER
+FORMULATEXT = FORMELTEKST
+GETPIVOTDATA = GETPIVOTDATA
+HLOOKUP = VOPSLAG
+HYPERLINK = HYPERLINK
+INDEX = INDEKS
+INDIRECT = INDIREKTE
+LOOKUP = SLÅ.OP
+MATCH = SAMMENLIGN
+OFFSET = FORSKYDNING
+ROW = RÆKKE
+ROWS = RÆKKER
+RTD = RTD
+TRANSPOSE = TRANSPONER
+VLOOKUP = LOPSLAG
+*RC = RK
+
+##
+## Matematiske og trigonometriske funktioner (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ARCCOS
+ACOSH = ARCCOSH
+ACOT = ARCCOT
+ACOTH = ARCCOTH
+AGGREGATE = SAMLING
+ARABIC = ARABISK
+ASIN = ARCSIN
+ASINH = ARCSINH
+ATAN = ARCTAN
+ATAN2 = ARCTAN2
+ATANH = ARCTANH
+BASE = BASIS
+CEILING.MATH = LOFT.MAT
+CEILING.PRECISE = LOFT.PRECISE
+COMBIN = KOMBIN
+COMBINA = KOMBINA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DECIMAL
+DEGREES = GRADER
+ECMA.CEILING = ECMA.LOFT
+EVEN = LIGE
+EXP = EKSP
+FACT = FAKULTET
+FACTDOUBLE = DOBBELT.FAKULTET
+FLOOR.MATH = AFRUND.BUND.MAT
+FLOOR.PRECISE = AFRUND.GULV.PRECISE
+GCD = STØRSTE.FÆLLES.DIVISOR
+INT = HELTAL
+ISO.CEILING = ISO.LOFT
+LCM = MINDSTE.FÆLLES.MULTIPLUM
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDETERM
+MINVERSE = MINVERT
+MMULT = MPRODUKT
+MOD = REST
+MROUND = MAFRUND
+MULTINOMIAL = MULTINOMIAL
+MUNIT = MENHED
+ODD = ULIGE
+PI = PI
+POWER = POTENS
+PRODUCT = PRODUKT
+QUOTIENT = KVOTIENT
+RADIANS = RADIANER
+RAND = SLUMP
+RANDBETWEEN = SLUMPMELLEM
+ROMAN = ROMERTAL
+ROUND = AFRUND
+ROUNDBAHTDOWN = RUNDBAHTNED
+ROUNDBAHTUP = RUNDBAHTOP
+ROUNDDOWN = RUND.NED
+ROUNDUP = RUND.OP
+SEC = SEC
+SECH = SECH
+SERIESSUM = SERIESUM
+SIGN = FORTEGN
+SIN = SIN
+SINH = SINH
+SQRT = KVROD
+SQRTPI = KVRODPI
+SUBTOTAL = SUBTOTAL
+SUM = SUM
+SUMIF = SUM.HVIS
+SUMIFS = SUM.HVISER
+SUMPRODUCT = SUMPRODUKT
+SUMSQ = SUMKV
+SUMX2MY2 = SUMX2MY2
+SUMX2PY2 = SUMX2PY2
+SUMXMY2 = SUMXMY2
+TAN = TAN
+TANH = TANH
+TRUNC = AFKORT
+
+##
+## Statistiske funktioner (Statistical Functions)
+##
+AVEDEV = MAD
+AVERAGE = MIDDEL
+AVERAGEA = MIDDELV
+AVERAGEIF = MIDDEL.HVIS
+AVERAGEIFS = MIDDEL.HVISER
+BETA.DIST = BETA.FORDELING
+BETA.INV = BETA.INV
+BINOM.DIST = BINOMIAL.FORDELING
+BINOM.DIST.RANGE = BINOMIAL.DIST.INTERVAL
+BINOM.INV = BINOMIAL.INV
+CHISQ.DIST = CHI2.FORDELING
+CHISQ.DIST.RT = CHI2.FORD.RT
+CHISQ.INV = CHI2.INV
+CHISQ.INV.RT = CHI2.INV.RT
+CHISQ.TEST = CHI2.TEST
+CONFIDENCE.NORM = KONFIDENS.NORM
+CONFIDENCE.T = KONFIDENST
+CORREL = KORRELATION
+COUNT = TÆL
+COUNTA = TÆLV
+COUNTBLANK = ANTAL.BLANKE
+COUNTIF = TÆL.HVIS
+COUNTIFS = TÆL.HVISER
+COVARIANCE.P = KOVARIANS.P
+COVARIANCE.S = KOVARIANS.S
+DEVSQ = SAK
+EXPON.DIST = EKSP.FORDELING
+F.DIST = F.FORDELING
+F.DIST.RT = F.FORDELING.RT
+F.INV = F.INV
+F.INV.RT = F.INV.RT
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = PROGNOSE.ETS
+FORECAST.ETS.CONFINT = PROGNOSE.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = PROGNOSE.ETS.SÆSONUDSVING
+FORECAST.ETS.STAT = PROGNOSE.ETS.STAT
+FORECAST.LINEAR = PROGNOSE.LINEÆR
+FREQUENCY = FREKVENS
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.FORDELING
+GAMMA.INV = GAMMA.INV
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.PRECISE
+GAUSS = GAUSS
+GEOMEAN = GEOMIDDELVÆRDI
+GROWTH = FORØGELSE
+HARMEAN = HARMIDDELVÆRDI
+HYPGEOM.DIST = HYPGEO.FORDELING
+INTERCEPT = SKÆRING
+KURT = TOPSTEJL
+LARGE = STØRSTE
+LINEST = LINREGR
+LOGEST = LOGREGR
+LOGNORM.DIST = LOGNORM.FORDELING
+LOGNORM.INV = LOGNORM.INV
+MAX = MAKS
+MAXA = MAKSV
+MAXIFS = MAKSHVISER
+MEDIAN = MEDIAN
+MIN = MIN
+MINA = MINV
+MINIFS = MINHVISER
+MODE.MULT = HYPPIGST.FLERE
+MODE.SNGL = HYPPIGST.ENKELT
+NEGBINOM.DIST = NEGBINOM.FORDELING
+NORM.DIST = NORMAL.FORDELING
+NORM.INV = NORM.INV
+NORM.S.DIST = STANDARD.NORM.FORDELING
+NORM.S.INV = STANDARD.NORM.INV
+PEARSON = PEARSON
+PERCENTILE.EXC = FRAKTIL.UDELAD
+PERCENTILE.INC = FRAKTIL.MEDTAG
+PERCENTRANK.EXC = PROCENTPLADS.UDELAD
+PERCENTRANK.INC = PROCENTPLADS.MEDTAG
+PERMUT = PERMUT
+PERMUTATIONA = PERMUTATIONA
+PHI = PHI
+POISSON.DIST = POISSON.FORDELING
+PROB = SANDSYNLIGHED
+QUARTILE.EXC = KVARTIL.UDELAD
+QUARTILE.INC = KVARTIL.MEDTAG
+RANK.AVG = PLADS.GNSN
+RANK.EQ = PLADS.LIGE
+RSQ = FORKLARINGSGRAD
+SKEW = SKÆVHED
+SKEW.P = SKÆVHED.P
+SLOPE = STIGNING
+SMALL = MINDSTE
+STANDARDIZE = STANDARDISER
+STDEV.P = STDAFV.P
+STDEV.S = STDAFV.S
+STDEVA = STDAFVV
+STDEVPA = STDAFVPV
+STEYX = STFYX
+T.DIST = T.FORDELING
+T.DIST.2T = T.FORDELING.2T
+T.DIST.RT = T.FORDELING.RT
+T.INV = T.INV
+T.INV.2T = T.INV.2T
+T.TEST = T.TEST
+TREND = TENDENS
+TRIMMEAN = TRIMMIDDELVÆRDI
+VAR.P = VARIANS.P
+VAR.S = VARIANS.S
+VARA = VARIANSV
+VARPA = VARIANSPV
+WEIBULL.DIST = WEIBULL.FORDELING
+Z.TEST = Z.TEST
+
+##
+## Tekstfunktioner (Text Functions)
+##
+BAHTTEXT = BAHTTEKST
+CHAR = TEGN
+CLEAN = RENS
+CODE = KODE
+CONCAT = CONCAT
+DOLLAR = KR
+EXACT = EKSAKT
+FIND = FIND
+FIXED = FAST
+ISTHAIDIGIT = ERTHAILANDSKCIFFER
+LEFT = VENSTRE
+LEN = LÆNGDE
+LOWER = SMÅ.BOGSTAVER
+MID = MIDT
+NUMBERSTRING = TALSTRENG
+NUMBERVALUE = TALVÆRDI
+PHONETIC = FONETISK
+PROPER = STORT.FORBOGSTAV
+REPLACE = ERSTAT
+REPT = GENTAG
+RIGHT = HØJRE
+SEARCH = SØG
+SUBSTITUTE = UDSKIFT
+T = T
+TEXT = TEKST
+TEXTJOIN = TEKST.KOMBINER
+THAIDIGIT = THAILANDSKCIFFER
+THAINUMSOUND = THAILANDSKNUMLYD
+THAINUMSTRING = THAILANDSKNUMSTRENG
+THAISTRINGLENGTH = THAILANDSKSTRENGLÆNGDE
+TRIM = FJERN.OVERFLØDIGE.BLANKE
+UNICHAR = UNICHAR
+UNICODE = UNICODE
+UPPER = STORE.BOGSTAVER
+VALUE = VÆRDI
+
+##
+## Webfunktioner (Web Functions)
+##
+ENCODEURL = KODNINGSURL
+FILTERXML = FILTRERXML
+WEBSERVICE = WEBTJENESTE
+
+##
+## Kompatibilitetsfunktioner (Compatibility Functions)
+##
+BETADIST = BETAFORDELING
+BETAINV = BETAINV
+BINOMDIST = BINOMIALFORDELING
+CEILING = AFRUND.LOFT
+CHIDIST = CHIFORDELING
+CHIINV = CHIINV
+CHITEST = CHITEST
+CONCATENATE = SAMMENKÆDNING
+CONFIDENCE = KONFIDENSINTERVAL
+COVAR = KOVARIANS
+CRITBINOM = KRITBINOM
+EXPONDIST = EKSPFORDELING
+FDIST = FFORDELING
+FINV = FINV
+FLOOR = AFRUND.GULV
+FORECAST = PROGNOSE
+FTEST = FTEST
+GAMMADIST = GAMMAFORDELING
+GAMMAINV = GAMMAINV
+HYPGEOMDIST = HYPGEOFORDELING
+LOGINV = LOGINV
+LOGNORMDIST = LOGNORMFORDELING
+MODE = HYPPIGST
+NEGBINOMDIST = NEGBINOMFORDELING
+NORMDIST = NORMFORDELING
+NORMINV = NORMINV
+NORMSDIST = STANDARDNORMFORDELING
+NORMSINV = STANDARDNORMINV
+PERCENTILE = FRAKTIL
+PERCENTRANK = PROCENTPLADS
+POISSON = POISSON
+QUARTILE = KVARTIL
+RANK = PLADS
+STDEV = STDAFV
+STDEVP = STDAFVP
+TDIST = TFORDELING
+TINV = TINV
+TTEST = TTEST
+VAR = VARIANS
+VARP = VARIANSP
+WEIBULL = WEIBULL
+ZTEST = ZTEST
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config
new file mode 100644
index 0000000..4ca2b82
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Deutsch (German)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL
+DIV0
+VALUE = #WERT!
+REF = #BEZUG!
+NAME
+NUM = #ZAHL!
+NA = #NV
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions
new file mode 100644
index 0000000..d49fc5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions
@@ -0,0 +1,534 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Deutsch (German)
+##
+############################################################
+
+
+##
+## Cubefunktionen (Cube Functions)
+##
+CUBEKPIMEMBER = CUBEKPIELEMENT
+CUBEMEMBER = CUBEELEMENT
+CUBEMEMBERPROPERTY = CUBEELEMENTEIGENSCHAFT
+CUBERANKEDMEMBER = CUBERANGELEMENT
+CUBESET = CUBEMENGE
+CUBESETCOUNT = CUBEMENGENANZAHL
+CUBEVALUE = CUBEWERT
+
+##
+## Datenbankfunktionen (Database Functions)
+##
+DAVERAGE = DBMITTELWERT
+DCOUNT = DBANZAHL
+DCOUNTA = DBANZAHL2
+DGET = DBAUSZUG
+DMAX = DBMAX
+DMIN = DBMIN
+DPRODUCT = DBPRODUKT
+DSTDEV = DBSTDABW
+DSTDEVP = DBSTDABWN
+DSUM = DBSUMME
+DVAR = DBVARIANZ
+DVARP = DBVARIANZEN
+
+##
+## Datums- und Uhrzeitfunktionen (Date & Time Functions)
+##
+DATE = DATUM
+DATEVALUE = DATWERT
+DAY = TAG
+DAYS = TAGE
+DAYS360 = TAGE360
+EDATE = EDATUM
+EOMONTH = MONATSENDE
+HOUR = STUNDE
+ISOWEEKNUM = ISOKALENDERWOCHE
+MINUTE = MINUTE
+MONTH = MONAT
+NETWORKDAYS = NETTOARBEITSTAGE
+NETWORKDAYS.INTL = NETTOARBEITSTAGE.INTL
+NOW = JETZT
+SECOND = SEKUNDE
+THAIDAYOFWEEK = THAIWOCHENTAG
+THAIMONTHOFYEAR = THAIMONATDESJAHRES
+THAIYEAR = THAIJAHR
+TIME = ZEIT
+TIMEVALUE = ZEITWERT
+TODAY = HEUTE
+WEEKDAY = WOCHENTAG
+WEEKNUM = KALENDERWOCHE
+WORKDAY = ARBEITSTAG
+WORKDAY.INTL = ARBEITSTAG.INTL
+YEAR = JAHR
+YEARFRAC = BRTEILJAHRE
+
+##
+## Technische Funktionen (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BININDEZ
+BIN2HEX = BININHEX
+BIN2OCT = BININOKT
+BITAND = BITUND
+BITLSHIFT = BITLVERSCHIEB
+BITOR = BITODER
+BITRSHIFT = BITRVERSCHIEB
+BITXOR = BITXODER
+COMPLEX = KOMPLEXE
+CONVERT = UMWANDELN
+DEC2BIN = DEZINBIN
+DEC2HEX = DEZINHEX
+DEC2OCT = DEZINOKT
+DELTA = DELTA
+ERF = GAUSSFEHLER
+ERF.PRECISE = GAUSSF.GENAU
+ERFC = GAUSSFKOMPL
+ERFC.PRECISE = GAUSSFKOMPL.GENAU
+GESTEP = GGANZZAHL
+HEX2BIN = HEXINBIN
+HEX2DEC = HEXINDEZ
+HEX2OCT = HEXINOKT
+IMABS = IMABS
+IMAGINARY = IMAGINÄRTEIL
+IMARGUMENT = IMARGUMENT
+IMCONJUGATE = IMKONJUGIERTE
+IMCOS = IMCOS
+IMCOSH = IMCOSHYP
+IMCOT = IMCOT
+IMCSC = IMCOSEC
+IMCSCH = IMCOSECHYP
+IMDIV = IMDIV
+IMEXP = IMEXP
+IMLN = IMLN
+IMLOG10 = IMLOG10
+IMLOG2 = IMLOG2
+IMPOWER = IMAPOTENZ
+IMPRODUCT = IMPRODUKT
+IMREAL = IMREALTEIL
+IMSEC = IMSEC
+IMSECH = IMSECHYP
+IMSIN = IMSIN
+IMSINH = IMSINHYP
+IMSQRT = IMWURZEL
+IMSUB = IMSUB
+IMSUM = IMSUMME
+IMTAN = IMTAN
+OCT2BIN = OKTINBIN
+OCT2DEC = OKTINDEZ
+OCT2HEX = OKTINHEX
+
+##
+## Finanzmathematische Funktionen (Financial Functions)
+##
+ACCRINT = AUFGELZINS
+ACCRINTM = AUFGELZINSF
+AMORDEGRC = AMORDEGRK
+AMORLINC = AMORLINEARK
+COUPDAYBS = ZINSTERMTAGVA
+COUPDAYS = ZINSTERMTAGE
+COUPDAYSNC = ZINSTERMTAGNZ
+COUPNCD = ZINSTERMNZ
+COUPNUM = ZINSTERMZAHL
+COUPPCD = ZINSTERMVZ
+CUMIPMT = KUMZINSZ
+CUMPRINC = KUMKAPITAL
+DB = GDA2
+DDB = GDA
+DISC = DISAGIO
+DOLLARDE = NOTIERUNGDEZ
+DOLLARFR = NOTIERUNGBRU
+DURATION = DURATION
+EFFECT = EFFEKTIV
+FV = ZW
+FVSCHEDULE = ZW2
+INTRATE = ZINSSATZ
+IPMT = ZINSZ
+IRR = IKV
+ISPMT = ISPMT
+MDURATION = MDURATION
+MIRR = QIKV
+NOMINAL = NOMINAL
+NPER = ZZR
+NPV = NBW
+ODDFPRICE = UNREGER.KURS
+ODDFYIELD = UNREGER.REND
+ODDLPRICE = UNREGLE.KURS
+ODDLYIELD = UNREGLE.REND
+PDURATION = PDURATION
+PMT = RMZ
+PPMT = KAPZ
+PRICE = KURS
+PRICEDISC = KURSDISAGIO
+PRICEMAT = KURSFÄLLIG
+PV = BW
+RATE = ZINS
+RECEIVED = AUSZAHLUNG
+RRI = ZSATZINVEST
+SLN = LIA
+SYD = DIA
+TBILLEQ = TBILLÄQUIV
+TBILLPRICE = TBILLKURS
+TBILLYIELD = TBILLRENDITE
+VDB = VDB
+XIRR = XINTZINSFUSS
+XNPV = XKAPITALWERT
+YIELD = RENDITE
+YIELDDISC = RENDITEDIS
+YIELDMAT = RENDITEFÄLL
+
+##
+## Informationsfunktionen (Information Functions)
+##
+CELL = ZELLE
+ERROR.TYPE = FEHLER.TYP
+INFO = INFO
+ISBLANK = ISTLEER
+ISERR = ISTFEHL
+ISERROR = ISTFEHLER
+ISEVEN = ISTGERADE
+ISFORMULA = ISTFORMEL
+ISLOGICAL = ISTLOG
+ISNA = ISTNV
+ISNONTEXT = ISTKTEXT
+ISNUMBER = ISTZAHL
+ISODD = ISTUNGERADE
+ISREF = ISTBEZUG
+ISTEXT = ISTTEXT
+N = N
+NA = NV
+SHEET = BLATT
+SHEETS = BLÄTTER
+TYPE = TYP
+
+##
+## Logische Funktionen (Logical Functions)
+##
+AND = UND
+FALSE = FALSCH
+IF = WENN
+IFERROR = WENNFEHLER
+IFNA = WENNNV
+IFS = WENNS
+NOT = NICHT
+OR = ODER
+SWITCH = ERSTERWERT
+TRUE = WAHR
+XOR = XODER
+
+##
+## Nachschlage- und Verweisfunktionen (Lookup & Reference Functions)
+##
+ADDRESS = ADRESSE
+AREAS = BEREICHE
+CHOOSE = WAHL
+COLUMN = SPALTE
+COLUMNS = SPALTEN
+FORMULATEXT = FORMELTEXT
+GETPIVOTDATA = PIVOTDATENZUORDNEN
+HLOOKUP = WVERWEIS
+HYPERLINK = HYPERLINK
+INDEX = INDEX
+INDIRECT = INDIREKT
+LOOKUP = VERWEIS
+MATCH = VERGLEICH
+OFFSET = BEREICH.VERSCHIEBEN
+ROW = ZEILE
+ROWS = ZEILEN
+RTD = RTD
+TRANSPOSE = MTRANS
+VLOOKUP = SVERWEIS
+*RC = ZS
+
+##
+## Mathematische und trigonometrische Funktionen (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ARCCOS
+ACOSH = ARCCOSHYP
+ACOT = ARCCOT
+ACOTH = ARCCOTHYP
+AGGREGATE = AGGREGAT
+ARABIC = ARABISCH
+ASIN = ARCSIN
+ASINH = ARCSINHYP
+ATAN = ARCTAN
+ATAN2 = ARCTAN2
+ATANH = ARCTANHYP
+BASE = BASIS
+CEILING.MATH = OBERGRENZE.MATHEMATIK
+CEILING.PRECISE = OBERGRENZE.GENAU
+COMBIN = KOMBINATIONEN
+COMBINA = KOMBINATIONEN2
+COS = COS
+COSH = COSHYP
+COT = COT
+COTH = COTHYP
+CSC = COSEC
+CSCH = COSECHYP
+DECIMAL = DEZIMAL
+DEGREES = GRAD
+ECMA.CEILING = ECMA.OBERGRENZE
+EVEN = GERADE
+EXP = EXP
+FACT = FAKULTÄT
+FACTDOUBLE = ZWEIFAKULTÄT
+FLOOR.MATH = UNTERGRENZE.MATHEMATIK
+FLOOR.PRECISE = UNTERGRENZE.GENAU
+GCD = GGT
+INT = GANZZAHL
+ISO.CEILING = ISO.OBERGRENZE
+LCM = KGV
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDET
+MINVERSE = MINV
+MMULT = MMULT
+MOD = REST
+MROUND = VRUNDEN
+MULTINOMIAL = POLYNOMIAL
+MUNIT = MEINHEIT
+ODD = UNGERADE
+PI = PI
+POWER = POTENZ
+PRODUCT = PRODUKT
+QUOTIENT = QUOTIENT
+RADIANS = BOGENMASS
+RAND = ZUFALLSZAHL
+RANDBETWEEN = ZUFALLSBEREICH
+ROMAN = RÖMISCH
+ROUND = RUNDEN
+ROUNDBAHTDOWN = RUNDBAHTNED
+ROUNDBAHTUP = BAHTAUFRUNDEN
+ROUNDDOWN = ABRUNDEN
+ROUNDUP = AUFRUNDEN
+SEC = SEC
+SECH = SECHYP
+SERIESSUM = POTENZREIHE
+SIGN = VORZEICHEN
+SIN = SIN
+SINH = SINHYP
+SQRT = WURZEL
+SQRTPI = WURZELPI
+SUBTOTAL = TEILERGEBNIS
+SUM = SUMME
+SUMIF = SUMMEWENN
+SUMIFS = SUMMEWENNS
+SUMPRODUCT = SUMMENPRODUKT
+SUMSQ = QUADRATESUMME
+SUMX2MY2 = SUMMEX2MY2
+SUMX2PY2 = SUMMEX2PY2
+SUMXMY2 = SUMMEXMY2
+TAN = TAN
+TANH = TANHYP
+TRUNC = KÜRZEN
+
+##
+## Statistische Funktionen (Statistical Functions)
+##
+AVEDEV = MITTELABW
+AVERAGE = MITTELWERT
+AVERAGEA = MITTELWERTA
+AVERAGEIF = MITTELWERTWENN
+AVERAGEIFS = MITTELWERTWENNS
+BETA.DIST = BETA.VERT
+BETA.INV = BETA.INV
+BINOM.DIST = BINOM.VERT
+BINOM.DIST.RANGE = BINOM.VERT.BEREICH
+BINOM.INV = BINOM.INV
+CHISQ.DIST = CHIQU.VERT
+CHISQ.DIST.RT = CHIQU.VERT.RE
+CHISQ.INV = CHIQU.INV
+CHISQ.INV.RT = CHIQU.INV.RE
+CHISQ.TEST = CHIQU.TEST
+CONFIDENCE.NORM = KONFIDENZ.NORM
+CONFIDENCE.T = KONFIDENZ.T
+CORREL = KORREL
+COUNT = ANZAHL
+COUNTA = ANZAHL2
+COUNTBLANK = ANZAHLLEEREZELLEN
+COUNTIF = ZÄHLENWENN
+COUNTIFS = ZÄHLENWENNS
+COVARIANCE.P = KOVARIANZ.P
+COVARIANCE.S = KOVARIANZ.S
+DEVSQ = SUMQUADABW
+EXPON.DIST = EXPON.VERT
+F.DIST = F.VERT
+F.DIST.RT = F.VERT.RE
+F.INV = F.INV
+F.INV.RT = F.INV.RE
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = PROGNOSE.ETS
+FORECAST.ETS.CONFINT = PROGNOSE.ETS.KONFINT
+FORECAST.ETS.SEASONALITY = PROGNOSE.ETS.SAISONALITÄT
+FORECAST.ETS.STAT = PROGNOSE.ETS.STAT
+FORECAST.LINEAR = PROGNOSE.LINEAR
+FREQUENCY = HÄUFIGKEIT
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.VERT
+GAMMA.INV = GAMMA.INV
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.GENAU
+GAUSS = GAUSS
+GEOMEAN = GEOMITTEL
+GROWTH = VARIATION
+HARMEAN = HARMITTEL
+HYPGEOM.DIST = HYPGEOM.VERT
+INTERCEPT = ACHSENABSCHNITT
+KURT = KURT
+LARGE = KGRÖSSTE
+LINEST = RGP
+LOGEST = RKP
+LOGNORM.DIST = LOGNORM.VERT
+LOGNORM.INV = LOGNORM.INV
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAXWENNS
+MEDIAN = MEDIAN
+MIN = MIN
+MINA = MINA
+MINIFS = MINWENNS
+MODE.MULT = MODUS.VIELF
+MODE.SNGL = MODUS.EINF
+NEGBINOM.DIST = NEGBINOM.VERT
+NORM.DIST = NORM.VERT
+NORM.INV = NORM.INV
+NORM.S.DIST = NORM.S.VERT
+NORM.S.INV = NORM.S.INV
+PEARSON = PEARSON
+PERCENTILE.EXC = QUANTIL.EXKL
+PERCENTILE.INC = QUANTIL.INKL
+PERCENTRANK.EXC = QUANTILSRANG.EXKL
+PERCENTRANK.INC = QUANTILSRANG.INKL
+PERMUT = VARIATIONEN
+PERMUTATIONA = VARIATIONEN2
+PHI = PHI
+POISSON.DIST = POISSON.VERT
+PROB = WAHRSCHBEREICH
+QUARTILE.EXC = QUARTILE.EXKL
+QUARTILE.INC = QUARTILE.INKL
+RANK.AVG = RANG.MITTELW
+RANK.EQ = RANG.GLEICH
+RSQ = BESTIMMTHEITSMASS
+SKEW = SCHIEFE
+SKEW.P = SCHIEFE.P
+SLOPE = STEIGUNG
+SMALL = KKLEINSTE
+STANDARDIZE = STANDARDISIERUNG
+STDEV.P = STABW.N
+STDEV.S = STABW.S
+STDEVA = STABWA
+STDEVPA = STABWNA
+STEYX = STFEHLERYX
+T.DIST = T.VERT
+T.DIST.2T = T.VERT.2S
+T.DIST.RT = T.VERT.RE
+T.INV = T.INV
+T.INV.2T = T.INV.2S
+T.TEST = T.TEST
+TREND = TREND
+TRIMMEAN = GESTUTZTMITTEL
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARIANZA
+VARPA = VARIANZENA
+WEIBULL.DIST = WEIBULL.VERT
+Z.TEST = G.TEST
+
+##
+## Textfunktionen (Text Functions)
+##
+BAHTTEXT = BAHTTEXT
+CHAR = ZEICHEN
+CLEAN = SÄUBERN
+CODE = CODE
+CONCAT = TEXTKETTE
+DOLLAR = DM
+EXACT = IDENTISCH
+FIND = FINDEN
+FIXED = FEST
+ISTHAIDIGIT = ISTTHAIZAHLENWORT
+LEFT = LINKS
+LEN = LÄNGE
+LOWER = KLEIN
+MID = TEIL
+NUMBERVALUE = ZAHLENWERT
+PROPER = GROSS2
+REPLACE = ERSETZEN
+REPT = WIEDERHOLEN
+RIGHT = RECHTS
+SEARCH = SUCHEN
+SUBSTITUTE = WECHSELN
+T = T
+TEXT = TEXT
+TEXTJOIN = TEXTVERKETTEN
+THAIDIGIT = THAIZAHLENWORT
+THAINUMSOUND = THAIZAHLSOUND
+THAINUMSTRING = THAILANDSKNUMSTRENG
+THAISTRINGLENGTH = THAIZEICHENFOLGENLÄNGE
+TRIM = GLÄTTEN
+UNICHAR = UNIZEICHEN
+UNICODE = UNICODE
+UPPER = GROSS
+VALUE = WERT
+
+##
+## Webfunktionen (Web Functions)
+##
+ENCODEURL = URLCODIEREN
+FILTERXML = XMLFILTERN
+WEBSERVICE = WEBDIENST
+
+##
+## Kompatibilitätsfunktionen (Compatibility Functions)
+##
+BETADIST = BETAVERT
+BETAINV = BETAINV
+BINOMDIST = BINOMVERT
+CEILING = OBERGRENZE
+CHIDIST = CHIVERT
+CHIINV = CHIINV
+CHITEST = CHITEST
+CONCATENATE = VERKETTEN
+CONFIDENCE = KONFIDENZ
+COVAR = KOVAR
+CRITBINOM = KRITBINOM
+EXPONDIST = EXPONVERT
+FDIST = FVERT
+FINV = FINV
+FLOOR = UNTERGRENZE
+FORECAST = SCHÄTZER
+FTEST = FTEST
+GAMMADIST = GAMMAVERT
+GAMMAINV = GAMMAINV
+HYPGEOMDIST = HYPGEOMVERT
+LOGINV = LOGINV
+LOGNORMDIST = LOGNORMVERT
+MODE = MODALWERT
+NEGBINOMDIST = NEGBINOMVERT
+NORMDIST = NORMVERT
+NORMINV = NORMINV
+NORMSDIST = STANDNORMVERT
+NORMSINV = STANDNORMINV
+PERCENTILE = QUANTIL
+PERCENTRANK = QUANTILSRANG
+POISSON = POISSON
+QUARTILE = QUARTILE
+RANK = RANG
+STDEV = STABW
+STDEVP = STABWN
+TDIST = TVERT
+TINV = TINV
+TTEST = TTEST
+VAR = VARIANZ
+VARP = VARIANZEN
+WEIBULL = WEIBULL
+ZTEST = GTEST
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
new file mode 100644
index 0000000..4e068ab
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
@@ -0,0 +1,24 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## English-UK (English-UK)
+##
+############################################################
+
+ArgumentSeparator = ,
+##
+## (For future use)
+##
+currencySymbol = £
+
+##
+## Error Codes
+##
+NULL
+DIV0
+VALUE
+REF
+NAME
+NUM
+NA
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config
new file mode 100644
index 0000000..fe044ef
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Español (Spanish)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #¡NULO!
+DIV0 = #¡DIV/0!
+VALUE = #¡VALOR!
+REF = #¡REF!
+NAME = #¿NOMBRE?
+NUM = #¡NUM!
+NA
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions
new file mode 100644
index 0000000..88012aa
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions
@@ -0,0 +1,538 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Español (Spanish)
+##
+############################################################
+
+
+##
+## Funciones de cubo (Cube Functions)
+##
+CUBEKPIMEMBER = MIEMBROKPICUBO
+CUBEMEMBER = MIEMBROCUBO
+CUBEMEMBERPROPERTY = PROPIEDADMIEMBROCUBO
+CUBERANKEDMEMBER = MIEMBRORANGOCUBO
+CUBESET = CONJUNTOCUBO
+CUBESETCOUNT = RECUENTOCONJUNTOCUBO
+CUBEVALUE = VALORCUBO
+
+##
+## Funciones de base de datos (Database Functions)
+##
+DAVERAGE = BDPROMEDIO
+DCOUNT = BDCONTAR
+DCOUNTA = BDCONTARA
+DGET = BDEXTRAER
+DMAX = BDMAX
+DMIN = BDMIN
+DPRODUCT = BDPRODUCTO
+DSTDEV = BDDESVEST
+DSTDEVP = BDDESVESTP
+DSUM = BDSUMA
+DVAR = BDVAR
+DVARP = BDVARP
+
+##
+## Funciones de fecha y hora (Date & Time Functions)
+##
+DATE = FECHA
+DATEDIF = SIFECHA
+DATESTRING = CADENA.FECHA
+DATEVALUE = FECHANUMERO
+DAY = DIA
+DAYS = DIAS
+DAYS360 = DIAS360
+EDATE = FECHA.MES
+EOMONTH = FIN.MES
+HOUR = HORA
+ISOWEEKNUM = ISO.NUM.DE.SEMANA
+MINUTE = MINUTO
+MONTH = MES
+NETWORKDAYS = DIAS.LAB
+NETWORKDAYS.INTL = DIAS.LAB.INTL
+NOW = AHORA
+SECOND = SEGUNDO
+THAIDAYOFWEEK = DIASEMTAI
+THAIMONTHOFYEAR = MESAÑOTAI
+THAIYEAR = AÑOTAI
+TIME = NSHORA
+TIMEVALUE = HORANUMERO
+TODAY = HOY
+WEEKDAY = DIASEM
+WEEKNUM = NUM.DE.SEMANA
+WORKDAY = DIA.LAB
+WORKDAY.INTL = DIA.LAB.INTL
+YEAR = AÑO
+YEARFRAC = FRAC.AÑO
+
+##
+## Funciones de ingeniería (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BIN.A.DEC
+BIN2HEX = BIN.A.HEX
+BIN2OCT = BIN.A.OCT
+BITAND = BIT.Y
+BITLSHIFT = BIT.DESPLIZQDA
+BITOR = BIT.O
+BITRSHIFT = BIT.DESPLDCHA
+BITXOR = BIT.XO
+COMPLEX = COMPLEJO
+CONVERT = CONVERTIR
+DEC2BIN = DEC.A.BIN
+DEC2HEX = DEC.A.HEX
+DEC2OCT = DEC.A.OCT
+DELTA = DELTA
+ERF = FUN.ERROR
+ERF.PRECISE = FUN.ERROR.EXACTO
+ERFC = FUN.ERROR.COMPL
+ERFC.PRECISE = FUN.ERROR.COMPL.EXACTO
+GESTEP = MAYOR.O.IGUAL
+HEX2BIN = HEX.A.BIN
+HEX2DEC = HEX.A.DEC
+HEX2OCT = HEX.A.OCT
+IMABS = IM.ABS
+IMAGINARY = IMAGINARIO
+IMARGUMENT = IM.ANGULO
+IMCONJUGATE = IM.CONJUGADA
+IMCOS = IM.COS
+IMCOSH = IM.COSH
+IMCOT = IM.COT
+IMCSC = IM.CSC
+IMCSCH = IM.CSCH
+IMDIV = IM.DIV
+IMEXP = IM.EXP
+IMLN = IM.LN
+IMLOG10 = IM.LOG10
+IMLOG2 = IM.LOG2
+IMPOWER = IM.POT
+IMPRODUCT = IM.PRODUCT
+IMREAL = IM.REAL
+IMSEC = IM.SEC
+IMSECH = IM.SECH
+IMSIN = IM.SENO
+IMSINH = IM.SENOH
+IMSQRT = IM.RAIZ2
+IMSUB = IM.SUSTR
+IMSUM = IM.SUM
+IMTAN = IM.TAN
+OCT2BIN = OCT.A.BIN
+OCT2DEC = OCT.A.DEC
+OCT2HEX = OCT.A.HEX
+
+##
+## Funciones financieras (Financial Functions)
+##
+ACCRINT = INT.ACUM
+ACCRINTM = INT.ACUM.V
+AMORDEGRC = AMORTIZ.PROGRE
+AMORLINC = AMORTIZ.LIN
+COUPDAYBS = CUPON.DIAS.L1
+COUPDAYS = CUPON.DIAS
+COUPDAYSNC = CUPON.DIAS.L2
+COUPNCD = CUPON.FECHA.L2
+COUPNUM = CUPON.NUM
+COUPPCD = CUPON.FECHA.L1
+CUMIPMT = PAGO.INT.ENTRE
+CUMPRINC = PAGO.PRINC.ENTRE
+DB = DB
+DDB = DDB
+DISC = TASA.DESC
+DOLLARDE = MONEDA.DEC
+DOLLARFR = MONEDA.FRAC
+DURATION = DURACION
+EFFECT = INT.EFECTIVO
+FV = VF
+FVSCHEDULE = VF.PLAN
+INTRATE = TASA.INT
+IPMT = PAGOINT
+IRR = TIR
+ISPMT = INT.PAGO.DIR
+MDURATION = DURACION.MODIF
+MIRR = TIRM
+NOMINAL = TASA.NOMINAL
+NPER = NPER
+NPV = VNA
+ODDFPRICE = PRECIO.PER.IRREGULAR.1
+ODDFYIELD = RENDTO.PER.IRREGULAR.1
+ODDLPRICE = PRECIO.PER.IRREGULAR.2
+ODDLYIELD = RENDTO.PER.IRREGULAR.2
+PDURATION = P.DURACION
+PMT = PAGO
+PPMT = PAGOPRIN
+PRICE = PRECIO
+PRICEDISC = PRECIO.DESCUENTO
+PRICEMAT = PRECIO.VENCIMIENTO
+PV = VA
+RATE = TASA
+RECEIVED = CANTIDAD.RECIBIDA
+RRI = RRI
+SLN = SLN
+SYD = SYD
+TBILLEQ = LETRA.DE.TEST.EQV.A.BONO
+TBILLPRICE = LETRA.DE.TES.PRECIO
+TBILLYIELD = LETRA.DE.TES.RENDTO
+VDB = DVS
+XIRR = TIR.NO.PER
+XNPV = VNA.NO.PER
+YIELD = RENDTO
+YIELDDISC = RENDTO.DESC
+YIELDMAT = RENDTO.VENCTO
+
+##
+## Funciones de información (Information Functions)
+##
+CELL = CELDA
+ERROR.TYPE = TIPO.DE.ERROR
+INFO = INFO
+ISBLANK = ESBLANCO
+ISERR = ESERR
+ISERROR = ESERROR
+ISEVEN = ES.PAR
+ISFORMULA = ESFORMULA
+ISLOGICAL = ESLOGICO
+ISNA = ESNOD
+ISNONTEXT = ESNOTEXTO
+ISNUMBER = ESNUMERO
+ISODD = ES.IMPAR
+ISREF = ESREF
+ISTEXT = ESTEXTO
+N = N
+NA = NOD
+SHEET = HOJA
+SHEETS = HOJAS
+TYPE = TIPO
+
+##
+## Funciones lógicas (Logical Functions)
+##
+AND = Y
+FALSE = FALSO
+IF = SI
+IFERROR = SI.ERROR
+IFNA = SI.ND
+IFS = SI.CONJUNTO
+NOT = NO
+OR = O
+SWITCH = CAMBIAR
+TRUE = VERDADERO
+XOR = XO
+
+##
+## Funciones de búsqueda y referencia (Lookup & Reference Functions)
+##
+ADDRESS = DIRECCION
+AREAS = AREAS
+CHOOSE = ELEGIR
+COLUMN = COLUMNA
+COLUMNS = COLUMNAS
+FORMULATEXT = FORMULATEXTO
+GETPIVOTDATA = IMPORTARDATOSDINAMICOS
+HLOOKUP = BUSCARH
+HYPERLINK = HIPERVINCULO
+INDEX = INDICE
+INDIRECT = INDIRECTO
+LOOKUP = BUSCAR
+MATCH = COINCIDIR
+OFFSET = DESREF
+ROW = FILA
+ROWS = FILAS
+RTD = RDTR
+TRANSPOSE = TRANSPONER
+VLOOKUP = BUSCARV
+*RC = FC
+
+##
+## Funciones matemáticas y trigonométricas (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = AGREGAR
+ARABIC = NUMERO.ARABE
+ASIN = ASENO
+ASINH = ASENOH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = BASE
+CEILING.MATH = MULTIPLO.SUPERIOR.MAT
+CEILING.PRECISE = MULTIPLO.SUPERIOR.EXACTO
+COMBIN = COMBINAT
+COMBINA = COMBINA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = CONV.DECIMAL
+DEGREES = GRADOS
+ECMA.CEILING = MULTIPLO.SUPERIOR.ECMA
+EVEN = REDONDEA.PAR
+EXP = EXP
+FACT = FACT
+FACTDOUBLE = FACT.DOBLE
+FLOOR.MATH = MULTIPLO.INFERIOR.MAT
+FLOOR.PRECISE = MULTIPLO.INFERIOR.EXACTO
+GCD = M.C.D
+INT = ENTERO
+ISO.CEILING = MULTIPLO.SUPERIOR.ISO
+LCM = M.C.M
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDETERM
+MINVERSE = MINVERSA
+MMULT = MMULT
+MOD = RESIDUO
+MROUND = REDOND.MULT
+MULTINOMIAL = MULTINOMIAL
+MUNIT = M.UNIDAD
+ODD = REDONDEA.IMPAR
+PI = PI
+POWER = POTENCIA
+PRODUCT = PRODUCTO
+QUOTIENT = COCIENTE
+RADIANS = RADIANES
+RAND = ALEATORIO
+RANDBETWEEN = ALEATORIO.ENTRE
+ROMAN = NUMERO.ROMANO
+ROUND = REDONDEAR
+ROUNDBAHTDOWN = REDONDEAR.BAHT.MAS
+ROUNDBAHTUP = REDONDEAR.BAHT.MENOS
+ROUNDDOWN = REDONDEAR.MENOS
+ROUNDUP = REDONDEAR.MAS
+SEC = SEC
+SECH = SECH
+SERIESSUM = SUMA.SERIES
+SIGN = SIGNO
+SIN = SENO
+SINH = SENOH
+SQRT = RAIZ
+SQRTPI = RAIZ2PI
+SUBTOTAL = SUBTOTALES
+SUM = SUMA
+SUMIF = SUMAR.SI
+SUMIFS = SUMAR.SI.CONJUNTO
+SUMPRODUCT = SUMAPRODUCTO
+SUMSQ = SUMA.CUADRADOS
+SUMX2MY2 = SUMAX2MENOSY2
+SUMX2PY2 = SUMAX2MASY2
+SUMXMY2 = SUMAXMENOSY2
+TAN = TAN
+TANH = TANH
+TRUNC = TRUNCAR
+
+##
+## Funciones estadísticas (Statistical Functions)
+##
+AVEDEV = DESVPROM
+AVERAGE = PROMEDIO
+AVERAGEA = PROMEDIOA
+AVERAGEIF = PROMEDIO.SI
+AVERAGEIFS = PROMEDIO.SI.CONJUNTO
+BETA.DIST = DISTR.BETA.N
+BETA.INV = INV.BETA.N
+BINOM.DIST = DISTR.BINOM.N
+BINOM.DIST.RANGE = DISTR.BINOM.SERIE
+BINOM.INV = INV.BINOM
+CHISQ.DIST = DISTR.CHICUAD
+CHISQ.DIST.RT = DISTR.CHICUAD.CD
+CHISQ.INV = INV.CHICUAD
+CHISQ.INV.RT = INV.CHICUAD.CD
+CHISQ.TEST = PRUEBA.CHICUAD
+CONFIDENCE.NORM = INTERVALO.CONFIANZA.NORM
+CONFIDENCE.T = INTERVALO.CONFIANZA.T
+CORREL = COEF.DE.CORREL
+COUNT = CONTAR
+COUNTA = CONTARA
+COUNTBLANK = CONTAR.BLANCO
+COUNTIF = CONTAR.SI
+COUNTIFS = CONTAR.SI.CONJUNTO
+COVARIANCE.P = COVARIANCE.P
+COVARIANCE.S = COVARIANZA.M
+DEVSQ = DESVIA2
+EXPON.DIST = DISTR.EXP.N
+F.DIST = DISTR.F.N
+F.DIST.RT = DISTR.F.CD
+F.INV = INV.F
+F.INV.RT = INV.F.CD
+F.TEST = PRUEBA.F.N
+FISHER = FISHER
+FISHERINV = PRUEBA.FISHER.INV
+FORECAST.ETS = PRONOSTICO.ETS
+FORECAST.ETS.CONFINT = PRONOSTICO.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = PRONOSTICO.ETS.ESTACIONALIDAD
+FORECAST.ETS.STAT = PRONOSTICO.ETS.STAT
+FORECAST.LINEAR = PRONOSTICO.LINEAL
+FREQUENCY = FRECUENCIA
+GAMMA = GAMMA
+GAMMA.DIST = DISTR.GAMMA.N
+GAMMA.INV = INV.GAMMA
+GAMMALN = GAMMA.LN
+GAMMALN.PRECISE = GAMMA.LN.EXACTO
+GAUSS = GAUSS
+GEOMEAN = MEDIA.GEOM
+GROWTH = CRECIMIENTO
+HARMEAN = MEDIA.ARMO
+HYPGEOM.DIST = DISTR.HIPERGEOM.N
+INTERCEPT = INTERSECCION.EJE
+KURT = CURTOSIS
+LARGE = K.ESIMO.MAYOR
+LINEST = ESTIMACION.LINEAL
+LOGEST = ESTIMACION.LOGARITMICA
+LOGNORM.DIST = DISTR.LOGNORM
+LOGNORM.INV = INV.LOGNORM
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAX.SI.CONJUNTO
+MEDIAN = MEDIANA
+MIN = MIN
+MINA = MINA
+MINIFS = MIN.SI.CONJUNTO
+MODE.MULT = MODA.VARIOS
+MODE.SNGL = MODA.UNO
+NEGBINOM.DIST = NEGBINOM.DIST
+NORM.DIST = DISTR.NORM.N
+NORM.INV = INV.NORM
+NORM.S.DIST = DISTR.NORM.ESTAND.N
+NORM.S.INV = INV.NORM.ESTAND
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTIL.EXC
+PERCENTILE.INC = PERCENTIL.INC
+PERCENTRANK.EXC = RANGO.PERCENTIL.EXC
+PERCENTRANK.INC = RANGO.PERCENTIL.INC
+PERMUT = PERMUTACIONES
+PERMUTATIONA = PERMUTACIONES.A
+PHI = FI
+POISSON.DIST = POISSON.DIST
+PROB = PROBABILIDAD
+QUARTILE.EXC = CUARTIL.EXC
+QUARTILE.INC = CUARTIL.INC
+RANK.AVG = JERARQUIA.MEDIA
+RANK.EQ = JERARQUIA.EQV
+RSQ = COEFICIENTE.R2
+SKEW = COEFICIENTE.ASIMETRIA
+SKEW.P = COEFICIENTE.ASIMETRIA.P
+SLOPE = PENDIENTE
+SMALL = K.ESIMO.MENOR
+STANDARDIZE = NORMALIZACION
+STDEV.P = DESVEST.P
+STDEV.S = DESVEST.M
+STDEVA = DESVESTA
+STDEVPA = DESVESTPA
+STEYX = ERROR.TIPICO.XY
+T.DIST = DISTR.T.N
+T.DIST.2T = DISTR.T.2C
+T.DIST.RT = DISTR.T.CD
+T.INV = INV.T
+T.INV.2T = INV.T.2C
+T.TEST = PRUEBA.T.N
+TREND = TENDENCIA
+TRIMMEAN = MEDIA.ACOTADA
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = DISTR.WEIBULL
+Z.TEST = PRUEBA.Z.N
+
+##
+## Funciones de texto (Text Functions)
+##
+BAHTTEXT = TEXTOBAHT
+CHAR = CARACTER
+CLEAN = LIMPIAR
+CODE = CODIGO
+CONCAT = CONCAT
+DOLLAR = MONEDA
+EXACT = IGUAL
+FIND = ENCONTRAR
+FIXED = DECIMAL
+ISTHAIDIGIT = ESDIGITOTAI
+LEFT = IZQUIERDA
+LEN = LARGO
+LOWER = MINUSC
+MID = EXTRAE
+NUMBERSTRING = CADENA.NUMERO
+NUMBERVALUE = VALOR.NUMERO
+PHONETIC = FONETICO
+PROPER = NOMPROPIO
+REPLACE = REEMPLAZAR
+REPT = REPETIR
+RIGHT = DERECHA
+SEARCH = HALLAR
+SUBSTITUTE = SUSTITUIR
+T = T
+TEXT = TEXTO
+TEXTJOIN = UNIRCADENAS
+THAIDIGIT = DIGITOTAI
+THAINUMSOUND = SONNUMTAI
+THAINUMSTRING = CADENANUMTAI
+THAISTRINGLENGTH = LONGCADENATAI
+TRIM = ESPACIOS
+UNICHAR = UNICAR
+UNICODE = UNICODE
+UPPER = MAYUSC
+VALUE = VALOR
+
+##
+## Funciones web (Web Functions)
+##
+ENCODEURL = URLCODIF
+FILTERXML = XMLFILTRO
+WEBSERVICE = SERVICIOWEB
+
+##
+## Funciones de compatibilidad (Compatibility Functions)
+##
+BETADIST = DISTR.BETA
+BETAINV = DISTR.BETA.INV
+BINOMDIST = DISTR.BINOM
+CEILING = MULTIPLO.SUPERIOR
+CHIDIST = DISTR.CHI
+CHIINV = PRUEBA.CHI.INV
+CHITEST = PRUEBA.CHI
+CONCATENATE = CONCATENAR
+CONFIDENCE = INTERVALO.CONFIANZA
+COVAR = COVAR
+CRITBINOM = BINOM.CRIT
+EXPONDIST = DISTR.EXP
+FDIST = DISTR.F
+FINV = DISTR.F.INV
+FLOOR = MULTIPLO.INFERIOR
+FORECAST = PRONOSTICO
+FTEST = PRUEBA.F
+GAMMADIST = DISTR.GAMMA
+GAMMAINV = DISTR.GAMMA.INV
+HYPGEOMDIST = DISTR.HIPERGEOM
+LOGINV = DISTR.LOG.INV
+LOGNORMDIST = DISTR.LOG.NORM
+MODE = MODA
+NEGBINOMDIST = NEGBINOMDIST
+NORMDIST = DISTR.NORM
+NORMINV = DISTR.NORM.INV
+NORMSDIST = DISTR.NORM.ESTAND
+NORMSINV = DISTR.NORM.ESTAND.INV
+PERCENTILE = PERCENTIL
+PERCENTRANK = RANGO.PERCENTIL
+POISSON = POISSON
+QUARTILE = CUARTIL
+RANK = JERARQUIA
+STDEV = DESVEST
+STDEVP = DESVESTP
+TDIST = DISTR.T
+TINV = DISTR.T.INV
+TTEST = PRUEBA.T
+VAR = VAR
+VARP = VARP
+WEIBULL = DIST.WEIBULL
+ZTEST = PRUEBA.Z
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config
new file mode 100644
index 0000000..5388f93
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Suomi (Finnish)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #TYHJÄ!
+DIV0 = #JAKO/0!
+VALUE = #ARVO!
+REF = #VIITTAUS!
+NAME = #NIMI?
+NUM = #LUKU!
+NA = #PUUTTUU!
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions
new file mode 100644
index 0000000..18f7c8c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions
@@ -0,0 +1,538 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Suomi (Finnish)
+##
+############################################################
+
+
+##
+## Kuutiofunktiot (Cube Functions)
+##
+CUBEKPIMEMBER = KUUTIOKPIJÄSEN
+CUBEMEMBER = KUUTIONJÄSEN
+CUBEMEMBERPROPERTY = KUUTIONJÄSENENOMINAISUUS
+CUBERANKEDMEMBER = KUUTIONLUOKITELTUJÄSEN
+CUBESET = KUUTIOJOUKKO
+CUBESETCOUNT = KUUTIOJOUKKOJENMÄÄRÄ
+CUBEVALUE = KUUTIONARVO
+
+##
+## Tietokantafunktiot (Database Functions)
+##
+DAVERAGE = TKESKIARVO
+DCOUNT = TLASKE
+DCOUNTA = TLASKEA
+DGET = TNOUDA
+DMAX = TMAKS
+DMIN = TMIN
+DPRODUCT = TTULO
+DSTDEV = TKESKIHAJONTA
+DSTDEVP = TKESKIHAJONTAP
+DSUM = TSUMMA
+DVAR = TVARIANSSI
+DVARP = TVARIANSSIP
+
+##
+## Päivämäärä- ja aikafunktiot (Date & Time Functions)
+##
+DATE = PÄIVÄYS
+DATEDIF = PVMERO
+DATESTRING = PVMMERKKIJONO
+DATEVALUE = PÄIVÄYSARVO
+DAY = PÄIVÄ
+DAYS = PÄIVÄT
+DAYS360 = PÄIVÄT360
+EDATE = PÄIVÄ.KUUKAUSI
+EOMONTH = KUUKAUSI.LOPPU
+HOUR = TUNNIT
+ISOWEEKNUM = VIIKKO.ISO.NRO
+MINUTE = MINUUTIT
+MONTH = KUUKAUSI
+NETWORKDAYS = TYÖPÄIVÄT
+NETWORKDAYS.INTL = TYÖPÄIVÄT.KANSVÄL
+NOW = NYT
+SECOND = SEKUNNIT
+THAIDAYOFWEEK = THAI.VIIKONPÄIVÄ
+THAIMONTHOFYEAR = THAI.KUUKAUSI
+THAIYEAR = THAI.VUOSI
+TIME = AIKA
+TIMEVALUE = AIKA_ARVO
+TODAY = TÄMÄ.PÄIVÄ
+WEEKDAY = VIIKONPÄIVÄ
+WEEKNUM = VIIKKO.NRO
+WORKDAY = TYÖPÄIVÄ
+WORKDAY.INTL = TYÖPÄIVÄ.KANSVÄL
+YEAR = VUOSI
+YEARFRAC = VUOSI.OSA
+
+##
+## Tekniset funktiot (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BINDES
+BIN2HEX = BINHEKSA
+BIN2OCT = BINOKT
+BITAND = BITTI.JA
+BITLSHIFT = BITTI.SIIRTO.V
+BITOR = BITTI.TAI
+BITRSHIFT = BITTI.SIIRTO.O
+BITXOR = BITTI.EHDOTON.TAI
+COMPLEX = KOMPLEKSI
+CONVERT = MUUNNA
+DEC2BIN = DESBIN
+DEC2HEX = DESHEKSA
+DEC2OCT = DESOKT
+DELTA = SAMA.ARVO
+ERF = VIRHEFUNKTIO
+ERF.PRECISE = VIRHEFUNKTIO.TARKKA
+ERFC = VIRHEFUNKTIO.KOMPLEMENTTI
+ERFC.PRECISE = VIRHEFUNKTIO.KOMPLEMENTTI.TARKKA
+GESTEP = RAJA
+HEX2BIN = HEKSABIN
+HEX2DEC = HEKSADES
+HEX2OCT = HEKSAOKT
+IMABS = KOMPLEKSI.ABS
+IMAGINARY = KOMPLEKSI.IMAG
+IMARGUMENT = KOMPLEKSI.ARG
+IMCONJUGATE = KOMPLEKSI.KONJ
+IMCOS = KOMPLEKSI.COS
+IMCOSH = IMCOSH
+IMCOT = KOMPLEKSI.COT
+IMCSC = KOMPLEKSI.KOSEK
+IMCSCH = KOMPLEKSI.KOSEKH
+IMDIV = KOMPLEKSI.OSAM
+IMEXP = KOMPLEKSI.EKSP
+IMLN = KOMPLEKSI.LN
+IMLOG10 = KOMPLEKSI.LOG10
+IMLOG2 = KOMPLEKSI.LOG2
+IMPOWER = KOMPLEKSI.POT
+IMPRODUCT = KOMPLEKSI.TULO
+IMREAL = KOMPLEKSI.REAALI
+IMSEC = KOMPLEKSI.SEK
+IMSECH = KOMPLEKSI.SEKH
+IMSIN = KOMPLEKSI.SIN
+IMSINH = KOMPLEKSI.SINH
+IMSQRT = KOMPLEKSI.NELIÖJ
+IMSUB = KOMPLEKSI.EROTUS
+IMSUM = KOMPLEKSI.SUM
+IMTAN = KOMPLEKSI.TAN
+OCT2BIN = OKTBIN
+OCT2DEC = OKTDES
+OCT2HEX = OKTHEKSA
+
+##
+## Rahoitusfunktiot (Financial Functions)
+##
+ACCRINT = KERTYNYT.KORKO
+ACCRINTM = KERTYNYT.KORKO.LOPUSSA
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = KORKOPÄIVÄT.ALUSTA
+COUPDAYS = KORKOPÄIVÄT
+COUPDAYSNC = KORKOPÄIVÄT.SEURAAVA
+COUPNCD = KORKOPÄIVÄ.SEURAAVA
+COUPNUM = KORKOPÄIVÄ.JAKSOT
+COUPPCD = KORKOPÄIVÄ.EDELLINEN
+CUMIPMT = MAKSETTU.KORKO
+CUMPRINC = MAKSETTU.LYHENNYS
+DB = DB
+DDB = DDB
+DISC = DISKONTTOKORKO
+DOLLARDE = VALUUTTA.DES
+DOLLARFR = VALUUTTA.MURTO
+DURATION = KESTO
+EFFECT = KORKO.EFEKT
+FV = TULEVA.ARVO
+FVSCHEDULE = TULEVA.ARVO.ERIKORKO
+INTRATE = KORKO.ARVOPAPERI
+IPMT = IPMT
+IRR = SISÄINEN.KORKO
+ISPMT = ISPMT
+MDURATION = KESTO.MUUNN
+MIRR = MSISÄINEN
+NOMINAL = KORKO.VUOSI
+NPER = NJAKSO
+NPV = NNA
+ODDFPRICE = PARITON.ENS.NIMELLISARVO
+ODDFYIELD = PARITON.ENS.TUOTTO
+ODDLPRICE = PARITON.VIIM.NIMELLISARVO
+ODDLYIELD = PARITON.VIIM.TUOTTO
+PDURATION = KESTO.JAKSO
+PMT = MAKSU
+PPMT = PPMT
+PRICE = HINTA
+PRICEDISC = HINTA.DISK
+PRICEMAT = HINTA.LUNASTUS
+PV = NA
+RATE = KORKO
+RECEIVED = SAATU.HINTA
+RRI = TOT.ROI
+SLN = STP
+SYD = VUOSIPOISTO
+TBILLEQ = OBLIG.TUOTTOPROS
+TBILLPRICE = OBLIG.HINTA
+TBILLYIELD = OBLIG.TUOTTO
+VDB = VDB
+XIRR = SISÄINEN.KORKO.JAKSOTON
+XNPV = NNA.JAKSOTON
+YIELD = TUOTTO
+YIELDDISC = TUOTTO.DISK
+YIELDMAT = TUOTTO.ERÄP
+
+##
+## Tietofunktiot (Information Functions)
+##
+CELL = SOLU
+ERROR.TYPE = VIRHEEN.LAJI
+INFO = KUVAUS
+ISBLANK = ONTYHJÄ
+ISERR = ONVIRH
+ISERROR = ONVIRHE
+ISEVEN = ONPARILLINEN
+ISFORMULA = ONKAAVA
+ISLOGICAL = ONTOTUUS
+ISNA = ONPUUTTUU
+ISNONTEXT = ONEI_TEKSTI
+ISNUMBER = ONLUKU
+ISODD = ONPARITON
+ISREF = ONVIITT
+ISTEXT = ONTEKSTI
+N = N
+NA = PUUTTUU
+SHEET = TAULUKKO
+SHEETS = TAULUKOT
+TYPE = TYYPPI
+
+##
+## Loogiset funktiot (Logical Functions)
+##
+AND = JA
+FALSE = EPÄTOSI
+IF = JOS
+IFERROR = JOSVIRHE
+IFNA = JOSPUUTTUU
+IFS = JOSS
+NOT = EI
+OR = TAI
+SWITCH = MUUTA
+TRUE = TOSI
+XOR = EHDOTON.TAI
+
+##
+## Haku- ja viitefunktiot (Lookup & Reference Functions)
+##
+ADDRESS = OSOITE
+AREAS = ALUEET
+CHOOSE = VALITSE.INDEKSI
+COLUMN = SARAKE
+COLUMNS = SARAKKEET
+FORMULATEXT = KAAVA.TEKSTI
+GETPIVOTDATA = NOUDA.PIVOT.TIEDOT
+HLOOKUP = VHAKU
+HYPERLINK = HYPERLINKKI
+INDEX = INDEKSI
+INDIRECT = EPÄSUORA
+LOOKUP = HAKU
+MATCH = VASTINE
+OFFSET = SIIRTYMÄ
+ROW = RIVI
+ROWS = RIVIT
+RTD = RTD
+TRANSPOSE = TRANSPONOI
+VLOOKUP = PHAKU
+*RC = RS
+
+##
+## Matemaattiset ja trigonometriset funktiot (Math & Trig Functions)
+##
+ABS = ITSEISARVO
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = KOOSTE
+ARABIC = ARABIA
+ASIN = ASIN
+ASINH = ASINH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = PERUS
+CEILING.MATH = PYÖRISTÄ.KERR.YLÖS.MATEMAATTINEN
+CEILING.PRECISE = PYÖRISTÄ.KERR.YLÖS.TARKKA
+COMBIN = KOMBINAATIO
+COMBINA = KOMBINAATIOA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = KOSEK
+CSCH = KOSEKH
+DECIMAL = DESIMAALI
+DEGREES = ASTEET
+ECMA.CEILING = ECMA.PYÖRISTÄ.KERR.YLÖS
+EVEN = PARILLINEN
+EXP = EKSPONENTTI
+FACT = KERTOMA
+FACTDOUBLE = KERTOMA.OSA
+FLOOR.MATH = PYÖRISTÄ.KERR.ALAS.MATEMAATTINEN
+FLOOR.PRECISE = PYÖRISTÄ.KERR.ALAS.TARKKA
+GCD = SUURIN.YHT.TEKIJÄ
+INT = KOKONAISLUKU
+ISO.CEILING = ISO.PYÖRISTÄ.KERR.YLÖS
+LCM = PIENIN.YHT.JAETTAVA
+LN = LUONNLOG
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDETERM
+MINVERSE = MKÄÄNTEINEN
+MMULT = MKERRO
+MOD = JAKOJ
+MROUND = PYÖRISTÄ.KERR
+MULTINOMIAL = MULTINOMI
+MUNIT = YKSIKKÖM
+ODD = PARITON
+PI = PII
+POWER = POTENSSI
+PRODUCT = TULO
+QUOTIENT = OSAMÄÄRÄ
+RADIANS = RADIAANIT
+RAND = SATUNNAISLUKU
+RANDBETWEEN = SATUNNAISLUKU.VÄLILTÄ
+ROMAN = ROMAN
+ROUND = PYÖRISTÄ
+ROUNDBAHTDOWN = PYÖRISTÄ.BAHT.ALAS
+ROUNDBAHTUP = PYÖRISTÄ.BAHT.YLÖS
+ROUNDDOWN = PYÖRISTÄ.DES.ALAS
+ROUNDUP = PYÖRISTÄ.DES.YLÖS
+SEC = SEK
+SECH = SEKH
+SERIESSUM = SARJA.SUMMA
+SIGN = ETUMERKKI
+SIN = SIN
+SINH = SINH
+SQRT = NELIÖJUURI
+SQRTPI = NELIÖJUURI.PII
+SUBTOTAL = VÄLISUMMA
+SUM = SUMMA
+SUMIF = SUMMA.JOS
+SUMIFS = SUMMA.JOS.JOUKKO
+SUMPRODUCT = TULOJEN.SUMMA
+SUMSQ = NELIÖSUMMA
+SUMX2MY2 = NELIÖSUMMIEN.EROTUS
+SUMX2PY2 = NELIÖSUMMIEN.SUMMA
+SUMXMY2 = EROTUSTEN.NELIÖSUMMA
+TAN = TAN
+TANH = TANH
+TRUNC = KATKAISE
+
+##
+## Tilastolliset funktiot (Statistical Functions)
+##
+AVEDEV = KESKIPOIKKEAMA
+AVERAGE = KESKIARVO
+AVERAGEA = KESKIARVOA
+AVERAGEIF = KESKIARVO.JOS
+AVERAGEIFS = KESKIARVO.JOS.JOUKKO
+BETA.DIST = BEETA.JAKAUMA
+BETA.INV = BEETA.KÄÄNT
+BINOM.DIST = BINOMI.JAKAUMA
+BINOM.DIST.RANGE = BINOMI.JAKAUMA.ALUE
+BINOM.INV = BINOMIJAKAUMA.KÄÄNT
+CHISQ.DIST = CHINELIÖ.JAKAUMA
+CHISQ.DIST.RT = CHINELIÖ.JAKAUMA.OH
+CHISQ.INV = CHINELIÖ.KÄÄNT
+CHISQ.INV.RT = CHINELIÖ.KÄÄNT.OH
+CHISQ.TEST = CHINELIÖ.TESTI
+CONFIDENCE.NORM = LUOTTAMUSVÄLI.NORM
+CONFIDENCE.T = LUOTTAMUSVÄLI.T
+CORREL = KORRELAATIO
+COUNT = LASKE
+COUNTA = LASKE.A
+COUNTBLANK = LASKE.TYHJÄT
+COUNTIF = LASKE.JOS
+COUNTIFS = LASKE.JOS.JOUKKO
+COVARIANCE.P = KOVARIANSSI.P
+COVARIANCE.S = KOVARIANSSI.S
+DEVSQ = OIKAISTU.NELIÖSUMMA
+EXPON.DIST = EKSPONENTIAALI.JAKAUMA
+F.DIST = F.JAKAUMA
+F.DIST.RT = F.JAKAUMA.OH
+F.INV = F.KÄÄNT
+F.INV.RT = F.KÄÄNT.OH
+F.TEST = F.TESTI
+FISHER = FISHER
+FISHERINV = FISHER.KÄÄNT
+FORECAST.ETS = ENNUSTE.ETS
+FORECAST.ETS.CONFINT = ENNUSTE.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = ENNUSTE.ETS.KAUSIVAIHTELU
+FORECAST.ETS.STAT = ENNUSTE.ETS.STAT
+FORECAST.LINEAR = ENNUSTE.LINEAARINEN
+FREQUENCY = TAAJUUS
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.JAKAUMA
+GAMMA.INV = GAMMA.JAKAUMA.KÄÄNT
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.TARKKA
+GAUSS = GAUSS
+GEOMEAN = KESKIARVO.GEOM
+GROWTH = KASVU
+HARMEAN = KESKIARVO.HARM
+HYPGEOM.DIST = HYPERGEOM_JAKAUMA
+INTERCEPT = LEIKKAUSPISTE
+KURT = KURT
+LARGE = SUURI
+LINEST = LINREGR
+LOGEST = LOGREGR
+LOGNORM.DIST = LOGNORM_JAKAUMA
+LOGNORM.INV = LOGNORM.KÄÄNT
+MAX = MAKS
+MAXA = MAKSA
+MAXIFS = MAKS.JOS
+MEDIAN = MEDIAANI
+MIN = MIN
+MINA = MINA
+MINIFS = MIN.JOS
+MODE.MULT = MOODI.USEA
+MODE.SNGL = MOODI.YKSI
+NEGBINOM.DIST = BINOMI.JAKAUMA.NEG
+NORM.DIST = NORMAALI.JAKAUMA
+NORM.INV = NORMAALI.JAKAUMA.KÄÄNT
+NORM.S.DIST = NORM_JAKAUMA.NORMIT
+NORM.S.INV = NORM_JAKAUMA.KÄÄNT
+PEARSON = PEARSON
+PERCENTILE.EXC = PROSENTTIPISTE.ULK
+PERCENTILE.INC = PROSENTTIPISTE.SIS
+PERCENTRANK.EXC = PROSENTTIJÄRJESTYS.ULK
+PERCENTRANK.INC = PROSENTTIJÄRJESTYS.SIS
+PERMUT = PERMUTAATIO
+PERMUTATIONA = PERMUTAATIOA
+PHI = FII
+POISSON.DIST = POISSON.JAKAUMA
+PROB = TODENNÄKÖISYYS
+QUARTILE.EXC = NELJÄNNES.ULK
+QUARTILE.INC = NELJÄNNES.SIS
+RANK.AVG = ARVON.MUKAAN.KESKIARVO
+RANK.EQ = ARVON.MUKAAN.TASAN
+RSQ = PEARSON.NELIÖ
+SKEW = JAKAUMAN.VINOUS
+SKEW.P = JAKAUMAN.VINOUS.POP
+SLOPE = KULMAKERROIN
+SMALL = PIENI
+STANDARDIZE = NORMITA
+STDEV.P = KESKIHAJONTA.P
+STDEV.S = KESKIHAJONTA.S
+STDEVA = KESKIHAJONTAA
+STDEVPA = KESKIHAJONTAPA
+STEYX = KESKIVIRHE
+T.DIST = T.JAKAUMA
+T.DIST.2T = T.JAKAUMA.2S
+T.DIST.RT = T.JAKAUMA.OH
+T.INV = T.KÄÄNT
+T.INV.2T = T.KÄÄNT.2S
+T.TEST = T.TESTI
+TREND = SUUNTAUS
+TRIMMEAN = KESKIARVO.TASATTU
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = WEIBULL.JAKAUMA
+Z.TEST = Z.TESTI
+
+##
+## Tekstifunktiot (Text Functions)
+##
+BAHTTEXT = BAHTTEKSTI
+CHAR = MERKKI
+CLEAN = SIIVOA
+CODE = KOODI
+CONCAT = YHDISTÄ
+DOLLAR = VALUUTTA
+EXACT = VERTAA
+FIND = ETSI
+FIXED = KIINTEÄ
+ISTHAIDIGIT = ON.THAI.NUMERO
+LEFT = VASEN
+LEN = PITUUS
+LOWER = PIENET
+MID = POIMI.TEKSTI
+NUMBERSTRING = NROMERKKIJONO
+NUMBERVALUE = NROARVO
+PHONETIC = FONEETTINEN
+PROPER = ERISNIMI
+REPLACE = KORVAA
+REPT = TOISTA
+RIGHT = OIKEA
+SEARCH = KÄY.LÄPI
+SUBSTITUTE = VAIHDA
+T = T
+TEXT = TEKSTI
+TEXTJOIN = TEKSTI.YHDISTÄ
+THAIDIGIT = THAI.NUMERO
+THAINUMSOUND = THAI.LUKU.ÄÄNI
+THAINUMSTRING = THAI.LUKU.MERKKIJONO
+THAISTRINGLENGTH = THAI.MERKKIJONON.PITUUS
+TRIM = POISTA.VÄLIT
+UNICHAR = UNICODEMERKKI
+UNICODE = UNICODE
+UPPER = ISOT
+VALUE = ARVO
+
+##
+## Verkkofunktiot (Web Functions)
+##
+ENCODEURL = URLKOODAUS
+FILTERXML = SUODATA.XML
+WEBSERVICE = VERKKOPALVELU
+
+##
+## Yhteensopivuusfunktiot (Compatibility Functions)
+##
+BETADIST = BEETAJAKAUMA
+BETAINV = BEETAJAKAUMA.KÄÄNT
+BINOMDIST = BINOMIJAKAUMA
+CEILING = PYÖRISTÄ.KERR.YLÖS
+CHIDIST = CHIJAKAUMA
+CHIINV = CHIJAKAUMA.KÄÄNT
+CHITEST = CHITESTI
+CONCATENATE = KETJUTA
+CONFIDENCE = LUOTTAMUSVÄLI
+COVAR = KOVARIANSSI
+CRITBINOM = BINOMIJAKAUMA.KRIT
+EXPONDIST = EKSPONENTIAALIJAKAUMA
+FDIST = FJAKAUMA
+FINV = FJAKAUMA.KÄÄNT
+FLOOR = PYÖRISTÄ.KERR.ALAS
+FORECAST = ENNUSTE
+FTEST = FTESTI
+GAMMADIST = GAMMAJAKAUMA
+GAMMAINV = GAMMAJAKAUMA.KÄÄNT
+HYPGEOMDIST = HYPERGEOM.JAKAUMA
+LOGINV = LOGNORM.JAKAUMA.KÄÄNT
+LOGNORMDIST = LOGNORM.JAKAUMA
+MODE = MOODI
+NEGBINOMDIST = BINOMIJAKAUMA.NEG
+NORMDIST = NORM.JAKAUMA
+NORMINV = NORM.JAKAUMA.KÄÄNT
+NORMSDIST = NORM.JAKAUMA.NORMIT
+NORMSINV = NORM.JAKAUMA.NORMIT.KÄÄNT
+PERCENTILE = PROSENTTIPISTE
+PERCENTRANK = PROSENTTIJÄRJESTYS
+POISSON = POISSON
+QUARTILE = NELJÄNNES
+RANK = ARVON.MUKAAN
+STDEV = KESKIHAJONTA
+STDEVP = KESKIHAJONTAP
+TDIST = TJAKAUMA
+TINV = TJAKAUMA.KÄÄNT
+TTEST = TTESTI
+VAR = VAR
+VARP = VARP
+WEIBULL = WEIBULL
+ZTEST = ZTESTI
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config
new file mode 100644
index 0000000..bdac412
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Français (French)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #NUL!
+DIV0
+VALUE = #VALEUR!
+REF
+NAME = #NOM?
+NUM = #NOMBRE!
+NA
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions
new file mode 100644
index 0000000..621cb0d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions
@@ -0,0 +1,525 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Français (French)
+##
+############################################################
+
+
+##
+## Fonctions Cube (Cube Functions)
+##
+CUBEKPIMEMBER = MEMBREKPICUBE
+CUBEMEMBER = MEMBRECUBE
+CUBEMEMBERPROPERTY = PROPRIETEMEMBRECUBE
+CUBERANKEDMEMBER = RANGMEMBRECUBE
+CUBESET = JEUCUBE
+CUBESETCOUNT = NBJEUCUBE
+CUBEVALUE = VALEURCUBE
+
+##
+## Fonctions de base de données (Database Functions)
+##
+DAVERAGE = BDMOYENNE
+DCOUNT = BDNB
+DCOUNTA = BDNBVAL
+DGET = BDLIRE
+DMAX = BDMAX
+DMIN = BDMIN
+DPRODUCT = BDPRODUIT
+DSTDEV = BDECARTYPE
+DSTDEVP = BDECARTYPEP
+DSUM = BDSOMME
+DVAR = BDVAR
+DVARP = BDVARP
+
+##
+## Fonctions de date et d’heure (Date & Time Functions)
+##
+DATE = DATE
+DATEVALUE = DATEVAL
+DAY = JOUR
+DAYS = JOURS
+DAYS360 = JOURS360
+EDATE = MOIS.DECALER
+EOMONTH = FIN.MOIS
+HOUR = HEURE
+ISOWEEKNUM = NO.SEMAINE.ISO
+MINUTE = MINUTE
+MONTH = MOIS
+NETWORKDAYS = NB.JOURS.OUVRES
+NETWORKDAYS.INTL = NB.JOURS.OUVRES.INTL
+NOW = MAINTENANT
+SECOND = SECONDE
+TIME = TEMPS
+TIMEVALUE = TEMPSVAL
+TODAY = AUJOURDHUI
+WEEKDAY = JOURSEM
+WEEKNUM = NO.SEMAINE
+WORKDAY = SERIE.JOUR.OUVRE
+WORKDAY.INTL = SERIE.JOUR.OUVRE.INTL
+YEAR = ANNEE
+YEARFRAC = FRACTION.ANNEE
+
+##
+## Fonctions d’ingénierie (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BINDEC
+BIN2HEX = BINHEX
+BIN2OCT = BINOCT
+BITAND = BITET
+BITLSHIFT = BITDECALG
+BITOR = BITOU
+BITRSHIFT = BITDECALD
+BITXOR = BITOUEXCLUSIF
+COMPLEX = COMPLEXE
+CONVERT = CONVERT
+DEC2BIN = DECBIN
+DEC2HEX = DECHEX
+DEC2OCT = DECOCT
+DELTA = DELTA
+ERF = ERF
+ERF.PRECISE = ERF.PRECIS
+ERFC = ERFC
+ERFC.PRECISE = ERFC.PRECIS
+GESTEP = SUP.SEUIL
+HEX2BIN = HEXBIN
+HEX2DEC = HEXDEC
+HEX2OCT = HEXOCT
+IMABS = COMPLEXE.MODULE
+IMAGINARY = COMPLEXE.IMAGINAIRE
+IMARGUMENT = COMPLEXE.ARGUMENT
+IMCONJUGATE = COMPLEXE.CONJUGUE
+IMCOS = COMPLEXE.COS
+IMCOSH = COMPLEXE.COSH
+IMCOT = COMPLEXE.COT
+IMCSC = COMPLEXE.CSC
+IMCSCH = COMPLEXE.CSCH
+IMDIV = COMPLEXE.DIV
+IMEXP = COMPLEXE.EXP
+IMLN = COMPLEXE.LN
+IMLOG10 = COMPLEXE.LOG10
+IMLOG2 = COMPLEXE.LOG2
+IMPOWER = COMPLEXE.PUISSANCE
+IMPRODUCT = COMPLEXE.PRODUIT
+IMREAL = COMPLEXE.REEL
+IMSEC = COMPLEXE.SEC
+IMSECH = COMPLEXE.SECH
+IMSIN = COMPLEXE.SIN
+IMSINH = COMPLEXE.SINH
+IMSQRT = COMPLEXE.RACINE
+IMSUB = COMPLEXE.DIFFERENCE
+IMSUM = COMPLEXE.SOMME
+IMTAN = COMPLEXE.TAN
+OCT2BIN = OCTBIN
+OCT2DEC = OCTDEC
+OCT2HEX = OCTHEX
+
+##
+## Fonctions financières (Financial Functions)
+##
+ACCRINT = INTERET.ACC
+ACCRINTM = INTERET.ACC.MAT
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = NB.JOURS.COUPON.PREC
+COUPDAYS = NB.JOURS.COUPONS
+COUPDAYSNC = NB.JOURS.COUPON.SUIV
+COUPNCD = DATE.COUPON.SUIV
+COUPNUM = NB.COUPONS
+COUPPCD = DATE.COUPON.PREC
+CUMIPMT = CUMUL.INTER
+CUMPRINC = CUMUL.PRINCPER
+DB = DB
+DDB = DDB
+DISC = TAUX.ESCOMPTE
+DOLLARDE = PRIX.DEC
+DOLLARFR = PRIX.FRAC
+DURATION = DUREE
+EFFECT = TAUX.EFFECTIF
+FV = VC
+FVSCHEDULE = VC.PAIEMENTS
+INTRATE = TAUX.INTERET
+IPMT = INTPER
+IRR = TRI
+ISPMT = ISPMT
+MDURATION = DUREE.MODIFIEE
+MIRR = TRIM
+NOMINAL = TAUX.NOMINAL
+NPER = NPM
+NPV = VAN
+ODDFPRICE = PRIX.PCOUPON.IRREG
+ODDFYIELD = REND.PCOUPON.IRREG
+ODDLPRICE = PRIX.DCOUPON.IRREG
+ODDLYIELD = REND.DCOUPON.IRREG
+PDURATION = PDUREE
+PMT = VPM
+PPMT = PRINCPER
+PRICE = PRIX.TITRE
+PRICEDISC = VALEUR.ENCAISSEMENT
+PRICEMAT = PRIX.TITRE.ECHEANCE
+PV = VA
+RATE = TAUX
+RECEIVED = VALEUR.NOMINALE
+RRI = TAUX.INT.EQUIV
+SLN = AMORLIN
+SYD = SYD
+TBILLEQ = TAUX.ESCOMPTE.R
+TBILLPRICE = PRIX.BON.TRESOR
+TBILLYIELD = RENDEMENT.BON.TRESOR
+VDB = VDB
+XIRR = TRI.PAIEMENTS
+XNPV = VAN.PAIEMENTS
+YIELD = RENDEMENT.TITRE
+YIELDDISC = RENDEMENT.SIMPLE
+YIELDMAT = RENDEMENT.TITRE.ECHEANCE
+
+##
+## Fonctions d’information (Information Functions)
+##
+CELL = CELLULE
+ERROR.TYPE = TYPE.ERREUR
+INFO = INFORMATIONS
+ISBLANK = ESTVIDE
+ISERR = ESTERR
+ISERROR = ESTERREUR
+ISEVEN = EST.PAIR
+ISFORMULA = ESTFORMULE
+ISLOGICAL = ESTLOGIQUE
+ISNA = ESTNA
+ISNONTEXT = ESTNONTEXTE
+ISNUMBER = ESTNUM
+ISODD = EST.IMPAIR
+ISREF = ESTREF
+ISTEXT = ESTTEXTE
+N = N
+NA = NA
+SHEET = FEUILLE
+SHEETS = FEUILLES
+TYPE = TYPE
+
+##
+## Fonctions logiques (Logical Functions)
+##
+AND = ET
+FALSE = FAUX
+IF = SI
+IFERROR = SIERREUR
+IFNA = SI.NON.DISP
+IFS = SI.CONDITIONS
+NOT = NON
+OR = OU
+SWITCH = SI.MULTIPLE
+TRUE = VRAI
+XOR = OUX
+
+##
+## Fonctions de recherche et de référence (Lookup & Reference Functions)
+##
+ADDRESS = ADRESSE
+AREAS = ZONES
+CHOOSE = CHOISIR
+COLUMN = COLONNE
+COLUMNS = COLONNES
+FORMULATEXT = FORMULETEXTE
+GETPIVOTDATA = LIREDONNEESTABCROISDYNAMIQUE
+HLOOKUP = RECHERCHEH
+HYPERLINK = LIEN_HYPERTEXTE
+INDEX = INDEX
+INDIRECT = INDIRECT
+LOOKUP = RECHERCHE
+MATCH = EQUIV
+OFFSET = DECALER
+ROW = LIGNE
+ROWS = LIGNES
+RTD = RTD
+TRANSPOSE = TRANSPOSE
+VLOOKUP = RECHERCHEV
+*RC = LC
+
+##
+## Fonctions mathématiques et trigonométriques (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = AGREGAT
+ARABIC = CHIFFRE.ARABE
+ASIN = ASIN
+ASINH = ASINH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = BASE
+CEILING.MATH = PLAFOND.MATH
+CEILING.PRECISE = PLAFOND.PRECIS
+COMBIN = COMBIN
+COMBINA = COMBINA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DECIMAL
+DEGREES = DEGRES
+ECMA.CEILING = ECMA.PLAFOND
+EVEN = PAIR
+EXP = EXP
+FACT = FACT
+FACTDOUBLE = FACTDOUBLE
+FLOOR.MATH = PLANCHER.MATH
+FLOOR.PRECISE = PLANCHER.PRECIS
+GCD = PGCD
+INT = ENT
+ISO.CEILING = ISO.PLAFOND
+LCM = PPCM
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = DETERMAT
+MINVERSE = INVERSEMAT
+MMULT = PRODUITMAT
+MOD = MOD
+MROUND = ARRONDI.AU.MULTIPLE
+MULTINOMIAL = MULTINOMIALE
+MUNIT = MATRICE.UNITAIRE
+ODD = IMPAIR
+PI = PI
+POWER = PUISSANCE
+PRODUCT = PRODUIT
+QUOTIENT = QUOTIENT
+RADIANS = RADIANS
+RAND = ALEA
+RANDBETWEEN = ALEA.ENTRE.BORNES
+ROMAN = ROMAIN
+ROUND = ARRONDI
+ROUNDDOWN = ARRONDI.INF
+ROUNDUP = ARRONDI.SUP
+SEC = SEC
+SECH = SECH
+SERIESSUM = SOMME.SERIES
+SIGN = SIGNE
+SIN = SIN
+SINH = SINH
+SQRT = RACINE
+SQRTPI = RACINE.PI
+SUBTOTAL = SOUS.TOTAL
+SUM = SOMME
+SUMIF = SOMME.SI
+SUMIFS = SOMME.SI.ENS
+SUMPRODUCT = SOMMEPROD
+SUMSQ = SOMME.CARRES
+SUMX2MY2 = SOMME.X2MY2
+SUMX2PY2 = SOMME.X2PY2
+SUMXMY2 = SOMME.XMY2
+TAN = TAN
+TANH = TANH
+TRUNC = TRONQUE
+
+##
+## Fonctions statistiques (Statistical Functions)
+##
+AVEDEV = ECART.MOYEN
+AVERAGE = MOYENNE
+AVERAGEA = AVERAGEA
+AVERAGEIF = MOYENNE.SI
+AVERAGEIFS = MOYENNE.SI.ENS
+BETA.DIST = LOI.BETA.N
+BETA.INV = BETA.INVERSE.N
+BINOM.DIST = LOI.BINOMIALE.N
+BINOM.DIST.RANGE = LOI.BINOMIALE.SERIE
+BINOM.INV = LOI.BINOMIALE.INVERSE
+CHISQ.DIST = LOI.KHIDEUX.N
+CHISQ.DIST.RT = LOI.KHIDEUX.DROITE
+CHISQ.INV = LOI.KHIDEUX.INVERSE
+CHISQ.INV.RT = LOI.KHIDEUX.INVERSE.DROITE
+CHISQ.TEST = CHISQ.TEST
+CONFIDENCE.NORM = INTERVALLE.CONFIANCE.NORMAL
+CONFIDENCE.T = INTERVALLE.CONFIANCE.STUDENT
+CORREL = COEFFICIENT.CORRELATION
+COUNT = NB
+COUNTA = NBVAL
+COUNTBLANK = NB.VIDE
+COUNTIF = NB.SI
+COUNTIFS = NB.SI.ENS
+COVARIANCE.P = COVARIANCE.PEARSON
+COVARIANCE.S = COVARIANCE.STANDARD
+DEVSQ = SOMME.CARRES.ECARTS
+EXPON.DIST = LOI.EXPONENTIELLE.N
+F.DIST = LOI.F.N
+F.DIST.RT = LOI.F.DROITE
+F.INV = INVERSE.LOI.F.N
+F.INV.RT = INVERSE.LOI.F.DROITE
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHER.INVERSE
+FORECAST.ETS = PREVISION.ETS
+FORECAST.ETS.CONFINT = PREVISION.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = PREVISION.ETS.CARACTERESAISONNIER
+FORECAST.ETS.STAT = PREVISION.ETS.STAT
+FORECAST.LINEAR = PREVISION.LINEAIRE
+FREQUENCY = FREQUENCE
+GAMMA = GAMMA
+GAMMA.DIST = LOI.GAMMA.N
+GAMMA.INV = LOI.GAMMA.INVERSE.N
+GAMMALN = LNGAMMA
+GAMMALN.PRECISE = LNGAMMA.PRECIS
+GAUSS = GAUSS
+GEOMEAN = MOYENNE.GEOMETRIQUE
+GROWTH = CROISSANCE
+HARMEAN = MOYENNE.HARMONIQUE
+HYPGEOM.DIST = LOI.HYPERGEOMETRIQUE.N
+INTERCEPT = ORDONNEE.ORIGINE
+KURT = KURTOSIS
+LARGE = GRANDE.VALEUR
+LINEST = DROITEREG
+LOGEST = LOGREG
+LOGNORM.DIST = LOI.LOGNORMALE.N
+LOGNORM.INV = LOI.LOGNORMALE.INVERSE.N
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAX.SI
+MEDIAN = MEDIANE
+MIN = MIN
+MINA = MINA
+MINIFS = MIN.SI
+MODE.MULT = MODE.MULTIPLE
+MODE.SNGL = MODE.SIMPLE
+NEGBINOM.DIST = LOI.BINOMIALE.NEG.N
+NORM.DIST = LOI.NORMALE.N
+NORM.INV = LOI.NORMALE.INVERSE.N
+NORM.S.DIST = LOI.NORMALE.STANDARD.N
+NORM.S.INV = LOI.NORMALE.STANDARD.INVERSE.N
+PEARSON = PEARSON
+PERCENTILE.EXC = CENTILE.EXCLURE
+PERCENTILE.INC = CENTILE.INCLURE
+PERCENTRANK.EXC = RANG.POURCENTAGE.EXCLURE
+PERCENTRANK.INC = RANG.POURCENTAGE.INCLURE
+PERMUT = PERMUTATION
+PERMUTATIONA = PERMUTATIONA
+PHI = PHI
+POISSON.DIST = LOI.POISSON.N
+PROB = PROBABILITE
+QUARTILE.EXC = QUARTILE.EXCLURE
+QUARTILE.INC = QUARTILE.INCLURE
+RANK.AVG = MOYENNE.RANG
+RANK.EQ = EQUATION.RANG
+RSQ = COEFFICIENT.DETERMINATION
+SKEW = COEFFICIENT.ASYMETRIE
+SKEW.P = COEFFICIENT.ASYMETRIE.P
+SLOPE = PENTE
+SMALL = PETITE.VALEUR
+STANDARDIZE = CENTREE.REDUITE
+STDEV.P = ECARTYPE.PEARSON
+STDEV.S = ECARTYPE.STANDARD
+STDEVA = STDEVA
+STDEVPA = STDEVPA
+STEYX = ERREUR.TYPE.XY
+T.DIST = LOI.STUDENT.N
+T.DIST.2T = LOI.STUDENT.BILATERALE
+T.DIST.RT = LOI.STUDENT.DROITE
+T.INV = LOI.STUDENT.INVERSE.N
+T.INV.2T = LOI.STUDENT.INVERSE.BILATERALE
+T.TEST = T.TEST
+TREND = TENDANCE
+TRIMMEAN = MOYENNE.REDUITE
+VAR.P = VAR.P.N
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = LOI.WEIBULL.N
+Z.TEST = Z.TEST
+
+##
+## Fonctions de texte (Text Functions)
+##
+BAHTTEXT = BAHTTEXT
+CHAR = CAR
+CLEAN = EPURAGE
+CODE = CODE
+CONCAT = CONCAT
+DOLLAR = DEVISE
+EXACT = EXACT
+FIND = TROUVE
+FIXED = CTXT
+LEFT = GAUCHE
+LEN = NBCAR
+LOWER = MINUSCULE
+MID = STXT
+NUMBERVALUE = VALEURNOMBRE
+PHONETIC = PHONETIQUE
+PROPER = NOMPROPRE
+REPLACE = REMPLACER
+REPT = REPT
+RIGHT = DROITE
+SEARCH = CHERCHE
+SUBSTITUTE = SUBSTITUE
+T = T
+TEXT = TEXTE
+TEXTJOIN = JOINDRE.TEXTE
+TRIM = SUPPRESPACE
+UNICHAR = UNICAR
+UNICODE = UNICODE
+UPPER = MAJUSCULE
+VALUE = CNUM
+
+##
+## Fonctions web (Web Functions)
+##
+ENCODEURL = URLENCODAGE
+FILTERXML = FILTRE.XML
+WEBSERVICE = SERVICEWEB
+
+##
+## Fonctions de compatibilité (Compatibility Functions)
+##
+BETADIST = LOI.BETA
+BETAINV = BETA.INVERSE
+BINOMDIST = LOI.BINOMIALE
+CEILING = PLAFOND
+CHIDIST = LOI.KHIDEUX
+CHIINV = KHIDEUX.INVERSE
+CHITEST = TEST.KHIDEUX
+CONCATENATE = CONCATENER
+CONFIDENCE = INTERVALLE.CONFIANCE
+COVAR = COVARIANCE
+CRITBINOM = CRITERE.LOI.BINOMIALE
+EXPONDIST = LOI.EXPONENTIELLE
+FDIST = LOI.F
+FINV = INVERSE.LOI.F
+FLOOR = PLANCHER
+FORECAST = PREVISION
+FTEST = TEST.F
+GAMMADIST = LOI.GAMMA
+GAMMAINV = LOI.GAMMA.INVERSE
+HYPGEOMDIST = LOI.HYPERGEOMETRIQUE
+LOGINV = LOI.LOGNORMALE.INVERSE
+LOGNORMDIST = LOI.LOGNORMALE
+MODE = MODE
+NEGBINOMDIST = LOI.BINOMIALE.NEG
+NORMDIST = LOI.NORMALE
+NORMINV = LOI.NORMALE.INVERSE
+NORMSDIST = LOI.NORMALE.STANDARD
+NORMSINV = LOI.NORMALE.STANDARD.INVERSE
+PERCENTILE = CENTILE
+PERCENTRANK = RANG.POURCENTAGE
+POISSON = LOI.POISSON
+QUARTILE = QUARTILE
+RANK = RANG
+STDEV = ECARTYPE
+STDEVP = ECARTYPEP
+TDIST = LOI.STUDENT
+TINV = LOI.STUDENT.INVERSE
+TTEST = TEST.STUDENT
+VAR = VAR
+VARP = VAR.P
+WEIBULL = LOI.WEIBULL
+ZTEST = TEST.Z
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config
new file mode 100644
index 0000000..dc585d7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Magyar (Hungarian)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #NULLA!
+DIV0 = #ZÉRÓOSZTÓ!
+VALUE = #ÉRTÉK!
+REF = #HIV!
+NAME = #NÉV?
+NUM = #SZÁM!
+NA = #HIÁNYZIK
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions
new file mode 100644
index 0000000..4a375ea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions
@@ -0,0 +1,538 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Magyar (Hungarian)
+##
+############################################################
+
+
+##
+## Kockafüggvények (Cube Functions)
+##
+CUBEKPIMEMBER = KOCKA.FŐTELJMUT
+CUBEMEMBER = KOCKA.TAG
+CUBEMEMBERPROPERTY = KOCKA.TAG.TUL
+CUBERANKEDMEMBER = KOCKA.HALM.ELEM
+CUBESET = KOCKA.HALM
+CUBESETCOUNT = KOCKA.HALM.DB
+CUBEVALUE = KOCKA.ÉRTÉK
+
+##
+## Adatbázis-kezelő függvények (Database Functions)
+##
+DAVERAGE = AB.ÁTLAG
+DCOUNT = AB.DARAB
+DCOUNTA = AB.DARAB2
+DGET = AB.MEZŐ
+DMAX = AB.MAX
+DMIN = AB.MIN
+DPRODUCT = AB.SZORZAT
+DSTDEV = AB.SZÓRÁS
+DSTDEVP = AB.SZÓRÁS2
+DSUM = AB.SZUM
+DVAR = AB.VAR
+DVARP = AB.VAR2
+
+##
+## Dátumfüggvények (Date & Time Functions)
+##
+DATE = DÁTUM
+DATEDIF = DÁTUMTÓLIG
+DATESTRING = DÁTUMSZÖVEG
+DATEVALUE = DÁTUMÉRTÉK
+DAY = NAP
+DAYS = NAPOK
+DAYS360 = NAP360
+EDATE = KALK.DÁTUM
+EOMONTH = HÓNAP.UTOLSÓ.NAP
+HOUR = ÓRA
+ISOWEEKNUM = ISO.HÉT.SZÁMA
+MINUTE = PERCEK
+MONTH = HÓNAP
+NETWORKDAYS = ÖSSZ.MUNKANAP
+NETWORKDAYS.INTL = ÖSSZ.MUNKANAP.INTL
+NOW = MOST
+SECOND = MPERC
+THAIDAYOFWEEK = THAIHÉTNAPJA
+THAIMONTHOFYEAR = THAIHÓNAP
+THAIYEAR = THAIÉV
+TIME = IDŐ
+TIMEVALUE = IDŐÉRTÉK
+TODAY = MA
+WEEKDAY = HÉT.NAPJA
+WEEKNUM = HÉT.SZÁMA
+WORKDAY = KALK.MUNKANAP
+WORKDAY.INTL = KALK.MUNKANAP.INTL
+YEAR = ÉV
+YEARFRAC = TÖRTÉV
+
+##
+## Mérnöki függvények (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BIN.DEC
+BIN2HEX = BIN.HEX
+BIN2OCT = BIN.OKT
+BITAND = BIT.ÉS
+BITLSHIFT = BIT.BAL.ELTOL
+BITOR = BIT.VAGY
+BITRSHIFT = BIT.JOBB.ELTOL
+BITXOR = BIT.XVAGY
+COMPLEX = KOMPLEX
+CONVERT = KONVERTÁLÁS
+DEC2BIN = DEC.BIN
+DEC2HEX = DEC.HEX
+DEC2OCT = DEC.OKT
+DELTA = DELTA
+ERF = HIBAF
+ERF.PRECISE = HIBAF.PONTOS
+ERFC = HIBAF.KOMPLEMENTER
+ERFC.PRECISE = HIBAFKOMPLEMENTER.PONTOS
+GESTEP = KÜSZÖBNÉL.NAGYOBB
+HEX2BIN = HEX.BIN
+HEX2DEC = HEX.DEC
+HEX2OCT = HEX.OKT
+IMABS = KÉPZ.ABSZ
+IMAGINARY = KÉPZETES
+IMARGUMENT = KÉPZ.ARGUMENT
+IMCONJUGATE = KÉPZ.KONJUGÁLT
+IMCOS = KÉPZ.COS
+IMCOSH = KÉPZ.COSH
+IMCOT = KÉPZ.COT
+IMCSC = KÉPZ.CSC
+IMCSCH = KÉPZ.CSCH
+IMDIV = KÉPZ.HÁNYAD
+IMEXP = KÉPZ.EXP
+IMLN = KÉPZ.LN
+IMLOG10 = KÉPZ.LOG10
+IMLOG2 = KÉPZ.LOG2
+IMPOWER = KÉPZ.HATV
+IMPRODUCT = KÉPZ.SZORZAT
+IMREAL = KÉPZ.VALÓS
+IMSEC = KÉPZ.SEC
+IMSECH = KÉPZ.SECH
+IMSIN = KÉPZ.SIN
+IMSINH = KÉPZ.SINH
+IMSQRT = KÉPZ.GYÖK
+IMSUB = KÉPZ.KÜL
+IMSUM = KÉPZ.ÖSSZEG
+IMTAN = KÉPZ.TAN
+OCT2BIN = OKT.BIN
+OCT2DEC = OKT.DEC
+OCT2HEX = OKT.HEX
+
+##
+## Pénzügyi függvények (Financial Functions)
+##
+ACCRINT = IDŐSZAKI.KAMAT
+ACCRINTM = LEJÁRATI.KAMAT
+AMORDEGRC = ÉRTÉKCSÖKK.TÉNYEZŐVEL
+AMORLINC = ÉRTÉKCSÖKK
+COUPDAYBS = SZELVÉNYIDŐ.KEZDETTŐL
+COUPDAYS = SZELVÉNYIDŐ
+COUPDAYSNC = SZELVÉNYIDŐ.KIFIZETÉSTŐL
+COUPNCD = ELSŐ.SZELVÉNYDÁTUM
+COUPNUM = SZELVÉNYSZÁM
+COUPPCD = UTOLSÓ.SZELVÉNYDÁTUM
+CUMIPMT = ÖSSZES.KAMAT
+CUMPRINC = ÖSSZES.TŐKERÉSZ
+DB = KCS2
+DDB = KCSA
+DISC = LESZÁM
+DOLLARDE = FORINT.DEC
+DOLLARFR = FORINT.TÖRT
+DURATION = KAMATÉRZ
+EFFECT = TÉNYLEGES
+FV = JBÉ
+FVSCHEDULE = KJÉ
+INTRATE = KAMATRÁTA
+IPMT = RRÉSZLET
+IRR = BMR
+ISPMT = LRÉSZLETKAMAT
+MDURATION = MKAMATÉRZ
+MIRR = MEGTÉRÜLÉS
+NOMINAL = NÉVLEGES
+NPER = PER.SZÁM
+NPV = NMÉ
+ODDFPRICE = ELTÉRŐ.EÁR
+ODDFYIELD = ELTÉRŐ.EHOZAM
+ODDLPRICE = ELTÉRŐ.UÁR
+ODDLYIELD = ELTÉRŐ.UHOZAM
+PDURATION = KAMATÉRZ.PER
+PMT = RÉSZLET
+PPMT = PRÉSZLET
+PRICE = ÁR
+PRICEDISC = ÁR.LESZÁM
+PRICEMAT = ÁR.LEJÁRAT
+PV = MÉ
+RATE = RÁTA
+RECEIVED = KAPOTT
+RRI = MR
+SLN = LCSA
+SYD = ÉSZÖ
+TBILLEQ = KJEGY.EGYENÉRT
+TBILLPRICE = KJEGY.ÁR
+TBILLYIELD = KJEGY.HOZAM
+VDB = ÉCSRI
+XIRR = XBMR
+XNPV = XNJÉ
+YIELD = HOZAM
+YIELDDISC = HOZAM.LESZÁM
+YIELDMAT = HOZAM.LEJÁRAT
+
+##
+## Információs függvények (Information Functions)
+##
+CELL = CELLA
+ERROR.TYPE = HIBA.TÍPUS
+INFO = INFÓ
+ISBLANK = ÜRES
+ISERR = HIBA.E
+ISERROR = HIBÁS
+ISEVEN = PÁROSE
+ISFORMULA = KÉPLET
+ISLOGICAL = LOGIKAI
+ISNA = NINCS
+ISNONTEXT = NEM.SZÖVEG
+ISNUMBER = SZÁM
+ISODD = PÁRATLANE
+ISREF = HIVATKOZÁS
+ISTEXT = SZÖVEG.E
+N = S
+NA = HIÁNYZIK
+SHEET = LAP
+SHEETS = LAPOK
+TYPE = TÍPUS
+
+##
+## Logikai függvények (Logical Functions)
+##
+AND = ÉS
+FALSE = HAMIS
+IF = HA
+IFERROR = HAHIBA
+IFNA = HAHIÁNYZIK
+IFS = HAELSŐIGAZ
+NOT = NEM
+OR = VAGY
+SWITCH = ÁTVÁLT
+TRUE = IGAZ
+XOR = XVAGY
+
+##
+## Keresési és hivatkozási függvények (Lookup & Reference Functions)
+##
+ADDRESS = CÍM
+AREAS = TERÜLET
+CHOOSE = VÁLASZT
+COLUMN = OSZLOP
+COLUMNS = OSZLOPOK
+FORMULATEXT = KÉPLETSZÖVEG
+GETPIVOTDATA = KIMUTATÁSADATOT.VESZ
+HLOOKUP = VKERES
+HYPERLINK = HIPERHIVATKOZÁS
+INDEX = INDEX
+INDIRECT = INDIREKT
+LOOKUP = KERES
+MATCH = HOL.VAN
+OFFSET = ELTOLÁS
+ROW = SOR
+ROWS = SOROK
+RTD = VIA
+TRANSPOSE = TRANSZPONÁLÁS
+VLOOKUP = FKERES
+*RC = SO
+
+##
+## Matematikai és trigonometrikus függvények (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ARCCOS
+ACOSH = ACOSH
+ACOT = ARCCOT
+ACOTH = ARCCOTH
+AGGREGATE = ÖSSZESÍT
+ARABIC = ARAB
+ASIN = ARCSIN
+ASINH = ASINH
+ATAN = ARCTAN
+ATAN2 = ARCTAN2
+ATANH = ATANH
+BASE = ALAP
+CEILING.MATH = PLAFON.MAT
+CEILING.PRECISE = PLAFON.PONTOS
+COMBIN = KOMBINÁCIÓK
+COMBINA = KOMBINÁCIÓK.ISM
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = TIZEDES
+DEGREES = FOK
+ECMA.CEILING = ECMA.PLAFON
+EVEN = PÁROS
+EXP = KITEVŐ
+FACT = FAKT
+FACTDOUBLE = FAKTDUPLA
+FLOOR.MATH = PADLÓ.MAT
+FLOOR.PRECISE = PADLÓ.PONTOS
+GCD = LKO
+INT = INT
+ISO.CEILING = ISO.PLAFON
+LCM = LKT
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDETERM
+MINVERSE = INVERZ.MÁTRIX
+MMULT = MSZORZAT
+MOD = MARADÉK
+MROUND = TÖBBSZ.KEREKÍT
+MULTINOMIAL = SZORHÁNYFAKT
+MUNIT = MMÁTRIX
+ODD = PÁRATLAN
+PI = PI
+POWER = HATVÁNY
+PRODUCT = SZORZAT
+QUOTIENT = KVÓCIENS
+RADIANS = RADIÁN
+RAND = VÉL
+RANDBETWEEN = VÉLETLEN.KÖZÖTT
+ROMAN = RÓMAI
+ROUND = KEREKÍTÉS
+ROUNDBAHTDOWN = BAHTKEREK.LE
+ROUNDBAHTUP = BAHTKEREK.FEL
+ROUNDDOWN = KEREK.LE
+ROUNDUP = KEREK.FEL
+SEC = SEC
+SECH = SECH
+SERIESSUM = SORÖSSZEG
+SIGN = ELŐJEL
+SIN = SIN
+SINH = SINH
+SQRT = GYÖK
+SQRTPI = GYÖKPI
+SUBTOTAL = RÉSZÖSSZEG
+SUM = SZUM
+SUMIF = SZUMHA
+SUMIFS = SZUMHATÖBB
+SUMPRODUCT = SZORZATÖSSZEG
+SUMSQ = NÉGYZETÖSSZEG
+SUMX2MY2 = SZUMX2BŐLY2
+SUMX2PY2 = SZUMX2MEGY2
+SUMXMY2 = SZUMXBŐLY2
+TAN = TAN
+TANH = TANH
+TRUNC = CSONK
+
+##
+## Statisztikai függvények (Statistical Functions)
+##
+AVEDEV = ÁTL.ELTÉRÉS
+AVERAGE = ÁTLAG
+AVERAGEA = ÁTLAGA
+AVERAGEIF = ÁTLAGHA
+AVERAGEIFS = ÁTLAGHATÖBB
+BETA.DIST = BÉTA.ELOSZL
+BETA.INV = BÉTA.INVERZ
+BINOM.DIST = BINOM.ELOSZL
+BINOM.DIST.RANGE = BINOM.ELOSZL.TART
+BINOM.INV = BINOM.INVERZ
+CHISQ.DIST = KHINÉGYZET.ELOSZLÁS
+CHISQ.DIST.RT = KHINÉGYZET.ELOSZLÁS.JOBB
+CHISQ.INV = KHINÉGYZET.INVERZ
+CHISQ.INV.RT = KHINÉGYZET.INVERZ.JOBB
+CHISQ.TEST = KHINÉGYZET.PRÓBA
+CONFIDENCE.NORM = MEGBÍZHATÓSÁG.NORM
+CONFIDENCE.T = MEGBÍZHATÓSÁG.T
+CORREL = KORREL
+COUNT = DARAB
+COUNTA = DARAB2
+COUNTBLANK = DARABÜRES
+COUNTIF = DARABTELI
+COUNTIFS = DARABHATÖBB
+COVARIANCE.P = KOVARIANCIA.S
+COVARIANCE.S = KOVARIANCIA.M
+DEVSQ = SQ
+EXPON.DIST = EXP.ELOSZL
+F.DIST = F.ELOSZL
+F.DIST.RT = F.ELOSZLÁS.JOBB
+F.INV = F.INVERZ
+F.INV.RT = F.INVERZ.JOBB
+F.TEST = F.PRÓB
+FISHER = FISHER
+FISHERINV = INVERZ.FISHER
+FORECAST.ETS = ELŐREJELZÉS.ESIM
+FORECAST.ETS.CONFINT = ELŐREJELZÉS.ESIM.KONFINT
+FORECAST.ETS.SEASONALITY = ELŐREJELZÉS.ESIM.SZEZONALITÁS
+FORECAST.ETS.STAT = ELŐREJELZÉS.ESIM.STAT
+FORECAST.LINEAR = ELŐREJELZÉS.LINEÁRIS
+FREQUENCY = GYAKORISÁG
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.ELOSZL
+GAMMA.INV = GAMMA.INVERZ
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.PONTOS
+GAUSS = GAUSS
+GEOMEAN = MÉRTANI.KÖZÉP
+GROWTH = NÖV
+HARMEAN = HARM.KÖZÉP
+HYPGEOM.DIST = HIPGEOM.ELOSZLÁS
+INTERCEPT = METSZ
+KURT = CSÚCSOSSÁG
+LARGE = NAGY
+LINEST = LIN.ILL
+LOGEST = LOG.ILL
+LOGNORM.DIST = LOGNORM.ELOSZLÁS
+LOGNORM.INV = LOGNORM.INVERZ
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAXHA
+MEDIAN = MEDIÁN
+MIN = MIN
+MINA = MIN2
+MINIFS = MINHA
+MODE.MULT = MÓDUSZ.TÖBB
+MODE.SNGL = MÓDUSZ.EGY
+NEGBINOM.DIST = NEGBINOM.ELOSZLÁS
+NORM.DIST = NORM.ELOSZLÁS
+NORM.INV = NORM.INVERZ
+NORM.S.DIST = NORM.S.ELOSZLÁS
+NORM.S.INV = NORM.S.INVERZ
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTILIS.KIZÁR
+PERCENTILE.INC = PERCENTILIS.TARTALMAZ
+PERCENTRANK.EXC = SZÁZALÉKRANG.KIZÁR
+PERCENTRANK.INC = SZÁZALÉKRANG.TARTALMAZ
+PERMUT = VARIÁCIÓK
+PERMUTATIONA = VARIÁCIÓK.ISM
+PHI = FI
+POISSON.DIST = POISSON.ELOSZLÁS
+PROB = VALÓSZÍNŰSÉG
+QUARTILE.EXC = KVARTILIS.KIZÁR
+QUARTILE.INC = KVARTILIS.TARTALMAZ
+RANK.AVG = RANG.ÁTL
+RANK.EQ = RANG.EGY
+RSQ = RNÉGYZET
+SKEW = FERDESÉG
+SKEW.P = FERDESÉG.P
+SLOPE = MEREDEKSÉG
+SMALL = KICSI
+STANDARDIZE = NORMALIZÁLÁS
+STDEV.P = SZÓR.S
+STDEV.S = SZÓR.M
+STDEVA = SZÓRÁSA
+STDEVPA = SZÓRÁSPA
+STEYX = STHIBAYX
+T.DIST = T.ELOSZL
+T.DIST.2T = T.ELOSZLÁS.2SZ
+T.DIST.RT = T.ELOSZLÁS.JOBB
+T.INV = T.INVERZ
+T.INV.2T = T.INVERZ.2SZ
+T.TEST = T.PRÓB
+TREND = TREND
+TRIMMEAN = RÉSZÁTLAG
+VAR.P = VAR.S
+VAR.S = VAR.M
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = WEIBULL.ELOSZLÁS
+Z.TEST = Z.PRÓB
+
+##
+## Szövegműveletekhez használható függvények (Text Functions)
+##
+BAHTTEXT = BAHTSZÖVEG
+CHAR = KARAKTER
+CLEAN = TISZTÍT
+CODE = KÓD
+CONCAT = FŰZ
+DOLLAR = FORINT
+EXACT = AZONOS
+FIND = SZÖVEG.TALÁL
+FIXED = FIX
+ISTHAIDIGIT = ON.THAI.NUMERO
+LEFT = BAL
+LEN = HOSSZ
+LOWER = KISBETŰ
+MID = KÖZÉP
+NUMBERSTRING = SZÁM.BETŰVEL
+NUMBERVALUE = SZÁMÉRTÉK
+PHONETIC = FONETIKUS
+PROPER = TNÉV
+REPLACE = CSERE
+REPT = SOKSZOR
+RIGHT = JOBB
+SEARCH = SZÖVEG.KERES
+SUBSTITUTE = HELYETTE
+T = T
+TEXT = SZÖVEG
+TEXTJOIN = SZÖVEGÖSSZEFŰZÉS
+THAIDIGIT = THAISZÁM
+THAINUMSOUND = THAISZÁMHANG
+THAINUMSTRING = THAISZÁMKAR
+THAISTRINGLENGTH = THAIKARHOSSZ
+TRIM = KIMETSZ
+UNICHAR = UNIKARAKTER
+UNICODE = UNICODE
+UPPER = NAGYBETŰS
+VALUE = ÉRTÉK
+
+##
+## Webes függvények (Web Functions)
+##
+ENCODEURL = URL.KÓDOL
+FILTERXML = XMLSZŰRÉS
+WEBSERVICE = WEBSZOLGÁLTATÁS
+
+##
+## Kompatibilitási függvények (Compatibility Functions)
+##
+BETADIST = BÉTA.ELOSZLÁS
+BETAINV = INVERZ.BÉTA
+BINOMDIST = BINOM.ELOSZLÁS
+CEILING = PLAFON
+CHIDIST = KHI.ELOSZLÁS
+CHIINV = INVERZ.KHI
+CHITEST = KHI.PRÓBA
+CONCATENATE = ÖSSZEFŰZ
+CONFIDENCE = MEGBÍZHATÓSÁG
+COVAR = KOVAR
+CRITBINOM = KRITBINOM
+EXPONDIST = EXP.ELOSZLÁS
+FDIST = F.ELOSZLÁS
+FINV = INVERZ.F
+FLOOR = PADLÓ
+FORECAST = ELŐREJELZÉS
+FTEST = F.PRÓBA
+GAMMADIST = GAMMA.ELOSZLÁS
+GAMMAINV = INVERZ.GAMMA
+HYPGEOMDIST = HIPERGEOM.ELOSZLÁS
+LOGINV = INVERZ.LOG.ELOSZLÁS
+LOGNORMDIST = LOG.ELOSZLÁS
+MODE = MÓDUSZ
+NEGBINOMDIST = NEGBINOM.ELOSZL
+NORMDIST = NORM.ELOSZL
+NORMINV = INVERZ.NORM
+NORMSDIST = STNORMELOSZL
+NORMSINV = INVERZ.STNORM
+PERCENTILE = PERCENTILIS
+PERCENTRANK = SZÁZALÉKRANG
+POISSON = POISSON
+QUARTILE = KVARTILIS
+RANK = SORSZÁM
+STDEV = SZÓRÁS
+STDEVP = SZÓRÁSP
+TDIST = T.ELOSZLÁS
+TINV = INVERZ.T
+TTEST = T.PRÓBA
+VAR = VAR
+VARP = VARP
+WEIBULL = WEIBULL
+ZTEST = Z.PRÓBA
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config
new file mode 100644
index 0000000..5c1e495
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Italiano (Italian)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL
+DIV0
+VALUE = #VALORE!
+REF = #RIF!
+NAME = #NOME?
+NUM
+NA = #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions
new file mode 100644
index 0000000..c14ed85
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions
@@ -0,0 +1,537 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Italiano (Italian)
+##
+############################################################
+
+
+##
+## Funzioni cubo (Cube Functions)
+##
+CUBEKPIMEMBER = MEMBRO.KPI.CUBO
+CUBEMEMBER = MEMBRO.CUBO
+CUBEMEMBERPROPERTY = PROPRIETÀ.MEMBRO.CUBO
+CUBERANKEDMEMBER = MEMBRO.CUBO.CON.RANGO
+CUBESET = SET.CUBO
+CUBESETCOUNT = CONTA.SET.CUBO
+CUBEVALUE = VALORE.CUBO
+
+##
+## Funzioni di database (Database Functions)
+##
+DAVERAGE = DB.MEDIA
+DCOUNT = DB.CONTA.NUMERI
+DCOUNTA = DB.CONTA.VALORI
+DGET = DB.VALORI
+DMAX = DB.MAX
+DMIN = DB.MIN
+DPRODUCT = DB.PRODOTTO
+DSTDEV = DB.DEV.ST
+DSTDEVP = DB.DEV.ST.POP
+DSUM = DB.SOMMA
+DVAR = DB.VAR
+DVARP = DB.VAR.POP
+
+##
+## Funzioni data e ora (Date & Time Functions)
+##
+DATE = DATA
+DATEDIF = DATA.DIFF
+DATESTRING = DATA.STRINGA
+DATEVALUE = DATA.VALORE
+DAY = GIORNO
+DAYS = GIORNI
+DAYS360 = GIORNO360
+EDATE = DATA.MESE
+EOMONTH = FINE.MESE
+HOUR = ORA
+ISOWEEKNUM = NUM.SETTIMANA.ISO
+MINUTE = MINUTO
+MONTH = MESE
+NETWORKDAYS = GIORNI.LAVORATIVI.TOT
+NETWORKDAYS.INTL = GIORNI.LAVORATIVI.TOT.INTL
+NOW = ADESSO
+SECOND = SECONDO
+THAIDAYOFWEEK = THAIGIORNODELLASETTIMANA
+THAIMONTHOFYEAR = THAIMESEDELLANNO
+THAIYEAR = THAIANNO
+TIME = ORARIO
+TIMEVALUE = ORARIO.VALORE
+TODAY = OGGI
+WEEKDAY = GIORNO.SETTIMANA
+WEEKNUM = NUM.SETTIMANA
+WORKDAY = GIORNO.LAVORATIVO
+WORKDAY.INTL = GIORNO.LAVORATIVO.INTL
+YEAR = ANNO
+YEARFRAC = FRAZIONE.ANNO
+
+##
+## Funzioni ingegneristiche (Engineering Functions)
+##
+BESSELI = BESSEL.I
+BESSELJ = BESSEL.J
+BESSELK = BESSEL.K
+BESSELY = BESSEL.Y
+BIN2DEC = BINARIO.DECIMALE
+BIN2HEX = BINARIO.HEX
+BIN2OCT = BINARIO.OCT
+BITAND = BITAND
+BITLSHIFT = BIT.SPOSTA.SX
+BITOR = BITOR
+BITRSHIFT = BIT.SPOSTA.DX
+BITXOR = BITXOR
+COMPLEX = COMPLESSO
+CONVERT = CONVERTI
+DEC2BIN = DECIMALE.BINARIO
+DEC2HEX = DECIMALE.HEX
+DEC2OCT = DECIMALE.OCT
+DELTA = DELTA
+ERF = FUNZ.ERRORE
+ERF.PRECISE = FUNZ.ERRORE.PRECISA
+ERFC = FUNZ.ERRORE.COMP
+ERFC.PRECISE = FUNZ.ERRORE.COMP.PRECISA
+GESTEP = SOGLIA
+HEX2BIN = HEX.BINARIO
+HEX2DEC = HEX.DECIMALE
+HEX2OCT = HEX.OCT
+IMABS = COMP.MODULO
+IMAGINARY = COMP.IMMAGINARIO
+IMARGUMENT = COMP.ARGOMENTO
+IMCONJUGATE = COMP.CONIUGATO
+IMCOS = COMP.COS
+IMCOSH = COMP.COSH
+IMCOT = COMP.COT
+IMCSC = COMP.CSC
+IMCSCH = COMP.CSCH
+IMDIV = COMP.DIV
+IMEXP = COMP.EXP
+IMLN = COMP.LN
+IMLOG10 = COMP.LOG10
+IMLOG2 = COMP.LOG2
+IMPOWER = COMP.POTENZA
+IMPRODUCT = COMP.PRODOTTO
+IMREAL = COMP.PARTE.REALE
+IMSEC = COMP.SEC
+IMSECH = COMP.SECH
+IMSIN = COMP.SEN
+IMSINH = COMP.SENH
+IMSQRT = COMP.RADQ
+IMSUB = COMP.DIFF
+IMSUM = COMP.SOMMA
+IMTAN = COMP.TAN
+OCT2BIN = OCT.BINARIO
+OCT2DEC = OCT.DECIMALE
+OCT2HEX = OCT.HEX
+
+##
+## Funzioni finanziarie (Financial Functions)
+##
+ACCRINT = INT.MATURATO.PER
+ACCRINTM = INT.MATURATO.SCAD
+AMORDEGRC = AMMORT.DEGR
+AMORLINC = AMMORT.PER
+COUPDAYBS = GIORNI.CED.INIZ.LIQ
+COUPDAYS = GIORNI.CED
+COUPDAYSNC = GIORNI.CED.NUOVA
+COUPNCD = DATA.CED.SUCC
+COUPNUM = NUM.CED
+COUPPCD = DATA.CED.PREC
+CUMIPMT = INT.CUMUL
+CUMPRINC = CAP.CUM
+DB = AMMORT.FISSO
+DDB = AMMORT
+DISC = TASSO.SCONTO
+DOLLARDE = VALUTA.DEC
+DOLLARFR = VALUTA.FRAZ
+DURATION = DURATA
+EFFECT = EFFETTIVO
+FV = VAL.FUT
+FVSCHEDULE = VAL.FUT.CAPITALE
+INTRATE = TASSO.INT
+IPMT = INTERESSI
+IRR = TIR.COST
+ISPMT = INTERESSE.RATA
+MDURATION = DURATA.M
+MIRR = TIR.VAR
+NOMINAL = NOMINALE
+NPER = NUM.RATE
+NPV = VAN
+ODDFPRICE = PREZZO.PRIMO.IRR
+ODDFYIELD = REND.PRIMO.IRR
+ODDLPRICE = PREZZO.ULTIMO.IRR
+ODDLYIELD = REND.ULTIMO.IRR
+PDURATION = DURATA.P
+PMT = RATA
+PPMT = P.RATA
+PRICE = PREZZO
+PRICEDISC = PREZZO.SCONT
+PRICEMAT = PREZZO.SCAD
+PV = VA
+RATE = TASSO
+RECEIVED = RICEV.SCAD
+RRI = RIT.INVEST.EFFETT
+SLN = AMMORT.COST
+SYD = AMMORT.ANNUO
+TBILLEQ = BOT.EQUIV
+TBILLPRICE = BOT.PREZZO
+TBILLYIELD = BOT.REND
+VDB = AMMORT.VAR
+XIRR = TIR.X
+XNPV = VAN.X
+YIELD = REND
+YIELDDISC = REND.TITOLI.SCONT
+YIELDMAT = REND.SCAD
+
+##
+## Funzioni relative alle informazioni (Information Functions)
+##
+CELL = CELLA
+ERROR.TYPE = ERRORE.TIPO
+INFO = AMBIENTE.INFO
+ISBLANK = VAL.VUOTO
+ISERR = VAL.ERR
+ISERROR = VAL.ERRORE
+ISEVEN = VAL.PARI
+ISFORMULA = VAL.FORMULA
+ISLOGICAL = VAL.LOGICO
+ISNA = VAL.NON.DISP
+ISNONTEXT = VAL.NON.TESTO
+ISNUMBER = VAL.NUMERO
+ISODD = VAL.DISPARI
+ISREF = VAL.RIF
+ISTEXT = VAL.TESTO
+N = NUM
+NA = NON.DISP
+SHEET = FOGLIO
+SHEETS = FOGLI
+TYPE = TIPO
+
+##
+## Funzioni logiche (Logical Functions)
+##
+AND = E
+FALSE = FALSO
+IF = SE
+IFERROR = SE.ERRORE
+IFNA = SE.NON.DISP.
+IFS = PIÙ.SE
+NOT = NON
+OR = O
+SWITCH = SWITCH
+TRUE = VERO
+XOR = XOR
+
+##
+## Funzioni di ricerca e di riferimento (Lookup & Reference Functions)
+##
+ADDRESS = INDIRIZZO
+AREAS = AREE
+CHOOSE = SCEGLI
+COLUMN = RIF.COLONNA
+COLUMNS = COLONNE
+FORMULATEXT = TESTO.FORMULA
+GETPIVOTDATA = INFO.DATI.TAB.PIVOT
+HLOOKUP = CERCA.ORIZZ
+HYPERLINK = COLLEG.IPERTESTUALE
+INDEX = INDICE
+INDIRECT = INDIRETTO
+LOOKUP = CERCA
+MATCH = CONFRONTA
+OFFSET = SCARTO
+ROW = RIF.RIGA
+ROWS = RIGHE
+RTD = DATITEMPOREALE
+TRANSPOSE = MATR.TRASPOSTA
+VLOOKUP = CERCA.VERT
+
+##
+## Funzioni matematiche e trigonometriche (Math & Trig Functions)
+##
+ABS = ASS
+ACOS = ARCCOS
+ACOSH = ARCCOSH
+ACOT = ARCCOT
+ACOTH = ARCCOTH
+AGGREGATE = AGGREGA
+ARABIC = ARABO
+ASIN = ARCSEN
+ASINH = ARCSENH
+ATAN = ARCTAN
+ATAN2 = ARCTAN.2
+ATANH = ARCTANH
+BASE = BASE
+CEILING.MATH = ARROTONDA.ECCESSO.MAT
+CEILING.PRECISE = ARROTONDA.ECCESSO.PRECISA
+COMBIN = COMBINAZIONE
+COMBINA = COMBINAZIONE.VALORI
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DECIMALE
+DEGREES = GRADI
+ECMA.CEILING = ECMA.ARROTONDA.ECCESSO
+EVEN = PARI
+EXP = EXP
+FACT = FATTORIALE
+FACTDOUBLE = FATT.DOPPIO
+FLOOR.MATH = ARROTONDA.DIFETTO.MAT
+FLOOR.PRECISE = ARROTONDA.DIFETTO.PRECISA
+GCD = MCD
+INT = INT
+ISO.CEILING = ISO.ARROTONDA.ECCESSO
+LCM = MCM
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MATR.DETERM
+MINVERSE = MATR.INVERSA
+MMULT = MATR.PRODOTTO
+MOD = RESTO
+MROUND = ARROTONDA.MULTIPLO
+MULTINOMIAL = MULTINOMIALE
+MUNIT = MATR.UNIT
+ODD = DISPARI
+PI = PI.GRECO
+POWER = POTENZA
+PRODUCT = PRODOTTO
+QUOTIENT = QUOZIENTE
+RADIANS = RADIANTI
+RAND = CASUALE
+RANDBETWEEN = CASUALE.TRA
+ROMAN = ROMANO
+ROUND = ARROTONDA
+ROUNDBAHTDOWN = ARROTBAHTGIU
+ROUNDBAHTUP = ARROTBAHTSU
+ROUNDDOWN = ARROTONDA.PER.DIF
+ROUNDUP = ARROTONDA.PER.ECC
+SEC = SEC
+SECH = SECH
+SERIESSUM = SOMMA.SERIE
+SIGN = SEGNO
+SIN = SEN
+SINH = SENH
+SQRT = RADQ
+SQRTPI = RADQ.PI.GRECO
+SUBTOTAL = SUBTOTALE
+SUM = SOMMA
+SUMIF = SOMMA.SE
+SUMIFS = SOMMA.PIÙ.SE
+SUMPRODUCT = MATR.SOMMA.PRODOTTO
+SUMSQ = SOMMA.Q
+SUMX2MY2 = SOMMA.DIFF.Q
+SUMX2PY2 = SOMMA.SOMMA.Q
+SUMXMY2 = SOMMA.Q.DIFF
+TAN = TAN
+TANH = TANH
+TRUNC = TRONCA
+
+##
+## Funzioni statistiche (Statistical Functions)
+##
+AVEDEV = MEDIA.DEV
+AVERAGE = MEDIA
+AVERAGEA = MEDIA.VALORI
+AVERAGEIF = MEDIA.SE
+AVERAGEIFS = MEDIA.PIÙ.SE
+BETA.DIST = DISTRIB.BETA.N
+BETA.INV = INV.BETA.N
+BINOM.DIST = DISTRIB.BINOM.N
+BINOM.DIST.RANGE = INTERVALLO.DISTRIB.BINOM.N.
+BINOM.INV = INV.BINOM
+CHISQ.DIST = DISTRIB.CHI.QUAD
+CHISQ.DIST.RT = DISTRIB.CHI.QUAD.DS
+CHISQ.INV = INV.CHI.QUAD
+CHISQ.INV.RT = INV.CHI.QUAD.DS
+CHISQ.TEST = TEST.CHI.QUAD
+CONFIDENCE.NORM = CONFIDENZA.NORM
+CONFIDENCE.T = CONFIDENZA.T
+CORREL = CORRELAZIONE
+COUNT = CONTA.NUMERI
+COUNTA = CONTA.VALORI
+COUNTBLANK = CONTA.VUOTE
+COUNTIF = CONTA.SE
+COUNTIFS = CONTA.PIÙ.SE
+COVARIANCE.P = COVARIANZA.P
+COVARIANCE.S = COVARIANZA.C
+DEVSQ = DEV.Q
+EXPON.DIST = DISTRIB.EXP.N
+F.DIST = DISTRIBF
+F.DIST.RT = DISTRIB.F.DS
+F.INV = INVF
+F.INV.RT = INV.F.DS
+F.TEST = TESTF
+FISHER = FISHER
+FISHERINV = INV.FISHER
+FORECAST.ETS = PREVISIONE.ETS
+FORECAST.ETS.CONFINT = PREVISIONE.ETS.INTCONF
+FORECAST.ETS.SEASONALITY = PREVISIONE.ETS.STAGIONALITÀ
+FORECAST.ETS.STAT = PREVISIONE.ETS.STAT
+FORECAST.LINEAR = PREVISIONE.LINEARE
+FREQUENCY = FREQUENZA
+GAMMA = GAMMA
+GAMMA.DIST = DISTRIB.GAMMA.N
+GAMMA.INV = INV.GAMMA.N
+GAMMALN = LN.GAMMA
+GAMMALN.PRECISE = LN.GAMMA.PRECISA
+GAUSS = GAUSS
+GEOMEAN = MEDIA.GEOMETRICA
+GROWTH = CRESCITA
+HARMEAN = MEDIA.ARMONICA
+HYPGEOM.DIST = DISTRIB.IPERGEOM.N
+INTERCEPT = INTERCETTA
+KURT = CURTOSI
+LARGE = GRANDE
+LINEST = REGR.LIN
+LOGEST = REGR.LOG
+LOGNORM.DIST = DISTRIB.LOGNORM.N
+LOGNORM.INV = INV.LOGNORM.N
+MAX = MAX
+MAXA = MAX.VALORI
+MAXIFS = MAX.PIÙ.SE
+MEDIAN = MEDIANA
+MIN = MIN
+MINA = MIN.VALORI
+MINIFS = MIN.PIÙ.SE
+MODE.MULT = MODA.MULT
+MODE.SNGL = MODA.SNGL
+NEGBINOM.DIST = DISTRIB.BINOM.NEG.N
+NORM.DIST = DISTRIB.NORM.N
+NORM.INV = INV.NORM.N
+NORM.S.DIST = DISTRIB.NORM.ST.N
+NORM.S.INV = INV.NORM.S
+PEARSON = PEARSON
+PERCENTILE.EXC = ESC.PERCENTILE
+PERCENTILE.INC = INC.PERCENTILE
+PERCENTRANK.EXC = ESC.PERCENT.RANGO
+PERCENTRANK.INC = INC.PERCENT.RANGO
+PERMUT = PERMUTAZIONE
+PERMUTATIONA = PERMUTAZIONE.VALORI
+PHI = PHI
+POISSON.DIST = DISTRIB.POISSON
+PROB = PROBABILITÀ
+QUARTILE.EXC = ESC.QUARTILE
+QUARTILE.INC = INC.QUARTILE
+RANK.AVG = RANGO.MEDIA
+RANK.EQ = RANGO.UG
+RSQ = RQ
+SKEW = ASIMMETRIA
+SKEW.P = ASIMMETRIA.P
+SLOPE = PENDENZA
+SMALL = PICCOLO
+STANDARDIZE = NORMALIZZA
+STDEV.P = DEV.ST.P
+STDEV.S = DEV.ST.C
+STDEVA = DEV.ST.VALORI
+STDEVPA = DEV.ST.POP.VALORI
+STEYX = ERR.STD.YX
+T.DIST = DISTRIB.T.N
+T.DIST.2T = DISTRIB.T.2T
+T.DIST.RT = DISTRIB.T.DS
+T.INV = INVT
+T.INV.2T = INV.T.2T
+T.TEST = TESTT
+TREND = TENDENZA
+TRIMMEAN = MEDIA.TRONCATA
+VAR.P = VAR.P
+VAR.S = VAR.C
+VARA = VAR.VALORI
+VARPA = VAR.POP.VALORI
+WEIBULL.DIST = DISTRIB.WEIBULL
+Z.TEST = TESTZ
+
+##
+## Funzioni di testo (Text Functions)
+##
+BAHTTEXT = BAHTTESTO
+CHAR = CODICE.CARATT
+CLEAN = LIBERA
+CODE = CODICE
+CONCAT = CONCAT
+DOLLAR = VALUTA
+EXACT = IDENTICO
+FIND = TROVA
+FIXED = FISSO
+ISTHAIDIGIT = ÈTHAICIFRA
+LEFT = SINISTRA
+LEN = LUNGHEZZA
+LOWER = MINUSC
+MID = STRINGA.ESTRAI
+NUMBERSTRING = NUMERO.STRINGA
+NUMBERVALUE = NUMERO.VALORE
+PHONETIC = FURIGANA
+PROPER = MAIUSC.INIZ
+REPLACE = RIMPIAZZA
+REPT = RIPETI
+RIGHT = DESTRA
+SEARCH = RICERCA
+SUBSTITUTE = SOSTITUISCI
+T = T
+TEXT = TESTO
+TEXTJOIN = TESTO.UNISCI
+THAIDIGIT = THAICIFRA
+THAINUMSOUND = THAINUMSUONO
+THAINUMSTRING = THAISZÁMKAR
+THAISTRINGLENGTH = THAILUNGSTRINGA
+TRIM = ANNULLA.SPAZI
+UNICHAR = CARATT.UNI
+UNICODE = UNICODE
+UPPER = MAIUSC
+VALUE = VALORE
+
+##
+## Funzioni Web (Web Functions)
+##
+ENCODEURL = CODIFICA.URL
+FILTERXML = FILTRO.XML
+WEBSERVICE = SERVIZIO.WEB
+
+##
+## Funzioni di compatibilità (Compatibility Functions)
+##
+BETADIST = DISTRIB.BETA
+BETAINV = INV.BETA
+BINOMDIST = DISTRIB.BINOM
+CEILING = ARROTONDA.ECCESSO
+CHIDIST = DISTRIB.CHI
+CHIINV = INV.CHI
+CHITEST = TEST.CHI
+CONCATENATE = CONCATENA
+CONFIDENCE = CONFIDENZA
+COVAR = COVARIANZA
+CRITBINOM = CRIT.BINOM
+EXPONDIST = DISTRIB.EXP
+FDIST = DISTRIB.F
+FINV = INV.F
+FLOOR = ARROTONDA.DIFETTO
+FORECAST = PREVISIONE
+FTEST = TEST.F
+GAMMADIST = DISTRIB.GAMMA
+GAMMAINV = INV.GAMMA
+HYPGEOMDIST = DISTRIB.IPERGEOM
+LOGINV = INV.LOGNORM
+LOGNORMDIST = DISTRIB.LOGNORM
+MODE = MODA
+NEGBINOMDIST = DISTRIB.BINOM.NEG
+NORMDIST = DISTRIB.NORM
+NORMINV = INV.NORM
+NORMSDIST = DISTRIB.NORM.ST
+NORMSINV = INV.NORM.ST
+PERCENTILE = PERCENTILE
+PERCENTRANK = PERCENT.RANGO
+POISSON = POISSON
+QUARTILE = QUARTILE
+RANK = RANGO
+STDEV = DEV.ST
+STDEVP = DEV.ST.POP
+TDIST = DISTRIB.T
+TINV = INV.T
+TTEST = TEST.T
+VAR = VAR
+VARP = VAR.POP
+WEIBULL = WEIBULL
+ZTEST = TEST.Z
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/config
new file mode 100644
index 0000000..a7f3be1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Norsk Bokmål (Norwegian Bokmål)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL
+DIV0
+VALUE = #VERDI!
+REF
+NAME = #NAVN?
+NUM
+NA = #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/functions
new file mode 100644
index 0000000..d352e1f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/functions
@@ -0,0 +1,539 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Norsk Bokmål (Norwegian Bokmål)
+##
+############################################################
+
+
+##
+## Kubefunksjoner (Cube Functions)
+##
+CUBEKPIMEMBER = KUBEKPIMEDLEM
+CUBEMEMBER = KUBEMEDLEM
+CUBEMEMBERPROPERTY = KUBEMEDLEMEGENSKAP
+CUBERANKEDMEMBER = KUBERANGERTMEDLEM
+CUBESET = KUBESETT
+CUBESETCOUNT = KUBESETTANTALL
+CUBEVALUE = KUBEVERDI
+
+##
+## Databasefunksjoner (Database Functions)
+##
+DAVERAGE = DGJENNOMSNITT
+DCOUNT = DANTALL
+DCOUNTA = DANTALLA
+DGET = DHENT
+DMAX = DMAKS
+DMIN = DMIN
+DPRODUCT = DPRODUKT
+DSTDEV = DSTDAV
+DSTDEVP = DSTDAVP
+DSUM = DSUMMER
+DVAR = DVARIANS
+DVARP = DVARIANSP
+
+##
+## Dato- og tidsfunksjoner (Date & Time Functions)
+##
+DATE = DATO
+DATEDIF = DATODIFF
+DATESTRING = DATOSTRENG
+DATEVALUE = DATOVERDI
+DAY = DAG
+DAYS = DAGER
+DAYS360 = DAGER360
+EDATE = DAG.ETTER
+EOMONTH = MÅNEDSSLUTT
+HOUR = TIME
+ISOWEEKNUM = ISOUKENR
+MINUTE = MINUTT
+MONTH = MÅNED
+NETWORKDAYS = NETT.ARBEIDSDAGER
+NETWORKDAYS.INTL = NETT.ARBEIDSDAGER.INTL
+NOW = NÅ
+SECOND = SEKUND
+THAIDAYOFWEEK = THAIUKEDAG
+THAIMONTHOFYEAR = THAIMÅNED
+THAIYEAR = THAIÅR
+TIME = TID
+TIMEVALUE = TIDSVERDI
+TODAY = IDAG
+WEEKDAY = UKEDAG
+WEEKNUM = UKENR
+WORKDAY = ARBEIDSDAG
+WORKDAY.INTL = ARBEIDSDAG.INTL
+YEAR = ÅR
+YEARFRAC = ÅRDEL
+
+##
+## Tekniske funksjoner (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BINTILDES
+BIN2HEX = BINTILHEKS
+BIN2OCT = BINTILOKT
+BITAND = BITOG
+BITLSHIFT = BITVFORSKYV
+BITOR = BITELLER
+BITRSHIFT = BITHFORSKYV
+BITXOR = BITEKSKLUSIVELLER
+COMPLEX = KOMPLEKS
+CONVERT = KONVERTER
+DEC2BIN = DESTILBIN
+DEC2HEX = DESTILHEKS
+DEC2OCT = DESTILOKT
+DELTA = DELTA
+ERF = FEILF
+ERF.PRECISE = FEILF.PRESIS
+ERFC = FEILFK
+ERFC.PRECISE = FEILFK.PRESIS
+GESTEP = GRENSEVERDI
+HEX2BIN = HEKSTILBIN
+HEX2DEC = HEKSTILDES
+HEX2OCT = HEKSTILOKT
+IMABS = IMABS
+IMAGINARY = IMAGINÆR
+IMARGUMENT = IMARGUMENT
+IMCONJUGATE = IMKONJUGERT
+IMCOS = IMCOS
+IMCOSH = IMCOSH
+IMCOT = IMCOT
+IMCSC = IMCSC
+IMCSCH = IMCSCH
+IMDIV = IMDIV
+IMEXP = IMEKSP
+IMLN = IMLN
+IMLOG10 = IMLOG10
+IMLOG2 = IMLOG2
+IMPOWER = IMOPPHØY
+IMPRODUCT = IMPRODUKT
+IMREAL = IMREELL
+IMSEC = IMSEC
+IMSECH = IMSECH
+IMSIN = IMSIN
+IMSINH = IMSINH
+IMSQRT = IMROT
+IMSUB = IMSUB
+IMSUM = IMSUMMER
+IMTAN = IMTAN
+OCT2BIN = OKTTILBIN
+OCT2DEC = OKTTILDES
+OCT2HEX = OKTTILHEKS
+
+##
+## Økonomiske funksjoner (Financial Functions)
+##
+ACCRINT = PÅLØPT.PERIODISK.RENTE
+ACCRINTM = PÅLØPT.FORFALLSRENTE
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = OBLIG.DAGER.FF
+COUPDAYS = OBLIG.DAGER
+COUPDAYSNC = OBLIG.DAGER.NF
+COUPNCD = OBLIG.DAGER.EF
+COUPNUM = OBLIG.ANTALL
+COUPPCD = OBLIG.DAG.FORRIGE
+CUMIPMT = SAMLET.RENTE
+CUMPRINC = SAMLET.HOVEDSTOL
+DB = DAVSKR
+DDB = DEGRAVS
+DISC = DISKONTERT
+DOLLARDE = DOLLARDE
+DOLLARFR = DOLLARBR
+DURATION = VARIGHET
+EFFECT = EFFEKTIV.RENTE
+FV = SLUTTVERDI
+FVSCHEDULE = SVPLAN
+INTRATE = RENTESATS
+IPMT = RAVDRAG
+IRR = IR
+ISPMT = ER.AVDRAG
+MDURATION = MVARIGHET
+MIRR = MODIR
+NOMINAL = NOMINELL
+NPER = PERIODER
+NPV = NNV
+ODDFPRICE = AVVIKFP.PRIS
+ODDFYIELD = AVVIKFP.AVKASTNING
+ODDLPRICE = AVVIKSP.PRIS
+ODDLYIELD = AVVIKSP.AVKASTNING
+PDURATION = PVARIGHET
+PMT = AVDRAG
+PPMT = AMORT
+PRICE = PRIS
+PRICEDISC = PRIS.DISKONTERT
+PRICEMAT = PRIS.FORFALL
+PV = NÅVERDI
+RATE = RENTE
+RECEIVED = MOTTATT.AVKAST
+RRI = REALISERT.AVKASTNING
+SLN = LINAVS
+SYD = ÅRSAVS
+TBILLEQ = TBILLEKV
+TBILLPRICE = TBILLPRIS
+TBILLYIELD = TBILLAVKASTNING
+VDB = VERDIAVS
+XIRR = XIR
+XNPV = XNNV
+YIELD = AVKAST
+YIELDDISC = AVKAST.DISKONTERT
+YIELDMAT = AVKAST.FORFALL
+
+##
+## Informasjonsfunksjoner (Information Functions)
+##
+CELL = CELLE
+ERROR.TYPE = FEIL.TYPE
+INFO = INFO
+ISBLANK = ERTOM
+ISERR = ERF
+ISERROR = ERFEIL
+ISEVEN = ERPARTALL
+ISFORMULA = ERFORMEL
+ISLOGICAL = ERLOGISK
+ISNA = ERIT
+ISNONTEXT = ERIKKETEKST
+ISNUMBER = ERTALL
+ISODD = ERODDE
+ISREF = ERREF
+ISTEXT = ERTEKST
+N = N
+NA = IT
+SHEET = ARK
+SHEETS = ANTALL.ARK
+TYPE = VERDITYPE
+
+##
+## Logiske funksjoner (Logical Functions)
+##
+AND = OG
+FALSE = USANN
+IF = HVIS
+IFERROR = HVISFEIL
+IFNA = HVIS.IT
+IFS = HVIS.SETT
+NOT = IKKE
+OR = ELLER
+SWITCH = BRYTER
+TRUE = SANN
+XOR = EKSKLUSIVELLER
+
+##
+## Oppslag- og referansefunksjoner (Lookup & Reference Functions)
+##
+ADDRESS = ADRESSE
+AREAS = OMRÅDER
+CHOOSE = VELG
+COLUMN = KOLONNE
+COLUMNS = KOLONNER
+FORMULATEXT = FORMELTEKST
+GETPIVOTDATA = HENTPIVOTDATA
+HLOOKUP = FINN.KOLONNE
+HYPERLINK = HYPERKOBLING
+INDEX = INDEKS
+INDIRECT = INDIREKTE
+LOOKUP = SLÅ.OPP
+MATCH = SAMMENLIGNE
+OFFSET = FORSKYVNING
+ROW = RAD
+ROWS = RADER
+RTD = RTD
+TRANSPOSE = TRANSPONER
+VLOOKUP = FINN.RAD
+*RC = RK
+
+##
+## Matematikk- og trigonometrifunksjoner (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ARCCOS
+ACOSH = ARCCOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = MENGDE
+ARABIC = ARABISK
+ASIN = ARCSIN
+ASINH = ARCSINH
+ATAN = ARCTAN
+ATAN2 = ARCTAN2
+ATANH = ARCTANH
+BASE = GRUNNTALL
+CEILING.MATH = AVRUND.GJELDENDE.MULTIPLUM.OPP.MATEMATISK
+CEILING.PRECISE = AVRUND.GJELDENDE.MULTIPLUM.PRESIS
+COMBIN = KOMBINASJON
+COMBINA = KOMBINASJONA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DESIMAL
+DEGREES = GRADER
+ECMA.CEILING = ECMA.AVRUND.GJELDENDE.MULTIPLUM
+EVEN = AVRUND.TIL.PARTALL
+EXP = EKSP
+FACT = FAKULTET
+FACTDOUBLE = DOBBELFAKT
+FLOOR.MATH = AVRUND.GJELDENDE.MULTIPLUM.NED.MATEMATISK
+FLOOR.PRECISE = AVRUND.GJELDENDE.MULTIPLUM.NED.PRESIS
+GCD = SFF
+INT = HELTALL
+ISO.CEILING = ISO.AVRUND.GJELDENDE.MULTIPLUM
+LCM = MFM
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDETERM
+MINVERSE = MINVERS
+MMULT = MMULT
+MOD = REST
+MROUND = MRUND
+MULTINOMIAL = MULTINOMINELL
+MUNIT = MENHET
+ODD = AVRUND.TIL.ODDETALL
+PI = PI
+POWER = OPPHØYD.I
+PRODUCT = PRODUKT
+QUOTIENT = KVOTIENT
+RADIANS = RADIANER
+RAND = TILFELDIG
+RANDBETWEEN = TILFELDIGMELLOM
+ROMAN = ROMERTALL
+ROUND = AVRUND
+ROUNDBAHTDOWN = RUNDAVBAHTNEDOVER
+ROUNDBAHTUP = RUNDAVBAHTOPPOVER
+ROUNDDOWN = AVRUND.NED
+ROUNDUP = AVRUND.OPP
+SEC = SEC
+SECH = SECH
+SERIESSUM = SUMMER.REKKE
+SIGN = FORTEGN
+SIN = SIN
+SINH = SINH
+SQRT = ROT
+SQRTPI = ROTPI
+SUBTOTAL = DELSUM
+SUM = SUMMER
+SUMIF = SUMMERHVIS
+SUMIFS = SUMMER.HVIS.SETT
+SUMPRODUCT = SUMMERPRODUKT
+SUMSQ = SUMMERKVADRAT
+SUMX2MY2 = SUMMERX2MY2
+SUMX2PY2 = SUMMERX2PY2
+SUMXMY2 = SUMMERXMY2
+TAN = TAN
+TANH = TANH
+TRUNC = AVKORT
+
+##
+## Statistiske funksjoner (Statistical Functions)
+##
+AVEDEV = GJENNOMSNITTSAVVIK
+AVERAGE = GJENNOMSNITT
+AVERAGEA = GJENNOMSNITTA
+AVERAGEIF = GJENNOMSNITTHVIS
+AVERAGEIFS = GJENNOMSNITT.HVIS.SETT
+BETA.DIST = BETA.FORDELING.N
+BETA.INV = BETA.INV
+BINOM.DIST = BINOM.FORDELING.N
+BINOM.DIST.RANGE = BINOM.FORDELING.OMRÅDE
+BINOM.INV = BINOM.INV
+CHISQ.DIST = KJIKVADRAT.FORDELING
+CHISQ.DIST.RT = KJIKVADRAT.FORDELING.H
+CHISQ.INV = KJIKVADRAT.INV
+CHISQ.INV.RT = KJIKVADRAT.INV.H
+CHISQ.TEST = KJIKVADRAT.TEST
+CONFIDENCE.NORM = KONFIDENS.NORM
+CONFIDENCE.T = KONFIDENS.T
+CORREL = KORRELASJON
+COUNT = ANTALL
+COUNTA = ANTALLA
+COUNTBLANK = TELLBLANKE
+COUNTIF = ANTALL.HVIS
+COUNTIFS = ANTALL.HVIS.SETT
+COVARIANCE.P = KOVARIANS.P
+COVARIANCE.S = KOVARIANS.S
+DEVSQ = AVVIK.KVADRERT
+EXPON.DIST = EKSP.FORDELING.N
+F.DIST = F.FORDELING
+F.DIST.RT = F.FORDELING.H
+F.INV = F.INV
+F.INV.RT = F.INV.H
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = PROGNOSE.ETS
+FORECAST.ETS.CONFINT = PROGNOSE.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = PROGNOSE.ETS.SESONGAVHENGIGHET
+FORECAST.ETS.STAT = PROGNOSE.ETS.STAT
+FORECAST.LINEAR = PROGNOSE.LINEÆR
+FREQUENCY = FREKVENS
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.FORDELING
+GAMMA.INV = GAMMA.INV
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.PRESIS
+GAUSS = GAUSS
+GEOMEAN = GJENNOMSNITT.GEOMETRISK
+GROWTH = VEKST
+HARMEAN = GJENNOMSNITT.HARMONISK
+HYPGEOM.DIST = HYPGEOM.FORDELING.N
+INTERCEPT = SKJÆRINGSPUNKT
+KURT = KURT
+LARGE = N.STØRST
+LINEST = RETTLINJE
+LOGEST = KURVE
+LOGNORM.DIST = LOGNORM.FORDELING
+LOGNORM.INV = LOGNORM.INV
+MAX = STØRST
+MAXA = MAKSA
+MAXIFS = MAKS.HVIS.SETT
+MEDIAN = MEDIAN
+MIN = MIN
+MINA = MINA
+MINIFS = MIN.HVIS.SETT
+MODE.MULT = MODUS.MULT
+MODE.SNGL = MODUS.SNGL
+NEGBINOM.DIST = NEGBINOM.FORDELING.N
+NORM.DIST = NORM.FORDELING
+NORM.INV = NORM.INV
+NORM.S.DIST = NORM.S.FORDELING
+NORM.S.INV = NORM.S.INV
+PEARSON = PEARSON
+PERCENTILE.EXC = PERSENTIL.EKS
+PERCENTILE.INC = PERSENTIL.INK
+PERCENTRANK.EXC = PROSENTDEL.EKS
+PERCENTRANK.INC = PROSENTDEL.INK
+PERMUT = PERMUTER
+PERMUTATIONA = PERMUTASJONA
+PHI = PHI
+POISSON.DIST = POISSON.FORDELING
+PROB = SANNSYNLIG
+QUARTILE.EXC = KVARTIL.EKS
+QUARTILE.INC = KVARTIL.INK
+RANK.AVG = RANG.GJSN
+RANK.EQ = RANG.EKV
+RSQ = RKVADRAT
+SKEW = SKJEVFORDELING
+SKEW.P = SKJEVFORDELING.P
+SLOPE = STIGNINGSTALL
+SMALL = N.MINST
+STANDARDIZE = NORMALISER
+STDEV.P = STDAV.P
+STDEV.S = STDAV.S
+STDEVA = STDAVVIKA
+STDEVPA = STDAVVIKPA
+STEYX = STANDARDFEIL
+T.DIST = T.FORDELING
+T.DIST.2T = T.FORDELING.2T
+T.DIST.RT = T.FORDELING.H
+T.INV = T.INV
+T.INV.2T = T.INV.2T
+T.TEST = T.TEST
+TREND = TREND
+TRIMMEAN = TRIMMET.GJENNOMSNITT
+VAR.P = VARIANS.P
+VAR.S = VARIANS.S
+VARA = VARIANSA
+VARPA = VARIANSPA
+WEIBULL.DIST = WEIBULL.DIST.N
+Z.TEST = Z.TEST
+
+##
+## Tekstfunksjoner (Text Functions)
+##
+ASC = STIGENDE
+BAHTTEXT = BAHTTEKST
+CHAR = TEGNKODE
+CLEAN = RENSK
+CODE = KODE
+CONCAT = KJED.SAMMEN
+DOLLAR = VALUTA
+EXACT = EKSAKT
+FIND = FINN
+FIXED = FASTSATT
+ISTHAIDIGIT = ERTHAISIFFER
+LEFT = VENSTRE
+LEN = LENGDE
+LOWER = SMÅ
+MID = DELTEKST
+NUMBERSTRING = TALLSTRENG
+NUMBERVALUE = TALLVERDI
+PHONETIC = FURIGANA
+PROPER = STOR.FORBOKSTAV
+REPLACE = ERSTATT
+REPT = GJENTA
+RIGHT = HØYRE
+SEARCH = SØK
+SUBSTITUTE = BYTT.UT
+T = T
+TEXT = TEKST
+TEXTJOIN = TEKST.KOMBINER
+THAIDIGIT = THAISIFFER
+THAINUMSOUND = THAINUMLYD
+THAINUMSTRING = THAINUMSTRENG
+THAISTRINGLENGTH = THAISTRENGLENGDE
+TRIM = TRIMME
+UNICHAR = UNICODETEGN
+UNICODE = UNICODE
+UPPER = STORE
+VALUE = VERDI
+
+##
+## Nettfunksjoner (Web Functions)
+##
+ENCODEURL = URL.KODE
+FILTERXML = FILTRERXML
+WEBSERVICE = NETTJENESTE
+
+##
+## Kompatibilitetsfunksjoner (Compatibility Functions)
+##
+BETADIST = BETA.FORDELING
+BETAINV = INVERS.BETA.FORDELING
+BINOMDIST = BINOM.FORDELING
+CEILING = AVRUND.GJELDENDE.MULTIPLUM
+CHIDIST = KJI.FORDELING
+CHIINV = INVERS.KJI.FORDELING
+CHITEST = KJI.TEST
+CONCATENATE = KJEDE.SAMMEN
+CONFIDENCE = KONFIDENS
+COVAR = KOVARIANS
+CRITBINOM = GRENSE.BINOM
+EXPONDIST = EKSP.FORDELING
+FDIST = FFORDELING
+FINV = FFORDELING.INVERS
+FLOOR = AVRUND.GJELDENDE.MULTIPLUM.NED
+FORECAST = PROGNOSE
+FTEST = FTEST
+GAMMADIST = GAMMAFORDELING
+GAMMAINV = GAMMAINV
+HYPGEOMDIST = HYPGEOM.FORDELING
+LOGINV = LOGINV
+LOGNORMDIST = LOGNORMFORD
+MODE = MODUS
+NEGBINOMDIST = NEGBINOM.FORDELING
+NORMDIST = NORMALFORDELING
+NORMINV = NORMINV
+NORMSDIST = NORMSFORDELING
+NORMSINV = NORMSINV
+PERCENTILE = PERSENTIL
+PERCENTRANK = PROSENTDEL
+POISSON = POISSON
+QUARTILE = KVARTIL
+RANK = RANG
+STDEV = STDAV
+STDEVP = STDAVP
+TDIST = TFORDELING
+TINV = TINV
+TTEST = TTEST
+VAR = VARIANS
+VARP = VARIANSP
+WEIBULL = WEIBULL.FORDELING
+ZTEST = ZTEST
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config
new file mode 100644
index 0000000..370567a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Nederlands (Dutch)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #LEEG!
+DIV0 = #DEEL/0!
+VALUE = #WAARDE!
+REF = #VERW!
+NAME = #NAAM?
+NUM = #GETAL!
+NA = #N/B
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions
new file mode 100644
index 0000000..ce0b30c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions
@@ -0,0 +1,537 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Nederlands (Dutch)
+##
+############################################################
+
+
+##
+## Kubusfuncties (Cube Functions)
+##
+CUBEKPIMEMBER = KUBUSKPILID
+CUBEMEMBER = KUBUSLID
+CUBEMEMBERPROPERTY = KUBUSLIDEIGENSCHAP
+CUBERANKEDMEMBER = KUBUSGERANGSCHIKTLID
+CUBESET = KUBUSSET
+CUBESETCOUNT = KUBUSSETAANTAL
+CUBEVALUE = KUBUSWAARDE
+
+##
+## Databasefuncties (Database Functions)
+##
+DAVERAGE = DBGEMIDDELDE
+DCOUNT = DBAANTAL
+DCOUNTA = DBAANTALC
+DGET = DBLEZEN
+DMAX = DBMAX
+DMIN = DBMIN
+DPRODUCT = DBPRODUCT
+DSTDEV = DBSTDEV
+DSTDEVP = DBSTDEVP
+DSUM = DBSOM
+DVAR = DBVAR
+DVARP = DBVARP
+
+##
+## Datum- en tijdfuncties (Date & Time Functions)
+##
+DATE = DATUM
+DATESTRING = DATUMNOTATIE
+DATEVALUE = DATUMWAARDE
+DAY = DAG
+DAYS = DAGEN
+DAYS360 = DAGEN360
+EDATE = ZELFDE.DAG
+EOMONTH = LAATSTE.DAG
+HOUR = UUR
+ISOWEEKNUM = ISO.WEEKNUMMER
+MINUTE = MINUUT
+MONTH = MAAND
+NETWORKDAYS = NETTO.WERKDAGEN
+NETWORKDAYS.INTL = NETWERKDAGEN.INTL
+NOW = NU
+SECOND = SECONDE
+THAIDAYOFWEEK = THAIS.WEEKDAG
+THAIMONTHOFYEAR = THAIS.MAAND.VAN.JAAR
+THAIYEAR = THAIS.JAAR
+TIME = TIJD
+TIMEVALUE = TIJDWAARDE
+TODAY = VANDAAG
+WEEKDAY = WEEKDAG
+WEEKNUM = WEEKNUMMER
+WORKDAY = WERKDAG
+WORKDAY.INTL = WERKDAG.INTL
+YEAR = JAAR
+YEARFRAC = JAAR.DEEL
+
+##
+## Technische functies (Engineering Functions)
+##
+BESSELI = BESSEL.I
+BESSELJ = BESSEL.J
+BESSELK = BESSEL.K
+BESSELY = BESSEL.Y
+BIN2DEC = BIN.N.DEC
+BIN2HEX = BIN.N.HEX
+BIN2OCT = BIN.N.OCT
+BITAND = BIT.EN
+BITLSHIFT = BIT.VERSCHUIF.LINKS
+BITOR = BIT.OF
+BITRSHIFT = BIT.VERSCHUIF.RECHTS
+BITXOR = BIT.EX.OF
+COMPLEX = COMPLEX
+CONVERT = CONVERTEREN
+DEC2BIN = DEC.N.BIN
+DEC2HEX = DEC.N.HEX
+DEC2OCT = DEC.N.OCT
+DELTA = DELTA
+ERF = FOUTFUNCTIE
+ERF.PRECISE = FOUTFUNCTIE.NAUWKEURIG
+ERFC = FOUT.COMPLEMENT
+ERFC.PRECISE = FOUT.COMPLEMENT.NAUWKEURIG
+GESTEP = GROTER.DAN
+HEX2BIN = HEX.N.BIN
+HEX2DEC = HEX.N.DEC
+HEX2OCT = HEX.N.OCT
+IMABS = C.ABS
+IMAGINARY = C.IM.DEEL
+IMARGUMENT = C.ARGUMENT
+IMCONJUGATE = C.TOEGEVOEGD
+IMCOS = C.COS
+IMCOSH = C.COSH
+IMCOT = C.COT
+IMCSC = C.COSEC
+IMCSCH = C.COSECH
+IMDIV = C.QUOTIENT
+IMEXP = C.EXP
+IMLN = C.LN
+IMLOG10 = C.LOG10
+IMLOG2 = C.LOG2
+IMPOWER = C.MACHT
+IMPRODUCT = C.PRODUCT
+IMREAL = C.REEEL.DEEL
+IMSEC = C.SEC
+IMSECH = C.SECH
+IMSIN = C.SIN
+IMSINH = C.SINH
+IMSQRT = C.WORTEL
+IMSUB = C.VERSCHIL
+IMSUM = C.SOM
+IMTAN = C.TAN
+OCT2BIN = OCT.N.BIN
+OCT2DEC = OCT.N.DEC
+OCT2HEX = OCT.N.HEX
+
+##
+## Financiële functies (Financial Functions)
+##
+ACCRINT = SAMENG.RENTE
+ACCRINTM = SAMENG.RENTE.V
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = COUP.DAGEN.BB
+COUPDAYS = COUP.DAGEN
+COUPDAYSNC = COUP.DAGEN.VV
+COUPNCD = COUP.DATUM.NB
+COUPNUM = COUP.AANTAL
+COUPPCD = COUP.DATUM.VB
+CUMIPMT = CUM.RENTE
+CUMPRINC = CUM.HOOFDSOM
+DB = DB
+DDB = DDB
+DISC = DISCONTO
+DOLLARDE = EURO.DE
+DOLLARFR = EURO.BR
+DURATION = DUUR
+EFFECT = EFFECT.RENTE
+FV = TW
+FVSCHEDULE = TOEK.WAARDE2
+INTRATE = RENTEPERCENTAGE
+IPMT = IBET
+IRR = IR
+ISPMT = ISBET
+MDURATION = AANG.DUUR
+MIRR = GIR
+NOMINAL = NOMINALE.RENTE
+NPER = NPER
+NPV = NHW
+ODDFPRICE = AFW.ET.PRIJS
+ODDFYIELD = AFW.ET.REND
+ODDLPRICE = AFW.LT.PRIJS
+ODDLYIELD = AFW.LT.REND
+PDURATION = PDUUR
+PMT = BET
+PPMT = PBET
+PRICE = PRIJS.NOM
+PRICEDISC = PRIJS.DISCONTO
+PRICEMAT = PRIJS.VERVALDAG
+PV = HW
+RATE = RENTE
+RECEIVED = OPBRENGST
+RRI = RRI
+SLN = LIN.AFSCHR
+SYD = SYD
+TBILLEQ = SCHATK.OBL
+TBILLPRICE = SCHATK.PRIJS
+TBILLYIELD = SCHATK.REND
+VDB = VDB
+XIRR = IR.SCHEMA
+XNPV = NHW2
+YIELD = RENDEMENT
+YIELDDISC = REND.DISCONTO
+YIELDMAT = REND.VERVAL
+
+##
+## Informatiefuncties (Information Functions)
+##
+CELL = CEL
+ERROR.TYPE = TYPE.FOUT
+INFO = INFO
+ISBLANK = ISLEEG
+ISERR = ISFOUT2
+ISERROR = ISFOUT
+ISEVEN = IS.EVEN
+ISFORMULA = ISFORMULE
+ISLOGICAL = ISLOGISCH
+ISNA = ISNB
+ISNONTEXT = ISGEENTEKST
+ISNUMBER = ISGETAL
+ISODD = IS.ONEVEN
+ISREF = ISVERWIJZING
+ISTEXT = ISTEKST
+N = N
+NA = NB
+SHEET = BLAD
+SHEETS = BLADEN
+TYPE = TYPE
+
+##
+## Logische functies (Logical Functions)
+##
+AND = EN
+FALSE = ONWAAR
+IF = ALS
+IFERROR = ALS.FOUT
+IFNA = ALS.NB
+IFS = ALS.VOORWAARDEN
+NOT = NIET
+OR = OF
+SWITCH = SCHAKELEN
+TRUE = WAAR
+XOR = EX.OF
+
+##
+## Zoek- en verwijzingsfuncties (Lookup & Reference Functions)
+##
+ADDRESS = ADRES
+AREAS = BEREIKEN
+CHOOSE = KIEZEN
+COLUMN = KOLOM
+COLUMNS = KOLOMMEN
+FORMULATEXT = FORMULETEKST
+GETPIVOTDATA = DRAAITABEL.OPHALEN
+HLOOKUP = HORIZ.ZOEKEN
+HYPERLINK = HYPERLINK
+INDEX = INDEX
+INDIRECT = INDIRECT
+LOOKUP = ZOEKEN
+MATCH = VERGELIJKEN
+OFFSET = VERSCHUIVING
+ROW = RIJ
+ROWS = RIJEN
+RTD = RTG
+TRANSPOSE = TRANSPONEREN
+VLOOKUP = VERT.ZOEKEN
+*RC = RK
+
+##
+## Wiskundige en trigonometrische functies (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = BOOGCOS
+ACOSH = BOOGCOSH
+ACOT = BOOGCOT
+ACOTH = BOOGCOTH
+AGGREGATE = AGGREGAAT
+ARABIC = ARABISCH
+ASIN = BOOGSIN
+ASINH = BOOGSINH
+ATAN = BOOGTAN
+ATAN2 = BOOGTAN2
+ATANH = BOOGTANH
+BASE = BASIS
+CEILING.MATH = AFRONDEN.BOVEN.WISK
+CEILING.PRECISE = AFRONDEN.BOVEN.NAUWKEURIG
+COMBIN = COMBINATIES
+COMBINA = COMBIN.A
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = COSEC
+CSCH = COSECH
+DECIMAL = DECIMAAL
+DEGREES = GRADEN
+ECMA.CEILING = ECMA.AFRONDEN.BOVEN
+EVEN = EVEN
+EXP = EXP
+FACT = FACULTEIT
+FACTDOUBLE = DUBBELE.FACULTEIT
+FLOOR.MATH = AFRONDEN.BENEDEN.WISK
+FLOOR.PRECISE = AFRONDEN.BENEDEN.NAUWKEURIG
+GCD = GGD
+INT = INTEGER
+ISO.CEILING = ISO.AFRONDEN.BOVEN
+LCM = KGV
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = DETERMINANTMAT
+MINVERSE = INVERSEMAT
+MMULT = PRODUCTMAT
+MOD = REST
+MROUND = AFRONDEN.N.VEELVOUD
+MULTINOMIAL = MULTINOMIAAL
+MUNIT = EENHEIDMAT
+ODD = ONEVEN
+PI = PI
+POWER = MACHT
+PRODUCT = PRODUCT
+QUOTIENT = QUOTIENT
+RADIANS = RADIALEN
+RAND = ASELECT
+RANDBETWEEN = ASELECTTUSSEN
+ROMAN = ROMEINS
+ROUND = AFRONDEN
+ROUNDBAHTDOWN = BAHT.AFR.NAAR.BENEDEN
+ROUNDBAHTUP = BAHT.AFR.NAAR.BOVEN
+ROUNDDOWN = AFRONDEN.NAAR.BENEDEN
+ROUNDUP = AFRONDEN.NAAR.BOVEN
+SEC = SEC
+SECH = SECH
+SERIESSUM = SOM.MACHTREEKS
+SIGN = POS.NEG
+SIN = SIN
+SINH = SINH
+SQRT = WORTEL
+SQRTPI = WORTEL.PI
+SUBTOTAL = SUBTOTAAL
+SUM = SOM
+SUMIF = SOM.ALS
+SUMIFS = SOMMEN.ALS
+SUMPRODUCT = SOMPRODUCT
+SUMSQ = KWADRATENSOM
+SUMX2MY2 = SOM.X2MINY2
+SUMX2PY2 = SOM.X2PLUSY2
+SUMXMY2 = SOM.XMINY.2
+TAN = TAN
+TANH = TANH
+TRUNC = GEHEEL
+
+##
+## Statistische functies (Statistical Functions)
+##
+AVEDEV = GEM.DEVIATIE
+AVERAGE = GEMIDDELDE
+AVERAGEA = GEMIDDELDEA
+AVERAGEIF = GEMIDDELDE.ALS
+AVERAGEIFS = GEMIDDELDEN.ALS
+BETA.DIST = BETA.VERD
+BETA.INV = BETA.INV
+BINOM.DIST = BINOM.VERD
+BINOM.DIST.RANGE = BINOM.VERD.BEREIK
+BINOM.INV = BINOMIALE.INV
+CHISQ.DIST = CHIKW.VERD
+CHISQ.DIST.RT = CHIKW.VERD.RECHTS
+CHISQ.INV = CHIKW.INV
+CHISQ.INV.RT = CHIKW.INV.RECHTS
+CHISQ.TEST = CHIKW.TEST
+CONFIDENCE.NORM = VERTROUWELIJKHEID.NORM
+CONFIDENCE.T = VERTROUWELIJKHEID.T
+CORREL = CORRELATIE
+COUNT = AANTAL
+COUNTA = AANTALARG
+COUNTBLANK = AANTAL.LEGE.CELLEN
+COUNTIF = AANTAL.ALS
+COUNTIFS = AANTALLEN.ALS
+COVARIANCE.P = COVARIANTIE.P
+COVARIANCE.S = COVARIANTIE.S
+DEVSQ = DEV.KWAD
+EXPON.DIST = EXPON.VERD.N
+F.DIST = F.VERD
+F.DIST.RT = F.VERD.RECHTS
+F.INV = F.INV
+F.INV.RT = F.INV.RECHTS
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHER.INV
+FORECAST.ETS = VOORSPELLEN.ETS
+FORECAST.ETS.CONFINT = VOORSPELLEN.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = VOORSPELLEN.ETS.SEASONALITY
+FORECAST.ETS.STAT = FORECAST.ETS.STAT
+FORECAST.LINEAR = VOORSPELLEN.LINEAR
+FREQUENCY = INTERVAL
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.VERD.N
+GAMMA.INV = GAMMA.INV.N
+GAMMALN = GAMMA.LN
+GAMMALN.PRECISE = GAMMA.LN.NAUWKEURIG
+GAUSS = GAUSS
+GEOMEAN = MEETK.GEM
+GROWTH = GROEI
+HARMEAN = HARM.GEM
+HYPGEOM.DIST = HYPGEOM.VERD
+INTERCEPT = SNIJPUNT
+KURT = KURTOSIS
+LARGE = GROOTSTE
+LINEST = LIJNSCH
+LOGEST = LOGSCH
+LOGNORM.DIST = LOGNORM.VERD
+LOGNORM.INV = LOGNORM.INV
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAX.ALS.VOORWAARDEN
+MEDIAN = MEDIAAN
+MIN = MIN
+MINA = MINA
+MINIFS = MIN.ALS.VOORWAARDEN
+MODE.MULT = MODUS.MEERV
+MODE.SNGL = MODUS.ENKELV
+NEGBINOM.DIST = NEGBINOM.VERD
+NORM.DIST = NORM.VERD.N
+NORM.INV = NORM.INV.N
+NORM.S.DIST = NORM.S.VERD
+NORM.S.INV = NORM.S.INV
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTIEL.EXC
+PERCENTILE.INC = PERCENTIEL.INC
+PERCENTRANK.EXC = PROCENTRANG.EXC
+PERCENTRANK.INC = PROCENTRANG.INC
+PERMUT = PERMUTATIES
+PERMUTATIONA = PERMUTATIE.A
+PHI = PHI
+POISSON.DIST = POISSON.VERD
+PROB = KANS
+QUARTILE.EXC = KWARTIEL.EXC
+QUARTILE.INC = KWARTIEL.INC
+RANK.AVG = RANG.GEMIDDELDE
+RANK.EQ = RANG.GELIJK
+RSQ = R.KWADRAAT
+SKEW = SCHEEFHEID
+SKEW.P = SCHEEFHEID.P
+SLOPE = RICHTING
+SMALL = KLEINSTE
+STANDARDIZE = NORMALISEREN
+STDEV.P = STDEV.P
+STDEV.S = STDEV.S
+STDEVA = STDEVA
+STDEVPA = STDEVPA
+STEYX = STAND.FOUT.YX
+T.DIST = T.DIST
+T.DIST.2T = T.VERD.2T
+T.DIST.RT = T.VERD.RECHTS
+T.INV = T.INV
+T.INV.2T = T.INV.2T
+T.TEST = T.TEST
+TREND = TREND
+TRIMMEAN = GETRIMD.GEM
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = WEIBULL.VERD
+Z.TEST = Z.TEST
+
+##
+## Tekstfuncties (Text Functions)
+##
+BAHTTEXT = BAHT.TEKST
+CHAR = TEKEN
+CLEAN = WISSEN.CONTROL
+CODE = CODE
+CONCAT = TEKST.SAMENV
+DOLLAR = EURO
+EXACT = GELIJK
+FIND = VIND.ALLES
+FIXED = VAST
+ISTHAIDIGIT = IS.THAIS.CIJFER
+LEFT = LINKS
+LEN = LENGTE
+LOWER = KLEINE.LETTERS
+MID = DEEL
+NUMBERSTRING = GETALNOTATIE
+NUMBERVALUE = NUMERIEKE.WAARDE
+PHONETIC = FONETISCH
+PROPER = BEGINLETTERS
+REPLACE = VERVANGEN
+REPT = HERHALING
+RIGHT = RECHTS
+SEARCH = VIND.SPEC
+SUBSTITUTE = SUBSTITUEREN
+T = T
+TEXT = TEKST
+TEXTJOIN = TEKST.COMBINEREN
+THAIDIGIT = THAIS.CIJFER
+THAINUMSOUND = THAIS.GETAL.GELUID
+THAINUMSTRING = THAIS.GETAL.REEKS
+THAISTRINGLENGTH = THAIS.REEKS.LENGTE
+TRIM = SPATIES.WISSEN
+UNICHAR = UNITEKEN
+UNICODE = UNICODE
+UPPER = HOOFDLETTERS
+VALUE = WAARDE
+
+##
+## Webfuncties (Web Functions)
+##
+ENCODEURL = URL.CODEREN
+FILTERXML = XML.FILTEREN
+WEBSERVICE = WEBSERVICE
+
+##
+## Compatibiliteitsfuncties (Compatibility Functions)
+##
+BETADIST = BETAVERD
+BETAINV = BETAINV
+BINOMDIST = BINOMIALE.VERD
+CEILING = AFRONDEN.BOVEN
+CHIDIST = CHI.KWADRAAT
+CHIINV = CHI.KWADRAAT.INV
+CHITEST = CHI.TOETS
+CONCATENATE = TEKST.SAMENVOEGEN
+CONFIDENCE = BETROUWBAARHEID
+COVAR = COVARIANTIE
+CRITBINOM = CRIT.BINOM
+EXPONDIST = EXPON.VERD
+FDIST = F.VERDELING
+FINV = F.INVERSE
+FLOOR = AFRONDEN.BENEDEN
+FORECAST = VOORSPELLEN
+FTEST = F.TOETS
+GAMMADIST = GAMMA.VERD
+GAMMAINV = GAMMA.INV
+HYPGEOMDIST = HYPERGEO.VERD
+LOGINV = LOG.NORM.INV
+LOGNORMDIST = LOG.NORM.VERD
+MODE = MODUS
+NEGBINOMDIST = NEG.BINOM.VERD
+NORMDIST = NORM.VERD
+NORMINV = NORM.INV
+NORMSDIST = STAND.NORM.VERD
+NORMSINV = STAND.NORM.INV
+PERCENTILE = PERCENTIEL
+PERCENTRANK = PERCENT.RANG
+POISSON = POISSON
+QUARTILE = KWARTIEL
+RANK = RANG
+STDEV = STDEV
+STDEVP = STDEVP
+TDIST = T.VERD
+TINV = TINV
+TTEST = T.TOETS
+VAR = VAR
+VARP = VARP
+WEIBULL = WEIBULL
+ZTEST = Z.TOETS
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config
new file mode 100644
index 0000000..1d8468b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Jezyk polski (Polish)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #ZERO!
+DIV0 = #DZIEL/0!
+VALUE = #ARG!
+REF = #ADR!
+NAME = #NAZWA?
+NUM = #LICZBA!
+NA = #N/D!
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions
new file mode 100644
index 0000000..d1b43b2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions
@@ -0,0 +1,536 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Jezyk polski (Polish)
+##
+############################################################
+
+
+##
+## Funkcje baz danych (Cube Functions)
+##
+CUBEKPIMEMBER = ELEMENT.KPI.MODUŁU
+CUBEMEMBER = ELEMENT.MODUŁU
+CUBEMEMBERPROPERTY = WŁAŚCIWOŚĆ.ELEMENTU.MODUŁU
+CUBERANKEDMEMBER = USZEREGOWANY.ELEMENT.MODUŁU
+CUBESET = ZESTAW.MODUŁÓW
+CUBESETCOUNT = LICZNIK.MODUŁÓW.ZESTAWU
+CUBEVALUE = WARTOŚĆ.MODUŁU
+
+##
+## Funkcje baz danych (Database Functions)
+##
+DAVERAGE = BD.ŚREDNIA
+DCOUNT = BD.ILE.REKORDÓW
+DCOUNTA = BD.ILE.REKORDÓW.A
+DGET = BD.POLE
+DMAX = BD.MAX
+DMIN = BD.MIN
+DPRODUCT = BD.ILOCZYN
+DSTDEV = BD.ODCH.STANDARD
+DSTDEVP = BD.ODCH.STANDARD.POPUL
+DSUM = BD.SUMA
+DVAR = BD.WARIANCJA
+DVARP = BD.WARIANCJA.POPUL
+
+##
+## Funkcje daty i godziny (Date & Time Functions)
+##
+DATE = DATA
+DATEDIF = DATA.RÓŻNICA
+DATESTRING = DATA.CIĄG.ZNAK
+DATEVALUE = DATA.WARTOŚĆ
+DAY = DZIEŃ
+DAYS = DNI
+DAYS360 = DNI.360
+EDATE = NR.SER.DATY
+EOMONTH = NR.SER.OST.DN.MIES
+HOUR = GODZINA
+ISOWEEKNUM = ISO.NUM.TYG
+MINUTE = MINUTA
+MONTH = MIESIĄC
+NETWORKDAYS = DNI.ROBOCZE
+NETWORKDAYS.INTL = DNI.ROBOCZE.NIESTAND
+NOW = TERAZ
+SECOND = SEKUNDA
+THAIDAYOFWEEK = TAJ.DZIEŃ.TYGODNIA
+THAIMONTHOFYEAR = TAJ.MIESIĄC.ROKU
+THAIYEAR = TAJ.ROK
+TIME = CZAS
+TIMEVALUE = CZAS.WARTOŚĆ
+TODAY = DZIŚ
+WEEKDAY = DZIEŃ.TYG
+WEEKNUM = NUM.TYG
+WORKDAY = DZIEŃ.ROBOCZY
+WORKDAY.INTL = DZIEŃ.ROBOCZY.NIESTAND
+YEAR = ROK
+YEARFRAC = CZĘŚĆ.ROKU
+
+##
+## Funkcje inżynierskie (Engineering Functions)
+##
+BESSELI = BESSEL.I
+BESSELJ = BESSEL.J
+BESSELK = BESSEL.K
+BESSELY = BESSEL.Y
+BIN2DEC = DWÓJK.NA.DZIES
+BIN2HEX = DWÓJK.NA.SZESN
+BIN2OCT = DWÓJK.NA.ÓSM
+BITAND = BITAND
+BITLSHIFT = BIT.PRZESUNIĘCIE.W.LEWO
+BITOR = BITOR
+BITRSHIFT = BIT.PRZESUNIĘCIE.W.PRAWO
+BITXOR = BITXOR
+COMPLEX = LICZBA.ZESP
+CONVERT = KONWERTUJ
+DEC2BIN = DZIES.NA.DWÓJK
+DEC2HEX = DZIES.NA.SZESN
+DEC2OCT = DZIES.NA.ÓSM
+DELTA = CZY.RÓWNE
+ERF = FUNKCJA.BŁ
+ERF.PRECISE = FUNKCJA.BŁ.DOKŁ
+ERFC = KOMP.FUNKCJA.BŁ
+ERFC.PRECISE = KOMP.FUNKCJA.BŁ.DOKŁ
+GESTEP = SPRAWDŹ.PRÓG
+HEX2BIN = SZESN.NA.DWÓJK
+HEX2DEC = SZESN.NA.DZIES
+HEX2OCT = SZESN.NA.ÓSM
+IMABS = MODUŁ.LICZBY.ZESP
+IMAGINARY = CZ.UROJ.LICZBY.ZESP
+IMARGUMENT = ARG.LICZBY.ZESP
+IMCONJUGATE = SPRZĘŻ.LICZBY.ZESP
+IMCOS = COS.LICZBY.ZESP
+IMCOSH = COSH.LICZBY.ZESP
+IMCOT = COT.LICZBY.ZESP
+IMCSC = CSC.LICZBY.ZESP
+IMCSCH = CSCH.LICZBY.ZESP
+IMDIV = ILORAZ.LICZB.ZESP
+IMEXP = EXP.LICZBY.ZESP
+IMLN = LN.LICZBY.ZESP
+IMLOG10 = LOG10.LICZBY.ZESP
+IMLOG2 = LOG2.LICZBY.ZESP
+IMPOWER = POTĘGA.LICZBY.ZESP
+IMPRODUCT = ILOCZYN.LICZB.ZESP
+IMREAL = CZ.RZECZ.LICZBY.ZESP
+IMSEC = SEC.LICZBY.ZESP
+IMSECH = SECH.LICZBY.ZESP
+IMSIN = SIN.LICZBY.ZESP
+IMSINH = SINH.LICZBY.ZESP
+IMSQRT = PIERWIASTEK.LICZBY.ZESP
+IMSUB = RÓŻN.LICZB.ZESP
+IMSUM = SUMA.LICZB.ZESP
+IMTAN = TAN.LICZBY.ZESP
+OCT2BIN = ÓSM.NA.DWÓJK
+OCT2DEC = ÓSM.NA.DZIES
+OCT2HEX = ÓSM.NA.SZESN
+
+##
+## Funkcje finansowe (Financial Functions)
+##
+ACCRINT = NAL.ODS
+ACCRINTM = NAL.ODS.WYKUP
+AMORDEGRC = AMORT.NIELIN
+AMORLINC = AMORT.LIN
+COUPDAYBS = WYPŁ.DNI.OD.POCZ
+COUPDAYS = WYPŁ.DNI
+COUPDAYSNC = WYPŁ.DNI.NAST
+COUPNCD = WYPŁ.DATA.NAST
+COUPNUM = WYPŁ.LICZBA
+COUPPCD = WYPŁ.DATA.POPRZ
+CUMIPMT = SPŁAC.ODS
+CUMPRINC = SPŁAC.KAPIT
+DB = DB
+DDB = DDB
+DISC = STOPA.DYSK
+DOLLARDE = CENA.DZIES
+DOLLARFR = CENA.UŁAM
+DURATION = ROCZ.PRZYCH
+EFFECT = EFEKTYWNA
+FV = FV
+FVSCHEDULE = WART.PRZYSZŁ.KAP
+INTRATE = STOPA.PROC
+IPMT = IPMT
+IRR = IRR
+ISPMT = ISPMT
+MDURATION = ROCZ.PRZYCH.M
+MIRR = MIRR
+NOMINAL = NOMINALNA
+NPER = NPER
+NPV = NPV
+ODDFPRICE = CENA.PIERW.OKR
+ODDFYIELD = RENT.PIERW.OKR
+ODDLPRICE = CENA.OST.OKR
+ODDLYIELD = RENT.OST.OKR
+PDURATION = O.CZAS.TRWANIA
+PMT = PMT
+PPMT = PPMT
+PRICE = CENA
+PRICEDISC = CENA.DYSK
+PRICEMAT = CENA.WYKUP
+PV = PV
+RATE = RATE
+RECEIVED = KWOTA.WYKUP
+RRI = RÓWNOW.STOPA.PROC
+SLN = SLN
+SYD = SYD
+TBILLEQ = RENT.EKW.BS
+TBILLPRICE = CENA.BS
+TBILLYIELD = RENT.BS
+VDB = VDB
+XIRR = XIRR
+XNPV = XNPV
+YIELD = RENTOWNOŚĆ
+YIELDDISC = RENT.DYSK
+YIELDMAT = RENT.WYKUP
+
+##
+## Funkcje informacyjne (Information Functions)
+##
+CELL = KOMÓRKA
+ERROR.TYPE = NR.BŁĘDU
+INFO = INFO
+ISBLANK = CZY.PUSTA
+ISERR = CZY.BŁ
+ISERROR = CZY.BŁĄD
+ISEVEN = CZY.PARZYSTE
+ISFORMULA = CZY.FORMUŁA
+ISLOGICAL = CZY.LOGICZNA
+ISNA = CZY.BRAK
+ISNONTEXT = CZY.NIE.TEKST
+ISNUMBER = CZY.LICZBA
+ISODD = CZY.NIEPARZYSTE
+ISREF = CZY.ADR
+ISTEXT = CZY.TEKST
+N = N
+NA = BRAK
+SHEET = ARKUSZ
+SHEETS = ARKUSZE
+TYPE = TYP
+
+##
+## Funkcje logiczne (Logical Functions)
+##
+AND = ORAZ
+FALSE = FAŁSZ
+IF = JEŻELI
+IFERROR = JEŻELI.BŁĄD
+IFNA = JEŻELI.ND
+IFS = WARUNKI
+NOT = NIE
+OR = LUB
+SWITCH = PRZEŁĄCZ
+TRUE = PRAWDA
+XOR = XOR
+
+##
+## Funkcje wyszukiwania i odwołań (Lookup & Reference Functions)
+##
+ADDRESS = ADRES
+AREAS = OBSZARY
+CHOOSE = WYBIERZ
+COLUMN = NR.KOLUMNY
+COLUMNS = LICZBA.KOLUMN
+FORMULATEXT = FORMUŁA.TEKST
+GETPIVOTDATA = WEŹDANETABELI
+HLOOKUP = WYSZUKAJ.POZIOMO
+HYPERLINK = HIPERŁĄCZE
+INDEX = INDEKS
+INDIRECT = ADR.POŚR
+LOOKUP = WYSZUKAJ
+MATCH = PODAJ.POZYCJĘ
+OFFSET = PRZESUNIĘCIE
+ROW = WIERSZ
+ROWS = ILE.WIERSZY
+RTD = DANE.CZASU.RZECZ
+TRANSPOSE = TRANSPONUJ
+VLOOKUP = WYSZUKAJ.PIONOWO
+
+##
+## Funkcje matematyczne i trygonometryczne (Math & Trig Functions)
+##
+ABS = MODUŁ.LICZBY
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = AGREGUJ
+ARABIC = ARABSKIE
+ASIN = ASIN
+ASINH = ASINH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = PODSTAWA
+CEILING.MATH = ZAOKR.W.GÓRĘ.MATEMATYCZNE
+CEILING.PRECISE = ZAOKR.W.GÓRĘ.DOKŁ
+COMBIN = KOMBINACJE
+COMBINA = KOMBINACJE.A
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DZIESIĘTNA
+DEGREES = STOPNIE
+ECMA.CEILING = ECMA.ZAOKR.W.GÓRĘ
+EVEN = ZAOKR.DO.PARZ
+EXP = EXP
+FACT = SILNIA
+FACTDOUBLE = SILNIA.DWUKR
+FLOOR.MATH = ZAOKR.W.DÓŁ.MATEMATYCZNE
+FLOOR.PRECISE = ZAOKR.W.DÓŁ.DOKŁ
+GCD = NAJW.WSP.DZIEL
+INT = ZAOKR.DO.CAŁK
+ISO.CEILING = ISO.ZAOKR.W.GÓRĘ
+LCM = NAJMN.WSP.WIEL
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = WYZNACZNIK.MACIERZY
+MINVERSE = MACIERZ.ODW
+MMULT = MACIERZ.ILOCZYN
+MOD = MOD
+MROUND = ZAOKR.DO.WIELOKR
+MULTINOMIAL = WIELOMIAN
+MUNIT = MACIERZ.JEDNOSTKOWA
+ODD = ZAOKR.DO.NPARZ
+PI = PI
+POWER = POTĘGA
+PRODUCT = ILOCZYN
+QUOTIENT = CZ.CAŁK.DZIELENIA
+RADIANS = RADIANY
+RAND = LOS
+RANDBETWEEN = LOS.ZAKR
+ROMAN = RZYMSKIE
+ROUND = ZAOKR
+ROUNDBAHTDOWN = ZAOKR.DÓŁ.BAT
+ROUNDBAHTUP = ZAOKR.GÓRA.BAT
+ROUNDDOWN = ZAOKR.DÓŁ
+ROUNDUP = ZAOKR.GÓRA
+SEC = SEC
+SECH = SECH
+SERIESSUM = SUMA.SZER.POT
+SIGN = ZNAK.LICZBY
+SIN = SIN
+SINH = SINH
+SQRT = PIERWIASTEK
+SQRTPI = PIERW.PI
+SUBTOTAL = SUMY.CZĘŚCIOWE
+SUM = SUMA
+SUMIF = SUMA.JEŻELI
+SUMIFS = SUMA.WARUNKÓW
+SUMPRODUCT = SUMA.ILOCZYNÓW
+SUMSQ = SUMA.KWADRATÓW
+SUMX2MY2 = SUMA.X2.M.Y2
+SUMX2PY2 = SUMA.X2.P.Y2
+SUMXMY2 = SUMA.XMY.2
+TAN = TAN
+TANH = TANH
+TRUNC = LICZBA.CAŁK
+
+##
+## Funkcje statystyczne (Statistical Functions)
+##
+AVEDEV = ODCH.ŚREDNIE
+AVERAGE = ŚREDNIA
+AVERAGEA = ŚREDNIA.A
+AVERAGEIF = ŚREDNIA.JEŻELI
+AVERAGEIFS = ŚREDNIA.WARUNKÓW
+BETA.DIST = ROZKŁ.BETA
+BETA.INV = ROZKŁ.BETA.ODWR
+BINOM.DIST = ROZKŁ.DWUM
+BINOM.DIST.RANGE = ROZKŁ.DWUM.ZAKRES
+BINOM.INV = ROZKŁ.DWUM.ODWR
+CHISQ.DIST = ROZKŁ.CHI
+CHISQ.DIST.RT = ROZKŁ.CHI.PS
+CHISQ.INV = ROZKŁ.CHI.ODWR
+CHISQ.INV.RT = ROZKŁ.CHI.ODWR.PS
+CHISQ.TEST = CHI.TEST
+CONFIDENCE.NORM = UFNOŚĆ.NORM
+CONFIDENCE.T = UFNOŚĆ.T
+CORREL = WSP.KORELACJI
+COUNT = ILE.LICZB
+COUNTA = ILE.NIEPUSTYCH
+COUNTBLANK = LICZ.PUSTE
+COUNTIF = LICZ.JEŻELI
+COUNTIFS = LICZ.WARUNKI
+COVARIANCE.P = KOWARIANCJA.POPUL
+COVARIANCE.S = KOWARIANCJA.PRÓBKI
+DEVSQ = ODCH.KWADRATOWE
+EXPON.DIST = ROZKŁ.EXP
+F.DIST = ROZKŁ.F
+F.DIST.RT = ROZKŁ.F.PS
+F.INV = ROZKŁ.F.ODWR
+F.INV.RT = ROZKŁ.F.ODWR.PS
+F.TEST = F.TEST
+FISHER = ROZKŁAD.FISHER
+FISHERINV = ROZKŁAD.FISHER.ODW
+FORECAST.ETS = REGLINX.ETS
+FORECAST.ETS.CONFINT = REGLINX.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = REGLINX.ETS.SEZONOWOŚĆ
+FORECAST.ETS.STAT = REGLINX.ETS.STATYSTYKA
+FORECAST.LINEAR = REGLINX.LINIOWA
+FREQUENCY = CZĘSTOŚĆ
+GAMMA = GAMMA
+GAMMA.DIST = ROZKŁ.GAMMA
+GAMMA.INV = ROZKŁ.GAMMA.ODWR
+GAMMALN = ROZKŁAD.LIN.GAMMA
+GAMMALN.PRECISE = ROZKŁAD.LIN.GAMMA.DOKŁ
+GAUSS = GAUSS
+GEOMEAN = ŚREDNIA.GEOMETRYCZNA
+GROWTH = REGEXPW
+HARMEAN = ŚREDNIA.HARMONICZNA
+HYPGEOM.DIST = ROZKŁ.HIPERGEOM
+INTERCEPT = ODCIĘTA
+KURT = KURTOZA
+LARGE = MAX.K
+LINEST = REGLINP
+LOGEST = REGEXPP
+LOGNORM.DIST = ROZKŁ.LOG
+LOGNORM.INV = ROZKŁ.LOG.ODWR
+MAX = MAX
+MAXA = MAX.A
+MAXIFS = MAKS.WARUNKÓW
+MEDIAN = MEDIANA
+MIN = MIN
+MINA = MIN.A
+MINIFS = MIN.WARUNKÓW
+MODE.MULT = WYST.NAJCZĘŚCIEJ.TABL
+MODE.SNGL = WYST.NAJCZĘŚCIEJ.WART
+NEGBINOM.DIST = ROZKŁ.DWUM.PRZEC
+NORM.DIST = ROZKŁ.NORMALNY
+NORM.INV = ROZKŁ.NORMALNY.ODWR
+NORM.S.DIST = ROZKŁ.NORMALNY.S
+NORM.S.INV = ROZKŁ.NORMALNY.S.ODWR
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTYL.PRZEDZ.OTW
+PERCENTILE.INC = PERCENTYL.PRZEDZ.ZAMK
+PERCENTRANK.EXC = PROC.POZ.PRZEDZ.OTW
+PERCENTRANK.INC = PROC.POZ.PRZEDZ.ZAMK
+PERMUT = PERMUTACJE
+PERMUTATIONA = PERMUTACJE.A
+PHI = PHI
+POISSON.DIST = ROZKŁ.POISSON
+PROB = PRAWDPD
+QUARTILE.EXC = KWARTYL.PRZEDZ.OTW
+QUARTILE.INC = KWARTYL.PRZEDZ.ZAMK
+RANK.AVG = POZYCJA.ŚR
+RANK.EQ = POZYCJA.NAJW
+RSQ = R.KWADRAT
+SKEW = SKOŚNOŚĆ
+SKEW.P = SKOŚNOŚĆ.P
+SLOPE = NACHYLENIE
+SMALL = MIN.K
+STANDARDIZE = NORMALIZUJ
+STDEV.P = ODCH.STAND.POPUL
+STDEV.S = ODCH.STANDARD.PRÓBKI
+STDEVA = ODCH.STANDARDOWE.A
+STDEVPA = ODCH.STANDARD.POPUL.A
+STEYX = REGBŁSTD
+T.DIST = ROZKŁ.T
+T.DIST.2T = ROZKŁ.T.DS
+T.DIST.RT = ROZKŁ.T.PS
+T.INV = ROZKŁ.T.ODWR
+T.INV.2T = ROZKŁ.T.ODWR.DS
+T.TEST = T.TEST
+TREND = REGLINW
+TRIMMEAN = ŚREDNIA.WEWN
+VAR.P = WARIANCJA.POP
+VAR.S = WARIANCJA.PRÓBKI
+VARA = WARIANCJA.A
+VARPA = WARIANCJA.POPUL.A
+WEIBULL.DIST = ROZKŁ.WEIBULL
+Z.TEST = Z.TEST
+
+##
+## Funkcje tekstowe (Text Functions)
+##
+BAHTTEXT = BAT.TEKST
+CHAR = ZNAK
+CLEAN = OCZYŚĆ
+CODE = KOD
+CONCAT = ZŁĄCZ.TEKST
+DOLLAR = KWOTA
+EXACT = PORÓWNAJ
+FIND = ZNAJDŹ
+FIXED = ZAOKR.DO.TEKST
+ISTHAIDIGIT = CZY.CYFRA.TAJ
+LEFT = LEWY
+LEN = DŁ
+LOWER = LITERY.MAŁE
+MID = FRAGMENT.TEKSTU
+NUMBERSTRING = LICZBA.CIĄG.ZNAK
+NUMBERVALUE = WARTOŚĆ.LICZBOWA
+PROPER = Z.WIELKIEJ.LITERY
+REPLACE = ZASTĄP
+REPT = POWT
+RIGHT = PRAWY
+SEARCH = SZUKAJ.TEKST
+SUBSTITUTE = PODSTAW
+T = T
+TEXT = TEKST
+TEXTJOIN = POŁĄCZ.TEKSTY
+THAIDIGIT = TAJ.CYFRA
+THAINUMSOUND = TAJ.DŹWIĘK.NUM
+THAINUMSTRING = TAJ.CIĄG.NUM
+THAISTRINGLENGTH = TAJ.DŁUGOŚĆ.CIĄGU
+TRIM = USUŃ.ZBĘDNE.ODSTĘPY
+UNICHAR = ZNAK.UNICODE
+UNICODE = UNICODE
+UPPER = LITERY.WIELKIE
+VALUE = WARTOŚĆ
+
+##
+## Funkcje sieci Web (Web Functions)
+##
+ENCODEURL = ENCODEURL
+FILTERXML = FILTERXML
+WEBSERVICE = WEBSERVICE
+
+##
+## Funkcje zgodności (Compatibility Functions)
+##
+BETADIST = ROZKŁAD.BETA
+BETAINV = ROZKŁAD.BETA.ODW
+BINOMDIST = ROZKŁAD.DWUM
+CEILING = ZAOKR.W.GÓRĘ
+CHIDIST = ROZKŁAD.CHI
+CHIINV = ROZKŁAD.CHI.ODW
+CHITEST = TEST.CHI
+CONCATENATE = ZŁĄCZ.TEKSTY
+CONFIDENCE = UFNOŚĆ
+COVAR = KOWARIANCJA
+CRITBINOM = PRÓG.ROZKŁAD.DWUM
+EXPONDIST = ROZKŁAD.EXP
+FDIST = ROZKŁAD.F
+FINV = ROZKŁAD.F.ODW
+FLOOR = ZAOKR.W.DÓŁ
+FORECAST = REGLINX
+FTEST = TEST.F
+GAMMADIST = ROZKŁAD.GAMMA
+GAMMAINV = ROZKŁAD.GAMMA.ODW
+HYPGEOMDIST = ROZKŁAD.HIPERGEOM
+LOGINV = ROZKŁAD.LOG.ODW
+LOGNORMDIST = ROZKŁAD.LOG
+MODE = WYST.NAJCZĘŚCIEJ
+NEGBINOMDIST = ROZKŁAD.DWUM.PRZEC
+NORMDIST = ROZKŁAD.NORMALNY
+NORMINV = ROZKŁAD.NORMALNY.ODW
+NORMSDIST = ROZKŁAD.NORMALNY.S
+NORMSINV = ROZKŁAD.NORMALNY.S.ODW
+PERCENTILE = PERCENTYL
+PERCENTRANK = PROCENT.POZYCJA
+POISSON = ROZKŁAD.POISSON
+QUARTILE = KWARTYL
+RANK = POZYCJA
+STDEV = ODCH.STANDARDOWE
+STDEVP = ODCH.STANDARD.POPUL
+TDIST = ROZKŁAD.T
+TINV = ROZKŁAD.T.ODW
+TTEST = TEST.T
+VAR = WARIANCJA
+VARP = WARIANCJA.POPUL
+WEIBULL = ROZKŁAD.WEIBULL
+ZTEST = TEST.Z
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config
new file mode 100644
index 0000000..c39057c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Português Brasileiro (Brazilian Portuguese)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #NULO!
+DIV0
+VALUE = #VALOR!
+REF
+NAME = #NOME?
+NUM = #NÚM!
+NA = #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions
new file mode 100644
index 0000000..5781b0c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions
@@ -0,0 +1,528 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Português Brasileiro (Brazilian Portuguese)
+##
+############################################################
+
+
+##
+## Funções de cubo (Cube Functions)
+##
+CUBEKPIMEMBER = MEMBROKPICUBO
+CUBEMEMBER = MEMBROCUBO
+CUBEMEMBERPROPERTY = PROPRIEDADEMEMBROCUBO
+CUBERANKEDMEMBER = MEMBROCLASSIFICADOCUBO
+CUBESET = CONJUNTOCUBO
+CUBESETCOUNT = CONTAGEMCONJUNTOCUBO
+CUBEVALUE = VALORCUBO
+
+##
+## Funções de banco de dados (Database Functions)
+##
+DAVERAGE = BDMÉDIA
+DCOUNT = BDCONTAR
+DCOUNTA = BDCONTARA
+DGET = BDEXTRAIR
+DMAX = BDMÁX
+DMIN = BDMÍN
+DPRODUCT = BDMULTIPL
+DSTDEV = BDEST
+DSTDEVP = BDDESVPA
+DSUM = BDSOMA
+DVAR = BDVAREST
+DVARP = BDVARP
+
+##
+## Funções de data e hora (Date & Time Functions)
+##
+DATE = DATA
+DATEDIF = DATADIF
+DATESTRING = DATA.SÉRIE
+DATEVALUE = DATA.VALOR
+DAY = DIA
+DAYS = DIAS
+DAYS360 = DIAS360
+EDATE = DATAM
+EOMONTH = FIMMÊS
+HOUR = HORA
+ISOWEEKNUM = NÚMSEMANAISO
+MINUTE = MINUTO
+MONTH = MÊS
+NETWORKDAYS = DIATRABALHOTOTAL
+NETWORKDAYS.INTL = DIATRABALHOTOTAL.INTL
+NOW = AGORA
+SECOND = SEGUNDO
+TIME = TEMPO
+TIMEVALUE = VALOR.TEMPO
+TODAY = HOJE
+WEEKDAY = DIA.DA.SEMANA
+WEEKNUM = NÚMSEMANA
+WORKDAY = DIATRABALHO
+WORKDAY.INTL = DIATRABALHO.INTL
+YEAR = ANO
+YEARFRAC = FRAÇÃOANO
+
+##
+## Funções de engenharia (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BINADEC
+BIN2HEX = BINAHEX
+BIN2OCT = BINAOCT
+BITAND = BITAND
+BITLSHIFT = DESLOCESQBIT
+BITOR = BITOR
+BITRSHIFT = DESLOCDIRBIT
+BITXOR = BITXOR
+COMPLEX = COMPLEXO
+CONVERT = CONVERTER
+DEC2BIN = DECABIN
+DEC2HEX = DECAHEX
+DEC2OCT = DECAOCT
+DELTA = DELTA
+ERF = FUNERRO
+ERF.PRECISE = FUNERRO.PRECISO
+ERFC = FUNERROCOMPL
+ERFC.PRECISE = FUNERROCOMPL.PRECISO
+GESTEP = DEGRAU
+HEX2BIN = HEXABIN
+HEX2DEC = HEXADEC
+HEX2OCT = HEXAOCT
+IMABS = IMABS
+IMAGINARY = IMAGINÁRIO
+IMARGUMENT = IMARG
+IMCONJUGATE = IMCONJ
+IMCOS = IMCOS
+IMCOSH = IMCOSH
+IMCOT = IMCOT
+IMCSC = IMCOSEC
+IMCSCH = IMCOSECH
+IMDIV = IMDIV
+IMEXP = IMEXP
+IMLN = IMLN
+IMLOG10 = IMLOG10
+IMLOG2 = IMLOG2
+IMPOWER = IMPOT
+IMPRODUCT = IMPROD
+IMREAL = IMREAL
+IMSEC = IMSEC
+IMSECH = IMSECH
+IMSIN = IMSENO
+IMSINH = IMSENH
+IMSQRT = IMRAIZ
+IMSUB = IMSUBTR
+IMSUM = IMSOMA
+IMTAN = IMTAN
+OCT2BIN = OCTABIN
+OCT2DEC = OCTADEC
+OCT2HEX = OCTAHEX
+
+##
+## Funções financeiras (Financial Functions)
+##
+ACCRINT = JUROSACUM
+ACCRINTM = JUROSACUMV
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = CUPDIASINLIQ
+COUPDAYS = CUPDIAS
+COUPDAYSNC = CUPDIASPRÓX
+COUPNCD = CUPDATAPRÓX
+COUPNUM = CUPNÚM
+COUPPCD = CUPDATAANT
+CUMIPMT = PGTOJURACUM
+CUMPRINC = PGTOCAPACUM
+DB = BD
+DDB = BDD
+DISC = DESC
+DOLLARDE = MOEDADEC
+DOLLARFR = MOEDAFRA
+DURATION = DURAÇÃO
+EFFECT = EFETIVA
+FV = VF
+FVSCHEDULE = VFPLANO
+INTRATE = TAXAJUROS
+IPMT = IPGTO
+IRR = TIR
+ISPMT = ÉPGTO
+MDURATION = MDURAÇÃO
+MIRR = MTIR
+NOMINAL = NOMINAL
+NPER = NPER
+NPV = VPL
+ODDFPRICE = PREÇOPRIMINC
+ODDFYIELD = LUCROPRIMINC
+ODDLPRICE = PREÇOÚLTINC
+ODDLYIELD = LUCROÚLTINC
+PDURATION = DURAÇÃOP
+PMT = PGTO
+PPMT = PPGTO
+PRICE = PREÇO
+PRICEDISC = PREÇODESC
+PRICEMAT = PREÇOVENC
+PV = VP
+RATE = TAXA
+RECEIVED = RECEBER
+RRI = TAXAJURO
+SLN = DPD
+SYD = SDA
+TBILLEQ = OTN
+TBILLPRICE = OTNVALOR
+TBILLYIELD = OTNLUCRO
+VDB = BDV
+XIRR = XTIR
+XNPV = XVPL
+YIELD = LUCRO
+YIELDDISC = LUCRODESC
+YIELDMAT = LUCROVENC
+
+##
+## Funções de informação (Information Functions)
+##
+CELL = CÉL
+ERROR.TYPE = TIPO.ERRO
+INFO = INFORMAÇÃO
+ISBLANK = ÉCÉL.VAZIA
+ISERR = ÉERRO
+ISERROR = ÉERROS
+ISEVEN = ÉPAR
+ISFORMULA = ÉFÓRMULA
+ISLOGICAL = ÉLÓGICO
+ISNA = É.NÃO.DISP
+ISNONTEXT = É.NÃO.TEXTO
+ISNUMBER = ÉNÚM
+ISODD = ÉIMPAR
+ISREF = ÉREF
+ISTEXT = ÉTEXTO
+N = N
+NA = NÃO.DISP
+SHEET = PLAN
+SHEETS = PLANS
+TYPE = TIPO
+
+##
+## Funções lógicas (Logical Functions)
+##
+AND = E
+FALSE = FALSO
+IF = SE
+IFERROR = SEERRO
+IFNA = SENÃODISP
+IFS = SES
+NOT = NÃO
+OR = OU
+SWITCH = PARÂMETRO
+TRUE = VERDADEIRO
+XOR = XOR
+
+##
+## Funções de pesquisa e referência (Lookup & Reference Functions)
+##
+ADDRESS = ENDEREÇO
+AREAS = ÁREAS
+CHOOSE = ESCOLHER
+COLUMN = COL
+COLUMNS = COLS
+FORMULATEXT = FÓRMULATEXTO
+GETPIVOTDATA = INFODADOSTABELADINÂMICA
+HLOOKUP = PROCH
+HYPERLINK = HIPERLINK
+INDEX = ÍNDICE
+INDIRECT = INDIRETO
+LOOKUP = PROC
+MATCH = CORRESP
+OFFSET = DESLOC
+ROW = LIN
+ROWS = LINS
+RTD = RTD
+TRANSPOSE = TRANSPOR
+VLOOKUP = PROCV
+*RC = LC
+
+##
+## Funções matemáticas e trigonométricas (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = AGREGAR
+ARABIC = ARÁBICO
+ASIN = ASEN
+ASINH = ASENH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = BASE
+CEILING.MATH = TETO.MAT
+CEILING.PRECISE = TETO.PRECISO
+COMBIN = COMBIN
+COMBINA = COMBINA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = COSEC
+CSCH = COSECH
+DECIMAL = DECIMAL
+DEGREES = GRAUS
+ECMA.CEILING = ECMA.TETO
+EVEN = PAR
+EXP = EXP
+FACT = FATORIAL
+FACTDOUBLE = FATDUPLO
+FLOOR.MATH = ARREDMULTB.MAT
+FLOOR.PRECISE = ARREDMULTB.PRECISO
+GCD = MDC
+INT = INT
+ISO.CEILING = ISO.TETO
+LCM = MMC
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MATRIZ.DETERM
+MINVERSE = MATRIZ.INVERSO
+MMULT = MATRIZ.MULT
+MOD = MOD
+MROUND = MARRED
+MULTINOMIAL = MULTINOMIAL
+MUNIT = MUNIT
+ODD = ÍMPAR
+PI = PI
+POWER = POTÊNCIA
+PRODUCT = MULT
+QUOTIENT = QUOCIENTE
+RADIANS = RADIANOS
+RAND = ALEATÓRIO
+RANDBETWEEN = ALEATÓRIOENTRE
+ROMAN = ROMANO
+ROUND = ARRED
+ROUNDDOWN = ARREDONDAR.PARA.BAIXO
+ROUNDUP = ARREDONDAR.PARA.CIMA
+SEC = SEC
+SECH = SECH
+SERIESSUM = SOMASEQÜÊNCIA
+SIGN = SINAL
+SIN = SEN
+SINH = SENH
+SQRT = RAIZ
+SQRTPI = RAIZPI
+SUBTOTAL = SUBTOTAL
+SUM = SOMA
+SUMIF = SOMASE
+SUMIFS = SOMASES
+SUMPRODUCT = SOMARPRODUTO
+SUMSQ = SOMAQUAD
+SUMX2MY2 = SOMAX2DY2
+SUMX2PY2 = SOMAX2SY2
+SUMXMY2 = SOMAXMY2
+TAN = TAN
+TANH = TANH
+TRUNC = TRUNCAR
+
+##
+## Funções estatísticas (Statistical Functions)
+##
+AVEDEV = DESV.MÉDIO
+AVERAGE = MÉDIA
+AVERAGEA = MÉDIAA
+AVERAGEIF = MÉDIASE
+AVERAGEIFS = MÉDIASES
+BETA.DIST = DIST.BETA
+BETA.INV = INV.BETA
+BINOM.DIST = DISTR.BINOM
+BINOM.DIST.RANGE = INTERV.DISTR.BINOM
+BINOM.INV = INV.BINOM
+CHISQ.DIST = DIST.QUIQUA
+CHISQ.DIST.RT = DIST.QUIQUA.CD
+CHISQ.INV = INV.QUIQUA
+CHISQ.INV.RT = INV.QUIQUA.CD
+CHISQ.TEST = TESTE.QUIQUA
+CONFIDENCE.NORM = INT.CONFIANÇA.NORM
+CONFIDENCE.T = INT.CONFIANÇA.T
+CORREL = CORREL
+COUNT = CONT.NÚM
+COUNTA = CONT.VALORES
+COUNTBLANK = CONTAR.VAZIO
+COUNTIF = CONT.SE
+COUNTIFS = CONT.SES
+COVARIANCE.P = COVARIAÇÃO.P
+COVARIANCE.S = COVARIAÇÃO.S
+DEVSQ = DESVQ
+EXPON.DIST = DISTR.EXPON
+F.DIST = DIST.F
+F.DIST.RT = DIST.F.CD
+F.INV = INV.F
+F.INV.RT = INV.F.CD
+F.TEST = TESTE.F
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = PREVISÃO.ETS
+FORECAST.ETS.CONFINT = PREVISÃO.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = PREVISÃO.ETS.SAZONALIDADE
+FORECAST.ETS.STAT = PREVISÃO.ETS.STAT
+FORECAST.LINEAR = PREVISÃO.LINEAR
+FREQUENCY = FREQÜÊNCIA
+GAMMA = GAMA
+GAMMA.DIST = DIST.GAMA
+GAMMA.INV = INV.GAMA
+GAMMALN = LNGAMA
+GAMMALN.PRECISE = LNGAMA.PRECISO
+GAUSS = GAUSS
+GEOMEAN = MÉDIA.GEOMÉTRICA
+GROWTH = CRESCIMENTO
+HARMEAN = MÉDIA.HARMÔNICA
+HYPGEOM.DIST = DIST.HIPERGEOM.N
+INTERCEPT = INTERCEPÇÃO
+KURT = CURT
+LARGE = MAIOR
+LINEST = PROJ.LIN
+LOGEST = PROJ.LOG
+LOGNORM.DIST = DIST.LOGNORMAL.N
+LOGNORM.INV = INV.LOGNORMAL
+MAX = MÁXIMO
+MAXA = MÁXIMOA
+MAXIFS = MÁXIMOSES
+MEDIAN = MED
+MIN = MÍNIMO
+MINA = MÍNIMOA
+MINIFS = MÍNIMOSES
+MODE.MULT = MODO.MULT
+MODE.SNGL = MODO.ÚNICO
+NEGBINOM.DIST = DIST.BIN.NEG.N
+NORM.DIST = DIST.NORM.N
+NORM.INV = INV.NORM.N
+NORM.S.DIST = DIST.NORMP.N
+NORM.S.INV = INV.NORMP.N
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTIL.EXC
+PERCENTILE.INC = PERCENTIL.INC
+PERCENTRANK.EXC = ORDEM.PORCENTUAL.EXC
+PERCENTRANK.INC = ORDEM.PORCENTUAL.INC
+PERMUT = PERMUT
+PERMUTATIONA = PERMUTAS
+PHI = PHI
+POISSON.DIST = DIST.POISSON
+PROB = PROB
+QUARTILE.EXC = QUARTIL.EXC
+QUARTILE.INC = QUARTIL.INC
+RANK.AVG = ORDEM.MÉD
+RANK.EQ = ORDEM.EQ
+RSQ = RQUAD
+SKEW = DISTORÇÃO
+SKEW.P = DISTORÇÃO.P
+SLOPE = INCLINAÇÃO
+SMALL = MENOR
+STANDARDIZE = PADRONIZAR
+STDEV.P = DESVPAD.P
+STDEV.S = DESVPAD.A
+STDEVA = DESVPADA
+STDEVPA = DESVPADPA
+STEYX = EPADYX
+T.DIST = DIST.T
+T.DIST.2T = DIST.T.BC
+T.DIST.RT = DIST.T.CD
+T.INV = INV.T
+T.INV.2T = INV.T.BC
+T.TEST = TESTE.T
+TREND = TENDÊNCIA
+TRIMMEAN = MÉDIA.INTERNA
+VAR.P = VAR.P
+VAR.S = VAR.A
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = DIST.WEIBULL
+Z.TEST = TESTE.Z
+
+##
+## Funções de texto (Text Functions)
+##
+BAHTTEXT = BAHTTEXT
+CHAR = CARACT
+CLEAN = TIRAR
+CODE = CÓDIGO
+CONCAT = CONCAT
+DOLLAR = MOEDA
+EXACT = EXATO
+FIND = PROCURAR
+FIXED = DEF.NÚM.DEC
+LEFT = ESQUERDA
+LEN = NÚM.CARACT
+LOWER = MINÚSCULA
+MID = EXT.TEXTO
+NUMBERSTRING = SEQÜÊNCIA.NÚMERO
+NUMBERVALUE = VALORNUMÉRICO
+PHONETIC = FONÉTICA
+PROPER = PRI.MAIÚSCULA
+REPLACE = MUDAR
+REPT = REPT
+RIGHT = DIREITA
+SEARCH = LOCALIZAR
+SUBSTITUTE = SUBSTITUIR
+T = T
+TEXT = TEXTO
+TEXTJOIN = UNIRTEXTO
+TRIM = ARRUMAR
+UNICHAR = CARACTUNICODE
+UNICODE = UNICODE
+UPPER = MAIÚSCULA
+VALUE = VALOR
+
+##
+## Funções da Web (Web Functions)
+##
+ENCODEURL = CODIFURL
+FILTERXML = FILTROXML
+WEBSERVICE = SERVIÇOWEB
+
+##
+## Funções de compatibilidade (Compatibility Functions)
+##
+BETADIST = DISTBETA
+BETAINV = BETA.ACUM.INV
+BINOMDIST = DISTRBINOM
+CEILING = TETO
+CHIDIST = DIST.QUI
+CHIINV = INV.QUI
+CHITEST = TESTE.QUI
+CONCATENATE = CONCATENAR
+CONFIDENCE = INT.CONFIANÇA
+COVAR = COVAR
+CRITBINOM = CRIT.BINOM
+EXPONDIST = DISTEXPON
+FDIST = DISTF
+FINV = INVF
+FLOOR = ARREDMULTB
+FORECAST = PREVISÃO
+FTEST = TESTEF
+GAMMADIST = DISTGAMA
+GAMMAINV = INVGAMA
+HYPGEOMDIST = DIST.HIPERGEOM
+LOGINV = INVLOG
+LOGNORMDIST = DIST.LOGNORMAL
+MODE = MODO
+NEGBINOMDIST = DIST.BIN.NEG
+NORMDIST = DISTNORM
+NORMINV = INV.NORM
+NORMSDIST = DISTNORMP
+NORMSINV = INV.NORMP
+PERCENTILE = PERCENTIL
+PERCENTRANK = ORDEM.PORCENTUAL
+POISSON = POISSON
+QUARTILE = QUARTIL
+RANK = ORDEM
+STDEV = DESVPAD
+STDEVP = DESVPADP
+TDIST = DISTT
+TINV = INVT
+TTEST = TESTET
+VAR = VAR
+VARP = VARP
+WEIBULL = WEIBULL
+ZTEST = TESTEZ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config
new file mode 100644
index 0000000..e661830
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Português (Portuguese)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #NULO!
+DIV0
+VALUE = #VALOR!
+REF
+NAME = #NOME?
+NUM = #NÚM!
+NA = #N/D
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions
new file mode 100644
index 0000000..70a3bb0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions
@@ -0,0 +1,538 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Português (Portuguese)
+##
+############################################################
+
+
+##
+## Funções de cubo (Cube Functions)
+##
+CUBEKPIMEMBER = MEMBROKPICUBO
+CUBEMEMBER = MEMBROCUBO
+CUBEMEMBERPROPERTY = PROPRIEDADEMEMBROCUBO
+CUBERANKEDMEMBER = MEMBROCLASSIFICADOCUBO
+CUBESET = CONJUNTOCUBO
+CUBESETCOUNT = CONTARCONJUNTOCUBO
+CUBEVALUE = VALORCUBO
+
+##
+## Funções de base de dados (Database Functions)
+##
+DAVERAGE = BDMÉDIA
+DCOUNT = BDCONTAR
+DCOUNTA = BDCONTAR.VAL
+DGET = BDOBTER
+DMAX = BDMÁX
+DMIN = BDMÍN
+DPRODUCT = BDMULTIPL
+DSTDEV = BDDESVPAD
+DSTDEVP = BDDESVPADP
+DSUM = BDSOMA
+DVAR = BDVAR
+DVARP = BDVARP
+
+##
+## Funções de data e hora (Date & Time Functions)
+##
+DATE = DATA
+DATEDIF = DATADIF
+DATESTRING = DATA.CADEIA
+DATEVALUE = DATA.VALOR
+DAY = DIA
+DAYS = DIAS
+DAYS360 = DIAS360
+EDATE = DATAM
+EOMONTH = FIMMÊS
+HOUR = HORA
+ISOWEEKNUM = NUMSEMANAISO
+MINUTE = MINUTO
+MONTH = MÊS
+NETWORKDAYS = DIATRABALHOTOTAL
+NETWORKDAYS.INTL = DIATRABALHOTOTAL.INTL
+NOW = AGORA
+SECOND = SEGUNDO
+THAIDAYOFWEEK = DIA.DA.SEMANA.TAILANDÊS
+THAIMONTHOFYEAR = MÊS.DO.ANO.TAILANDÊS
+THAIYEAR = ANO.TAILANDÊS
+TIME = TEMPO
+TIMEVALUE = VALOR.TEMPO
+TODAY = HOJE
+WEEKDAY = DIA.SEMANA
+WEEKNUM = NÚMSEMANA
+WORKDAY = DIATRABALHO
+WORKDAY.INTL = DIATRABALHO.INTL
+YEAR = ANO
+YEARFRAC = FRAÇÃOANO
+
+##
+## Funções de engenharia (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BINADEC
+BIN2HEX = BINAHEX
+BIN2OCT = BINAOCT
+BITAND = BIT.E
+BITLSHIFT = BITDESL.ESQ
+BITOR = BIT.OU
+BITRSHIFT = BITDESL.DIR
+BITXOR = BIT.XOU
+COMPLEX = COMPLEXO
+CONVERT = CONVERTER
+DEC2BIN = DECABIN
+DEC2HEX = DECAHEX
+DEC2OCT = DECAOCT
+DELTA = DELTA
+ERF = FUNCERRO
+ERF.PRECISE = FUNCERRO.PRECISO
+ERFC = FUNCERROCOMPL
+ERFC.PRECISE = FUNCERROCOMPL.PRECISO
+GESTEP = DEGRAU
+HEX2BIN = HEXABIN
+HEX2DEC = HEXADEC
+HEX2OCT = HEXAOCT
+IMABS = IMABS
+IMAGINARY = IMAGINÁRIO
+IMARGUMENT = IMARG
+IMCONJUGATE = IMCONJ
+IMCOS = IMCOS
+IMCOSH = IMCOSH
+IMCOT = IMCOT
+IMCSC = IMCSC
+IMCSCH = IMCSCH
+IMDIV = IMDIV
+IMEXP = IMEXP
+IMLN = IMLN
+IMLOG10 = IMLOG10
+IMLOG2 = IMLOG2
+IMPOWER = IMPOT
+IMPRODUCT = IMPROD
+IMREAL = IMREAL
+IMSEC = IMSEC
+IMSECH = IMSECH
+IMSIN = IMSENO
+IMSINH = IMSENOH
+IMSQRT = IMRAIZ
+IMSUB = IMSUBTR
+IMSUM = IMSOMA
+IMTAN = IMTAN
+OCT2BIN = OCTABIN
+OCT2DEC = OCTADEC
+OCT2HEX = OCTAHEX
+
+##
+## Funções financeiras (Financial Functions)
+##
+ACCRINT = JUROSACUM
+ACCRINTM = JUROSACUMV
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = CUPDIASINLIQ
+COUPDAYS = CUPDIAS
+COUPDAYSNC = CUPDIASPRÓX
+COUPNCD = CUPDATAPRÓX
+COUPNUM = CUPNÚM
+COUPPCD = CUPDATAANT
+CUMIPMT = PGTOJURACUM
+CUMPRINC = PGTOCAPACUM
+DB = BD
+DDB = BDD
+DISC = DESC
+DOLLARDE = MOEDADEC
+DOLLARFR = MOEDAFRA
+DURATION = DURAÇÃO
+EFFECT = EFETIVA
+FV = VF
+FVSCHEDULE = VFPLANO
+INTRATE = TAXAJUROS
+IPMT = IPGTO
+IRR = TIR
+ISPMT = É.PGTO
+MDURATION = MDURAÇÃO
+MIRR = MTIR
+NOMINAL = NOMINAL
+NPER = NPER
+NPV = VAL
+ODDFPRICE = PREÇOPRIMINC
+ODDFYIELD = LUCROPRIMINC
+ODDLPRICE = PREÇOÚLTINC
+ODDLYIELD = LUCROÚLTINC
+PDURATION = PDURAÇÃO
+PMT = PGTO
+PPMT = PPGTO
+PRICE = PREÇO
+PRICEDISC = PREÇODESC
+PRICEMAT = PREÇOVENC
+PV = VA
+RATE = TAXA
+RECEIVED = RECEBER
+RRI = DEVOLVERTAXAJUROS
+SLN = AMORT
+SYD = AMORTD
+TBILLEQ = OTN
+TBILLPRICE = OTNVALOR
+TBILLYIELD = OTNLUCRO
+VDB = BDV
+XIRR = XTIR
+XNPV = XVAL
+YIELD = LUCRO
+YIELDDISC = LUCRODESC
+YIELDMAT = LUCROVENC
+
+##
+## Funções de informação (Information Functions)
+##
+CELL = CÉL
+ERROR.TYPE = TIPO.ERRO
+INFO = INFORMAÇÃO
+ISBLANK = É.CÉL.VAZIA
+ISERR = É.ERROS
+ISERROR = É.ERRO
+ISEVEN = ÉPAR
+ISFORMULA = É.FORMULA
+ISLOGICAL = É.LÓGICO
+ISNA = É.NÃO.DISP
+ISNONTEXT = É.NÃO.TEXTO
+ISNUMBER = É.NÚM
+ISODD = ÉÍMPAR
+ISREF = É.REF
+ISTEXT = É.TEXTO
+N = N
+NA = NÃO.DISP
+SHEET = FOLHA
+SHEETS = FOLHAS
+TYPE = TIPO
+
+##
+## Funções lógicas (Logical Functions)
+##
+AND = E
+FALSE = FALSO
+IF = SE
+IFERROR = SE.ERRO
+IFNA = SEND
+IFS = SE.S
+NOT = NÃO
+OR = OU
+SWITCH = PARÂMETRO
+TRUE = VERDADEIRO
+XOR = XOU
+
+##
+## Funções de pesquisa e referência (Lookup & Reference Functions)
+##
+ADDRESS = ENDEREÇO
+AREAS = ÁREAS
+CHOOSE = SELECIONAR
+COLUMN = COL
+COLUMNS = COLS
+FORMULATEXT = FÓRMULA.TEXTO
+GETPIVOTDATA = OBTERDADOSDIN
+HLOOKUP = PROCH
+HYPERLINK = HIPERLIGAÇÃO
+INDEX = ÍNDICE
+INDIRECT = INDIRETO
+LOOKUP = PROC
+MATCH = CORRESP
+OFFSET = DESLOCAMENTO
+ROW = LIN
+ROWS = LINS
+RTD = RTD
+TRANSPOSE = TRANSPOR
+VLOOKUP = PROCV
+*RC = LC
+
+##
+## Funções matemáticas e trigonométricas (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = AGREGAR
+ARABIC = ÁRABE
+ASIN = ASEN
+ASINH = ASENH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = BASE
+CEILING.MATH = ARRED.EXCESSO.MAT
+CEILING.PRECISE = ARRED.EXCESSO.PRECISO
+COMBIN = COMBIN
+COMBINA = COMBIN.R
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DECIMAL
+DEGREES = GRAUS
+ECMA.CEILING = ARRED.EXCESSO.ECMA
+EVEN = PAR
+EXP = EXP
+FACT = FATORIAL
+FACTDOUBLE = FATDUPLO
+FLOOR.MATH = ARRED.DEFEITO.MAT
+FLOOR.PRECISE = ARRED.DEFEITO.PRECISO
+GCD = MDC
+INT = INT
+ISO.CEILING = ARRED.EXCESSO.ISO
+LCM = MMC
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MATRIZ.DETERM
+MINVERSE = MATRIZ.INVERSA
+MMULT = MATRIZ.MULT
+MOD = RESTO
+MROUND = MARRED
+MULTINOMIAL = POLINOMIAL
+MUNIT = UNIDM
+ODD = ÍMPAR
+PI = PI
+POWER = POTÊNCIA
+PRODUCT = PRODUTO
+QUOTIENT = QUOCIENTE
+RADIANS = RADIANOS
+RAND = ALEATÓRIO
+RANDBETWEEN = ALEATÓRIOENTRE
+ROMAN = ROMANO
+ROUND = ARRED
+ROUNDBAHTDOWN = ARREDOND.BAHT.BAIXO
+ROUNDBAHTUP = ARREDOND.BAHT.CIMA
+ROUNDDOWN = ARRED.PARA.BAIXO
+ROUNDUP = ARRED.PARA.CIMA
+SEC = SEC
+SECH = SECH
+SERIESSUM = SOMASÉRIE
+SIGN = SINAL
+SIN = SEN
+SINH = SENH
+SQRT = RAIZQ
+SQRTPI = RAIZPI
+SUBTOTAL = SUBTOTAL
+SUM = SOMA
+SUMIF = SOMA.SE
+SUMIFS = SOMA.SE.S
+SUMPRODUCT = SOMARPRODUTO
+SUMSQ = SOMARQUAD
+SUMX2MY2 = SOMAX2DY2
+SUMX2PY2 = SOMAX2SY2
+SUMXMY2 = SOMAXMY2
+TAN = TAN
+TANH = TANH
+TRUNC = TRUNCAR
+
+##
+## Funções estatísticas (Statistical Functions)
+##
+AVEDEV = DESV.MÉDIO
+AVERAGE = MÉDIA
+AVERAGEA = MÉDIAA
+AVERAGEIF = MÉDIA.SE
+AVERAGEIFS = MÉDIA.SE.S
+BETA.DIST = DIST.BETA
+BETA.INV = INV.BETA
+BINOM.DIST = DISTR.BINOM
+BINOM.DIST.RANGE = DIST.BINOM.INTERVALO
+BINOM.INV = INV.BINOM
+CHISQ.DIST = DIST.CHIQ
+CHISQ.DIST.RT = DIST.CHIQ.DIR
+CHISQ.INV = INV.CHIQ
+CHISQ.INV.RT = INV.CHIQ.DIR
+CHISQ.TEST = TESTE.CHIQ
+CONFIDENCE.NORM = INT.CONFIANÇA.NORM
+CONFIDENCE.T = INT.CONFIANÇA.T
+CORREL = CORREL
+COUNT = CONTAR
+COUNTA = CONTAR.VAL
+COUNTBLANK = CONTAR.VAZIO
+COUNTIF = CONTAR.SE
+COUNTIFS = CONTAR.SE.S
+COVARIANCE.P = COVARIÂNCIA.P
+COVARIANCE.S = COVARIÂNCIA.S
+DEVSQ = DESVQ
+EXPON.DIST = DIST.EXPON
+F.DIST = DIST.F
+F.DIST.RT = DIST.F.DIR
+F.INV = INV.F
+F.INV.RT = INV.F.DIR
+F.TEST = TESTE.F
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = PREVISÃO.ETS
+FORECAST.ETS.CONFINT = PREVISÃO.ETS.CONFINT
+FORECAST.ETS.SEASONALITY = PREVISÃO.ETS.SAZONALIDADE
+FORECAST.ETS.STAT = PREVISÃO.ETS.ESTATÍSTICA
+FORECAST.LINEAR = PREVISÃO.LINEAR
+FREQUENCY = FREQUÊNCIA
+GAMMA = GAMA
+GAMMA.DIST = DIST.GAMA
+GAMMA.INV = INV.GAMA
+GAMMALN = LNGAMA
+GAMMALN.PRECISE = LNGAMA.PRECISO
+GAUSS = GAUSS
+GEOMEAN = MÉDIA.GEOMÉTRICA
+GROWTH = CRESCIMENTO
+HARMEAN = MÉDIA.HARMÓNICA
+HYPGEOM.DIST = DIST.HIPGEOM
+INTERCEPT = INTERCETAR
+KURT = CURT
+LARGE = MAIOR
+LINEST = PROJ.LIN
+LOGEST = PROJ.LOG
+LOGNORM.DIST = DIST.NORMLOG
+LOGNORM.INV = INV.NORMALLOG
+MAX = MÁXIMO
+MAXA = MÁXIMOA
+MAXIFS = MÁXIMO.SE.S
+MEDIAN = MED
+MIN = MÍNIMO
+MINA = MÍNIMOA
+MINIFS = MÍNIMO.SE.S
+MODE.MULT = MODO.MÚLT
+MODE.SNGL = MODO.SIMPLES
+NEGBINOM.DIST = DIST.BINOM.NEG
+NORM.DIST = DIST.NORMAL
+NORM.INV = INV.NORMAL
+NORM.S.DIST = DIST.S.NORM
+NORM.S.INV = INV.S.NORM
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTIL.EXC
+PERCENTILE.INC = PERCENTIL.INC
+PERCENTRANK.EXC = ORDEM.PERCENTUAL.EXC
+PERCENTRANK.INC = ORDEM.PERCENTUAL.INC
+PERMUT = PERMUTAR
+PERMUTATIONA = PERMUTAR.R
+PHI = PHI
+POISSON.DIST = DIST.POISSON
+PROB = PROB
+QUARTILE.EXC = QUARTIL.EXC
+QUARTILE.INC = QUARTIL.INC
+RANK.AVG = ORDEM.MÉD
+RANK.EQ = ORDEM.EQ
+RSQ = RQUAD
+SKEW = DISTORÇÃO
+SKEW.P = DISTORÇÃO.P
+SLOPE = DECLIVE
+SMALL = MENOR
+STANDARDIZE = NORMALIZAR
+STDEV.P = DESVPAD.P
+STDEV.S = DESVPAD.S
+STDEVA = DESVPADA
+STDEVPA = DESVPADPA
+STEYX = EPADYX
+T.DIST = DIST.T
+T.DIST.2T = DIST.T.2C
+T.DIST.RT = DIST.T.DIR
+T.INV = INV.T
+T.INV.2T = INV.T.2C
+T.TEST = TESTE.T
+TREND = TENDÊNCIA
+TRIMMEAN = MÉDIA.INTERNA
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = DIST.WEIBULL
+Z.TEST = TESTE.Z
+
+##
+## Funções de texto (Text Functions)
+##
+BAHTTEXT = TEXTO.BAHT
+CHAR = CARÁT
+CLEAN = LIMPARB
+CODE = CÓDIGO
+CONCAT = CONCAT
+DOLLAR = MOEDA
+EXACT = EXATO
+FIND = LOCALIZAR
+FIXED = FIXA
+ISTHAIDIGIT = É.DÍGITO.TAILANDÊS
+LEFT = ESQUERDA
+LEN = NÚM.CARAT
+LOWER = MINÚSCULAS
+MID = SEG.TEXTO
+NUMBERSTRING = NÚMERO.CADEIA
+NUMBERVALUE = VALOR.NÚMERO
+PHONETIC = FONÉTICA
+PROPER = INICIAL.MAIÚSCULA
+REPLACE = SUBSTITUIR
+REPT = REPETIR
+RIGHT = DIREITA
+SEARCH = PROCURAR
+SUBSTITUTE = SUBST
+T = T
+TEXT = TEXTO
+TEXTJOIN = UNIRTEXTO
+THAIDIGIT = DÍGITO.TAILANDÊS
+THAINUMSOUND = SOM.NÚM.TAILANDÊS
+THAINUMSTRING = CADEIA.NÚM.TAILANDÊS
+THAISTRINGLENGTH = COMP.CADEIA.TAILANDÊS
+TRIM = COMPACTAR
+UNICHAR = UNICARÁT
+UNICODE = UNICODE
+UPPER = MAIÚSCULAS
+VALUE = VALOR
+
+##
+## Funções da Web (Web Functions)
+##
+ENCODEURL = CODIFICAÇÃOURL
+FILTERXML = FILTRARXML
+WEBSERVICE = SERVIÇOWEB
+
+##
+## Funções de compatibilidade (Compatibility Functions)
+##
+BETADIST = DISTBETA
+BETAINV = BETA.ACUM.INV
+BINOMDIST = DISTRBINOM
+CEILING = ARRED.EXCESSO
+CHIDIST = DIST.CHI
+CHIINV = INV.CHI
+CHITEST = TESTE.CHI
+CONCATENATE = CONCATENAR
+CONFIDENCE = INT.CONFIANÇA
+COVAR = COVAR
+CRITBINOM = CRIT.BINOM
+EXPONDIST = DISTEXPON
+FDIST = DISTF
+FINV = INVF
+FLOOR = ARRED.DEFEITO
+FORECAST = PREVISÃO
+FTEST = TESTEF
+GAMMADIST = DISTGAMA
+GAMMAINV = INVGAMA
+HYPGEOMDIST = DIST.HIPERGEOM
+LOGINV = INVLOG
+LOGNORMDIST = DIST.NORMALLOG
+MODE = MODA
+NEGBINOMDIST = DIST.BIN.NEG
+NORMDIST = DIST.NORM
+NORMINV = INV.NORM
+NORMSDIST = DIST.NORMP
+NORMSINV = INV.NORMP
+PERCENTILE = PERCENTIL
+PERCENTRANK = ORDEM.PERCENTUAL
+POISSON = POISSON
+QUARTILE = QUARTIL
+RANK = ORDEM
+STDEV = DESVPAD
+STDEVP = DESVPADP
+TDIST = DISTT
+TINV = INVT
+TTEST = TESTET
+VAR = VAR
+VARP = VARP
+WEIBULL = WEIBULL
+ZTEST = TESTEZ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config
new file mode 100644
index 0000000..2a5a0db
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## русский язык (Russian)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #ПУСТО!
+DIV0 = #ДЕЛ/0!
+VALUE = #ЗНАЧ!
+REF = #ССЫЛКА!
+NAME = #ИМЯ?
+NUM = #ЧИСЛО!
+NA = #Н/Д
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions
new file mode 100644
index 0000000..9f05d5a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions
@@ -0,0 +1,555 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## русский язык (Russian)
+##
+############################################################
+
+
+##
+## Функции кубов (Cube Functions)
+##
+CUBEKPIMEMBER = КУБЭЛЕМЕНТКИП
+CUBEMEMBER = КУБЭЛЕМЕНТ
+CUBEMEMBERPROPERTY = КУБСВОЙСТВОЭЛЕМЕНТА
+CUBERANKEDMEMBER = КУБПОРЭЛЕМЕНТ
+CUBESET = КУБМНОЖ
+CUBESETCOUNT = КУБЧИСЛОЭЛМНОЖ
+CUBEVALUE = КУБЗНАЧЕНИЕ
+
+##
+## Функции для работы с базами данных (Database Functions)
+##
+DAVERAGE = ДСРЗНАЧ
+DCOUNT = БСЧЁТ
+DCOUNTA = БСЧЁТА
+DGET = БИЗВЛЕЧЬ
+DMAX = ДМАКС
+DMIN = ДМИН
+DPRODUCT = БДПРОИЗВЕД
+DSTDEV = ДСТАНДОТКЛ
+DSTDEVP = ДСТАНДОТКЛП
+DSUM = БДСУММ
+DVAR = БДДИСП
+DVARP = БДДИСПП
+
+##
+## Функции даты и времени (Date & Time Functions)
+##
+DATE = ДАТА
+DATEDIF = РАЗНДАТ
+DATESTRING = СТРОКАДАННЫХ
+DATEVALUE = ДАТАЗНАЧ
+DAY = ДЕНЬ
+DAYS = ДНИ
+DAYS360 = ДНЕЙ360
+EDATE = ДАТАМЕС
+EOMONTH = КОНМЕСЯЦА
+HOUR = ЧАС
+ISOWEEKNUM = НОМНЕДЕЛИ.ISO
+MINUTE = МИНУТЫ
+MONTH = МЕСЯЦ
+NETWORKDAYS = ЧИСТРАБДНИ
+NETWORKDAYS.INTL = ЧИСТРАБДНИ.МЕЖД
+NOW = ТДАТА
+SECOND = СЕКУНДЫ
+THAIDAYOFWEEK = ТАЙДЕНЬНЕД
+THAIMONTHOFYEAR = ТАЙМЕСЯЦ
+THAIYEAR = ТАЙГОД
+TIME = ВРЕМЯ
+TIMEVALUE = ВРЕМЗНАЧ
+TODAY = СЕГОДНЯ
+WEEKDAY = ДЕНЬНЕД
+WEEKNUM = НОМНЕДЕЛИ
+WORKDAY = РАБДЕНЬ
+WORKDAY.INTL = РАБДЕНЬ.МЕЖД
+YEAR = ГОД
+YEARFRAC = ДОЛЯГОДА
+
+##
+## Инженерные функции (Engineering Functions)
+##
+BESSELI = БЕССЕЛЬ.I
+BESSELJ = БЕССЕЛЬ.J
+BESSELK = БЕССЕЛЬ.K
+BESSELY = БЕССЕЛЬ.Y
+BIN2DEC = ДВ.В.ДЕС
+BIN2HEX = ДВ.В.ШЕСТН
+BIN2OCT = ДВ.В.ВОСЬМ
+BITAND = БИТ.И
+BITLSHIFT = БИТ.СДВИГЛ
+BITOR = БИТ.ИЛИ
+BITRSHIFT = БИТ.СДВИГП
+BITXOR = БИТ.ИСКЛИЛИ
+COMPLEX = КОМПЛЕКСН
+CONVERT = ПРЕОБР
+DEC2BIN = ДЕС.В.ДВ
+DEC2HEX = ДЕС.В.ШЕСТН
+DEC2OCT = ДЕС.В.ВОСЬМ
+DELTA = ДЕЛЬТА
+ERF = ФОШ
+ERF.PRECISE = ФОШ.ТОЧН
+ERFC = ДФОШ
+ERFC.PRECISE = ДФОШ.ТОЧН
+GESTEP = ПОРОГ
+HEX2BIN = ШЕСТН.В.ДВ
+HEX2DEC = ШЕСТН.В.ДЕС
+HEX2OCT = ШЕСТН.В.ВОСЬМ
+IMABS = МНИМ.ABS
+IMAGINARY = МНИМ.ЧАСТЬ
+IMARGUMENT = МНИМ.АРГУМЕНТ
+IMCONJUGATE = МНИМ.СОПРЯЖ
+IMCOS = МНИМ.COS
+IMCOSH = МНИМ.COSH
+IMCOT = МНИМ.COT
+IMCSC = МНИМ.CSC
+IMCSCH = МНИМ.CSCH
+IMDIV = МНИМ.ДЕЛ
+IMEXP = МНИМ.EXP
+IMLN = МНИМ.LN
+IMLOG10 = МНИМ.LOG10
+IMLOG2 = МНИМ.LOG2
+IMPOWER = МНИМ.СТЕПЕНЬ
+IMPRODUCT = МНИМ.ПРОИЗВЕД
+IMREAL = МНИМ.ВЕЩ
+IMSEC = МНИМ.SEC
+IMSECH = МНИМ.SECH
+IMSIN = МНИМ.SIN
+IMSINH = МНИМ.SINH
+IMSQRT = МНИМ.КОРЕНЬ
+IMSUB = МНИМ.РАЗН
+IMSUM = МНИМ.СУММ
+IMTAN = МНИМ.TAN
+OCT2BIN = ВОСЬМ.В.ДВ
+OCT2DEC = ВОСЬМ.В.ДЕС
+OCT2HEX = ВОСЬМ.В.ШЕСТН
+
+##
+## Финансовые функции (Financial Functions)
+##
+ACCRINT = НАКОПДОХОД
+ACCRINTM = НАКОПДОХОДПОГАШ
+AMORDEGRC = АМОРУМ
+AMORLINC = АМОРУВ
+COUPDAYBS = ДНЕЙКУПОНДО
+COUPDAYS = ДНЕЙКУПОН
+COUPDAYSNC = ДНЕЙКУПОНПОСЛЕ
+COUPNCD = ДАТАКУПОНПОСЛЕ
+COUPNUM = ЧИСЛКУПОН
+COUPPCD = ДАТАКУПОНДО
+CUMIPMT = ОБЩПЛАТ
+CUMPRINC = ОБЩДОХОД
+DB = ФУО
+DDB = ДДОБ
+DISC = СКИДКА
+DOLLARDE = РУБЛЬ.ДЕС
+DOLLARFR = РУБЛЬ.ДРОБЬ
+DURATION = ДЛИТ
+EFFECT = ЭФФЕКТ
+FV = БС
+FVSCHEDULE = БЗРАСПИС
+INTRATE = ИНОРМА
+IPMT = ПРПЛТ
+IRR = ВСД
+ISPMT = ПРОЦПЛАТ
+MDURATION = МДЛИТ
+MIRR = МВСД
+NOMINAL = НОМИНАЛ
+NPER = КПЕР
+NPV = ЧПС
+ODDFPRICE = ЦЕНАПЕРВНЕРЕГ
+ODDFYIELD = ДОХОДПЕРВНЕРЕГ
+ODDLPRICE = ЦЕНАПОСЛНЕРЕГ
+ODDLYIELD = ДОХОДПОСЛНЕРЕГ
+PDURATION = ПДЛИТ
+PMT = ПЛТ
+PPMT = ОСПЛТ
+PRICE = ЦЕНА
+PRICEDISC = ЦЕНАСКИДКА
+PRICEMAT = ЦЕНАПОГАШ
+PV = ПС
+RATE = СТАВКА
+RECEIVED = ПОЛУЧЕНО
+RRI = ЭКВ.СТАВКА
+SLN = АПЛ
+SYD = АСЧ
+TBILLEQ = РАВНОКЧЕК
+TBILLPRICE = ЦЕНАКЧЕК
+TBILLYIELD = ДОХОДКЧЕК
+USDOLLAR = ДОЛЛСША
+VDB = ПУО
+XIRR = ЧИСТВНДОХ
+XNPV = ЧИСТНЗ
+YIELD = ДОХОД
+YIELDDISC = ДОХОДСКИДКА
+YIELDMAT = ДОХОДПОГАШ
+
+##
+## Информационные функции (Information Functions)
+##
+CELL = ЯЧЕЙКА
+ERROR.TYPE = ТИП.ОШИБКИ
+INFO = ИНФОРМ
+ISBLANK = ЕПУСТО
+ISERR = ЕОШ
+ISERROR = ЕОШИБКА
+ISEVEN = ЕЧЁТН
+ISFORMULA = ЕФОРМУЛА
+ISLOGICAL = ЕЛОГИЧ
+ISNA = ЕНД
+ISNONTEXT = ЕНЕТЕКСТ
+ISNUMBER = ЕЧИСЛО
+ISODD = ЕНЕЧЁТ
+ISREF = ЕССЫЛКА
+ISTEXT = ЕТЕКСТ
+N = Ч
+NA = НД
+SHEET = ЛИСТ
+SHEETS = ЛИСТЫ
+TYPE = ТИП
+
+##
+## Логические функции (Logical Functions)
+##
+AND = И
+FALSE = ЛОЖЬ
+IF = ЕСЛИ
+IFERROR = ЕСЛИОШИБКА
+IFNA = ЕСНД
+IFS = УСЛОВИЯ
+NOT = НЕ
+OR = ИЛИ
+SWITCH = ПЕРЕКЛЮЧ
+TRUE = ИСТИНА
+XOR = ИСКЛИЛИ
+
+##
+## Функции ссылки и поиска (Lookup & Reference Functions)
+##
+ADDRESS = АДРЕС
+AREAS = ОБЛАСТИ
+CHOOSE = ВЫБОР
+COLUMN = СТОЛБЕЦ
+COLUMNS = ЧИСЛСТОЛБ
+FILTER = ФИЛЬТР
+FORMULATEXT = Ф.ТЕКСТ
+GETPIVOTDATA = ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ
+HLOOKUP = ГПР
+HYPERLINK = ГИПЕРССЫЛКА
+INDEX = ИНДЕКС
+INDIRECT = ДВССЫЛ
+LOOKUP = ПРОСМОТР
+MATCH = ПОИСКПОЗ
+OFFSET = СМЕЩ
+ROW = СТРОКА
+ROWS = ЧСТРОК
+RTD = ДРВ
+SORT = СОРТ
+SORTBY = СОРТПО
+TRANSPOSE = ТРАНСП
+UNIQUE = УНИК
+VLOOKUP = ВПР
+XLOOKUP = ПРОСМОТРX
+XMATCH = ПОИСКПОЗX
+
+##
+## Математические и тригонометрические функции (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = АГРЕГАТ
+ARABIC = АРАБСКОЕ
+ASIN = ASIN
+ASINH = ASINH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = ОСНОВАНИЕ
+CEILING.MATH = ОКРВВЕРХ.МАТ
+CEILING.PRECISE = ОКРВВЕРХ.ТОЧН
+COMBIN = ЧИСЛКОМБ
+COMBINA = ЧИСЛКОМБА
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = ДЕС
+DEGREES = ГРАДУСЫ
+ECMA.CEILING = ECMA.ОКРВВЕРХ
+EVEN = ЧЁТН
+EXP = EXP
+FACT = ФАКТР
+FACTDOUBLE = ДВФАКТР
+FLOOR.MATH = ОКРВНИЗ.МАТ
+FLOOR.PRECISE = ОКРВНИЗ.ТОЧН
+GCD = НОД
+INT = ЦЕЛОЕ
+ISO.CEILING = ISO.ОКРВВЕРХ
+LCM = НОК
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = МОПРЕД
+MINVERSE = МОБР
+MMULT = МУМНОЖ
+MOD = ОСТАТ
+MROUND = ОКРУГЛТ
+MULTINOMIAL = МУЛЬТИНОМ
+MUNIT = МЕДИН
+ODD = НЕЧЁТ
+PI = ПИ
+POWER = СТЕПЕНЬ
+PRODUCT = ПРОИЗВЕД
+QUOTIENT = ЧАСТНОЕ
+RADIANS = РАДИАНЫ
+RAND = СЛЧИС
+RANDARRAY = СЛУЧМАССИВ
+RANDBETWEEN = СЛУЧМЕЖДУ
+ROMAN = РИМСКОЕ
+ROUND = ОКРУГЛ
+ROUNDBAHTDOWN = ОКРУГЛБАТВНИЗ
+ROUNDBAHTUP = ОКРУГЛБАТВВЕРХ
+ROUNDDOWN = ОКРУГЛВНИЗ
+ROUNDUP = ОКРУГЛВВЕРХ
+SEC = SEC
+SECH = SECH
+SERIESSUM = РЯД.СУММ
+SEQUENCE = ПОСЛЕДОВ
+SIGN = ЗНАК
+SIN = SIN
+SINH = SINH
+SQRT = КОРЕНЬ
+SQRTPI = КОРЕНЬПИ
+SUBTOTAL = ПРОМЕЖУТОЧНЫЕ.ИТОГИ
+SUM = СУММ
+SUMIF = СУММЕСЛИ
+SUMIFS = СУММЕСЛИМН
+SUMPRODUCT = СУММПРОИЗВ
+SUMSQ = СУММКВ
+SUMX2MY2 = СУММРАЗНКВ
+SUMX2PY2 = СУММСУММКВ
+SUMXMY2 = СУММКВРАЗН
+TAN = TAN
+TANH = TANH
+TRUNC = ОТБР
+
+##
+## Статистические функции (Statistical Functions)
+##
+AVEDEV = СРОТКЛ
+AVERAGE = СРЗНАЧ
+AVERAGEA = СРЗНАЧА
+AVERAGEIF = СРЗНАЧЕСЛИ
+AVERAGEIFS = СРЗНАЧЕСЛИМН
+BETA.DIST = БЕТА.РАСП
+BETA.INV = БЕТА.ОБР
+BINOM.DIST = БИНОМ.РАСП
+BINOM.DIST.RANGE = БИНОМ.РАСП.ДИАП
+BINOM.INV = БИНОМ.ОБР
+CHISQ.DIST = ХИ2.РАСП
+CHISQ.DIST.RT = ХИ2.РАСП.ПХ
+CHISQ.INV = ХИ2.ОБР
+CHISQ.INV.RT = ХИ2.ОБР.ПХ
+CHISQ.TEST = ХИ2.ТЕСТ
+CONFIDENCE.NORM = ДОВЕРИТ.НОРМ
+CONFIDENCE.T = ДОВЕРИТ.СТЬЮДЕНТ
+CORREL = КОРРЕЛ
+COUNT = СЧЁТ
+COUNTA = СЧЁТЗ
+COUNTBLANK = СЧИТАТЬПУСТОТЫ
+COUNTIF = СЧЁТЕСЛИ
+COUNTIFS = СЧЁТЕСЛИМН
+COVARIANCE.P = КОВАРИАЦИЯ.Г
+COVARIANCE.S = КОВАРИАЦИЯ.В
+DEVSQ = КВАДРОТКЛ
+EXPON.DIST = ЭКСП.РАСП
+F.DIST = F.РАСП
+F.DIST.RT = F.РАСП.ПХ
+F.INV = F.ОБР
+F.INV.RT = F.ОБР.ПХ
+F.TEST = F.ТЕСТ
+FISHER = ФИШЕР
+FISHERINV = ФИШЕРОБР
+FORECAST.ETS = ПРЕДСКАЗ.ETS
+FORECAST.ETS.CONFINT = ПРЕДСКАЗ.ЕTS.ДОВИНТЕРВАЛ
+FORECAST.ETS.SEASONALITY = ПРЕДСКАЗ.ETS.СЕЗОННОСТЬ
+FORECAST.ETS.STAT = ПРЕДСКАЗ.ETS.СТАТ
+FORECAST.LINEAR = ПРЕДСКАЗ.ЛИНЕЙН
+FREQUENCY = ЧАСТОТА
+GAMMA = ГАММА
+GAMMA.DIST = ГАММА.РАСП
+GAMMA.INV = ГАММА.ОБР
+GAMMALN = ГАММАНЛОГ
+GAMMALN.PRECISE = ГАММАНЛОГ.ТОЧН
+GAUSS = ГАУСС
+GEOMEAN = СРГЕОМ
+GROWTH = РОСТ
+HARMEAN = СРГАРМ
+HYPGEOM.DIST = ГИПЕРГЕОМ.РАСП
+INTERCEPT = ОТРЕЗОК
+KURT = ЭКСЦЕСС
+LARGE = НАИБОЛЬШИЙ
+LINEST = ЛИНЕЙН
+LOGEST = ЛГРФПРИБЛ
+LOGNORM.DIST = ЛОГНОРМ.РАСП
+LOGNORM.INV = ЛОГНОРМ.ОБР
+MAX = МАКС
+MAXA = МАКСА
+MAXIFS = МАКСЕСЛИ
+MEDIAN = МЕДИАНА
+MIN = МИН
+MINA = МИНА
+MINIFS = МИНЕСЛИ
+MODE.MULT = МОДА.НСК
+MODE.SNGL = МОДА.ОДН
+NEGBINOM.DIST = ОТРБИНОМ.РАСП
+NORM.DIST = НОРМ.РАСП
+NORM.INV = НОРМ.ОБР
+NORM.S.DIST = НОРМ.СТ.РАСП
+NORM.S.INV = НОРМ.СТ.ОБР
+PEARSON = PEARSON
+PERCENTILE.EXC = ПРОЦЕНТИЛЬ.ИСКЛ
+PERCENTILE.INC = ПРОЦЕНТИЛЬ.ВКЛ
+PERCENTRANK.EXC = ПРОЦЕНТРАНГ.ИСКЛ
+PERCENTRANK.INC = ПРОЦЕНТРАНГ.ВКЛ
+PERMUT = ПЕРЕСТ
+PERMUTATIONA = ПЕРЕСТА
+PHI = ФИ
+POISSON.DIST = ПУАССОН.РАСП
+PROB = ВЕРОЯТНОСТЬ
+QUARTILE.EXC = КВАРТИЛЬ.ИСКЛ
+QUARTILE.INC = КВАРТИЛЬ.ВКЛ
+RANK.AVG = РАНГ.СР
+RANK.EQ = РАНГ.РВ
+RSQ = КВПИРСОН
+SKEW = СКОС
+SKEW.P = СКОС.Г
+SLOPE = НАКЛОН
+SMALL = НАИМЕНЬШИЙ
+STANDARDIZE = НОРМАЛИЗАЦИЯ
+STDEV.P = СТАНДОТКЛОН.Г
+STDEV.S = СТАНДОТКЛОН.В
+STDEVA = СТАНДОТКЛОНА
+STDEVPA = СТАНДОТКЛОНПА
+STEYX = СТОШYX
+T.DIST = СТЬЮДЕНТ.РАСП
+T.DIST.2T = СТЬЮДЕНТ.РАСП.2Х
+T.DIST.RT = СТЬЮДЕНТ.РАСП.ПХ
+T.INV = СТЬЮДЕНТ.ОБР
+T.INV.2T = СТЬЮДЕНТ.ОБР.2Х
+T.TEST = СТЬЮДЕНТ.ТЕСТ
+TREND = ТЕНДЕНЦИЯ
+TRIMMEAN = УРЕЗСРЕДНЕЕ
+VAR.P = ДИСП.Г
+VAR.S = ДИСП.В
+VARA = ДИСПА
+VARPA = ДИСПРА
+WEIBULL.DIST = ВЕЙБУЛЛ.РАСП
+Z.TEST = Z.ТЕСТ
+
+##
+## Текстовые функции (Text Functions)
+##
+ARRAYTOTEXT = МАССИВВТЕКСТ
+BAHTTEXT = БАТТЕКСТ
+CHAR = СИМВОЛ
+CLEAN = ПЕЧСИМВ
+CODE = КОДСИМВ
+CONCAT = СЦЕП
+DBCS = БДЦС
+DOLLAR = РУБЛЬ
+EXACT = СОВПАД
+FIND = НАЙТИ
+FINDB = НАЙТИБ
+FIXED = ФИКСИРОВАННЫЙ
+ISTHAIDIGIT = ЕТАЙЦИФРЫ
+LEFT = ЛЕВСИМВ
+LEFTB = ЛЕВБ
+LEN = ДЛСТР
+LENB = ДЛИНБ
+LOWER = СТРОЧН
+MID = ПСТР
+MIDB = ПСТРБ
+NUMBERSTRING = СТРОКАЧИСЕЛ
+NUMBERVALUE = ЧЗНАЧ
+PROPER = ПРОПНАЧ
+REPLACE = ЗАМЕНИТЬ
+REPLACEB = ЗАМЕНИТЬБ
+REPT = ПОВТОР
+RIGHT = ПРАВСИМВ
+RIGHTB = ПРАВБ
+SEARCH = ПОИСК
+SEARCHB = ПОИСКБ
+SUBSTITUTE = ПОДСТАВИТЬ
+T = Т
+TEXT = ТЕКСТ
+TEXTJOIN = ОБЪЕДИНИТЬ
+THAIDIGIT = ТАЙЦИФРА
+THAINUMSOUND = ТАЙЧИСЛОВЗВУК
+THAINUMSTRING = ТАЙЧИСЛОВСТРОКУ
+THAISTRINGLENGTH = ТАЙДЛИНАСТРОКИ
+TRIM = СЖПРОБЕЛЫ
+UNICHAR = ЮНИСИМВ
+UNICODE = UNICODE
+UPPER = ПРОПИСН
+VALUE = ЗНАЧЕН
+VALUETOTEXT = ЗНАЧЕНИЕВТЕКСТ
+
+##
+## Веб-функции (Web Functions)
+##
+ENCODEURL = КОДИР.URL
+FILTERXML = ФИЛЬТР.XML
+WEBSERVICE = ВЕБСЛУЖБА
+
+##
+## Функции совместимости (Compatibility Functions)
+##
+BETADIST = БЕТАРАСП
+BETAINV = БЕТАОБР
+BINOMDIST = БИНОМРАСП
+CEILING = ОКРВВЕРХ
+CHIDIST = ХИ2РАСП
+CHIINV = ХИ2ОБР
+CHITEST = ХИ2ТЕСТ
+CONCATENATE = СЦЕПИТЬ
+CONFIDENCE = ДОВЕРИТ
+COVAR = КОВАР
+CRITBINOM = КРИТБИНОМ
+EXPONDIST = ЭКСПРАСП
+FDIST = FРАСП
+FINV = FРАСПОБР
+FLOOR = ОКРВНИЗ
+FORECAST = ПРЕДСКАЗ
+FTEST = ФТЕСТ
+GAMMADIST = ГАММАРАСП
+GAMMAINV = ГАММАОБР
+HYPGEOMDIST = ГИПЕРГЕОМЕТ
+LOGINV = ЛОГНОРМОБР
+LOGNORMDIST = ЛОГНОРМРАСП
+MODE = МОДА
+NEGBINOMDIST = ОТРБИНОМРАСП
+NORMDIST = НОРМРАСП
+NORMINV = НОРМОБР
+NORMSDIST = НОРМСТРАСП
+NORMSINV = НОРМСТОБР
+PERCENTILE = ПЕРСЕНТИЛЬ
+PERCENTRANK = ПРОЦЕНТРАНГ
+POISSON = ПУАССОН
+QUARTILE = КВАРТИЛЬ
+RANK = РАНГ
+STDEV = СТАНДОТКЛОН
+STDEVP = СТАНДОТКЛОНП
+TDIST = СТЬЮДРАСП
+TINV = СТЬЮДРАСПОБР
+TTEST = ТТЕСТ
+VAR = ДИСП
+VARP = ДИСПР
+WEIBULL = ВЕЙБУЛЛ
+ZTEST = ZТЕСТ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config
new file mode 100644
index 0000000..c7440f7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Svenska (Swedish)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #SKÄRNING!
+DIV0 = #DIVISION/0!
+VALUE = #VÄRDEFEL!
+REF = #REFERENS!
+NAME = #NAMN?
+NUM = #OGILTIGT!
+NA = #SAKNAS!
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions
new file mode 100644
index 0000000..491ecfb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions
@@ -0,0 +1,533 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Svenska (Swedish)
+##
+############################################################
+
+
+##
+## Kubfunktioner (Cube Functions)
+##
+CUBEKPIMEMBER = KUBKPIMEDLEM
+CUBEMEMBER = KUBMEDLEM
+CUBEMEMBERPROPERTY = KUBMEDLEMSEGENSKAP
+CUBERANKEDMEMBER = KUBRANGORDNADMEDLEM
+CUBESET = KUBUPPSÄTTNING
+CUBESETCOUNT = KUBUPPSÄTTNINGANTAL
+CUBEVALUE = KUBVÄRDE
+
+##
+## Databasfunktioner (Database Functions)
+##
+DAVERAGE = DMEDEL
+DCOUNT = DANTAL
+DCOUNTA = DANTALV
+DGET = DHÄMTA
+DMAX = DMAX
+DMIN = DMIN
+DPRODUCT = DPRODUKT
+DSTDEV = DSTDAV
+DSTDEVP = DSTDAVP
+DSUM = DSUMMA
+DVAR = DVARIANS
+DVARP = DVARIANSP
+
+##
+## Tid- och datumfunktioner (Date & Time Functions)
+##
+DATE = DATUM
+DATEVALUE = DATUMVÄRDE
+DAY = DAG
+DAYS = DAGAR
+DAYS360 = DAGAR360
+EDATE = EDATUM
+EOMONTH = SLUTMÅNAD
+HOUR = TIMME
+ISOWEEKNUM = ISOVECKONR
+MINUTE = MINUT
+MONTH = MÅNAD
+NETWORKDAYS = NETTOARBETSDAGAR
+NETWORKDAYS.INTL = NETTOARBETSDAGAR.INT
+NOW = NU
+SECOND = SEKUND
+THAIDAYOFWEEK = THAIVECKODAG
+THAIMONTHOFYEAR = THAIMÅNAD
+THAIYEAR = THAIÅR
+TIME = KLOCKSLAG
+TIMEVALUE = TIDVÄRDE
+TODAY = IDAG
+WEEKDAY = VECKODAG
+WEEKNUM = VECKONR
+WORKDAY = ARBETSDAGAR
+WORKDAY.INTL = ARBETSDAGAR.INT
+YEAR = ÅR
+YEARFRAC = ÅRDEL
+
+##
+## Tekniska funktioner (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BIN.TILL.DEC
+BIN2HEX = BIN.TILL.HEX
+BIN2OCT = BIN.TILL.OKT
+BITAND = BITOCH
+BITLSHIFT = BITVSKIFT
+BITOR = BITELLER
+BITRSHIFT = BITHSKIFT
+BITXOR = BITXELLER
+COMPLEX = KOMPLEX
+CONVERT = KONVERTERA
+DEC2BIN = DEC.TILL.BIN
+DEC2HEX = DEC.TILL.HEX
+DEC2OCT = DEC.TILL.OKT
+DELTA = DELTA
+ERF = FELF
+ERF.PRECISE = FELF.EXAKT
+ERFC = FELFK
+ERFC.PRECISE = FELFK.EXAKT
+GESTEP = SLSTEG
+HEX2BIN = HEX.TILL.BIN
+HEX2DEC = HEX.TILL.DEC
+HEX2OCT = HEX.TILL.OKT
+IMABS = IMABS
+IMAGINARY = IMAGINÄR
+IMARGUMENT = IMARGUMENT
+IMCONJUGATE = IMKONJUGAT
+IMCOS = IMCOS
+IMCOSH = IMCOSH
+IMCOT = IMCOT
+IMCSC = IMCSC
+IMCSCH = IMCSCH
+IMDIV = IMDIV
+IMEXP = IMEUPPHÖJT
+IMLN = IMLN
+IMLOG10 = IMLOG10
+IMLOG2 = IMLOG2
+IMPOWER = IMUPPHÖJT
+IMPRODUCT = IMPRODUKT
+IMREAL = IMREAL
+IMSEC = IMSEK
+IMSECH = IMSEKH
+IMSIN = IMSIN
+IMSINH = IMSINH
+IMSQRT = IMROT
+IMSUB = IMDIFF
+IMSUM = IMSUM
+IMTAN = IMTAN
+OCT2BIN = OKT.TILL.BIN
+OCT2DEC = OKT.TILL.DEC
+OCT2HEX = OKT.TILL.HEX
+
+##
+## Finansiella funktioner (Financial Functions)
+##
+ACCRINT = UPPLRÄNTA
+ACCRINTM = UPPLOBLRÄNTA
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = KUPDAGBB
+COUPDAYS = KUPDAGB
+COUPDAYSNC = KUPDAGNK
+COUPNCD = KUPNKD
+COUPNUM = KUPANT
+COUPPCD = KUPFKD
+CUMIPMT = KUMRÄNTA
+CUMPRINC = KUMPRIS
+DB = DB
+DDB = DEGAVSKR
+DISC = DISK
+DOLLARDE = DECTAL
+DOLLARFR = BRÅK
+DURATION = LÖPTID
+EFFECT = EFFRÄNTA
+FV = SLUTVÄRDE
+FVSCHEDULE = FÖRRÄNTNING
+INTRATE = ÅRSRÄNTA
+IPMT = RBETALNING
+IRR = IR
+ISPMT = RALÅN
+MDURATION = MLÖPTID
+MIRR = MODIR
+NOMINAL = NOMRÄNTA
+NPER = PERIODER
+NPV = NETNUVÄRDE
+ODDFPRICE = UDDAFPRIS
+ODDFYIELD = UDDAFAVKASTNING
+ODDLPRICE = UDDASPRIS
+ODDLYIELD = UDDASAVKASTNING
+PDURATION = PLÖPTID
+PMT = BETALNING
+PPMT = AMORT
+PRICE = PRIS
+PRICEDISC = PRISDISK
+PRICEMAT = PRISFÖRF
+PV = NUVÄRDE
+RATE = RÄNTA
+RECEIVED = BELOPP
+RRI = AVKPÅINVEST
+SLN = LINAVSKR
+SYD = ÅRSAVSKR
+TBILLEQ = SSVXEKV
+TBILLPRICE = SSVXPRIS
+TBILLYIELD = SSVXRÄNTA
+VDB = VDEGRAVSKR
+XIRR = XIRR
+XNPV = XNUVÄRDE
+YIELD = NOMAVK
+YIELDDISC = NOMAVKDISK
+YIELDMAT = NOMAVKFÖRF
+
+##
+## Informationsfunktioner (Information Functions)
+##
+CELL = CELL
+ERROR.TYPE = FEL.TYP
+INFO = INFO
+ISBLANK = ÄRTOM
+ISERR = ÄRF
+ISERROR = ÄRFEL
+ISEVEN = ÄRJÄMN
+ISFORMULA = ÄRFORMEL
+ISLOGICAL = ÄRLOGISK
+ISNA = ÄRSAKNAD
+ISNONTEXT = ÄREJTEXT
+ISNUMBER = ÄRTAL
+ISODD = ÄRUDDA
+ISREF = ÄRREF
+ISTEXT = ÄRTEXT
+N = N
+NA = SAKNAS
+SHEET = BLAD
+SHEETS = ANTALBLAD
+TYPE = VÄRDETYP
+
+##
+## Logiska funktioner (Logical Functions)
+##
+AND = OCH
+FALSE = FALSKT
+IF = OM
+IFERROR = OMFEL
+IFNA = OMSAKNAS
+IFS = IFS
+NOT = ICKE
+OR = ELLER
+SWITCH = VÄXLA
+TRUE = SANT
+XOR = XELLER
+
+##
+## Sök- och referensfunktioner (Lookup & Reference Functions)
+##
+ADDRESS = ADRESS
+AREAS = OMRÅDEN
+CHOOSE = VÄLJ
+COLUMN = KOLUMN
+COLUMNS = KOLUMNER
+FORMULATEXT = FORMELTEXT
+GETPIVOTDATA = HÄMTA.PIVOTDATA
+HLOOKUP = LETAKOLUMN
+HYPERLINK = HYPERLÄNK
+INDEX = INDEX
+INDIRECT = INDIREKT
+LOOKUP = LETAUPP
+MATCH = PASSA
+OFFSET = FÖRSKJUTNING
+ROW = RAD
+ROWS = RADER
+RTD = RTD
+TRANSPOSE = TRANSPONERA
+VLOOKUP = LETARAD
+*RC = RK
+
+##
+## Matematiska och trigonometriska funktioner (Math & Trig Functions)
+##
+ABS = ABS
+ACOS = ARCCOS
+ACOSH = ARCCOSH
+ACOT = ARCCOT
+ACOTH = ARCCOTH
+AGGREGATE = MÄNGD
+ARABIC = ARABISKA
+ASIN = ARCSIN
+ASINH = ARCSINH
+ATAN = ARCTAN
+ATAN2 = ARCTAN2
+ATANH = ARCTANH
+BASE = BAS
+CEILING.MATH = RUNDA.UPP.MATEMATISKT
+CEILING.PRECISE = RUNDA.UPP.EXAKT
+COMBIN = KOMBIN
+COMBINA = KOMBINA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = DECIMAL
+DEGREES = GRADER
+ECMA.CEILING = ECMA.RUNDA.UPP
+EVEN = JÄMN
+EXP = EXP
+FACT = FAKULTET
+FACTDOUBLE = DUBBELFAKULTET
+FLOOR.MATH = RUNDA.NER.MATEMATISKT
+FLOOR.PRECISE = RUNDA.NER.EXAKT
+GCD = SGD
+INT = HELTAL
+ISO.CEILING = ISO.RUNDA.UPP
+LCM = MGM
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = MDETERM
+MINVERSE = MINVERT
+MMULT = MMULT
+MOD = REST
+MROUND = MAVRUNDA
+MULTINOMIAL = MULTINOMIAL
+MUNIT = MENHET
+ODD = UDDA
+PI = PI
+POWER = UPPHÖJT.TILL
+PRODUCT = PRODUKT
+QUOTIENT = KVOT
+RADIANS = RADIANER
+RAND = SLUMP
+RANDBETWEEN = SLUMP.MELLAN
+ROMAN = ROMERSK
+ROUND = AVRUNDA
+ROUNDBAHTDOWN = AVRUNDABAHTNEDÅT
+ROUNDBAHTUP = AVRUNDABAHTUPPÅT
+ROUNDDOWN = AVRUNDA.NEDÅT
+ROUNDUP = AVRUNDA.UPPÅT
+SEC = SEK
+SECH = SEKH
+SERIESSUM = SERIESUMMA
+SIGN = TECKEN
+SIN = SIN
+SINH = SINH
+SQRT = ROT
+SQRTPI = ROTPI
+SUBTOTAL = DELSUMMA
+SUM = SUMMA
+SUMIF = SUMMA.OM
+SUMIFS = SUMMA.OMF
+SUMPRODUCT = PRODUKTSUMMA
+SUMSQ = KVADRATSUMMA
+SUMX2MY2 = SUMMAX2MY2
+SUMX2PY2 = SUMMAX2PY2
+SUMXMY2 = SUMMAXMY2
+TAN = TAN
+TANH = TANH
+TRUNC = AVKORTA
+
+##
+## Statistiska funktioner (Statistical Functions)
+##
+AVEDEV = MEDELAVV
+AVERAGE = MEDEL
+AVERAGEA = AVERAGEA
+AVERAGEIF = MEDEL.OM
+AVERAGEIFS = MEDEL.OMF
+BETA.DIST = BETA.FÖRD
+BETA.INV = BETA.INV
+BINOM.DIST = BINOM.FÖRD
+BINOM.DIST.RANGE = BINOM.FÖRD.INTERVALL
+BINOM.INV = BINOM.INV
+CHISQ.DIST = CHI2.FÖRD
+CHISQ.DIST.RT = CHI2.FÖRD.RT
+CHISQ.INV = CHI2.INV
+CHISQ.INV.RT = CHI2.INV.RT
+CHISQ.TEST = CHI2.TEST
+CONFIDENCE.NORM = KONFIDENS.NORM
+CONFIDENCE.T = KONFIDENS.T
+CORREL = KORREL
+COUNT = ANTAL
+COUNTA = ANTALV
+COUNTBLANK = ANTAL.TOMMA
+COUNTIF = ANTAL.OM
+COUNTIFS = ANTAL.OMF
+COVARIANCE.P = KOVARIANS.P
+COVARIANCE.S = KOVARIANS.S
+DEVSQ = KVADAVV
+EXPON.DIST = EXPON.FÖRD
+F.DIST = F.FÖRD
+F.DIST.RT = F.FÖRD.RT
+F.INV = F.INV
+F.INV.RT = F.INV.RT
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHERINV
+FORECAST.ETS = PROGNOS.ETS
+FORECAST.ETS.CONFINT = PROGNOS.ETS.KONFINT
+FORECAST.ETS.SEASONALITY = PROGNOS.ETS.SÄSONGSBEROENDE
+FORECAST.ETS.STAT = PROGNOS.ETS.STAT
+FORECAST.LINEAR = PROGNOS.LINJÄR
+FREQUENCY = FREKVENS
+GAMMA = GAMMA
+GAMMA.DIST = GAMMA.FÖRD
+GAMMA.INV = GAMMA.INV
+GAMMALN = GAMMALN
+GAMMALN.PRECISE = GAMMALN.EXAKT
+GAUSS = GAUSS
+GEOMEAN = GEOMEDEL
+GROWTH = EXPTREND
+HARMEAN = HARMMEDEL
+HYPGEOM.DIST = HYPGEOM.FÖRD
+INTERCEPT = SKÄRNINGSPUNKT
+KURT = TOPPIGHET
+LARGE = STÖRSTA
+LINEST = REGR
+LOGEST = EXPREGR
+LOGNORM.DIST = LOGNORM.FÖRD
+LOGNORM.INV = LOGNORM.INV
+MAX = MAX
+MAXA = MAXA
+MAXIFS = MAXIFS
+MEDIAN = MEDIAN
+MIN = MIN
+MINA = MINA
+MINIFS = MINIFS
+MODE.MULT = TYPVÄRDE.FLERA
+MODE.SNGL = TYPVÄRDE.ETT
+NEGBINOM.DIST = NEGBINOM.FÖRD
+NORM.DIST = NORM.FÖRD
+NORM.INV = NORM.INV
+NORM.S.DIST = NORM.S.FÖRD
+NORM.S.INV = NORM.S.INV
+PEARSON = PEARSON
+PERCENTILE.EXC = PERCENTIL.EXK
+PERCENTILE.INC = PERCENTIL.INK
+PERCENTRANK.EXC = PROCENTRANG.EXK
+PERCENTRANK.INC = PROCENTRANG.INK
+PERMUT = PERMUT
+PERMUTATIONA = PERMUTATIONA
+PHI = PHI
+POISSON.DIST = POISSON.FÖRD
+PROB = SANNOLIKHET
+QUARTILE.EXC = KVARTIL.EXK
+QUARTILE.INC = KVARTIL.INK
+RANK.AVG = RANG.MED
+RANK.EQ = RANG.EKV
+RSQ = RKV
+SKEW = SNEDHET
+SKEW.P = SNEDHET.P
+SLOPE = LUTNING
+SMALL = MINSTA
+STANDARDIZE = STANDARDISERA
+STDEV.P = STDAV.P
+STDEV.S = STDAV.S
+STDEVA = STDEVA
+STDEVPA = STDEVPA
+STEYX = STDFELYX
+T.DIST = T.FÖRD
+T.DIST.2T = T.FÖRD.2T
+T.DIST.RT = T.FÖRD.RT
+T.INV = T.INV
+T.INV.2T = T.INV.2T
+T.TEST = T.TEST
+TREND = TREND
+TRIMMEAN = TRIMMEDEL
+VAR.P = VARIANS.P
+VAR.S = VARIANS.S
+VARA = VARA
+VARPA = VARPA
+WEIBULL.DIST = WEIBULL.FÖRD
+Z.TEST = Z.TEST
+
+##
+## Textfunktioner (Text Functions)
+##
+BAHTTEXT = BAHTTEXT
+CHAR = TECKENKOD
+CLEAN = STÄDA
+CODE = KOD
+CONCAT = SAMMAN
+DOLLAR = VALUTA
+EXACT = EXAKT
+FIND = HITTA
+FIXED = FASTTAL
+LEFT = VÄNSTER
+LEN = LÄNGD
+LOWER = GEMENER
+MID = EXTEXT
+NUMBERVALUE = TALVÄRDE
+PROPER = INITIAL
+REPLACE = ERSÄTT
+REPT = REP
+RIGHT = HÖGER
+SEARCH = SÖK
+SUBSTITUTE = BYT.UT
+T = T
+TEXT = TEXT
+TEXTJOIN = TEXTJOIN
+THAIDIGIT = THAISIFFRA
+THAINUMSOUND = THAITALLJUD
+THAINUMSTRING = THAITALSTRÄNG
+THAISTRINGLENGTH = THAISTRÄNGLÄNGD
+TRIM = RENSA
+UNICHAR = UNITECKENKOD
+UNICODE = UNICODE
+UPPER = VERSALER
+VALUE = TEXTNUM
+
+##
+## Webbfunktioner (Web Functions)
+##
+ENCODEURL = KODAWEBBADRESS
+FILTERXML = FILTRERAXML
+WEBSERVICE = WEBBTJÄNST
+
+##
+## Kompatibilitetsfunktioner (Compatibility Functions)
+##
+BETADIST = BETAFÖRD
+BETAINV = BETAINV
+BINOMDIST = BINOMFÖRD
+CEILING = RUNDA.UPP
+CHIDIST = CHI2FÖRD
+CHIINV = CHI2INV
+CHITEST = CHI2TEST
+CONCATENATE = SAMMANFOGA
+CONFIDENCE = KONFIDENS
+COVAR = KOVAR
+CRITBINOM = KRITBINOM
+EXPONDIST = EXPONFÖRD
+FDIST = FFÖRD
+FINV = FINV
+FLOOR = RUNDA.NER
+FORECAST = PREDIKTION
+FTEST = FTEST
+GAMMADIST = GAMMAFÖRD
+GAMMAINV = GAMMAINV
+HYPGEOMDIST = HYPGEOMFÖRD
+LOGINV = LOGINV
+LOGNORMDIST = LOGNORMFÖRD
+MODE = TYPVÄRDE
+NEGBINOMDIST = NEGBINOMFÖRD
+NORMDIST = NORMFÖRD
+NORMINV = NORMINV
+NORMSDIST = NORMSFÖRD
+NORMSINV = NORMSINV
+PERCENTILE = PERCENTIL
+PERCENTRANK = PROCENTRANG
+POISSON = POISSON
+QUARTILE = KVARTIL
+RANK = RANG
+STDEV = STDAV
+STDEVP = STDAVP
+TDIST = TFÖRD
+TINV = TINV
+TTEST = TTEST
+VAR = VARIANS
+VARP = VARIANSP
+WEIBULL = WEIBULL
+ZTEST = ZTEST
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config
new file mode 100644
index 0000000..63d22fd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config
@@ -0,0 +1,20 @@
+############################################################
+##
+## PhpSpreadsheet - locale settings
+##
+## Türkçe (Turkish)
+##
+############################################################
+
+ArgumentSeparator = ;
+
+##
+## Error Codes
+##
+NULL = #BOŞ!
+DIV0 = #SAYI/0!
+VALUE = #DEĞER!
+REF = #BAŞV!
+NAME = #AD?
+NUM = #SAYI!
+NA = #YOK
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions
new file mode 100644
index 0000000..f872274
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions
@@ -0,0 +1,537 @@
+############################################################
+##
+## PhpSpreadsheet - function name translations
+##
+## Türkçe (Turkish)
+##
+############################################################
+
+
+##
+## Küp işlevleri (Cube Functions)
+##
+CUBEKPIMEMBER = KÜPKPIÜYESİ
+CUBEMEMBER = KÜPÜYESİ
+CUBEMEMBERPROPERTY = KÜPÜYEÖZELLİĞİ
+CUBERANKEDMEMBER = DERECELİKÜPÜYESİ
+CUBESET = KÜPKÜMESİ
+CUBESETCOUNT = KÜPKÜMESAYISI
+CUBEVALUE = KÜPDEĞERİ
+
+##
+## Veritabanı işlevleri (Database Functions)
+##
+DAVERAGE = VSEÇORT
+DCOUNT = VSEÇSAY
+DCOUNTA = VSEÇSAYDOLU
+DGET = VAL
+DMAX = VSEÇMAK
+DMIN = VSEÇMİN
+DPRODUCT = VSEÇÇARP
+DSTDEV = VSEÇSTDSAPMA
+DSTDEVP = VSEÇSTDSAPMAS
+DSUM = VSEÇTOPLA
+DVAR = VSEÇVAR
+DVARP = VSEÇVARS
+
+##
+## Tarih ve saat işlevleri (Date & Time Functions)
+##
+DATE = TARİH
+DATEDIF = ETARİHLİ
+DATESTRING = TARİHDİZİ
+DATEVALUE = TARİHSAYISI
+DAY = GÜN
+DAYS = GÜNSAY
+DAYS360 = GÜN360
+EDATE = SERİTARİH
+EOMONTH = SERİAY
+HOUR = SAAT
+ISOWEEKNUM = ISOHAFTASAY
+MINUTE = DAKİKA
+MONTH = AY
+NETWORKDAYS = TAMİŞGÜNÜ
+NETWORKDAYS.INTL = TAMİŞGÜNÜ.ULUSL
+NOW = ŞİMDİ
+SECOND = SANİYE
+THAIDAYOFWEEK = TAYHAFTANINGÜNÜ
+THAIMONTHOFYEAR = TAYYILINAYI
+THAIYEAR = TAYYILI
+TIME = ZAMAN
+TIMEVALUE = ZAMANSAYISI
+TODAY = BUGÜN
+WEEKDAY = HAFTANINGÜNÜ
+WEEKNUM = HAFTASAY
+WORKDAY = İŞGÜNÜ
+WORKDAY.INTL = İŞGÜNÜ.ULUSL
+YEAR = YIL
+YEARFRAC = YILORAN
+
+##
+## Mühendislik işlevleri (Engineering Functions)
+##
+BESSELI = BESSELI
+BESSELJ = BESSELJ
+BESSELK = BESSELK
+BESSELY = BESSELY
+BIN2DEC = BIN2DEC
+BIN2HEX = BIN2HEX
+BIN2OCT = BIN2OCT
+BITAND = BİTVE
+BITLSHIFT = BİTSOLAKAYDIR
+BITOR = BİTVEYA
+BITRSHIFT = BİTSAĞAKAYDIR
+BITXOR = BİTÖZELVEYA
+COMPLEX = KARMAŞIK
+CONVERT = ÇEVİR
+DEC2BIN = DEC2BIN
+DEC2HEX = DEC2HEX
+DEC2OCT = DEC2OCT
+DELTA = DELTA
+ERF = HATAİŞLEV
+ERF.PRECISE = HATAİŞLEV.DUYARLI
+ERFC = TÜMHATAİŞLEV
+ERFC.PRECISE = TÜMHATAİŞLEV.DUYARLI
+GESTEP = BESINIR
+HEX2BIN = HEX2BIN
+HEX2DEC = HEX2DEC
+HEX2OCT = HEX2OCT
+IMABS = SANMUTLAK
+IMAGINARY = SANAL
+IMARGUMENT = SANBAĞ_DEĞİŞKEN
+IMCONJUGATE = SANEŞLENEK
+IMCOS = SANCOS
+IMCOSH = SANCOSH
+IMCOT = SANCOT
+IMCSC = SANCSC
+IMCSCH = SANCSCH
+IMDIV = SANBÖL
+IMEXP = SANÜS
+IMLN = SANLN
+IMLOG10 = SANLOG10
+IMLOG2 = SANLOG2
+IMPOWER = SANKUVVET
+IMPRODUCT = SANÇARP
+IMREAL = SANGERÇEK
+IMSEC = SANSEC
+IMSECH = SANSECH
+IMSIN = SANSIN
+IMSINH = SANSINH
+IMSQRT = SANKAREKÖK
+IMSUB = SANTOPLA
+IMSUM = SANÇIKAR
+IMTAN = SANTAN
+OCT2BIN = OCT2BIN
+OCT2DEC = OCT2DEC
+OCT2HEX = OCT2HEX
+
+##
+## Finansal işlevler (Financial Functions)
+##
+ACCRINT = GERÇEKFAİZ
+ACCRINTM = GERÇEKFAİZV
+AMORDEGRC = AMORDEGRC
+AMORLINC = AMORLINC
+COUPDAYBS = KUPONGÜNBD
+COUPDAYS = KUPONGÜN
+COUPDAYSNC = KUPONGÜNDSK
+COUPNCD = KUPONGÜNSKT
+COUPNUM = KUPONSAYI
+COUPPCD = KUPONGÜNÖKT
+CUMIPMT = TOPÖDENENFAİZ
+CUMPRINC = TOPANAPARA
+DB = AZALANBAKİYE
+DDB = ÇİFTAZALANBAKİYE
+DISC = İNDİRİM
+DOLLARDE = LİRAON
+DOLLARFR = LİRAKES
+DURATION = SÜRE
+EFFECT = ETKİN
+FV = GD
+FVSCHEDULE = GDPROGRAM
+INTRATE = FAİZORANI
+IPMT = FAİZTUTARI
+IRR = İÇ_VERİM_ORANI
+ISPMT = ISPMT
+MDURATION = MSÜRE
+MIRR = D_İÇ_VERİM_ORANI
+NOMINAL = NOMİNAL
+NPER = TAKSİT_SAYISI
+NPV = NBD
+ODDFPRICE = TEKYDEĞER
+ODDFYIELD = TEKYÖDEME
+ODDLPRICE = TEKSDEĞER
+ODDLYIELD = TEKSÖDEME
+PDURATION = PSÜRE
+PMT = DEVRESEL_ÖDEME
+PPMT = ANA_PARA_ÖDEMESİ
+PRICE = DEĞER
+PRICEDISC = DEĞERİND
+PRICEMAT = DEĞERVADE
+PV = BD
+RATE = FAİZ_ORANI
+RECEIVED = GETİRİ
+RRI = GERÇEKLEŞENYATIRIMGETİRİSİ
+SLN = DA
+SYD = YAT
+TBILLEQ = HTAHEŞ
+TBILLPRICE = HTAHDEĞER
+TBILLYIELD = HTAHÖDEME
+VDB = DAB
+XIRR = AİÇVERİMORANI
+XNPV = ANBD
+YIELD = ÖDEME
+YIELDDISC = ÖDEMEİND
+YIELDMAT = ÖDEMEVADE
+
+##
+## Bilgi işlevleri (Information Functions)
+##
+CELL = HÜCRE
+ERROR.TYPE = HATA.TİPİ
+INFO = BİLGİ
+ISBLANK = EBOŞSA
+ISERR = EHATA
+ISERROR = EHATALIYSA
+ISEVEN = ÇİFTMİ
+ISFORMULA = EFORMÜLSE
+ISLOGICAL = EMANTIKSALSA
+ISNA = EYOKSA
+ISNONTEXT = EMETİNDEĞİLSE
+ISNUMBER = ESAYIYSA
+ISODD = TEKMİ
+ISREF = EREFSE
+ISTEXT = EMETİNSE
+N = S
+NA = YOKSAY
+SHEET = SAYFA
+SHEETS = SAYFALAR
+TYPE = TÜR
+
+##
+## Mantıksal işlevler (Logical Functions)
+##
+AND = VE
+FALSE = YANLIŞ
+IF = EĞER
+IFERROR = EĞERHATA
+IFNA = EĞERYOKSA
+IFS = ÇOKEĞER
+NOT = DEĞİL
+OR = YADA
+SWITCH = İLKEŞLEŞEN
+TRUE = DOĞRU
+XOR = ÖZELVEYA
+
+##
+## Arama ve başvuru işlevleri (Lookup & Reference Functions)
+##
+ADDRESS = ADRES
+AREAS = ALANSAY
+CHOOSE = ELEMAN
+COLUMN = SÜTUN
+COLUMNS = SÜTUNSAY
+FORMULATEXT = FORMÜLMETNİ
+GETPIVOTDATA = ÖZETVERİAL
+HLOOKUP = YATAYARA
+HYPERLINK = KÖPRÜ
+INDEX = İNDİS
+INDIRECT = DOLAYLI
+LOOKUP = ARA
+MATCH = KAÇINCI
+OFFSET = KAYDIR
+ROW = SATIR
+ROWS = SATIRSAY
+RTD = GZV
+TRANSPOSE = DEVRİK_DÖNÜŞÜM
+VLOOKUP = DÜŞEYARA
+
+##
+## Matematik ve trigonometri işlevleri (Math & Trig Functions)
+##
+ABS = MUTLAK
+ACOS = ACOS
+ACOSH = ACOSH
+ACOT = ACOT
+ACOTH = ACOTH
+AGGREGATE = TOPLAMA
+ARABIC = ARAP
+ASIN = ASİN
+ASINH = ASİNH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+BASE = TABAN
+CEILING.MATH = TAVANAYUVARLA.MATEMATİK
+CEILING.PRECISE = TAVANAYUVARLA.DUYARLI
+COMBIN = KOMBİNASYON
+COMBINA = KOMBİNASYONA
+COS = COS
+COSH = COSH
+COT = COT
+COTH = COTH
+CSC = CSC
+CSCH = CSCH
+DECIMAL = ONDALIK
+DEGREES = DERECE
+ECMA.CEILING = ECMA.TAVAN
+EVEN = ÇİFT
+EXP = ÜS
+FACT = ÇARPINIM
+FACTDOUBLE = ÇİFTFAKTÖR
+FLOOR.MATH = TABANAYUVARLA.MATEMATİK
+FLOOR.PRECISE = TABANAYUVARLA.DUYARLI
+GCD = OBEB
+INT = TAMSAYI
+ISO.CEILING = ISO.TAVAN
+LCM = OKEK
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = DETERMİNANT
+MINVERSE = DİZEY_TERS
+MMULT = DÇARP
+MOD = MOD
+MROUND = KYUVARLA
+MULTINOMIAL = ÇOKTERİMLİ
+MUNIT = BİRİMMATRİS
+ODD = TEK
+PI = Pİ
+POWER = KUVVET
+PRODUCT = ÇARPIM
+QUOTIENT = BÖLÜM
+RADIANS = RADYAN
+RAND = S_SAYI_ÜRET
+RANDBETWEEN = RASTGELEARADA
+ROMAN = ROMEN
+ROUND = YUVARLA
+ROUNDBAHTDOWN = BAHTAŞAĞIYUVARLA
+ROUNDBAHTUP = BAHTYUKARIYUVARLA
+ROUNDDOWN = AŞAĞIYUVARLA
+ROUNDUP = YUKARIYUVARLA
+SEC = SEC
+SECH = SECH
+SERIESSUM = SERİTOPLA
+SIGN = İŞARET
+SIN = SİN
+SINH = SİNH
+SQRT = KAREKÖK
+SQRTPI = KAREKÖKPİ
+SUBTOTAL = ALTTOPLAM
+SUM = TOPLA
+SUMIF = ETOPLA
+SUMIFS = ÇOKETOPLA
+SUMPRODUCT = TOPLA.ÇARPIM
+SUMSQ = TOPKARE
+SUMX2MY2 = TOPX2EY2
+SUMX2PY2 = TOPX2AY2
+SUMXMY2 = TOPXEY2
+TAN = TAN
+TANH = TANH
+TRUNC = NSAT
+
+##
+## İstatistik işlevleri (Statistical Functions)
+##
+AVEDEV = ORTSAP
+AVERAGE = ORTALAMA
+AVERAGEA = ORTALAMAA
+AVERAGEIF = EĞERORTALAMA
+AVERAGEIFS = ÇOKEĞERORTALAMA
+BETA.DIST = BETA.DAĞ
+BETA.INV = BETA.TERS
+BINOM.DIST = BİNOM.DAĞ
+BINOM.DIST.RANGE = BİNOM.DAĞ.ARALIK
+BINOM.INV = BİNOM.TERS
+CHISQ.DIST = KİKARE.DAĞ
+CHISQ.DIST.RT = KİKARE.DAĞ.SAĞK
+CHISQ.INV = KİKARE.TERS
+CHISQ.INV.RT = KİKARE.TERS.SAĞK
+CHISQ.TEST = KİKARE.TEST
+CONFIDENCE.NORM = GÜVENİLİRLİK.NORM
+CONFIDENCE.T = GÜVENİLİRLİK.T
+CORREL = KORELASYON
+COUNT = BAĞ_DEĞ_SAY
+COUNTA = BAĞ_DEĞ_DOLU_SAY
+COUNTBLANK = BOŞLUKSAY
+COUNTIF = EĞERSAY
+COUNTIFS = ÇOKEĞERSAY
+COVARIANCE.P = KOVARYANS.P
+COVARIANCE.S = KOVARYANS.S
+DEVSQ = SAPKARE
+EXPON.DIST = ÜSTEL.DAĞ
+F.DIST = F.DAĞ
+F.DIST.RT = F.DAĞ.SAĞK
+F.INV = F.TERS
+F.INV.RT = F.TERS.SAĞK
+F.TEST = F.TEST
+FISHER = FISHER
+FISHERINV = FISHERTERS
+FORECAST.ETS = TAHMİN.ETS
+FORECAST.ETS.CONFINT = TAHMİN.ETS.GVNARAL
+FORECAST.ETS.SEASONALITY = TAHMİN.ETS.MEVSİMSELLİK
+FORECAST.ETS.STAT = TAHMİN.ETS.İSTAT
+FORECAST.LINEAR = TAHMİN.DOĞRUSAL
+FREQUENCY = SIKLIK
+GAMMA = GAMA
+GAMMA.DIST = GAMA.DAĞ
+GAMMA.INV = GAMA.TERS
+GAMMALN = GAMALN
+GAMMALN.PRECISE = GAMALN.DUYARLI
+GAUSS = GAUSS
+GEOMEAN = GEOORT
+GROWTH = BÜYÜME
+HARMEAN = HARORT
+HYPGEOM.DIST = HİPERGEOM.DAĞ
+INTERCEPT = KESMENOKTASI
+KURT = BASIKLIK
+LARGE = BÜYÜK
+LINEST = DOT
+LOGEST = LOT
+LOGNORM.DIST = LOGNORM.DAĞ
+LOGNORM.INV = LOGNORM.TERS
+MAX = MAK
+MAXA = MAKA
+MAXIFS = ÇOKEĞERMAK
+MEDIAN = ORTANCA
+MIN = MİN
+MINA = MİNA
+MINIFS = ÇOKEĞERMİN
+MODE.MULT = ENÇOK_OLAN.ÇOK
+MODE.SNGL = ENÇOK_OLAN.TEK
+NEGBINOM.DIST = NEGBİNOM.DAĞ
+NORM.DIST = NORM.DAĞ
+NORM.INV = NORM.TERS
+NORM.S.DIST = NORM.S.DAĞ
+NORM.S.INV = NORM.S.TERS
+PEARSON = PEARSON
+PERCENTILE.EXC = YÜZDEBİRLİK.HRC
+PERCENTILE.INC = YÜZDEBİRLİK.DHL
+PERCENTRANK.EXC = YÜZDERANK.HRC
+PERCENTRANK.INC = YÜZDERANK.DHL
+PERMUT = PERMÜTASYON
+PERMUTATIONA = PERMÜTASYONA
+PHI = PHI
+POISSON.DIST = POISSON.DAĞ
+PROB = OLASILIK
+QUARTILE.EXC = DÖRTTEBİRLİK.HRC
+QUARTILE.INC = DÖRTTEBİRLİK.DHL
+RANK.AVG = RANK.ORT
+RANK.EQ = RANK.EŞİT
+RSQ = RKARE
+SKEW = ÇARPIKLIK
+SKEW.P = ÇARPIKLIK.P
+SLOPE = EĞİM
+SMALL = KÜÇÜK
+STANDARDIZE = STANDARTLAŞTIRMA
+STDEV.P = STDSAPMA.P
+STDEV.S = STDSAPMA.S
+STDEVA = STDSAPMAA
+STDEVPA = STDSAPMASA
+STEYX = STHYX
+T.DIST = T.DAĞ
+T.DIST.2T = T.DAĞ.2K
+T.DIST.RT = T.DAĞ.SAĞK
+T.INV = T.TERS
+T.INV.2T = T.TERS.2K
+T.TEST = T.TEST
+TREND = EĞİLİM
+TRIMMEAN = KIRPORTALAMA
+VAR.P = VAR.P
+VAR.S = VAR.S
+VARA = VARA
+VARPA = VARSA
+WEIBULL.DIST = WEIBULL.DAĞ
+Z.TEST = Z.TEST
+
+##
+## Metin işlevleri (Text Functions)
+##
+BAHTTEXT = BAHTMETİN
+CHAR = DAMGA
+CLEAN = TEMİZ
+CODE = KOD
+CONCAT = ARALIKBİRLEŞTİR
+DOLLAR = LİRA
+EXACT = ÖZDEŞ
+FIND = BUL
+FIXED = SAYIDÜZENLE
+ISTHAIDIGIT = TAYRAKAMIYSA
+LEFT = SOLDAN
+LEN = UZUNLUK
+LOWER = KÜÇÜKHARF
+MID = PARÇAAL
+NUMBERSTRING = SAYIDİZİ
+NUMBERVALUE = SAYIDEĞERİ
+PHONETIC = SES
+PROPER = YAZIM.DÜZENİ
+REPLACE = DEĞİŞTİR
+REPT = YİNELE
+RIGHT = SAĞDAN
+SEARCH = MBUL
+SUBSTITUTE = YERİNEKOY
+T = M
+TEXT = METNEÇEVİR
+TEXTJOIN = METİNBİRLEŞTİR
+THAIDIGIT = TAYRAKAM
+THAINUMSOUND = TAYSAYISES
+THAINUMSTRING = TAYSAYIDİZE
+THAISTRINGLENGTH = TAYDİZEUZUNLUĞU
+TRIM = KIRP
+UNICHAR = UNICODEKARAKTERİ
+UNICODE = UNICODE
+UPPER = BÜYÜKHARF
+VALUE = SAYIYAÇEVİR
+
+##
+## Metin işlevleri (Web Functions)
+##
+ENCODEURL = URLKODLA
+FILTERXML = XMLFİLTRELE
+WEBSERVICE = WEBHİZMETİ
+
+##
+## Uyumluluk işlevleri (Compatibility Functions)
+##
+BETADIST = BETADAĞ
+BETAINV = BETATERS
+BINOMDIST = BİNOMDAĞ
+CEILING = TAVANAYUVARLA
+CHIDIST = KİKAREDAĞ
+CHIINV = KİKARETERS
+CHITEST = KİKARETEST
+CONCATENATE = BİRLEŞTİR
+CONFIDENCE = GÜVENİRLİK
+COVAR = KOVARYANS
+CRITBINOM = KRİTİKBİNOM
+EXPONDIST = ÜSTELDAĞ
+FDIST = FDAĞ
+FINV = FTERS
+FLOOR = TABANAYUVARLA
+FORECAST = TAHMİN
+FTEST = FTEST
+GAMMADIST = GAMADAĞ
+GAMMAINV = GAMATERS
+HYPGEOMDIST = HİPERGEOMDAĞ
+LOGINV = LOGTERS
+LOGNORMDIST = LOGNORMDAĞ
+MODE = ENÇOK_OLAN
+NEGBINOMDIST = NEGBİNOMDAĞ
+NORMDIST = NORMDAĞ
+NORMINV = NORMTERS
+NORMSDIST = NORMSDAĞ
+NORMSINV = NORMSTERS
+PERCENTILE = YÜZDEBİRLİK
+PERCENTRANK = YÜZDERANK
+POISSON = POISSON
+QUARTILE = DÖRTTEBİRLİK
+RANK = RANK
+STDEV = STDSAPMA
+STDEVP = STDSAPMAS
+TDIST = TDAĞ
+TINV = TTERS
+TTEST = TTEST
+VAR = VAR
+VARP = VARS
+WEIBULL = WEIBULL
+ZTEST = ZTEST
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
new file mode 100644
index 0000000..923f941
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
@@ -0,0 +1,177 @@
+setValueExplicit(true, DataType::TYPE_BOOL);
+
+ return true;
+ } elseif (StringHelper::strToUpper($value) === Calculation::getFALSE()) {
+ $cell->setValueExplicit(false, DataType::TYPE_BOOL);
+
+ return true;
+ }
+
+ // Check for fractions
+ if (preg_match('/^([+-]?)\s*(\d+)\s?\/\s*(\d+)$/', $value, $matches)) {
+ return $this->setProperFraction($matches, $cell);
+ } elseif (preg_match('/^([+-]?)(\d*) +(\d*)\s?\/\s*(\d*)$/', $value, $matches)) {
+ return $this->setImproperFraction($matches, $cell);
+ }
+
+ $decimalSeparatorNoPreg = StringHelper::getDecimalSeparator();
+ $decimalSeparator = preg_quote($decimalSeparatorNoPreg, '/');
+ $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+
+ // Check for percentage
+ if (preg_match('/^\-?\d*' . $decimalSeparator . '?\d*\s?\%$/', preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value))) {
+ return $this->setPercentage(preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $cell);
+ }
+
+ // Check for currency
+ if (preg_match(FormattedNumber::currencyMatcherRegexp(), preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $matches, PREG_UNMATCHED_AS_NULL)) {
+ // Convert value to number
+ $sign = ($matches['PrefixedSign'] ?? $matches['PrefixedSign2'] ?? $matches['PostfixedSign']) ?? null;
+ $currencyCode = $matches['PrefixedCurrency'] ?? $matches['PostfixedCurrency'];
+ $value = (float) ($sign . trim(str_replace([$decimalSeparatorNoPreg, $currencyCode, ' ', '-'], ['.', '', '', ''], preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value)))); // @phpstan-ignore-line
+
+ return $this->setCurrency($value, $cell, $currencyCode ?? '');
+ }
+
+ // 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)) {
+ return $this->setTimeHoursMinutes($value, $cell);
+ }
+
+ // 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)) {
+ return $this->setTimeHoursMinutesSeconds($value, $cell);
+ }
+
+ // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
+ if (($d = Date::stringToExcel($value)) !== false) {
+ // Convert value to number
+ $cell->setValueExplicit($d, DataType::TYPE_NUMERIC);
+ // Determine style. Either there is a time part or not. Look for ':'
+ if (str_contains($value, ':')) {
+ $formatCode = 'yyyy-mm-dd h:mm';
+ } else {
+ $formatCode = 'yyyy-mm-dd';
+ }
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode($formatCode);
+
+ return true;
+ }
+
+ // Check for newline character "\n"
+ if (str_contains($value, "\n")) {
+ $cell->setValueExplicit($value, DataType::TYPE_STRING);
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getAlignment()->setWrapText(true);
+
+ return true;
+ }
+ }
+
+ // Not bound yet? Use parent...
+ return parent::bindValue($cell, $value);
+ }
+
+ protected function setImproperFraction(array $matches, Cell $cell): bool
+ {
+ // Convert value to number
+ $value = $matches[2] + ($matches[3] / $matches[4]);
+ if ($matches[1] === '-') {
+ $value = 0 - $value;
+ }
+ $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
+
+ // Build the number format mask based on the size of the matched values
+ $dividend = str_repeat('?', strlen($matches[3]));
+ $divisor = str_repeat('?', strlen($matches[4]));
+ $fractionMask = "# {$dividend}/{$divisor}";
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode($fractionMask);
+
+ return true;
+ }
+
+ protected function setProperFraction(array $matches, Cell $cell): bool
+ {
+ // Convert value to number
+ $value = $matches[2] / $matches[3];
+ if ($matches[1] === '-') {
+ $value = 0 - $value;
+ }
+ $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
+
+ // Build the number format mask based on the size of the matched values
+ $dividend = str_repeat('?', strlen($matches[2]));
+ $divisor = str_repeat('?', strlen($matches[3]));
+ $fractionMask = "{$dividend}/{$divisor}";
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode($fractionMask);
+
+ return true;
+ }
+
+ protected function setPercentage(string $value, Cell $cell): bool
+ {
+ // Convert value to number
+ $value = ((float) str_replace('%', '', $value)) / 100;
+ $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_PERCENTAGE_00);
+
+ return true;
+ }
+
+ protected function setCurrency(float $value, Cell $cell, string $currencyCode): bool
+ {
+ $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode(
+ str_replace('$', '[$' . $currencyCode . ']', NumberFormat::FORMAT_CURRENCY_USD)
+ );
+
+ return true;
+ }
+
+ protected function setTimeHoursMinutes(string $value, Cell $cell): bool
+ {
+ // Convert value to number
+ [$hours, $minutes] = explode(':', $value);
+ $hours = (int) $hours;
+ $minutes = (int) $minutes;
+ $days = ($hours / 24) + ($minutes / 1440);
+ $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME3);
+
+ return true;
+ }
+
+ protected function setTimeHoursMinutesSeconds(string $value, Cell $cell): bool
+ {
+ // Convert value to number
+ [$hours, $minutes, $seconds] = explode(':', $value);
+ $hours = (int) $hours;
+ $minutes = (int) $minutes;
+ $seconds = (int) $seconds;
+ $days = ($hours / 24) + ($minutes / 1440) + ($seconds / 86400);
+ $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
+
+ // Set style
+ $cell->getWorksheet()->getStyle($cell->getCoordinate())
+ ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME4);
+
+ return true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
new file mode 100644
index 0000000..8ebc115
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
@@ -0,0 +1,827 @@
+parent;
+ if ($parent === null) {
+ throw new SpreadsheetException('Cannot update when cell is not bound to a worksheet');
+ }
+ $parent->update($this);
+
+ return $this;
+ }
+
+ public function detach(): void
+ {
+ $this->parent = null;
+ }
+
+ public function attach(Cells $parent): void
+ {
+ $this->parent = $parent;
+ }
+
+ /**
+ * Create a new Cell.
+ *
+ * @throws SpreadsheetException
+ */
+ public function __construct(mixed $value, ?string $dataType, Worksheet $worksheet)
+ {
+ // Initialise cell value
+ $this->value = $value;
+
+ // Set worksheet cache
+ $this->parent = $worksheet->getCellCollection();
+
+ // Set datatype?
+ if ($dataType !== null) {
+ if ($dataType == DataType::TYPE_STRING2) {
+ $dataType = DataType::TYPE_STRING;
+ }
+ $this->dataType = $dataType;
+ } elseif (self::getValueBinder()->bindValue($this, $value) === false) {
+ throw new SpreadsheetException('Value could not be bound to cell.');
+ }
+ $this->ignoredErrors = new IgnoredErrors();
+ }
+
+ /**
+ * Get cell coordinate column.
+ *
+ * @throws SpreadsheetException
+ */
+ public function getColumn(): string
+ {
+ $parent = $this->parent;
+ if ($parent === null) {
+ throw new SpreadsheetException('Cannot get column when cell is not bound to a worksheet');
+ }
+
+ return $parent->getCurrentColumn();
+ }
+
+ /**
+ * Get cell coordinate row.
+ *
+ * @throws SpreadsheetException
+ */
+ public function getRow(): int
+ {
+ $parent = $this->parent;
+ if ($parent === null) {
+ throw new SpreadsheetException('Cannot get row when cell is not bound to a worksheet');
+ }
+
+ return $parent->getCurrentRow();
+ }
+
+ /**
+ * Get cell coordinate.
+ *
+ * @throws SpreadsheetException
+ */
+ public function getCoordinate(): string
+ {
+ $parent = $this->parent;
+ if ($parent !== null) {
+ $coordinate = $parent->getCurrentCoordinate();
+ } else {
+ $coordinate = null;
+ }
+ if ($coordinate === null) {
+ throw new SpreadsheetException('Coordinate no longer exists');
+ }
+
+ return $coordinate;
+ }
+
+ /**
+ * Get cell value.
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Get cell value with formatting.
+ */
+ public function getFormattedValue(): string
+ {
+ return (string) NumberFormat::toFormattedString(
+ $this->getCalculatedValue(),
+ (string) $this->getStyle()->getNumberFormat()->getFormatCode()
+ );
+ }
+
+ protected static function updateIfCellIsTableHeader(?Worksheet $workSheet, self $cell, mixed $oldValue, mixed $newValue): void
+ {
+ if (StringHelper::strToLower($oldValue ?? '') === StringHelper::strToLower($newValue ?? '') || $workSheet === null) {
+ return;
+ }
+
+ foreach ($workSheet->getTableCollection() as $table) {
+ /** @var Table $table */
+ if ($cell->isInRange($table->getRange())) {
+ $rangeRowsColumns = Coordinate::getRangeBoundaries($table->getRange());
+ if ($cell->getRow() === (int) $rangeRowsColumns[0][1]) {
+ Table\Column::updateStructuredReferences($workSheet, $oldValue, $newValue);
+ }
+
+ return;
+ }
+ }
+ }
+
+ /**
+ * Set cell value.
+ *
+ * Sets the value for a cell, automatically determining the datatype using the value binder
+ *
+ * @param mixed $value Value
+ * @param null|IValueBinder $binder Value Binder to override the currently set Value Binder
+ *
+ * @throws SpreadsheetException
+ */
+ public function setValue(mixed $value, ?IValueBinder $binder = null): self
+ {
+ $binder ??= self::getValueBinder();
+ if (!$binder->bindValue($this, $value)) {
+ throw new SpreadsheetException('Value could not be bound to cell.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder).
+ *
+ * @param mixed $value Value
+ * @param string $dataType Explicit data type, see DataType::TYPE_*
+ * Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
+ * method, then it is your responsibility as an end-user developer to validate that the value and
+ * the datatype match.
+ * If you do mismatch value and datatype, then the value you enter may be changed to match the datatype
+ * that you specify.
+ *
+ * @throws SpreadsheetException
+ */
+ public function setValueExplicit(mixed $value, string $dataType = DataType::TYPE_STRING): self
+ {
+ $oldValue = $this->value;
+
+ // set the value according to data type
+ switch ($dataType) {
+ case DataType::TYPE_NULL:
+ $this->value = null;
+
+ break;
+ case DataType::TYPE_STRING2:
+ $dataType = DataType::TYPE_STRING;
+ // no break
+ case DataType::TYPE_STRING:
+ // Synonym for string
+ case DataType::TYPE_INLINE:
+ // Rich text
+ $this->value = DataType::checkString($value);
+
+ break;
+ case DataType::TYPE_NUMERIC:
+ if (is_string($value) && !is_numeric($value)) {
+ throw new SpreadsheetException('Invalid numeric value for datatype Numeric');
+ }
+ $this->value = 0 + $value;
+
+ break;
+ case DataType::TYPE_FORMULA:
+ $this->value = (string) $value;
+
+ break;
+ case DataType::TYPE_BOOL:
+ $this->value = (bool) $value;
+
+ break;
+ case DataType::TYPE_ISO_DATE:
+ $this->value = SharedDate::convertIsoDate($value);
+ $dataType = DataType::TYPE_NUMERIC;
+
+ break;
+ case DataType::TYPE_ERROR:
+ $this->value = DataType::checkErrorCode($value);
+
+ break;
+ default:
+ throw new SpreadsheetException('Invalid datatype: ' . $dataType);
+ }
+
+ // set the datatype
+ $this->dataType = $dataType;
+
+ $this->updateInCollection();
+ $cellCoordinate = $this->getCoordinate();
+ self::updateIfCellIsTableHeader($this->getParent()?->getParent(), $this, $oldValue, $value);
+
+ return $this->getParent()?->get($cellCoordinate) ?? $this;
+ }
+
+ public const CALCULATE_DATE_TIME_ASIS = 0;
+ public const CALCULATE_DATE_TIME_FLOAT = 1;
+ public const CALCULATE_TIME_FLOAT = 2;
+
+ /** @var int */
+ private static $calculateDateTimeType = self::CALCULATE_DATE_TIME_ASIS;
+
+ public static function getCalculateDateTimeType(): int
+ {
+ return self::$calculateDateTimeType;
+ }
+
+ /** @throws CalculationException*/
+ public static function setCalculateDateTimeType(int $calculateDateTimeType): void
+ {
+ self::$calculateDateTimeType = match ($calculateDateTimeType) {
+ self::CALCULATE_DATE_TIME_ASIS, self::CALCULATE_DATE_TIME_FLOAT, self::CALCULATE_TIME_FLOAT => $calculateDateTimeType,
+ default => throw new CalculationException("Invalid value $calculateDateTimeType for calculated date time type"),
+ };
+ }
+
+ /**
+ * Convert date, time, or datetime from int to float if desired.
+ *
+ * @return mixed
+ */
+ private function convertDateTimeInt(mixed $result)
+ {
+ if (is_int($result)) {
+ if (self::$calculateDateTimeType === self::CALCULATE_TIME_FLOAT) {
+ if (SharedDate::isDateTime($this, $result, false)) {
+ $result = (float) $result;
+ }
+ } elseif (self::$calculateDateTimeType === self::CALCULATE_DATE_TIME_FLOAT) {
+ if (SharedDate::isDateTime($this, $result, true)) {
+ $result = (float) $result;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get calculated cell value.
+ *
+ * @param bool $resetLog Whether the calculation engine logger should be reset or not
+ *
+ * @throws CalculationException
+ */
+ public function getCalculatedValue(bool $resetLog = true): mixed
+ {
+ if ($this->dataType === DataType::TYPE_FORMULA) {
+ try {
+ $index = $this->getWorksheet()->getParentOrThrow()->getActiveSheetIndex();
+ $selected = $this->getWorksheet()->getSelectedCells();
+ $result = Calculation::getInstance(
+ $this->getWorksheet()->getParent()
+ )->calculateCellValue($this, $resetLog);
+ $result = $this->convertDateTimeInt($result);
+ $this->getWorksheet()->setSelectedCells($selected);
+ $this->getWorksheet()->getParentOrThrow()->setActiveSheetIndex($index);
+ // We don't yet handle array returns
+ if (is_array($result)) {
+ while (is_array($result)) {
+ $result = array_shift($result);
+ }
+ }
+ } catch (SpreadsheetException $ex) {
+ if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
+ return $this->calculatedValue; // Fallback for calculations referencing external files.
+ } elseif (preg_match('/[Uu]ndefined (name|offset: 2|array key 2)/', $ex->getMessage()) === 1) {
+ return ExcelError::NAME();
+ }
+
+ throw new CalculationException(
+ $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage(),
+ $ex->getCode(),
+ $ex
+ );
+ }
+
+ if ($result === '#Not Yet Implemented') {
+ return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
+ }
+
+ return $result;
+ } elseif ($this->value instanceof RichText) {
+ return $this->value->getPlainText();
+ }
+
+ return $this->convertDateTimeInt($this->value);
+ }
+
+ /**
+ * Set old calculated value (cached).
+ *
+ * @param mixed $originalValue Value
+ */
+ public function setCalculatedValue(mixed $originalValue, bool $tryNumeric = true): self
+ {
+ if ($originalValue !== null) {
+ $this->calculatedValue = ($tryNumeric && is_numeric($originalValue)) ? (0 + $originalValue) : $originalValue;
+ }
+
+ return $this->updateInCollection();
+ }
+
+ /**
+ * Get old calculated value (cached)
+ * This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
+ * create the original spreadsheet file.
+ * Note that this value is not guaranteed to reflect the actual calculated value because it is
+ * possible that auto-calculation was disabled in the original spreadsheet, and underlying data
+ * values used by the formula have changed since it was last calculated.
+ *
+ * @return mixed
+ */
+ public function getOldCalculatedValue()
+ {
+ return $this->calculatedValue;
+ }
+
+ /**
+ * Get cell data type.
+ */
+ public function getDataType(): string
+ {
+ return $this->dataType;
+ }
+
+ /**
+ * Set cell data type.
+ *
+ * @param string $dataType see DataType::TYPE_*
+ */
+ public function setDataType($dataType): self
+ {
+ $this->setValueExplicit($this->value, $dataType);
+
+ return $this;
+ }
+
+ /**
+ * Identify if the cell contains a formula.
+ */
+ public function isFormula(): bool
+ {
+ return $this->dataType === DataType::TYPE_FORMULA && $this->getStyle()->getQuotePrefix() === false;
+ }
+
+ /**
+ * Does this cell contain Data validation rules?
+ *
+ * @throws SpreadsheetException
+ */
+ public function hasDataValidation(): bool
+ {
+ if (!isset($this->parent)) {
+ throw new SpreadsheetException('Cannot check for data validation when cell is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Data validation rules.
+ *
+ * @throws SpreadsheetException
+ */
+ public function getDataValidation(): DataValidation
+ {
+ if (!isset($this->parent)) {
+ throw new SpreadsheetException('Cannot get data validation for cell that is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->getDataValidation($this->getCoordinate());
+ }
+
+ /**
+ * Set Data validation rules.
+ *
+ * @throws SpreadsheetException
+ */
+ public function setDataValidation(?DataValidation $dataValidation = null): self
+ {
+ if (!isset($this->parent)) {
+ throw new SpreadsheetException('Cannot set data validation for cell that is not bound to a worksheet');
+ }
+
+ $this->getWorksheet()->setDataValidation($this->getCoordinate(), $dataValidation);
+
+ return $this->updateInCollection();
+ }
+
+ /**
+ * Does this cell contain valid value?
+ */
+ public function hasValidValue(): bool
+ {
+ $validator = new DataValidator();
+
+ return $validator->isValid($this);
+ }
+
+ /**
+ * Does this cell contain a Hyperlink?
+ *
+ * @throws SpreadsheetException
+ */
+ public function hasHyperlink(): bool
+ {
+ if (!isset($this->parent)) {
+ throw new SpreadsheetException('Cannot check for hyperlink when cell is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
+ }
+
+ /**
+ * Get Hyperlink.
+ *
+ * @throws SpreadsheetException
+ */
+ public function getHyperlink(): Hyperlink
+ {
+ if (!isset($this->parent)) {
+ throw new SpreadsheetException('Cannot get hyperlink for cell that is not bound to a worksheet');
+ }
+
+ return $this->getWorksheet()->getHyperlink($this->getCoordinate());
+ }
+
+ /**
+ * Set Hyperlink.
+ *
+ * @throws SpreadsheetException
+ */
+ public function setHyperlink(?Hyperlink $hyperlink = null): self
+ {
+ if (!isset($this->parent)) {
+ throw new SpreadsheetException('Cannot set hyperlink for cell that is not bound to a worksheet');
+ }
+
+ $this->getWorksheet()->setHyperlink($this->getCoordinate(), $hyperlink);
+
+ return $this->updateInCollection();
+ }
+
+ /**
+ * Get cell collection.
+ *
+ * @return ?Cells
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Get parent worksheet.
+ *
+ * @throws SpreadsheetException
+ */
+ public function getWorksheet(): Worksheet
+ {
+ $parent = $this->parent;
+ if ($parent !== null) {
+ $worksheet = $parent->getParent();
+ } else {
+ $worksheet = null;
+ }
+
+ if ($worksheet === null) {
+ throw new SpreadsheetException('Worksheet no longer exists');
+ }
+
+ return $worksheet;
+ }
+
+ public function getWorksheetOrNull(): ?Worksheet
+ {
+ $parent = $this->parent;
+ if ($parent !== null) {
+ $worksheet = $parent->getParent();
+ } else {
+ $worksheet = null;
+ }
+
+ return $worksheet;
+ }
+
+ /**
+ * Is this cell in a merge range.
+ */
+ public function isInMergeRange(): bool
+ {
+ return (bool) $this->getMergeRange();
+ }
+
+ /**
+ * Is this cell the master (top left cell) in a merge range (that holds the actual data value).
+ */
+ public function isMergeRangeValueCell(): bool
+ {
+ if ($mergeRange = $this->getMergeRange()) {
+ $mergeRange = Coordinate::splitRange($mergeRange);
+ [$startCell] = $mergeRange[0];
+
+ return $this->getCoordinate() === $startCell;
+ }
+
+ return false;
+ }
+
+ /**
+ * If this cell is in a merge range, then return the range.
+ *
+ * @return false|string
+ */
+ public function getMergeRange()
+ {
+ foreach ($this->getWorksheet()->getMergeCells() as $mergeRange) {
+ if ($this->isInRange($mergeRange)) {
+ return $mergeRange;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get cell style.
+ */
+ public function getStyle(): Style
+ {
+ return $this->getWorksheet()->getStyle($this->getCoordinate());
+ }
+
+ /**
+ * Get cell style.
+ */
+ public function getAppliedStyle(): Style
+ {
+ if ($this->getWorksheet()->conditionalStylesExists($this->getCoordinate()) === false) {
+ return $this->getStyle();
+ }
+ $range = $this->getWorksheet()->getConditionalRange($this->getCoordinate());
+ if ($range === null) {
+ return $this->getStyle();
+ }
+
+ $matcher = new CellStyleAssessor($this, $range);
+
+ return $matcher->matchConditions($this->getWorksheet()->getConditionalStyles($this->getCoordinate()));
+ }
+
+ /**
+ * Re-bind parent.
+ */
+ public function rebindParent(Worksheet $parent): self
+ {
+ $this->parent = $parent->getCellCollection();
+
+ return $this->updateInCollection();
+ }
+
+ /**
+ * Is cell in a specific range?
+ *
+ * @param string $range Cell range (e.g. A1:A1)
+ */
+ public function isInRange(string $range): bool
+ {
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($range);
+
+ // Translate properties
+ $myColumn = Coordinate::columnIndexFromString($this->getColumn());
+ $myRow = $this->getRow();
+
+ // Verify if cell is in range
+ return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn)
+ && ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
+ }
+
+ /**
+ * Compare 2 cells.
+ *
+ * @param Cell $a Cell a
+ * @param Cell $b Cell b
+ *
+ * @return int Result of comparison (always -1 or 1, never zero!)
+ */
+ public static function compareCells(self $a, self $b): int
+ {
+ if ($a->getRow() < $b->getRow()) {
+ return -1;
+ } elseif ($a->getRow() > $b->getRow()) {
+ return 1;
+ } elseif (Coordinate::columnIndexFromString($a->getColumn()) < Coordinate::columnIndexFromString($b->getColumn())) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Get value binder to use.
+ */
+ public static function getValueBinder(): IValueBinder
+ {
+ if (self::$valueBinder === null) {
+ self::$valueBinder = new DefaultValueBinder();
+ }
+
+ return self::$valueBinder;
+ }
+
+ /**
+ * Set value binder to use.
+ */
+ public static function setValueBinder(IValueBinder $binder): void
+ {
+ self::$valueBinder = $binder;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $propertyName => $propertyValue) {
+ if ((is_object($propertyValue)) && ($propertyName !== 'parent')) {
+ $this->$propertyName = clone $propertyValue;
+ } else {
+ $this->$propertyName = $propertyValue;
+ }
+ }
+ }
+
+ /**
+ * Get index to cellXf.
+ */
+ public function getXfIndex(): int
+ {
+ return $this->xfIndex;
+ }
+
+ /**
+ * Set index to cellXf.
+ */
+ public function setXfIndex(int $indexValue): self
+ {
+ $this->xfIndex = $indexValue;
+
+ return $this->updateInCollection();
+ }
+
+ /**
+ * Set the formula attributes.
+ *
+ * @return $this
+ */
+ public function setFormulaAttributes(mixed $attributes): self
+ {
+ $this->formulaAttributes = $attributes;
+
+ return $this;
+ }
+
+ /**
+ * Get the formula attributes.
+ *
+ * @return mixed
+ */
+ public function getFormulaAttributes()
+ {
+ return $this->formulaAttributes;
+ }
+
+ /**
+ * Convert to string.
+ */
+ public function __toString(): string
+ {
+ return (string) $this->getValue();
+ }
+
+ public function getIgnoredErrors(): IgnoredErrors
+ {
+ return $this->ignoredErrors;
+ }
+
+ public function isLocked(): bool
+ {
+ $protected = $this->parent?->getParent()?->getProtection()?->getSheet();
+ if ($protected !== true) {
+ return false;
+ }
+ $locked = $this->getStyle()->getProtection()->getLocked();
+
+ return $locked !== Protection::PROTECTION_UNPROTECTED;
+ }
+
+ public function isHiddenOnFormulaBar(): bool
+ {
+ if ($this->getDataType() !== DataType::TYPE_FORMULA) {
+ return false;
+ }
+ $protected = $this->parent?->getParent()?->getProtection()?->getSheet();
+ if ($protected !== true) {
+ return false;
+ }
+ $hidden = $this->getStyle()->getProtection()->getHidden();
+
+ return $hidden !== Protection::PROTECTION_UNPROTECTED;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php
new file mode 100644
index 0000000..d6dff0d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php
@@ -0,0 +1,148 @@
+cellAddress = str_replace('$', '', $cellAddress);
+ [$this->columnId, $this->rowId, $this->columnName] = Coordinate::indexesFromString($this->cellAddress);
+ $this->worksheet = $worksheet;
+ }
+
+ public function __destruct()
+ {
+ unset($this->worksheet);
+ }
+
+ /**
+ * @phpstan-assert int|numeric-string $columnId
+ * @phpstan-assert int|numeric-string $rowId
+ */
+ private static function validateColumnAndRow(mixed $columnId, mixed $rowId): void
+ {
+ if (!is_numeric($columnId) || $columnId <= 0 || !is_numeric($rowId) || $rowId <= 0) {
+ throw new Exception('Row and Column Ids must be positive integer values');
+ }
+ }
+
+ public static function fromColumnAndRow(mixed $columnId, mixed $rowId, ?Worksheet $worksheet = null): self
+ {
+ self::validateColumnAndRow($columnId, $rowId);
+
+ return new self(Coordinate::stringFromColumnIndex($columnId) . ((string) $rowId), $worksheet);
+ }
+
+ public static function fromColumnRowArray(array $array, ?Worksheet $worksheet = null): self
+ {
+ [$columnId, $rowId] = $array;
+
+ return self::fromColumnAndRow($columnId, $rowId, $worksheet);
+ }
+
+ public static function fromCellAddress(mixed $cellAddress, ?Worksheet $worksheet = null): self
+ {
+ return new self($cellAddress, $worksheet);
+ }
+
+ /**
+ * The returned address string will contain the worksheet name as well, if available,
+ * (ie. if a Worksheet was provided to the constructor).
+ * e.g. "'Mark''s Worksheet'!C5".
+ */
+ public function fullCellAddress(): string
+ {
+ if ($this->worksheet !== null) {
+ $title = str_replace("'", "''", $this->worksheet->getTitle());
+
+ return "'{$title}'!{$this->cellAddress}";
+ }
+
+ return $this->cellAddress;
+ }
+
+ public function worksheet(): ?Worksheet
+ {
+ return $this->worksheet;
+ }
+
+ /**
+ * The returned address string will contain just the column/row address,
+ * (even if a Worksheet was provided to the constructor).
+ * e.g. "C5".
+ */
+ public function cellAddress(): string
+ {
+ return $this->cellAddress;
+ }
+
+ public function rowId(): int
+ {
+ return $this->rowId;
+ }
+
+ public function columnId(): int
+ {
+ return $this->columnId;
+ }
+
+ public function columnName(): string
+ {
+ return $this->columnName;
+ }
+
+ public function nextRow(int $offset = 1): self
+ {
+ $newRowId = $this->rowId + $offset;
+ if ($newRowId < 1) {
+ $newRowId = 1;
+ }
+
+ return self::fromColumnAndRow($this->columnId, $newRowId);
+ }
+
+ public function previousRow(int $offset = 1): self
+ {
+ return $this->nextRow(0 - $offset);
+ }
+
+ public function nextColumn(int $offset = 1): self
+ {
+ $newColumnId = $this->columnId + $offset;
+ if ($newColumnId < 1) {
+ $newColumnId = 1;
+ }
+
+ return self::fromColumnAndRow($newColumnId, $this->rowId);
+ }
+
+ public function previousColumn(int $offset = 1): self
+ {
+ return $this->nextColumn(0 - $offset);
+ }
+
+ /**
+ * The returned address string will contain the worksheet name as well, if available,
+ * (ie. if a Worksheet was provided to the constructor).
+ * e.g. "'Mark''s Worksheet'!C5".
+ */
+ public function __toString(): string
+ {
+ return $this->fullCellAddress();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php
new file mode 100644
index 0000000..3063aa8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php
@@ -0,0 +1,137 @@
+validateFromTo($from, $to);
+ }
+
+ private function validateFromTo(CellAddress $from, CellAddress $to): void
+ {
+ // Identify actual top-left and bottom-right values (in case we've been given top-right and bottom-left)
+ $firstColumn = min($from->columnId(), $to->columnId());
+ $firstRow = min($from->rowId(), $to->rowId());
+ $lastColumn = max($from->columnId(), $to->columnId());
+ $lastRow = max($from->rowId(), $to->rowId());
+
+ $fromWorksheet = $from->worksheet();
+ $toWorksheet = $to->worksheet();
+ $this->validateWorksheets($fromWorksheet, $toWorksheet);
+
+ $this->from = $this->cellAddressWrapper($firstColumn, $firstRow, $fromWorksheet);
+ $this->to = $this->cellAddressWrapper($lastColumn, $lastRow, $toWorksheet);
+ }
+
+ private function validateWorksheets(?Worksheet $fromWorksheet, ?Worksheet $toWorksheet): void
+ {
+ if ($fromWorksheet !== null && $toWorksheet !== null) {
+ // We could simply compare worksheets rather than worksheet titles; but at some point we may introduce
+ // support for 3d ranges; and at that point we drop this check and let the validation fall through
+ // to the check for same workbook; but unless we check on titles, this test will also detect if the
+ // worksheets are in different spreadsheets, and the next check will never execute or throw its
+ // own exception.
+ if ($fromWorksheet->getTitle() !== $toWorksheet->getTitle()) {
+ throw new Exception('3d Cell Ranges are not supported');
+ } elseif ($fromWorksheet->getParent() !== $toWorksheet->getParent()) {
+ throw new Exception('Worksheets must be in the same spreadsheet');
+ }
+ }
+ }
+
+ private function cellAddressWrapper(int $column, int $row, ?Worksheet $worksheet = null): CellAddress
+ {
+ $cellAddress = Coordinate::stringFromColumnIndex($column) . (string) $row;
+
+ return new class ($cellAddress, $worksheet) extends CellAddress {
+ public function nextRow(int $offset = 1): CellAddress
+ {
+ /** @var CellAddress $result */
+ $result = parent::nextRow($offset);
+ $this->rowId = $result->rowId;
+ $this->cellAddress = $result->cellAddress;
+
+ return $this;
+ }
+
+ public function previousRow(int $offset = 1): CellAddress
+ {
+ /** @var CellAddress $result */
+ $result = parent::previousRow($offset);
+ $this->rowId = $result->rowId;
+ $this->cellAddress = $result->cellAddress;
+
+ return $this;
+ }
+
+ public function nextColumn(int $offset = 1): CellAddress
+ {
+ /** @var CellAddress $result */
+ $result = parent::nextColumn($offset);
+ $this->columnId = $result->columnId;
+ $this->columnName = $result->columnName;
+ $this->cellAddress = $result->cellAddress;
+
+ return $this;
+ }
+
+ public function previousColumn(int $offset = 1): CellAddress
+ {
+ /** @var CellAddress $result */
+ $result = parent::previousColumn($offset);
+ $this->columnId = $result->columnId;
+ $this->columnName = $result->columnName;
+ $this->cellAddress = $result->cellAddress;
+
+ return $this;
+ }
+ };
+ }
+
+ public function from(): CellAddress
+ {
+ // Re-order from/to in case the cell addresses have been modified
+ $this->validateFromTo($this->from, $this->to);
+
+ return $this->from;
+ }
+
+ public function to(): CellAddress
+ {
+ // Re-order from/to in case the cell addresses have been modified
+ $this->validateFromTo($this->from, $this->to);
+
+ return $this->to;
+ }
+
+ public function __toString(): string
+ {
+ // Re-order from/to in case the cell addresses have been modified
+ $this->validateFromTo($this->from, $this->to);
+
+ if ($this->from->cellAddress() === $this->to->cellAddress()) {
+ return "{$this->from->fullCellAddress()}";
+ }
+
+ $fromAddress = $this->from->fullCellAddress();
+ $toAddress = $this->to->cellAddress();
+
+ return "{$fromAddress}:{$toAddress}";
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php
new file mode 100644
index 0000000..e25b9b7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php
@@ -0,0 +1,122 @@
+validateFromTo(
+ Coordinate::columnIndexFromString($from),
+ Coordinate::columnIndexFromString($to ?? $from)
+ );
+ $this->worksheet = $worksheet;
+ }
+
+ public function __destruct()
+ {
+ $this->worksheet = null;
+ }
+
+ public static function fromColumnIndexes(int $from, int $to, ?Worksheet $worksheet = null): self
+ {
+ return new self(Coordinate::stringFromColumnIndex($from), Coordinate::stringFromColumnIndex($to), $worksheet);
+ }
+
+ /**
+ * @param array $array
+ */
+ public static function fromArray(array $array, ?Worksheet $worksheet = null): self
+ {
+ array_walk(
+ $array,
+ function (&$column): void {
+ $column = is_numeric($column) ? Coordinate::stringFromColumnIndex((int) $column) : $column;
+ }
+ );
+ /** @var string $from */
+ /** @var string $to */
+ [$from, $to] = $array;
+
+ return new self($from, $to, $worksheet);
+ }
+
+ private function validateFromTo(int $from, int $to): void
+ {
+ // Identify actual top and bottom values (in case we've been given bottom and top)
+ $this->from = min($from, $to);
+ $this->to = max($from, $to);
+ }
+
+ public function columnCount(): int
+ {
+ return $this->to - $this->from + 1;
+ }
+
+ public function shiftDown(int $offset = 1): self
+ {
+ $newFrom = $this->from + $offset;
+ $newFrom = ($newFrom < 1) ? 1 : $newFrom;
+
+ $newTo = $this->to + $offset;
+ $newTo = ($newTo < 1) ? 1 : $newTo;
+
+ return self::fromColumnIndexes($newFrom, $newTo, $this->worksheet);
+ }
+
+ public function shiftUp(int $offset = 1): self
+ {
+ return $this->shiftDown(0 - $offset);
+ }
+
+ public function from(): string
+ {
+ return Coordinate::stringFromColumnIndex($this->from);
+ }
+
+ public function to(): string
+ {
+ return Coordinate::stringFromColumnIndex($this->to);
+ }
+
+ public function fromIndex(): int
+ {
+ return $this->from;
+ }
+
+ public function toIndex(): int
+ {
+ return $this->to;
+ }
+
+ public function toCellRange(): CellRange
+ {
+ return new CellRange(
+ CellAddress::fromColumnAndRow($this->from, 1, $this->worksheet),
+ CellAddress::fromColumnAndRow($this->to, AddressRange::MAX_ROW)
+ );
+ }
+
+ public function __toString(): string
+ {
+ $from = $this->from();
+ $to = $this->to();
+
+ if ($this->worksheet !== null) {
+ $title = str_replace("'", "''", $this->worksheet->getTitle());
+
+ return "'{$title}'!{$from}:{$to}";
+ }
+
+ return "{$from}:{$to}";
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
new file mode 100644
index 0000000..81ad64d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
@@ -0,0 +1,683 @@
+\$?[A-Z]{1,3})(?\$?\d{1,7})$/i';
+ public const FULL_REFERENCE_REGEX = '/^(?:(?[^!]*)!)?(?(?[$]?[A-Z]{1,3}[$]?\d{1,7})(?:\:(?[$]?[A-Z]{1,3}[$]?\d{1,7}))?)$/i';
+
+ /**
+ * Default range variable constant.
+ *
+ * @var string
+ */
+ const DEFAULT_RANGE = 'A1:A1';
+
+ /**
+ * Convert string coordinate to [0 => int column index, 1 => int row index].
+ *
+ * @param string $cellAddress eg: 'A1'
+ *
+ * @return array{0: string, 1: string} Array containing column and row (indexes 0 and 1)
+ */
+ public static function coordinateFromString(string $cellAddress): array
+ {
+ if (preg_match(self::A1_COORDINATE_REGEX, $cellAddress, $matches)) {
+ return [$matches['col'], $matches['row']];
+ } elseif (self::coordinateIsRange($cellAddress)) {
+ throw new Exception('Cell coordinate string can not be a range of cells');
+ } elseif ($cellAddress == '') {
+ throw new Exception('Cell coordinate can not be zero-length string');
+ }
+
+ throw new Exception('Invalid cell coordinate ' . $cellAddress);
+ }
+
+ /**
+ * Convert string coordinate to [0 => int column index, 1 => int row index, 2 => string column string].
+ *
+ * @param string $coordinates eg: 'A1', '$B$12'
+ *
+ * @return array{0: int, 1: int, 2: string} Array containing column and row index, and column string
+ */
+ public static function indexesFromString(string $coordinates): array
+ {
+ [$column, $row] = self::coordinateFromString($coordinates);
+ $column = ltrim($column, '$');
+
+ return [
+ self::columnIndexFromString($column),
+ (int) ltrim($row, '$'),
+ $column,
+ ];
+ }
+
+ /**
+ * Checks if a Cell Address represents a range of cells.
+ *
+ * @param string $cellAddress eg: 'A1' or 'A1:A2' or 'A1:A2,C1:C2'
+ *
+ * @return bool Whether the coordinate represents a range of cells
+ */
+ public static function coordinateIsRange($cellAddress): bool
+ {
+ return str_contains($cellAddress, ':') || str_contains($cellAddress, ',');
+ }
+
+ /**
+ * Make string row, column or cell coordinate absolute.
+ *
+ * @param string $cellAddress e.g. 'A' or '1' or 'A1'
+ * Note that this value can be a row or column reference as well as a cell reference
+ *
+ * @return string Absolute coordinate e.g. '$A' or '$1' or '$A$1'
+ */
+ public static function absoluteReference($cellAddress)
+ {
+ if (self::coordinateIsRange($cellAddress)) {
+ throw new Exception('Cell coordinate string can not be a range of cells');
+ }
+
+ // Split out any worksheet name from the reference
+ [$worksheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ if ($worksheet > '') {
+ $worksheet .= '!';
+ }
+
+ // Create absolute coordinate
+ $cellAddress = "$cellAddress";
+ if (ctype_digit($cellAddress)) {
+ return $worksheet . '$' . $cellAddress;
+ } elseif (ctype_alpha($cellAddress)) {
+ return $worksheet . '$' . strtoupper($cellAddress);
+ }
+
+ return $worksheet . self::absoluteCoordinate($cellAddress);
+ }
+
+ /**
+ * Make string coordinate absolute.
+ *
+ * @param string $cellAddress e.g. 'A1'
+ *
+ * @return string Absolute coordinate e.g. '$A$1'
+ */
+ public static function absoluteCoordinate(string $cellAddress)
+ {
+ if (self::coordinateIsRange($cellAddress)) {
+ throw new Exception('Cell coordinate string can not be a range of cells');
+ }
+
+ // Split out any worksheet name from the coordinate
+ [$worksheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
+ if ($worksheet > '') {
+ $worksheet .= '!';
+ }
+
+ // Create absolute coordinate
+ [$column, $row] = self::coordinateFromString($cellAddress ?? 'A1');
+ $column = ltrim($column, '$');
+ $row = ltrim($row, '$');
+
+ return $worksheet . '$' . $column . '$' . $row;
+ }
+
+ /**
+ * Split range into coordinate strings.
+ *
+ * @param string $range e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4'
+ *
+ * @return array Array containing one or more arrays containing one or two coordinate strings
+ * e.g. ['B4','D9'] or [['B4','D9'], ['H2','O11']]
+ * or ['B4']
+ */
+ public static function splitRange($range)
+ {
+ // Ensure $pRange is a valid range
+ if (empty($range)) {
+ $range = self::DEFAULT_RANGE;
+ }
+
+ $exploded = explode(',', $range);
+ $outArray = [];
+ foreach ($exploded as $value) {
+ $outArray[] = explode(':', $value);
+ }
+
+ return $outArray;
+ }
+
+ /**
+ * Build range from coordinate strings.
+ *
+ * @param array $range Array containing one or more arrays containing one or two coordinate strings
+ *
+ * @return string String representation of $pRange
+ */
+ public static function buildRange(array $range)
+ {
+ // Verify range
+ if (empty($range) || !is_array($range[0])) {
+ throw new Exception('Range does not contain any information');
+ }
+
+ // Build range
+ $counter = count($range);
+ for ($i = 0; $i < $counter; ++$i) {
+ $range[$i] = implode(':', $range[$i]);
+ }
+
+ return implode(',', $range);
+ }
+
+ /**
+ * Calculate range boundaries.
+ *
+ * @param string $range Cell range, Single Cell, Row/Column Range (e.g. A1:A1, B2, B:C, 2:3)
+ *
+ * @return array Range coordinates [Start Cell, End Cell]
+ * where Start Cell and End Cell are arrays (Column Number, Row Number)
+ */
+ public static function rangeBoundaries(string $range): array
+ {
+ // Ensure $pRange is a valid range
+ if (empty($range)) {
+ $range = self::DEFAULT_RANGE;
+ }
+
+ // Uppercase coordinate
+ $range = strtoupper($range);
+
+ // Extract range
+ if (!str_contains($range, ':')) {
+ $rangeA = $rangeB = $range;
+ } else {
+ [$rangeA, $rangeB] = explode(':', $range);
+ }
+
+ if (is_numeric($rangeA) && is_numeric($rangeB)) {
+ $rangeA = 'A' . $rangeA;
+ $rangeB = AddressRange::MAX_COLUMN . $rangeB;
+ }
+
+ if (ctype_alpha($rangeA) && ctype_alpha($rangeB)) {
+ $rangeA = $rangeA . '1';
+ $rangeB = $rangeB . AddressRange::MAX_ROW;
+ }
+
+ // Calculate range outer borders
+ $rangeStart = self::coordinateFromString($rangeA);
+ $rangeEnd = self::coordinateFromString($rangeB);
+
+ // Translate column into index
+ $rangeStart[0] = self::columnIndexFromString($rangeStart[0]);
+ $rangeEnd[0] = self::columnIndexFromString($rangeEnd[0]);
+
+ return [$rangeStart, $rangeEnd];
+ }
+
+ /**
+ * Calculate range dimension.
+ *
+ * @param string $range Cell range, Single Cell, Row/Column Range (e.g. A1:A1, B2, B:C, 2:3)
+ *
+ * @return array Range dimension (width, height)
+ */
+ public static function rangeDimension(string $range)
+ {
+ // Calculate range outer borders
+ [$rangeStart, $rangeEnd] = self::rangeBoundaries($range);
+
+ return [($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1)];
+ }
+
+ /**
+ * Calculate range boundaries.
+ *
+ * @param string $range Cell range, Single Cell, Row/Column Range (e.g. A1:A1, B2, B:C, 2:3)
+ *
+ * @return array Range coordinates [Start Cell, End Cell]
+ * where Start Cell and End Cell are arrays [Column ID, Row Number]
+ */
+ public static function getRangeBoundaries(string $range)
+ {
+ [$rangeA, $rangeB] = self::rangeBoundaries($range);
+
+ return [
+ [self::stringFromColumnIndex($rangeA[0]), $rangeA[1]],
+ [self::stringFromColumnIndex($rangeB[0]), $rangeB[1]],
+ ];
+ }
+
+ /**
+ * Check if cell or range reference is valid and return an array with type of reference (cell or range), worksheet (if it was given)
+ * and the coordinate or the first coordinate and second coordinate if it is a range.
+ *
+ * @param string $reference Coordinate or Range (e.g. A1:A1, B2, B:C, 2:3)
+ *
+ * @return array reference data
+ */
+ private static function validateReferenceAndGetData($reference): array
+ {
+ $data = [];
+ preg_match(self::FULL_REFERENCE_REGEX, $reference, $matches);
+ if (count($matches) === 0) {
+ return ['type' => 'invalid'];
+ }
+
+ if (isset($matches['secondCoordinate'])) {
+ $data['type'] = 'range';
+ $data['firstCoordinate'] = str_replace('$', '', $matches['firstCoordinate']);
+ $data['secondCoordinate'] = str_replace('$', '', $matches['secondCoordinate']);
+ } else {
+ $data['type'] = 'coordinate';
+ $data['coordinate'] = str_replace('$', '', $matches['firstCoordinate']);
+ }
+
+ $worksheet = $matches['worksheet'];
+ if ($worksheet !== '') {
+ if (substr($worksheet, 0, 1) === "'" && substr($worksheet, -1, 1) === "'") {
+ $worksheet = substr($worksheet, 1, -1);
+ }
+ $data['worksheet'] = strtolower($worksheet);
+ }
+ $data['localReference'] = str_replace('$', '', $matches['localReference']);
+
+ return $data;
+ }
+
+ /**
+ * Check if coordinate is inside a range.
+ *
+ * @param string $range Cell range, Single Cell, Row/Column Range (e.g. A1:A1, B2, B:C, 2:3)
+ * @param string $coordinate Cell coordinate (e.g. A1)
+ *
+ * @return bool true if coordinate is inside range
+ */
+ public static function coordinateIsInsideRange(string $range, string $coordinate): bool
+ {
+ $rangeData = self::validateReferenceAndGetData($range);
+ if ($rangeData['type'] === 'invalid') {
+ throw new Exception('First argument needs to be a range');
+ }
+
+ $coordinateData = self::validateReferenceAndGetData($coordinate);
+ if ($coordinateData['type'] === 'invalid') {
+ throw new Exception('Second argument needs to be a single coordinate');
+ }
+
+ if (isset($coordinateData['worksheet']) && !isset($rangeData['worksheet'])) {
+ return false;
+ }
+ if (!isset($coordinateData['worksheet']) && isset($rangeData['worksheet'])) {
+ return false;
+ }
+
+ if (isset($coordinateData['worksheet'], $rangeData['worksheet'])) {
+ if ($coordinateData['worksheet'] !== $rangeData['worksheet']) {
+ return false;
+ }
+ }
+
+ $boundaries = self::rangeBoundaries($rangeData['localReference']);
+ $coordinates = self::indexesFromString($coordinateData['localReference']);
+
+ $columnIsInside = $boundaries[0][0] <= $coordinates[0] && $coordinates[0] <= $boundaries[1][0];
+ if (!$columnIsInside) {
+ return false;
+ }
+ $rowIsInside = $boundaries[0][1] <= $coordinates[1] && $coordinates[1] <= $boundaries[1][1];
+ if (!$rowIsInside) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Column index from string.
+ *
+ * @param ?string $columnAddress eg 'A'
+ *
+ * @return int Column index (A = 1)
+ */
+ public static function columnIndexFromString(?string $columnAddress): int
+ {
+ // Using a lookup cache adds a slight memory overhead, but boosts speed
+ // caching using a static within the method is faster than a class static,
+ // though it's additional memory overhead
+ static $indexCache = [];
+ $columnAddress = $columnAddress ?? '';
+
+ if (isset($indexCache[$columnAddress])) {
+ return $indexCache[$columnAddress];
+ }
+ // It's surprising how costly the strtoupper() and ord() calls actually are, so we use a lookup array
+ // rather than use ord() and make it case insensitive to get rid of the strtoupper() as well.
+ // Because it's a static, there's no significant memory overhead either.
+ static $columnLookup = [
+ 'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8, 'I' => 9, 'J' => 10,
+ 'K' => 11, 'L' => 12, 'M' => 13, 'N' => 14, 'O' => 15, 'P' => 16, 'Q' => 17, 'R' => 18, 'S' => 19,
+ 'T' => 20, 'U' => 21, 'V' => 22, 'W' => 23, 'X' => 24, 'Y' => 25, 'Z' => 26,
+ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8, 'i' => 9, 'j' => 10,
+ 'k' => 11, 'l' => 12, 'm' => 13, 'n' => 14, 'o' => 15, 'p' => 16, 'q' => 17, 'r' => 18, 's' => 19,
+ 't' => 20, 'u' => 21, 'v' => 22, 'w' => 23, 'x' => 24, 'y' => 25, 'z' => 26,
+ ];
+
+ // We also use the language construct isset() rather than the more costly strlen() function to match the
+ // length of $columnAddress for improved performance
+ if (isset($columnAddress[0])) {
+ if (!isset($columnAddress[1])) {
+ $indexCache[$columnAddress] = $columnLookup[$columnAddress];
+
+ return $indexCache[$columnAddress];
+ } elseif (!isset($columnAddress[2])) {
+ $indexCache[$columnAddress] = $columnLookup[$columnAddress[0]] * 26
+ + $columnLookup[$columnAddress[1]];
+
+ return $indexCache[$columnAddress];
+ } elseif (!isset($columnAddress[3])) {
+ $indexCache[$columnAddress] = $columnLookup[$columnAddress[0]] * 676
+ + $columnLookup[$columnAddress[1]] * 26
+ + $columnLookup[$columnAddress[2]];
+
+ return $indexCache[$columnAddress];
+ }
+ }
+
+ throw new Exception(
+ 'Column string index can not be ' . ((isset($columnAddress[0])) ? 'longer than 3 characters' : 'empty')
+ );
+ }
+
+ /**
+ * String from column index.
+ *
+ * @param int|numeric-string $columnIndex Column index (A = 1)
+ */
+ public static function stringFromColumnIndex(int|string $columnIndex): string
+ {
+ static $indexCache = [];
+ static $lookupCache = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+ if (!isset($indexCache[$columnIndex])) {
+ $indexValue = $columnIndex;
+ $base26 = '';
+ do {
+ $characterValue = ($indexValue % 26) ?: 26;
+ $indexValue = ($indexValue - $characterValue) / 26;
+ $base26 = $lookupCache[$characterValue] . $base26;
+ } while ($indexValue > 0);
+ $indexCache[$columnIndex] = $base26;
+ }
+
+ return $indexCache[$columnIndex];
+ }
+
+ /**
+ * Extract all cell references in range, which may be comprised of multiple cell ranges.
+ *
+ * @param string $cellRange Range: e.g. 'A1' or 'A1:C10' or 'A1:E10,A20:E25' or 'A1:E5 C3:G7' or 'A1:C1,A3:C3 B1:C3'
+ *
+ * @return array Array containing single cell references
+ */
+ public static function extractAllCellReferencesInRange($cellRange): array
+ {
+ if (substr_count($cellRange, '!') > 1) {
+ throw new Exception('3-D Range References are not supported');
+ }
+
+ [$worksheet, $cellRange] = Worksheet::extractSheetTitle($cellRange, true);
+ $quoted = '';
+ if ($worksheet) {
+ $quoted = Worksheet::nameRequiresQuotes($worksheet) ? "'" : '';
+ if (str_starts_with($worksheet, "'") && str_ends_with($worksheet, "'")) {
+ $worksheet = substr($worksheet, 1, -1);
+ }
+ $worksheet = str_replace("'", "''", $worksheet);
+ }
+ [$ranges, $operators] = self::getCellBlocksFromRangeString($cellRange ?? 'A1');
+
+ $cells = [];
+ foreach ($ranges as $range) {
+ $cells[] = self::getReferencesForCellBlock($range);
+ }
+
+ $cells = self::processRangeSetOperators($operators, $cells);
+
+ if (empty($cells)) {
+ return [];
+ }
+
+ $cellList = array_merge(...$cells);
+
+ return array_map(
+ fn ($cellAddress) => ($worksheet !== '') ? "{$quoted}{$worksheet}{$quoted}!{$cellAddress}" : $cellAddress,
+ self::sortCellReferenceArray($cellList)
+ );
+ }
+
+ private static function processRangeSetOperators(array $operators, array $cells): array
+ {
+ $operatorCount = count($operators);
+ for ($offset = 0; $offset < $operatorCount; ++$offset) {
+ $operator = $operators[$offset];
+ if ($operator !== ' ') {
+ continue;
+ }
+
+ $cells[$offset] = array_intersect($cells[$offset], $cells[$offset + 1]);
+ unset($operators[$offset], $cells[$offset + 1]);
+ $operators = array_values($operators);
+ $cells = array_values($cells);
+ --$offset;
+ --$operatorCount;
+ }
+
+ return $cells;
+ }
+
+ private static function sortCellReferenceArray(array $cellList): array
+ {
+ // Sort the result by column and row
+ $sortKeys = [];
+ foreach ($cellList as $coordinate) {
+ $column = '';
+ $row = 0;
+ sscanf($coordinate, '%[A-Z]%d', $column, $row);
+ $key = (--$row * 16384) + self::columnIndexFromString((string) $column);
+ $sortKeys[$key] = $coordinate;
+ }
+ ksort($sortKeys);
+
+ return array_values($sortKeys);
+ }
+
+ /**
+ * Get all cell references for an individual cell block.
+ *
+ * @param string $cellBlock A cell range e.g. A4:B5
+ *
+ * @return array All individual cells in that range
+ */
+ private static function getReferencesForCellBlock($cellBlock)
+ {
+ $returnValue = [];
+
+ // Single cell?
+ if (!self::coordinateIsRange($cellBlock)) {
+ return (array) $cellBlock;
+ }
+
+ // Range...
+ $ranges = self::splitRange($cellBlock);
+ foreach ($ranges as $range) {
+ // Single cell?
+ if (!isset($range[1])) {
+ $returnValue[] = $range[0];
+
+ continue;
+ }
+
+ // Range...
+ [$rangeStart, $rangeEnd] = $range;
+ [$startColumn, $startRow] = self::coordinateFromString($rangeStart);
+ [$endColumn, $endRow] = self::coordinateFromString($rangeEnd);
+ $startColumnIndex = self::columnIndexFromString($startColumn);
+ $endColumnIndex = self::columnIndexFromString($endColumn);
+ ++$endColumnIndex;
+
+ // Current data
+ $currentColumnIndex = $startColumnIndex;
+ $currentRow = $startRow;
+
+ self::validateRange($cellBlock, $startColumnIndex, $endColumnIndex, (int) $currentRow, (int) $endRow);
+
+ // Loop cells
+ while ($currentColumnIndex < $endColumnIndex) {
+ while ($currentRow <= $endRow) {
+ $returnValue[] = self::stringFromColumnIndex($currentColumnIndex) . $currentRow;
+ ++$currentRow;
+ }
+ ++$currentColumnIndex;
+ $currentRow = $startRow;
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Convert an associative array of single cell coordinates to values to an associative array
+ * of cell ranges to values. Only adjacent cell coordinates with the same
+ * value will be merged. If the value is an object, it must implement the method getHashCode().
+ *
+ * For example, this function converts:
+ *
+ * [ 'A1' => 'x', 'A2' => 'x', 'A3' => 'x', 'A4' => 'y' ]
+ *
+ * to:
+ *
+ * [ 'A1:A3' => 'x', 'A4' => 'y' ]
+ *
+ * @param array $coordinateCollection associative array mapping coordinates to values
+ *
+ * @return array associative array mapping coordinate ranges to valuea
+ */
+ public static function mergeRangesInCollection(array $coordinateCollection)
+ {
+ $hashedValues = [];
+ $mergedCoordCollection = [];
+
+ foreach ($coordinateCollection as $coord => $value) {
+ if (self::coordinateIsRange($coord)) {
+ $mergedCoordCollection[$coord] = $value;
+
+ continue;
+ }
+
+ [$column, $row] = self::coordinateFromString($coord);
+ $row = (int) (ltrim($row, '$'));
+ $hashCode = $column . '-' . ((is_object($value) && method_exists($value, 'getHashCode')) ? $value->getHashCode() : $value);
+
+ if (!isset($hashedValues[$hashCode])) {
+ $hashedValues[$hashCode] = (object) [
+ 'value' => $value,
+ 'col' => $column,
+ 'rows' => [$row],
+ ];
+ } else {
+ $hashedValues[$hashCode]->rows[] = $row;
+ }
+ }
+
+ ksort($hashedValues);
+
+ foreach ($hashedValues as $hashedValue) {
+ sort($hashedValue->rows);
+ $rowStart = null;
+ $rowEnd = null;
+ $ranges = [];
+
+ foreach ($hashedValue->rows as $row) {
+ if ($rowStart === null) {
+ $rowStart = $row;
+ $rowEnd = $row;
+ } elseif ($rowEnd === $row - 1) {
+ $rowEnd = $row;
+ } else {
+ if ($rowStart == $rowEnd) {
+ $ranges[] = $hashedValue->col . $rowStart;
+ } else {
+ $ranges[] = $hashedValue->col . $rowStart . ':' . $hashedValue->col . $rowEnd;
+ }
+
+ $rowStart = $row;
+ $rowEnd = $row;
+ }
+ }
+
+ if ($rowStart !== null) {
+ if ($rowStart == $rowEnd) {
+ $ranges[] = $hashedValue->col . $rowStart;
+ } else {
+ $ranges[] = $hashedValue->col . $rowStart . ':' . $hashedValue->col . $rowEnd;
+ }
+ }
+
+ foreach ($ranges as $range) {
+ $mergedCoordCollection[$range] = $hashedValue->value;
+ }
+ }
+
+ return $mergedCoordCollection;
+ }
+
+ /**
+ * Get the individual cell blocks from a range string, removing any $ characters.
+ * then splitting by operators and returning an array with ranges and operators.
+ *
+ * @param string $rangeString
+ *
+ * @return array[]
+ */
+ private static function getCellBlocksFromRangeString($rangeString)
+ {
+ $rangeString = str_replace('$', '', strtoupper($rangeString));
+
+ // split range sets on intersection (space) or union (,) operators
+ $tokens = preg_split('/([ ,])/', $rangeString, -1, PREG_SPLIT_DELIM_CAPTURE) ?: [];
+ $split = array_chunk($tokens, 2);
+ $ranges = array_column($split, 0);
+ $operators = array_column($split, 1);
+
+ return [$ranges, $operators];
+ }
+
+ /**
+ * Check that the given range is valid, i.e. that the start column and row are not greater than the end column and
+ * row.
+ *
+ * @param string $cellBlock The original range, for displaying a meaningful error message
+ * @param int $startColumnIndex
+ * @param int $endColumnIndex
+ * @param int $currentRow
+ * @param int $endRow
+ */
+ private static function validateRange(string $cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow): void
+ {
+ if ($startColumnIndex >= $endColumnIndex || $currentRow > $endRow) {
+ throw new Exception('Invalid range: "' . $cellBlock . '"');
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
new file mode 100644
index 0000000..c887d5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
@@ -0,0 +1,89 @@
+
+ */
+ private static $errorCodes = [
+ '#NULL!' => 0,
+ '#DIV/0!' => 1,
+ '#VALUE!' => 2,
+ '#REF!' => 3,
+ '#NAME?' => 4,
+ '#NUM!' => 5,
+ '#N/A' => 6,
+ '#CALC!' => 7,
+ ];
+
+ public const MAX_STRING_LENGTH = 32767;
+
+ /**
+ * Get list of error codes.
+ *
+ * @return array
+ */
+ public static function getErrorCodes()
+ {
+ return self::$errorCodes;
+ }
+
+ /**
+ * Check a string that it satisfies Excel requirements.
+ *
+ * @param null|RichText|string $textValue Value to sanitize to an Excel string
+ *
+ * @return RichText|string Sanitized value
+ */
+ public static function checkString($textValue): RichText|string|array
+ {
+ if ($textValue instanceof RichText) {
+ // TODO: Sanitize Rich-Text string (max. character count is 32,767)
+ return $textValue;
+ }
+
+ // string must never be longer than 32,767 characters, truncate if necessary
+ $textValue = StringHelper::substring((string) $textValue, 0, self::MAX_STRING_LENGTH);
+
+ // we require that newline is represented as "\n" in core, not as "\r\n" or "\r"
+ $textValue = str_replace(["\r\n", "\r"], "\n", $textValue);
+
+ return $textValue;
+ }
+
+ /**
+ * Check a value that it is a valid error code.
+ *
+ * @param mixed $value Value to sanitize to an Excel error code
+ *
+ * @return string Sanitized value
+ */
+ public static function checkErrorCode(mixed $value): string
+ {
+ $value = (string) $value;
+
+ if (!isset(self::$errorCodes[$value])) {
+ $value = '#NULL!';
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
new file mode 100644
index 0000000..22b285b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
@@ -0,0 +1,496 @@
+formula1;
+ }
+
+ /**
+ * Set Formula 1.
+ *
+ * @param float|int|string $formula usually string, but can be number (test for equal)
+ *
+ * @return $this
+ */
+ public function setFormula1($formula): static
+ {
+ $this->formula1 = (string) $formula;
+
+ return $this;
+ }
+
+ /**
+ * Get Formula 2.
+ *
+ * @return string
+ */
+ public function getFormula2()
+ {
+ return $this->formula2;
+ }
+
+ /**
+ * Set Formula 2.
+ *
+ * @param float|int|string $formula usually string, but can be number (test for equal)
+ *
+ * @return $this
+ */
+ public function setFormula2($formula): static
+ {
+ $this->formula2 = (string) $formula;
+
+ return $this;
+ }
+
+ /**
+ * Get Type.
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Set Type.
+ *
+ * @param string $type
+ *
+ * @return $this
+ */
+ public function setType($type): static
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ /**
+ * Get Error style.
+ *
+ * @return string
+ */
+ public function getErrorStyle()
+ {
+ return $this->errorStyle;
+ }
+
+ /**
+ * Set Error style.
+ *
+ * @param string $errorStyle see self::STYLE_*
+ *
+ * @return $this
+ */
+ public function setErrorStyle($errorStyle): static
+ {
+ $this->errorStyle = $errorStyle;
+
+ return $this;
+ }
+
+ /**
+ * Get Operator.
+ *
+ * @return string
+ */
+ public function getOperator()
+ {
+ return $this->operator;
+ }
+
+ /**
+ * Set Operator.
+ *
+ * @param string $operator
+ *
+ * @return $this
+ */
+ public function setOperator($operator): static
+ {
+ $this->operator = $operator;
+
+ return $this;
+ }
+
+ /**
+ * Get Allow Blank.
+ *
+ * @return bool
+ */
+ public function getAllowBlank()
+ {
+ return $this->allowBlank;
+ }
+
+ /**
+ * Set Allow Blank.
+ *
+ * @param bool $allowBlank
+ *
+ * @return $this
+ */
+ public function setAllowBlank($allowBlank): static
+ {
+ $this->allowBlank = $allowBlank;
+
+ return $this;
+ }
+
+ /**
+ * Get Show DropDown.
+ *
+ * @return bool
+ */
+ public function getShowDropDown()
+ {
+ return $this->showDropDown;
+ }
+
+ /**
+ * Set Show DropDown.
+ *
+ * @param bool $showDropDown
+ *
+ * @return $this
+ */
+ public function setShowDropDown($showDropDown): static
+ {
+ $this->showDropDown = $showDropDown;
+
+ return $this;
+ }
+
+ /**
+ * Get Show InputMessage.
+ *
+ * @return bool
+ */
+ public function getShowInputMessage()
+ {
+ return $this->showInputMessage;
+ }
+
+ /**
+ * Set Show InputMessage.
+ *
+ * @param bool $showInputMessage
+ *
+ * @return $this
+ */
+ public function setShowInputMessage($showInputMessage): static
+ {
+ $this->showInputMessage = $showInputMessage;
+
+ return $this;
+ }
+
+ /**
+ * Get Show ErrorMessage.
+ *
+ * @return bool
+ */
+ public function getShowErrorMessage()
+ {
+ return $this->showErrorMessage;
+ }
+
+ /**
+ * Set Show ErrorMessage.
+ *
+ * @param bool $showErrorMessage
+ *
+ * @return $this
+ */
+ public function setShowErrorMessage($showErrorMessage): static
+ {
+ $this->showErrorMessage = $showErrorMessage;
+
+ return $this;
+ }
+
+ /**
+ * Get Error title.
+ *
+ * @return string
+ */
+ public function getErrorTitle()
+ {
+ return $this->errorTitle;
+ }
+
+ /**
+ * Set Error title.
+ *
+ * @param string $errorTitle
+ *
+ * @return $this
+ */
+ public function setErrorTitle($errorTitle): static
+ {
+ $this->errorTitle = $errorTitle;
+
+ return $this;
+ }
+
+ /**
+ * Get Error.
+ *
+ * @return string
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Set Error.
+ *
+ * @param string $error
+ *
+ * @return $this
+ */
+ public function setError($error): static
+ {
+ $this->error = $error;
+
+ return $this;
+ }
+
+ /**
+ * Get Prompt title.
+ *
+ * @return string
+ */
+ public function getPromptTitle()
+ {
+ return $this->promptTitle;
+ }
+
+ /**
+ * Set Prompt title.
+ *
+ * @param string $promptTitle
+ *
+ * @return $this
+ */
+ public function setPromptTitle($promptTitle): static
+ {
+ $this->promptTitle = $promptTitle;
+
+ return $this;
+ }
+
+ /**
+ * Get Prompt.
+ *
+ * @return string
+ */
+ public function getPrompt()
+ {
+ return $this->prompt;
+ }
+
+ /**
+ * Set Prompt.
+ *
+ * @param string $prompt
+ *
+ * @return $this
+ */
+ public function setPrompt($prompt): static
+ {
+ $this->prompt = $prompt;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ return md5(
+ $this->formula1
+ . $this->formula2
+ . $this->type
+ . $this->errorStyle
+ . $this->operator
+ . ($this->allowBlank ? 't' : 'f')
+ . ($this->showDropDown ? 't' : 'f')
+ . ($this->showInputMessage ? 't' : 'f')
+ . ($this->showErrorMessage ? 't' : 'f')
+ . $this->errorTitle
+ . $this->error
+ . $this->promptTitle
+ . $this->prompt
+ . $this->sqref
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ private ?string $sqref = null;
+
+ public function getSqref(): ?string
+ {
+ return $this->sqref;
+ }
+
+ public function setSqref(?string $str): self
+ {
+ $this->sqref = $str;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
new file mode 100644
index 0000000..a3524df
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
@@ -0,0 +1,117 @@
+hasDataValidation() || $cell->getDataValidation()->getType() === DataValidation::TYPE_NONE) {
+ return true;
+ }
+
+ $cellValue = $cell->getValue();
+ $dataValidation = $cell->getDataValidation();
+
+ if (!$dataValidation->getAllowBlank() && ($cellValue === null || $cellValue === '')) {
+ return false;
+ }
+
+ $returnValue = false;
+ $type = $dataValidation->getType();
+ if ($type === DataValidation::TYPE_LIST) {
+ $returnValue = $this->isValueInList($cell);
+ } elseif ($type === DataValidation::TYPE_WHOLE) {
+ if (!is_numeric($cellValue) || fmod((float) $cellValue, 1) != 0) {
+ $returnValue = false;
+ } else {
+ $returnValue = $this->numericOperator($dataValidation, (int) $cellValue);
+ }
+ } elseif ($type === DataValidation::TYPE_DECIMAL || $type === DataValidation::TYPE_DATE || $type === DataValidation::TYPE_TIME) {
+ if (!is_numeric($cellValue)) {
+ $returnValue = false;
+ } else {
+ $returnValue = $this->numericOperator($dataValidation, (float) $cellValue);
+ }
+ } elseif ($type === DataValidation::TYPE_TEXTLENGTH) {
+ $returnValue = $this->numericOperator($dataValidation, mb_strlen((string) $cellValue));
+ }
+
+ return $returnValue;
+ }
+
+ private function numericOperator(DataValidation $dataValidation, int|float $cellValue): bool
+ {
+ $operator = $dataValidation->getOperator();
+ $formula1 = $dataValidation->getFormula1();
+ $formula2 = $dataValidation->getFormula2();
+ $returnValue = false;
+ if ($operator === DataValidation::OPERATOR_BETWEEN) {
+ $returnValue = $cellValue >= $formula1 && $cellValue <= $formula2;
+ } elseif ($operator === DataValidation::OPERATOR_NOTBETWEEN) {
+ $returnValue = $cellValue < $formula1 || $cellValue > $formula2;
+ } elseif ($operator === DataValidation::OPERATOR_EQUAL) {
+ $returnValue = $cellValue == $formula1;
+ } elseif ($operator === DataValidation::OPERATOR_NOTEQUAL) {
+ $returnValue = $cellValue != $formula1;
+ } elseif ($operator === DataValidation::OPERATOR_LESSTHAN) {
+ $returnValue = $cellValue < $formula1;
+ } elseif ($operator === DataValidation::OPERATOR_LESSTHANOREQUAL) {
+ $returnValue = $cellValue <= $formula1;
+ } elseif ($operator === DataValidation::OPERATOR_GREATERTHAN) {
+ $returnValue = $cellValue > $formula1;
+ } elseif ($operator === DataValidation::OPERATOR_GREATERTHANOREQUAL) {
+ $returnValue = $cellValue >= $formula1;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Does this cell contain valid value, based on list?
+ *
+ * @param Cell $cell Cell to check the value
+ */
+ private function isValueInList(Cell $cell): bool
+ {
+ $cellValue = $cell->getValue();
+ $dataValidation = $cell->getDataValidation();
+
+ $formula1 = $dataValidation->getFormula1();
+ if (!empty($formula1)) {
+ // inline values list
+ if ($formula1[0] === '"') {
+ return in_array(strtolower($cellValue), explode(',', strtolower(trim($formula1, '"'))), true);
+ } elseif (strpos($formula1, ':') > 0) {
+ // values list cells
+ $matchFormula = '=MATCH(' . $cell->getCoordinate() . ', ' . $formula1 . ', 0)';
+ $calculation = Calculation::getInstance($cell->getWorksheet()->getParent());
+
+ try {
+ $result = $calculation->calculateFormula($matchFormula, $cell->getCoordinate(), $cell);
+ while (is_array($result)) {
+ $result = array_pop($result);
+ }
+
+ return $result !== ExcelError::NA();
+ } catch (Exception) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
new file mode 100644
index 0000000..f86e37c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
@@ -0,0 +1,83 @@
+format('Y-m-d H:i:s');
+ } elseif ($value instanceof Stringable) {
+ $value = (string) $value;
+ } else {
+ throw new SpreadsheetException('Unable to bind unstringable ' . gettype($value));
+ }
+
+ // Set value explicit
+ $cell->setValueExplicit($value, static::dataTypeForValue($value));
+
+ // Done!
+ return true;
+ }
+
+ /**
+ * DataType for value.
+ *
+ * @return string
+ */
+ public static function dataTypeForValue(mixed $value)
+ {
+ // Match the value against a few data types
+ if ($value === null) {
+ return DataType::TYPE_NULL;
+ } elseif (is_float($value) || is_int($value)) {
+ return DataType::TYPE_NUMERIC;
+ } elseif (is_bool($value)) {
+ return DataType::TYPE_BOOL;
+ } elseif ($value === '') {
+ return DataType::TYPE_STRING;
+ } elseif ($value instanceof RichText) {
+ return DataType::TYPE_INLINE;
+ } elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
+ return DataType::TYPE_FORMULA;
+ } elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $value)) {
+ $tValue = ltrim($value, '+-');
+ if (is_string($value) && strlen($tValue) > 1 && $tValue[0] === '0' && $tValue[1] !== '.') {
+ return DataType::TYPE_STRING;
+ } elseif ((!str_contains($value, '.')) && ($value > PHP_INT_MAX)) {
+ return DataType::TYPE_STRING;
+ } elseif (!is_numeric($value)) {
+ return DataType::TYPE_STRING;
+ }
+
+ return DataType::TYPE_NUMERIC;
+ } elseif (is_string($value)) {
+ $errorCodes = DataType::getErrorCodes();
+ if (isset($errorCodes[$value])) {
+ return DataType::TYPE_ERROR;
+ }
+ }
+
+ return DataType::TYPE_STRING;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
new file mode 100644
index 0000000..a296c62
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
@@ -0,0 +1,108 @@
+url = $url;
+ $this->tooltip = $tooltip;
+ }
+
+ /**
+ * Get URL.
+ *
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * Set URL.
+ *
+ * @param string $url
+ *
+ * @return $this
+ */
+ public function setUrl($url): static
+ {
+ $this->url = $url;
+
+ return $this;
+ }
+
+ /**
+ * Get tooltip.
+ *
+ * @return string
+ */
+ public function getTooltip()
+ {
+ return $this->tooltip;
+ }
+
+ /**
+ * Set tooltip.
+ *
+ * @param string $tooltip
+ *
+ * @return $this
+ */
+ public function setTooltip($tooltip): static
+ {
+ $this->tooltip = $tooltip;
+
+ return $this;
+ }
+
+ /**
+ * Is this hyperlink internal? (to another worksheet).
+ */
+ public function isInternal(): bool
+ {
+ return str_contains($this->url, 'sheet://');
+ }
+
+ public function getTypeHyperlink(): string
+ {
+ return $this->isInternal() ? '' : 'External';
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ return md5(
+ $this->url
+ . $this->tooltip
+ . __CLASS__
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
new file mode 100644
index 0000000..5af9f5f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
@@ -0,0 +1,16 @@
+numberStoredAsText = $value;
+
+ return $this;
+ }
+
+ public function getNumberStoredAsText(): bool
+ {
+ return $this->numberStoredAsText;
+ }
+
+ public function setFormula(bool $value): self
+ {
+ $this->formula = $value;
+
+ return $this;
+ }
+
+ public function getFormula(): bool
+ {
+ return $this->formula;
+ }
+
+ public function setTwoDigitTextYear(bool $value): self
+ {
+ $this->twoDigitTextYear = $value;
+
+ return $this;
+ }
+
+ public function getTwoDigitTextYear(): bool
+ {
+ return $this->twoDigitTextYear;
+ }
+
+ public function setEvalError(bool $value): self
+ {
+ $this->evalError = $value;
+
+ return $this;
+ }
+
+ public function getEvalError(): bool
+ {
+ return $this->evalError;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php
new file mode 100644
index 0000000..cb2f6b0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php
@@ -0,0 +1,90 @@
+validateFromTo($from, $to ?? $from);
+ $this->worksheet = $worksheet;
+ }
+
+ public function __destruct()
+ {
+ $this->worksheet = null;
+ }
+
+ public static function fromArray(array $array, ?Worksheet $worksheet = null): self
+ {
+ [$from, $to] = $array;
+
+ return new self($from, $to, $worksheet);
+ }
+
+ private function validateFromTo(int $from, int $to): void
+ {
+ // Identify actual top and bottom values (in case we've been given bottom and top)
+ $this->from = min($from, $to);
+ $this->to = max($from, $to);
+ }
+
+ public function from(): int
+ {
+ return $this->from;
+ }
+
+ public function to(): int
+ {
+ return $this->to;
+ }
+
+ public function rowCount(): int
+ {
+ return $this->to - $this->from + 1;
+ }
+
+ public function shiftRight(int $offset = 1): self
+ {
+ $newFrom = $this->from + $offset;
+ $newFrom = ($newFrom < 1) ? 1 : $newFrom;
+
+ $newTo = $this->to + $offset;
+ $newTo = ($newTo < 1) ? 1 : $newTo;
+
+ return new self($newFrom, $newTo, $this->worksheet);
+ }
+
+ public function shiftLeft(int $offset = 1): self
+ {
+ return $this->shiftRight(0 - $offset);
+ }
+
+ public function toCellRange(): CellRange
+ {
+ return new CellRange(
+ CellAddress::fromColumnAndRow(Coordinate::columnIndexFromString('A'), $this->from, $this->worksheet),
+ CellAddress::fromColumnAndRow(Coordinate::columnIndexFromString(AddressRange::MAX_COLUMN), $this->to)
+ );
+ }
+
+ public function __toString(): string
+ {
+ if ($this->worksheet !== null) {
+ $title = str_replace("'", "''", $this->worksheet->getTitle());
+
+ return "'{$title}'!{$this->from}:{$this->to}";
+ }
+
+ return "{$this->from}:{$this->to}";
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
new file mode 100644
index 0000000..f4c6321
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
@@ -0,0 +1,130 @@
+convertNull = $suppressConversion;
+
+ return $this;
+ }
+
+ public function setBooleanConversion(bool $suppressConversion = false): self
+ {
+ $this->convertBoolean = $suppressConversion;
+
+ return $this;
+ }
+
+ public function getBooleanConversion(): bool
+ {
+ return $this->convertBoolean;
+ }
+
+ public function setNumericConversion(bool $suppressConversion = false): self
+ {
+ $this->convertNumeric = $suppressConversion;
+
+ return $this;
+ }
+
+ public function setFormulaConversion(bool $suppressConversion = false): self
+ {
+ $this->convertFormula = $suppressConversion;
+
+ return $this;
+ }
+
+ public function setConversionForAllValueTypes(bool $suppressConversion = false): self
+ {
+ $this->convertNull = $suppressConversion;
+ $this->convertBoolean = $suppressConversion;
+ $this->convertNumeric = $suppressConversion;
+ $this->convertFormula = $suppressConversion;
+
+ return $this;
+ }
+
+ /**
+ * Bind value to a cell.
+ *
+ * @param Cell $cell Cell to bind value to
+ * @param mixed $value Value to bind in cell
+ */
+ public function bindValue(Cell $cell, $value): bool
+ {
+ if (is_object($value)) {
+ return $this->bindObjectValue($cell, $value);
+ }
+ if ($value !== null && !is_scalar($value)) {
+ throw new SpreadsheetException('Unable to bind unstringable ' . gettype($value));
+ }
+
+ // sanitize UTF-8 strings
+ if (is_string($value)) {
+ $value = StringHelper::sanitizeUTF8($value);
+ }
+
+ if ($value === null && $this->convertNull === false) {
+ $cell->setValueExplicit($value, DataType::TYPE_NULL);
+ } elseif (is_bool($value) && $this->convertBoolean === false) {
+ $cell->setValueExplicit($value, DataType::TYPE_BOOL);
+ } elseif ((is_int($value) || is_float($value)) && $this->convertNumeric === false) {
+ $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+ } elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false) {
+ $cell->setValueExplicit($value, DataType::TYPE_FORMULA);
+ } else {
+ if (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
+ $cell->getStyle()->setQuotePrefix(true);
+ }
+ $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
+ }
+
+ return true;
+ }
+
+ protected function bindObjectValue(Cell $cell, object $value): bool
+ {
+ // Handle any objects that might be injected
+ if ($value instanceof DateTimeInterface) {
+ $value = $value->format('Y-m-d H:i:s');
+ $cell->setValueExplicit($value, DataType::TYPE_STRING);
+ } elseif ($value instanceof RichText) {
+ $cell->setValueExplicit($value, DataType::TYPE_INLINE);
+ } elseif ($value instanceof Stringable) {
+ $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
+ } else {
+ throw new SpreadsheetException('Unable to bind unstringable object of type ' . get_class($value));
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php
new file mode 100644
index 0000000..b2f18f6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php
@@ -0,0 +1,119 @@
+beforeCellAddress = str_replace('$', '', $beforeCellAddress);
+ $this->numberOfColumns = $numberOfColumns;
+ $this->numberOfRows = $numberOfRows;
+
+ // Get coordinate of $beforeCellAddress
+ [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
+ $this->beforeColumn = Coordinate::columnIndexFromString($beforeColumn);
+ $this->beforeRow = (int) $beforeRow;
+ }
+
+ public function beforeCellAddress(): string
+ {
+ return $this->beforeCellAddress;
+ }
+
+ public function refreshRequired(string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): bool
+ {
+ return $this->beforeCellAddress !== $beforeCellAddress
+ || $this->numberOfColumns !== $numberOfColumns
+ || $this->numberOfRows !== $numberOfRows;
+ }
+
+ public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
+ {
+ if (Coordinate::coordinateIsRange($cellReference)) {
+ throw new Exception('Only single cell references may be passed to this method.');
+ }
+
+ // Get coordinate of $cellReference
+ [$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
+ $newColumnIndex = Coordinate::columnIndexFromString(str_replace('$', '', $newColumn));
+ $newRowIndex = (int) str_replace('$', '', $newRow);
+
+ $absoluteColumn = $newColumn[0] === '$' ? '$' : '';
+ $absoluteRow = $newRow[0] === '$' ? '$' : '';
+ // Verify which parts should be updated
+ if ($onlyAbsoluteReferences === true) {
+ $updateColumn = (($absoluteColumn === '$') && $newColumnIndex >= $this->beforeColumn);
+ $updateRow = (($absoluteRow === '$') && $newRowIndex >= $this->beforeRow);
+ } elseif ($includeAbsoluteReferences === false) {
+ $updateColumn = (($absoluteColumn !== '$') && $newColumnIndex >= $this->beforeColumn);
+ $updateRow = (($absoluteRow !== '$') && $newRowIndex >= $this->beforeRow);
+ } else {
+ $updateColumn = ($newColumnIndex >= $this->beforeColumn);
+ $updateRow = ($newRowIndex >= $this->beforeRow);
+ }
+
+ // Create new column reference
+ if ($updateColumn) {
+ $newColumn = $this->updateColumnReference($newColumnIndex, $absoluteColumn);
+ }
+
+ // Create new row reference
+ if ($updateRow) {
+ $newRow = $this->updateRowReference($newRowIndex, $absoluteRow);
+ }
+
+ // Return new reference
+ return "{$newColumn}{$newRow}";
+ }
+
+ public function cellAddressInDeleteRange(string $cellAddress): bool
+ {
+ [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
+ $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
+ // Is cell within the range of rows/columns if we're deleting
+ if (
+ $this->numberOfRows < 0
+ && ($cellRow >= ($this->beforeRow + $this->numberOfRows))
+ && ($cellRow < $this->beforeRow)
+ ) {
+ return true;
+ } elseif (
+ $this->numberOfColumns < 0
+ && ($cellColumnIndex >= ($this->beforeColumn + $this->numberOfColumns))
+ && ($cellColumnIndex < $this->beforeColumn)
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function updateColumnReference(int $newColumnIndex, string $absoluteColumn): string
+ {
+ $newColumn = Coordinate::stringFromColumnIndex(min($newColumnIndex + $this->numberOfColumns, AddressRange::MAX_COLUMN_INT));
+
+ return "{$absoluteColumn}{$newColumn}";
+ }
+
+ protected function updateRowReference(int $newRowIndex, string $absoluteRow): string
+ {
+ $newRow = $newRowIndex + $this->numberOfRows;
+ $newRow = ($newRow > AddressRange::MAX_ROW) ? AddressRange::MAX_ROW : $newRow;
+
+ return "{$absoluteRow}{$newRow}";
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
new file mode 100644
index 0000000..9b804b2
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
@@ -0,0 +1,372 @@
+fillColor = new ChartColor();
+ }
+
+ /**
+ * Chart Major Gridlines as.
+ */
+ private ?GridLines $majorGridlines = null;
+
+ /**
+ * Chart Minor Gridlines as.
+ */
+ private ?GridLines $minorGridlines = null;
+
+ /**
+ * Axis Number.
+ *
+ * @var mixed[]
+ */
+ private $axisNumber = [
+ 'format' => self::FORMAT_CODE_GENERAL,
+ 'source_linked' => 1,
+ 'numeric' => null,
+ ];
+
+ private string $axisType = '';
+
+ private ?AxisText $axisText = null;
+
+ private ?Title $dispUnitsTitle = null;
+
+ /**
+ * Axis Options.
+ *
+ * @var mixed[]
+ */
+ private $axisOptions = [
+ 'minimum' => null,
+ 'maximum' => null,
+ 'major_unit' => null,
+ 'minor_unit' => null,
+ 'orientation' => self::ORIENTATION_NORMAL,
+ 'minor_tick_mark' => self::TICK_MARK_NONE,
+ 'major_tick_mark' => self::TICK_MARK_NONE,
+ 'axis_labels' => self::AXIS_LABELS_NEXT_TO,
+ 'horizontal_crosses' => self::HORIZONTAL_CROSSES_AUTOZERO,
+ 'horizontal_crosses_value' => null,
+ 'textRotation' => null,
+ 'hidden' => null,
+ 'majorTimeUnit' => self::TIME_UNIT_YEARS,
+ 'minorTimeUnit' => self::TIME_UNIT_MONTHS,
+ 'baseTimeUnit' => self::TIME_UNIT_DAYS,
+ 'logBase' => null,
+ 'dispUnitsBuiltIn' => null,
+ ];
+ public const DISP_UNITS_HUNDREDS = 'hundreds';
+ public const DISP_UNITS_THOUSANDS = 'thousands';
+ public const DISP_UNITS_TEN_THOUSANDS = 'tenThousands';
+ public const DISP_UNITS_HUNDRED_THOUSANDS = 'hundredThousands';
+ public const DISP_UNITS_MILLIONS = 'millions';
+ public const DISP_UNITS_TEN_MILLIONS = 'tenMillions';
+ public const DISP_UNITS_HUNDRED_MILLIONS = 'hundredMillions';
+ public const DISP_UNITS_BILLIONS = 'billions';
+ public const DISP_UNITS_TRILLIONS = 'trillions';
+ public const DISP_UNITS_BUILTIN_INT = [
+ 100 => self::DISP_UNITS_HUNDREDS,
+ 1000 => self::DISP_UNITS_THOUSANDS,
+ 10000 => self::DISP_UNITS_TEN_THOUSANDS,
+ 100000 => self::DISP_UNITS_HUNDRED_THOUSANDS,
+ 1000000 => self::DISP_UNITS_MILLIONS,
+ 10000000 => self::DISP_UNITS_TEN_MILLIONS,
+ 100000000 => self::DISP_UNITS_HUNDRED_MILLIONS,
+ 1000000000 => self::DISP_UNITS_BILLIONS,
+ 1000000000000 => self::DISP_UNITS_TRILLIONS,
+ ];
+
+ /**
+ * Fill Properties.
+ */
+ private ChartColor $fillColor;
+
+ private const NUMERIC_FORMAT = [
+ Properties::FORMAT_CODE_NUMBER,
+ Properties::FORMAT_CODE_DATE,
+ Properties::FORMAT_CODE_DATE_ISO8601,
+ ];
+
+ /** @var bool */
+ private $noFill = false;
+
+ /**
+ * Get Series Data Type.
+ */
+ public function setAxisNumberProperties(mixed $format_code, ?bool $numeric = null, int $sourceLinked = 0): void
+ {
+ $format = (string) $format_code;
+ $this->axisNumber['format'] = $format;
+ $this->axisNumber['source_linked'] = $sourceLinked;
+ if (is_bool($numeric)) {
+ $this->axisNumber['numeric'] = $numeric;
+ } elseif (in_array($format, self::NUMERIC_FORMAT, true)) {
+ $this->axisNumber['numeric'] = true;
+ }
+ }
+
+ /**
+ * Get Axis Number Format Data Type.
+ *
+ * @return string
+ */
+ public function getAxisNumberFormat()
+ {
+ return $this->axisNumber['format'];
+ }
+
+ /**
+ * Get Axis Number Source Linked.
+ */
+ public function getAxisNumberSourceLinked(): string
+ {
+ return (string) $this->axisNumber['source_linked'];
+ }
+
+ public function getAxisIsNumericFormat(): bool
+ {
+ return $this->axisType === self::AXIS_TYPE_DATE || (bool) $this->axisNumber['numeric'];
+ }
+
+ public function setAxisOption(string $key, null|float|int|string $value): void
+ {
+ if ($value !== null && $value !== '') {
+ $this->axisOptions[$key] = (string) $value;
+ }
+ }
+
+ /**
+ * Set Axis Options Properties.
+ */
+ public function setAxisOptionsProperties(
+ string $axisLabels,
+ ?string $horizontalCrossesValue = null,
+ ?string $horizontalCrosses = null,
+ ?string $axisOrientation = null,
+ ?string $majorTmt = null,
+ ?string $minorTmt = null,
+ null|float|int|string $minimum = null,
+ null|float|int|string $maximum = null,
+ null|float|int|string $majorUnit = null,
+ null|float|int|string $minorUnit = null,
+ null|float|int|string $textRotation = null,
+ ?string $hidden = null,
+ ?string $baseTimeUnit = null,
+ ?string $majorTimeUnit = null,
+ ?string $minorTimeUnit = null,
+ null|float|int|string $logBase = null,
+ ?string $dispUnitsBuiltIn = null
+ ): void {
+ $this->axisOptions['axis_labels'] = $axisLabels;
+ $this->setAxisOption('horizontal_crosses_value', $horizontalCrossesValue);
+ $this->setAxisOption('horizontal_crosses', $horizontalCrosses);
+ $this->setAxisOption('orientation', $axisOrientation);
+ $this->setAxisOption('major_tick_mark', $majorTmt);
+ $this->setAxisOption('minor_tick_mark', $minorTmt);
+ $this->setAxisOption('minimum', $minimum);
+ $this->setAxisOption('maximum', $maximum);
+ $this->setAxisOption('major_unit', $majorUnit);
+ $this->setAxisOption('minor_unit', $minorUnit);
+ $this->setAxisOption('textRotation', $textRotation);
+ $this->setAxisOption('hidden', $hidden);
+ $this->setAxisOption('baseTimeUnit', $baseTimeUnit);
+ $this->setAxisOption('majorTimeUnit', $majorTimeUnit);
+ $this->setAxisOption('minorTimeUnit', $minorTimeUnit);
+ $this->setAxisOption('logBase', $logBase);
+ $this->setAxisOption('dispUnitsBuiltIn', $dispUnitsBuiltIn);
+ }
+
+ /**
+ * Get Axis Options Property.
+ *
+ * @param string $property
+ *
+ * @return ?string
+ */
+ public function getAxisOptionsProperty($property)
+ {
+ if ($property === 'textRotation') {
+ if ($this->axisText !== null) {
+ if ($this->axisText->getRotation() !== null) {
+ return (string) $this->axisText->getRotation();
+ }
+ }
+ }
+
+ return $this->axisOptions[$property];
+ }
+
+ /**
+ * Set Axis Orientation Property.
+ *
+ * @param string $orientation
+ */
+ public function setAxisOrientation($orientation): void
+ {
+ $this->axisOptions['orientation'] = (string) $orientation;
+ }
+
+ public function getAxisType(): string
+ {
+ return $this->axisType;
+ }
+
+ public function setAxisType(string $type): self
+ {
+ if ($type === self::AXIS_TYPE_CATEGORY || $type === self::AXIS_TYPE_VALUE || $type === self::AXIS_TYPE_DATE) {
+ $this->axisType = $type;
+ } else {
+ $this->axisType = '';
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set Fill Property.
+ *
+ * @param ?int $alpha
+ */
+ public function setFillParameters(?string $color, $alpha = null, ?string $AlphaType = ChartColor::EXCEL_COLOR_TYPE_RGB): void
+ {
+ $this->fillColor->setColorProperties($color, $alpha, $AlphaType);
+ }
+
+ /**
+ * Get Fill Property.
+ *
+ * @param string $property
+ */
+ public function getFillProperty($property): string
+ {
+ return (string) $this->fillColor->getColorProperty($property);
+ }
+
+ public function getFillColorObject(): ChartColor
+ {
+ return $this->fillColor;
+ }
+
+ /**
+ * Get Line Color Property.
+ *
+ * @deprecated 1.24.0
+ * Use the getLineColor property in the Properties class instead
+ * @see Properties::getLineColorProperty()
+ *
+ * @param string $propertyName
+ *
+ * @return null|int|string
+ */
+ public function getLineProperty($propertyName)
+ {
+ return $this->getLineColorProperty($propertyName);
+ }
+
+ private string $crossBetween = ''; // 'between' or 'midCat' might be better
+
+ public function setCrossBetween(string $crossBetween): self
+ {
+ $this->crossBetween = $crossBetween;
+
+ return $this;
+ }
+
+ public function getCrossBetween(): string
+ {
+ return $this->crossBetween;
+ }
+
+ public function getMajorGridlines(): ?GridLines
+ {
+ return $this->majorGridlines;
+ }
+
+ public function getMinorGridlines(): ?GridLines
+ {
+ return $this->minorGridlines;
+ }
+
+ public function setMajorGridlines(?GridLines $gridlines): self
+ {
+ $this->majorGridlines = $gridlines;
+
+ return $this;
+ }
+
+ public function setMinorGridlines(?GridLines $gridlines): self
+ {
+ $this->minorGridlines = $gridlines;
+
+ return $this;
+ }
+
+ public function getAxisText(): ?AxisText
+ {
+ return $this->axisText;
+ }
+
+ public function setAxisText(?AxisText $axisText): self
+ {
+ $this->axisText = $axisText;
+
+ return $this;
+ }
+
+ public function setNoFill(bool $noFill): self
+ {
+ $this->noFill = $noFill;
+
+ return $this;
+ }
+
+ public function getNoFill(): bool
+ {
+ return $this->noFill;
+ }
+
+ public function setDispUnitsTitle(?Title $dispUnitsTitle): self
+ {
+ $this->dispUnitsTitle = $dispUnitsTitle;
+
+ return $this;
+ }
+
+ public function getDispUnitsTitle(): ?Title
+ {
+ return $this->dispUnitsTitle;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ parent::__clone();
+ $this->majorGridlines = ($this->majorGridlines === null) ? null : clone $this->majorGridlines;
+ $this->majorGridlines = ($this->minorGridlines === null) ? null : clone $this->minorGridlines;
+ $this->axisText = ($this->axisText === null) ? null : clone $this->axisText;
+ $this->dispUnitsTitle = ($this->dispUnitsTitle === null) ? null : clone $this->dispUnitsTitle;
+ $this->fillColor = clone $this->fillColor;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php
new file mode 100644
index 0000000..09d6b2a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php
@@ -0,0 +1,63 @@
+font = new Font();
+ $this->font->setSize(null, true);
+ }
+
+ public function setRotation(?int $rotation): self
+ {
+ $this->rotation = $rotation;
+
+ return $this;
+ }
+
+ public function getRotation(): ?int
+ {
+ return $this->rotation;
+ }
+
+ public function getFillColorObject(): ChartColor
+ {
+ $fillColor = $this->font->getChartColor();
+ if ($fillColor === null) {
+ $fillColor = new ChartColor();
+ $this->font->setChartColorFromObject($fillColor);
+ }
+
+ return $fillColor;
+ }
+
+ public function getFont(): Font
+ {
+ return $this->font;
+ }
+
+ public function setFont(Font $font): self
+ {
+ $this->font = $font;
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ parent::__clone();
+ $this->font = clone $this->font;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
new file mode 100644
index 0000000..70f6b4a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
@@ -0,0 +1,855 @@
+name = $name;
+ $this->title = $title;
+ $this->legend = $legend;
+ $this->xAxisLabel = $xAxisLabel;
+ $this->yAxisLabel = $yAxisLabel;
+ $this->plotArea = $plotArea;
+ $this->plotVisibleOnly = $plotVisibleOnly;
+ $this->displayBlanksAs = $displayBlanksAs;
+ $this->xAxis = $xAxis ?? new Axis();
+ $this->yAxis = $yAxis ?? new Axis();
+ if ($majorGridlines !== null) {
+ $this->yAxis->setMajorGridlines($majorGridlines);
+ }
+ if ($minorGridlines !== null) {
+ $this->yAxis->setMinorGridlines($minorGridlines);
+ }
+ $this->fillColor = new ChartColor();
+ $this->borderLines = new GridLines();
+ }
+
+ public function __destruct()
+ {
+ $this->worksheet = null;
+ }
+
+ /**
+ * Get Name.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Get Worksheet.
+ */
+ public function getWorksheet(): ?Worksheet
+ {
+ return $this->worksheet;
+ }
+
+ /**
+ * Set Worksheet.
+ *
+ * @return $this
+ */
+ public function setWorksheet(?Worksheet $worksheet = null): static
+ {
+ $this->worksheet = $worksheet;
+
+ return $this;
+ }
+
+ public function getTitle(): ?Title
+ {
+ return $this->title;
+ }
+
+ /**
+ * Set Title.
+ *
+ * @return $this
+ */
+ public function setTitle(Title $title): static
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ public function getLegend(): ?Legend
+ {
+ return $this->legend;
+ }
+
+ /**
+ * Set Legend.
+ *
+ * @return $this
+ */
+ public function setLegend(Legend $legend): static
+ {
+ $this->legend = $legend;
+
+ return $this;
+ }
+
+ public function getXAxisLabel(): ?Title
+ {
+ return $this->xAxisLabel;
+ }
+
+ /**
+ * Set X-Axis Label.
+ *
+ * @return $this
+ */
+ public function setXAxisLabel(Title $label): static
+ {
+ $this->xAxisLabel = $label;
+
+ return $this;
+ }
+
+ public function getYAxisLabel(): ?Title
+ {
+ return $this->yAxisLabel;
+ }
+
+ /**
+ * Set Y-Axis Label.
+ *
+ * @return $this
+ */
+ public function setYAxisLabel(Title $label): static
+ {
+ $this->yAxisLabel = $label;
+
+ return $this;
+ }
+
+ public function getPlotArea(): ?PlotArea
+ {
+ return $this->plotArea;
+ }
+
+ public function getPlotAreaOrThrow(): PlotArea
+ {
+ $plotArea = $this->getPlotArea();
+ if ($plotArea !== null) {
+ return $plotArea;
+ }
+
+ throw new Exception('Chart has no PlotArea');
+ }
+
+ /**
+ * Set Plot Area.
+ */
+ public function setPlotArea(PlotArea $plotArea): self
+ {
+ $this->plotArea = $plotArea;
+
+ return $this;
+ }
+
+ /**
+ * Get Plot Visible Only.
+ *
+ * @return bool
+ */
+ public function getPlotVisibleOnly()
+ {
+ return $this->plotVisibleOnly;
+ }
+
+ /**
+ * Set Plot Visible Only.
+ *
+ * @param bool $plotVisibleOnly
+ *
+ * @return $this
+ */
+ public function setPlotVisibleOnly($plotVisibleOnly): static
+ {
+ $this->plotVisibleOnly = $plotVisibleOnly;
+
+ return $this;
+ }
+
+ /**
+ * Get Display Blanks as.
+ *
+ * @return string
+ */
+ public function getDisplayBlanksAs()
+ {
+ return $this->displayBlanksAs;
+ }
+
+ /**
+ * Set Display Blanks as.
+ *
+ * @param string $displayBlanksAs
+ *
+ * @return $this
+ */
+ public function setDisplayBlanksAs($displayBlanksAs): static
+ {
+ $this->displayBlanksAs = $displayBlanksAs;
+
+ return $this;
+ }
+
+ public function getChartAxisY(): Axis
+ {
+ return $this->yAxis;
+ }
+
+ /**
+ * Set yAxis.
+ */
+ public function setChartAxisY(?Axis $axis): self
+ {
+ $this->yAxis = $axis ?? new Axis();
+
+ return $this;
+ }
+
+ public function getChartAxisX(): Axis
+ {
+ return $this->xAxis;
+ }
+
+ /**
+ * Set xAxis.
+ */
+ public function setChartAxisX(?Axis $axis): self
+ {
+ $this->xAxis = $axis ?? new Axis();
+
+ return $this;
+ }
+
+ /**
+ * Get Major Gridlines.
+ *
+ * @deprecated 1.24.0 Use Axis->getMajorGridlines()
+ * @see Axis::getMajorGridlines()
+ *
+ * @codeCoverageIgnore
+ */
+ public function getMajorGridlines(): ?GridLines
+ {
+ return $this->yAxis->getMajorGridLines();
+ }
+
+ /**
+ * Get Minor Gridlines.
+ *
+ * @deprecated 1.24.0 Use Axis->getMinorGridlines()
+ * @see Axis::getMinorGridlines()
+ *
+ * @codeCoverageIgnore
+ */
+ public function getMinorGridlines(): ?GridLines
+ {
+ return $this->yAxis->getMinorGridLines();
+ }
+
+ /**
+ * Set the Top Left position for the chart.
+ *
+ * @param string $cellAddress
+ * @param int $xOffset
+ * @param int $yOffset
+ *
+ * @return $this
+ */
+ public function setTopLeftPosition($cellAddress, $xOffset = null, $yOffset = null): static
+ {
+ $this->topLeftCellRef = $cellAddress;
+ if ($xOffset !== null) {
+ $this->setTopLeftXOffset($xOffset);
+ }
+ if ($yOffset !== null) {
+ $this->setTopLeftYOffset($yOffset);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the top left position of the chart.
+ *
+ * Returns ['cell' => string cell address, 'xOffset' => int, 'yOffset' => int].
+ *
+ * @return array{cell: string, xOffset: int, yOffset: int} an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
+ */
+ public function getTopLeftPosition(): array
+ {
+ return [
+ 'cell' => $this->topLeftCellRef,
+ 'xOffset' => $this->topLeftXOffset,
+ 'yOffset' => $this->topLeftYOffset,
+ ];
+ }
+
+ /**
+ * Get the cell address where the top left of the chart is fixed.
+ *
+ * @return string
+ */
+ public function getTopLeftCell()
+ {
+ return $this->topLeftCellRef;
+ }
+
+ /**
+ * Set the Top Left cell position for the chart.
+ *
+ * @param string $cellAddress
+ *
+ * @return $this
+ */
+ public function setTopLeftCell($cellAddress): static
+ {
+ $this->topLeftCellRef = $cellAddress;
+
+ return $this;
+ }
+
+ /**
+ * Set the offset position within the Top Left cell for the chart.
+ *
+ * @param ?int $xOffset
+ * @param ?int $yOffset
+ *
+ * @return $this
+ */
+ public function setTopLeftOffset($xOffset, $yOffset): static
+ {
+ if ($xOffset !== null) {
+ $this->setTopLeftXOffset($xOffset);
+ }
+
+ if ($yOffset !== null) {
+ $this->setTopLeftYOffset($yOffset);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the offset position within the Top Left cell for the chart.
+ *
+ * @return int[]
+ */
+ public function getTopLeftOffset(): array
+ {
+ return [
+ 'X' => $this->topLeftXOffset,
+ 'Y' => $this->topLeftYOffset,
+ ];
+ }
+
+ /**
+ * @param int $xOffset
+ *
+ * @return $this
+ */
+ public function setTopLeftXOffset($xOffset): static
+ {
+ $this->topLeftXOffset = $xOffset;
+
+ return $this;
+ }
+
+ public function getTopLeftXOffset(): int
+ {
+ return $this->topLeftXOffset;
+ }
+
+ /**
+ * @param int $yOffset
+ *
+ * @return $this
+ */
+ public function setTopLeftYOffset($yOffset): static
+ {
+ $this->topLeftYOffset = $yOffset;
+
+ return $this;
+ }
+
+ public function getTopLeftYOffset(): int
+ {
+ return $this->topLeftYOffset;
+ }
+
+ /**
+ * Set the Bottom Right position of the chart.
+ *
+ * @param string $cellAddress
+ * @param int $xOffset
+ * @param int $yOffset
+ *
+ * @return $this
+ */
+ public function setBottomRightPosition($cellAddress = '', $xOffset = null, $yOffset = null): static
+ {
+ $this->bottomRightCellRef = $cellAddress;
+ if ($xOffset !== null) {
+ $this->setBottomRightXOffset($xOffset);
+ }
+ if ($yOffset !== null) {
+ $this->setBottomRightYOffset($yOffset);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the bottom right position of the chart.
+ *
+ * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
+ */
+ public function getBottomRightPosition(): array
+ {
+ return [
+ 'cell' => $this->bottomRightCellRef,
+ 'xOffset' => $this->bottomRightXOffset,
+ 'yOffset' => $this->bottomRightYOffset,
+ ];
+ }
+
+ /**
+ * Set the Bottom Right cell for the chart.
+ *
+ * @return $this
+ */
+ public function setBottomRightCell(string $cellAddress = ''): static
+ {
+ $this->bottomRightCellRef = $cellAddress;
+
+ return $this;
+ }
+
+ /**
+ * Get the cell address where the bottom right of the chart is fixed.
+ */
+ public function getBottomRightCell(): string
+ {
+ return $this->bottomRightCellRef;
+ }
+
+ /**
+ * Set the offset position within the Bottom Right cell for the chart.
+ *
+ * @param ?int $xOffset
+ * @param ?int $yOffset
+ *
+ * @return $this
+ */
+ public function setBottomRightOffset($xOffset, $yOffset): static
+ {
+ if ($xOffset !== null) {
+ $this->setBottomRightXOffset($xOffset);
+ }
+
+ if ($yOffset !== null) {
+ $this->setBottomRightYOffset($yOffset);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the offset position within the Bottom Right cell for the chart.
+ *
+ * @return int[]
+ */
+ public function getBottomRightOffset(): array
+ {
+ return [
+ 'X' => $this->bottomRightXOffset,
+ 'Y' => $this->bottomRightYOffset,
+ ];
+ }
+
+ /**
+ * @param int $xOffset
+ *
+ * @return $this
+ */
+ public function setBottomRightXOffset($xOffset): static
+ {
+ $this->bottomRightXOffset = $xOffset;
+
+ return $this;
+ }
+
+ public function getBottomRightXOffset(): int
+ {
+ return $this->bottomRightXOffset;
+ }
+
+ /**
+ * @param int $yOffset
+ *
+ * @return $this
+ */
+ public function setBottomRightYOffset($yOffset): static
+ {
+ $this->bottomRightYOffset = $yOffset;
+
+ return $this;
+ }
+
+ public function getBottomRightYOffset(): int
+ {
+ return $this->bottomRightYOffset;
+ }
+
+ public function refresh(): void
+ {
+ if ($this->worksheet !== null && $this->plotArea !== null) {
+ $this->plotArea->refresh($this->worksheet);
+ }
+ }
+
+ /**
+ * Render the chart to given file (or stream).
+ *
+ * @param string $outputDestination Name of the file render to
+ *
+ * @return bool true on success
+ */
+ public function render($outputDestination = null)
+ {
+ if ($outputDestination == 'php://output') {
+ $outputDestination = null;
+ }
+
+ $libraryName = Settings::getChartRenderer();
+ if ($libraryName === null) {
+ return false;
+ }
+
+ // Ensure that data series values are up-to-date before we render
+ $this->refresh();
+
+ /** @var Renderer\IRenderer */
+ $renderer = new $libraryName($this);
+
+ return $renderer->render($outputDestination);
+ }
+
+ public function getRotX(): ?int
+ {
+ return $this->rotX;
+ }
+
+ public function setRotX(?int $rotX): self
+ {
+ $this->rotX = $rotX;
+
+ return $this;
+ }
+
+ public function getRotY(): ?int
+ {
+ return $this->rotY;
+ }
+
+ public function setRotY(?int $rotY): self
+ {
+ $this->rotY = $rotY;
+
+ return $this;
+ }
+
+ public function getRAngAx(): ?int
+ {
+ return $this->rAngAx;
+ }
+
+ public function setRAngAx(?int $rAngAx): self
+ {
+ $this->rAngAx = $rAngAx;
+
+ return $this;
+ }
+
+ public function getPerspective(): ?int
+ {
+ return $this->perspective;
+ }
+
+ public function setPerspective(?int $perspective): self
+ {
+ $this->perspective = $perspective;
+
+ return $this;
+ }
+
+ public function getOneCellAnchor(): bool
+ {
+ return $this->oneCellAnchor;
+ }
+
+ public function setOneCellAnchor(bool $oneCellAnchor): self
+ {
+ $this->oneCellAnchor = $oneCellAnchor;
+
+ return $this;
+ }
+
+ public function getAutoTitleDeleted(): bool
+ {
+ return $this->autoTitleDeleted;
+ }
+
+ public function setAutoTitleDeleted(bool $autoTitleDeleted): self
+ {
+ $this->autoTitleDeleted = $autoTitleDeleted;
+
+ return $this;
+ }
+
+ public function getNoFill(): bool
+ {
+ return $this->noFill;
+ }
+
+ public function setNoFill(bool $noFill): self
+ {
+ $this->noFill = $noFill;
+
+ return $this;
+ }
+
+ public function getRoundedCorners(): bool
+ {
+ return $this->roundedCorners;
+ }
+
+ public function setRoundedCorners(?bool $roundedCorners): self
+ {
+ if ($roundedCorners !== null) {
+ $this->roundedCorners = $roundedCorners;
+ }
+
+ return $this;
+ }
+
+ public function getBorderLines(): GridLines
+ {
+ return $this->borderLines;
+ }
+
+ public function setBorderLines(GridLines $borderLines): self
+ {
+ $this->borderLines = $borderLines;
+
+ return $this;
+ }
+
+ public function getFillColor(): ChartColor
+ {
+ return $this->fillColor;
+ }
+
+ public function setRenderedWidth(?float $width): self
+ {
+ $this->renderedWidth = $width;
+
+ return $this;
+ }
+
+ public function getRenderedWidth(): ?float
+ {
+ return $this->renderedWidth;
+ }
+
+ public function setRenderedHeight(?float $height): self
+ {
+ $this->renderedHeight = $height;
+
+ return $this;
+ }
+
+ public function getRenderedHeight(): ?float
+ {
+ return $this->renderedHeight;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->worksheet = null;
+ $this->title = ($this->title === null) ? null : clone $this->title;
+ $this->legend = ($this->legend === null) ? null : clone $this->legend;
+ $this->xAxisLabel = ($this->xAxisLabel === null) ? null : clone $this->xAxisLabel;
+ $this->yAxisLabel = ($this->yAxisLabel === null) ? null : clone $this->yAxisLabel;
+ $this->plotArea = ($this->plotArea === null) ? null : clone $this->plotArea;
+ $this->xAxis = clone $this->xAxis;
+ $this->yAxis = clone $this->yAxis;
+ $this->borderLines = clone $this->borderLines;
+ $this->fillColor = clone $this->fillColor;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php
new file mode 100644
index 0000000..c857930
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php
@@ -0,0 +1,173 @@
+setColorPropertiesArray($value);
+ } else {
+ $this->setColorProperties($value, $alpha, $type, $brightness);
+ }
+ }
+
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ public function setValue(string $value): self
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ public function getAlpha(): ?int
+ {
+ return $this->alpha;
+ }
+
+ public function setAlpha(?int $alpha): self
+ {
+ $this->alpha = $alpha;
+
+ return $this;
+ }
+
+ public function getBrightness(): ?int
+ {
+ return $this->brightness;
+ }
+
+ public function setBrightness(?int $brightness): self
+ {
+ $this->brightness = $brightness;
+
+ return $this;
+ }
+
+ /**
+ * @param null|float|int|string $alpha
+ * @param null|float|int|string $brightness
+ */
+ public function setColorProperties(?string $color, $alpha = null, ?string $type = null, $brightness = null): self
+ {
+ if (empty($type) && !empty($color)) {
+ if (str_starts_with($color, '*')) {
+ $type = 'schemeClr';
+ $color = substr($color, 1);
+ } elseif (str_starts_with($color, '/')) {
+ $type = 'prstClr';
+ $color = substr($color, 1);
+ } elseif (preg_match('/^[0-9A-Fa-f]{6}$/', $color) === 1) {
+ $type = 'srgbClr';
+ }
+ }
+ if ($color !== null) {
+ $this->setValue("$color");
+ }
+ if ($type !== null) {
+ $this->setType($type);
+ }
+ if ($alpha === null) {
+ $this->setAlpha(null);
+ } elseif (is_numeric($alpha)) {
+ $this->setAlpha((int) $alpha);
+ }
+ if ($brightness === null) {
+ $this->setBrightness(null);
+ } elseif (is_numeric($brightness)) {
+ $this->setBrightness((int) $brightness);
+ }
+
+ return $this;
+ }
+
+ public function setColorPropertiesArray(array $color): self
+ {
+ return $this->setColorProperties(
+ $color['value'] ?? '',
+ $color['alpha'] ?? null,
+ $color['type'] ?? null,
+ $color['brightness'] ?? null
+ );
+ }
+
+ public function isUsable(): bool
+ {
+ return $this->type !== '' && $this->value !== '';
+ }
+
+ /**
+ * Get Color Property.
+ *
+ * @param string $propertyName
+ *
+ * @return null|int|string
+ */
+ public function getColorProperty($propertyName)
+ {
+ $retVal = null;
+ if ($propertyName === 'value') {
+ $retVal = $this->value;
+ } elseif ($propertyName === 'type') {
+ $retVal = $this->type;
+ } elseif ($propertyName === 'alpha') {
+ $retVal = $this->alpha;
+ } elseif ($propertyName === 'brightness') {
+ $retVal = $this->brightness;
+ }
+
+ return $retVal;
+ }
+
+ public static function alphaToXml(int $alpha): string
+ {
+ return (string) (100 - $alpha) . '000';
+ }
+
+ /**
+ * @param float|int|string $alpha
+ */
+ public static function alphaFromXml($alpha): int
+ {
+ return 100 - ((int) $alpha / 1000);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
new file mode 100644
index 0000000..b8656ad
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
@@ -0,0 +1,438 @@
+plotType = $plotType;
+ $this->plotGrouping = $plotGrouping;
+ $this->plotOrder = $plotOrder;
+ $keys = array_keys($plotValues);
+ $this->plotValues = $plotValues;
+ if (!isset($plotLabel[$keys[0]])) {
+ $plotLabel[$keys[0]] = new DataSeriesValues();
+ }
+ $this->plotLabel = $plotLabel;
+
+ if (!isset($plotCategory[$keys[0]])) {
+ $plotCategory[$keys[0]] = new DataSeriesValues();
+ }
+ $this->plotCategory = $plotCategory;
+
+ $this->smoothLine = (bool) $smoothLine;
+ $this->plotStyle = $plotStyle;
+
+ if ($plotDirection === null) {
+ $plotDirection = self::DIRECTION_COL;
+ }
+ $this->plotDirection = $plotDirection;
+ }
+
+ /**
+ * Get Plot Type.
+ *
+ * @return string
+ */
+ public function getPlotType()
+ {
+ return $this->plotType;
+ }
+
+ /**
+ * Set Plot Type.
+ *
+ * @param string $plotType
+ *
+ * @return $this
+ */
+ public function setPlotType($plotType): static
+ {
+ $this->plotType = $plotType;
+
+ return $this;
+ }
+
+ /**
+ * Get Plot Grouping Type.
+ *
+ * @return string
+ */
+ public function getPlotGrouping()
+ {
+ return $this->plotGrouping;
+ }
+
+ /**
+ * Set Plot Grouping Type.
+ *
+ * @param string $groupingType
+ *
+ * @return $this
+ */
+ public function setPlotGrouping($groupingType): static
+ {
+ $this->plotGrouping = $groupingType;
+
+ return $this;
+ }
+
+ /**
+ * Get Plot Direction.
+ *
+ * @return string
+ */
+ public function getPlotDirection()
+ {
+ return $this->plotDirection;
+ }
+
+ /**
+ * Set Plot Direction.
+ *
+ * @param string $plotDirection
+ *
+ * @return $this
+ */
+ public function setPlotDirection($plotDirection): static
+ {
+ $this->plotDirection = $plotDirection;
+
+ return $this;
+ }
+
+ /**
+ * Get Plot Order.
+ *
+ * @return int[]
+ */
+ public function getPlotOrder()
+ {
+ return $this->plotOrder;
+ }
+
+ /**
+ * Get Plot Labels.
+ *
+ * @return DataSeriesValues[]
+ */
+ public function getPlotLabels()
+ {
+ return $this->plotLabel;
+ }
+
+ /**
+ * Get Plot Label by Index.
+ *
+ * @return DataSeriesValues|false
+ */
+ public function getPlotLabelByIndex(mixed $index)
+ {
+ $keys = array_keys($this->plotLabel);
+ if (in_array($index, $keys)) {
+ return $this->plotLabel[$index];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get Plot Categories.
+ *
+ * @return DataSeriesValues[]
+ */
+ public function getPlotCategories()
+ {
+ return $this->plotCategory;
+ }
+
+ /**
+ * Get Plot Category by Index.
+ *
+ * @return DataSeriesValues|false
+ */
+ public function getPlotCategoryByIndex(mixed $index)
+ {
+ $keys = array_keys($this->plotCategory);
+ if (in_array($index, $keys)) {
+ return $this->plotCategory[$index];
+ } elseif (isset($keys[$index])) {
+ return $this->plotCategory[$keys[$index]];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get Plot Style.
+ *
+ * @return null|string
+ */
+ public function getPlotStyle()
+ {
+ return $this->plotStyle;
+ }
+
+ /**
+ * Set Plot Style.
+ *
+ * @param null|string $plotStyle
+ *
+ * @return $this
+ */
+ public function setPlotStyle($plotStyle): static
+ {
+ $this->plotStyle = $plotStyle;
+
+ return $this;
+ }
+
+ /**
+ * Get Plot Values.
+ *
+ * @return DataSeriesValues[]
+ */
+ public function getPlotValues()
+ {
+ return $this->plotValues;
+ }
+
+ /**
+ * Get Plot Values by Index.
+ *
+ * @return DataSeriesValues|false
+ */
+ public function getPlotValuesByIndex(mixed $index)
+ {
+ $keys = array_keys($this->plotValues);
+ if (in_array($index, $keys)) {
+ return $this->plotValues[$index];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get Plot Bubble Sizes.
+ *
+ * @return DataSeriesValues[]
+ */
+ public function getPlotBubbleSizes(): array
+ {
+ return $this->plotBubbleSizes;
+ }
+
+ /**
+ * Set Plot Bubble Sizes.
+ *
+ * @param DataSeriesValues[] $plotBubbleSizes
+ */
+ public function setPlotBubbleSizes(array $plotBubbleSizes): self
+ {
+ $this->plotBubbleSizes = $plotBubbleSizes;
+
+ return $this;
+ }
+
+ /**
+ * Get Number of Plot Series.
+ */
+ public function getPlotSeriesCount(): int
+ {
+ return count($this->plotValues);
+ }
+
+ /**
+ * Get Smooth Line.
+ *
+ * @return bool
+ */
+ public function getSmoothLine()
+ {
+ return $this->smoothLine;
+ }
+
+ /**
+ * Set Smooth Line.
+ *
+ * @param bool $smoothLine
+ *
+ * @return $this
+ */
+ public function setSmoothLine($smoothLine): static
+ {
+ $this->smoothLine = $smoothLine;
+
+ return $this;
+ }
+
+ public function refresh(Worksheet $worksheet): void
+ {
+ foreach ($this->plotValues as $plotValues) {
+ if ($plotValues !== null) {
+ $plotValues->refresh($worksheet, true);
+ }
+ }
+ foreach ($this->plotLabel as $plotValues) {
+ if ($plotValues !== null) {
+ $plotValues->refresh($worksheet, true);
+ }
+ }
+ foreach ($this->plotCategory as $plotValues) {
+ if ($plotValues !== null) {
+ $plotValues->refresh($worksheet, false);
+ }
+ }
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $plotLabels = $this->plotLabel;
+ $this->plotLabel = [];
+ foreach ($plotLabels as $plotLabel) {
+ $this->plotLabel[] = $plotLabel;
+ }
+ $plotCategories = $this->plotCategory;
+ $this->plotCategory = [];
+ foreach ($plotCategories as $plotCategory) {
+ $this->plotCategory[] = clone $plotCategory;
+ }
+ $plotValues = $this->plotValues;
+ $this->plotValues = [];
+ foreach ($plotValues as $plotValue) {
+ $this->plotValues[] = clone $plotValue;
+ }
+ $plotBubbleSizes = $this->plotBubbleSizes;
+ $this->plotBubbleSizes = [];
+ foreach ($plotBubbleSizes as $plotBubbleSize) {
+ $this->plotBubbleSizes[] = clone $plotBubbleSize;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
new file mode 100644
index 0000000..b31714b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
@@ -0,0 +1,615 @@
+markerFillColor = new ChartColor();
+ $this->markerBorderColor = new ChartColor();
+ $this->setDataType($dataType);
+ $this->dataSource = $dataSource;
+ $this->formatCode = $formatCode;
+ $this->pointCount = $pointCount;
+ $this->dataValues = $dataValues;
+ $this->pointMarker = $marker;
+ if ($fillColor !== null) {
+ $this->setFillColor($fillColor);
+ }
+ if (is_numeric($pointSize)) {
+ $this->pointSize = (int) $pointSize;
+ }
+ }
+
+ /**
+ * Get Series Data Type.
+ *
+ * @return string
+ */
+ public function getDataType()
+ {
+ return $this->dataType;
+ }
+
+ /**
+ * Set Series Data Type.
+ *
+ * @param string $dataType Datatype of this data series
+ * Typical values are:
+ * DataSeriesValues::DATASERIES_TYPE_STRING
+ * Normally used for axis point values
+ * DataSeriesValues::DATASERIES_TYPE_NUMBER
+ * Normally used for chart data values
+ *
+ * @return $this
+ */
+ public function setDataType($dataType): static
+ {
+ if (!in_array($dataType, self::DATA_TYPE_VALUES)) {
+ throw new Exception('Invalid datatype for chart data series values');
+ }
+ $this->dataType = $dataType;
+
+ return $this;
+ }
+
+ /**
+ * Get Series Data Source (formula).
+ *
+ * @return ?string
+ */
+ public function getDataSource()
+ {
+ return $this->dataSource;
+ }
+
+ /**
+ * Set Series Data Source (formula).
+ *
+ * @param ?string $dataSource
+ *
+ * @return $this
+ */
+ public function setDataSource($dataSource): static
+ {
+ $this->dataSource = $dataSource;
+
+ return $this;
+ }
+
+ /**
+ * Get Point Marker.
+ *
+ * @return string
+ */
+ public function getPointMarker()
+ {
+ return $this->pointMarker;
+ }
+
+ /**
+ * Set Point Marker.
+ *
+ * @param string $marker
+ *
+ * @return $this
+ */
+ public function setPointMarker($marker): static
+ {
+ $this->pointMarker = $marker;
+
+ return $this;
+ }
+
+ public function getMarkerFillColor(): ChartColor
+ {
+ return $this->markerFillColor;
+ }
+
+ public function getMarkerBorderColor(): ChartColor
+ {
+ return $this->markerBorderColor;
+ }
+
+ /**
+ * Get Point Size.
+ */
+ public function getPointSize(): int
+ {
+ return $this->pointSize;
+ }
+
+ /**
+ * Set Point Size.
+ *
+ * @return $this
+ */
+ public function setPointSize(int $size = 3): static
+ {
+ $this->pointSize = $size;
+
+ return $this;
+ }
+
+ /**
+ * Get Series Format Code.
+ *
+ * @return string
+ */
+ public function getFormatCode()
+ {
+ return $this->formatCode;
+ }
+
+ /**
+ * Set Series Format Code.
+ *
+ * @param string $formatCode
+ *
+ * @return $this
+ */
+ public function setFormatCode($formatCode): static
+ {
+ $this->formatCode = $formatCode;
+
+ return $this;
+ }
+
+ /**
+ * Get Series Point Count.
+ *
+ * @return int
+ */
+ public function getPointCount()
+ {
+ return $this->pointCount;
+ }
+
+ /**
+ * Get fill color object.
+ *
+ * @return null|ChartColor|ChartColor[]
+ */
+ public function getFillColorObject()
+ {
+ return $this->fillColor;
+ }
+
+ private function stringToChartColor(string $fillString): ChartColor
+ {
+ $value = $type = '';
+ if (str_starts_with($fillString, '*')) {
+ $type = 'schemeClr';
+ $value = substr($fillString, 1);
+ } elseif (str_starts_with($fillString, '/')) {
+ $type = 'prstClr';
+ $value = substr($fillString, 1);
+ } elseif ($fillString !== '') {
+ $type = 'srgbClr';
+ $value = $fillString;
+ $this->validateColor($value);
+ }
+
+ return new ChartColor($value, null, $type);
+ }
+
+ private function chartColorToString(ChartColor $chartColor): string
+ {
+ $type = (string) $chartColor->getColorProperty('type');
+ $value = (string) $chartColor->getColorProperty('value');
+ if ($type === '' || $value === '') {
+ return '';
+ }
+ if ($type === 'schemeClr') {
+ return "*$value";
+ }
+ if ($type === 'prstClr') {
+ return "/$value";
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get fill color.
+ *
+ * @return string|string[] HEX color or array with HEX colors
+ */
+ public function getFillColor(): string|array
+ {
+ if ($this->fillColor === null) {
+ return '';
+ }
+ if (is_array($this->fillColor)) {
+ $array = [];
+ foreach ($this->fillColor as $chartColor) {
+ $array[] = $this->chartColorToString($chartColor);
+ }
+
+ return $array;
+ }
+
+ return $this->chartColorToString($this->fillColor);
+ }
+
+ /**
+ * Set fill color for series.
+ *
+ * @param ChartColor|ChartColor[]|string|string[] $color HEX color or array with HEX colors
+ *
+ * @return $this
+ */
+ public function setFillColor($color): static
+ {
+ if (is_array($color)) {
+ $this->fillColor = [];
+ foreach ($color as $fillString) {
+ if ($fillString instanceof ChartColor) {
+ $this->fillColor[] = $fillString;
+ } else {
+ $this->fillColor[] = $this->stringToChartColor($fillString);
+ }
+ }
+ } elseif ($color instanceof ChartColor) {
+ $this->fillColor = $color;
+ } else {
+ $this->fillColor = $this->stringToChartColor($color);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Method for validating hex color.
+ *
+ * @param string $color value for color
+ *
+ * @return bool true if validation was successful
+ */
+ private function validateColor(string $color): bool
+ {
+ if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
+ throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
+ }
+
+ return true;
+ }
+
+ /**
+ * Get line width for series.
+ *
+ * @return null|float|int
+ */
+ public function getLineWidth()
+ {
+ return $this->lineStyleProperties['width'];
+ }
+
+ /**
+ * Set line width for the series.
+ *
+ * @param null|float|int $width
+ *
+ * @return $this
+ */
+ public function setLineWidth($width): static
+ {
+ $this->lineStyleProperties['width'] = $width;
+
+ return $this;
+ }
+
+ /**
+ * Identify if the Data Series is a multi-level or a simple series.
+ */
+ public function isMultiLevelSeries(): ?bool
+ {
+ if (!empty($this->dataValues)) {
+ return is_array(array_values($this->dataValues)[0]);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the level count of a multi-level Data Series.
+ *
+ * @return int
+ */
+ public function multiLevelCount()
+ {
+ $levelCount = 0;
+ foreach ($this->dataValues as $dataValueSet) {
+ $levelCount = max($levelCount, count($dataValueSet));
+ }
+
+ return $levelCount;
+ }
+
+ /**
+ * Get Series Data Values.
+ *
+ * @return mixed[]
+ */
+ public function getDataValues()
+ {
+ return $this->dataValues;
+ }
+
+ /**
+ * Get the first Series Data value.
+ *
+ * @return mixed
+ */
+ public function getDataValue()
+ {
+ $count = count($this->dataValues);
+ if ($count == 0) {
+ return null;
+ } elseif ($count == 1) {
+ return $this->dataValues[0];
+ }
+
+ return $this->dataValues;
+ }
+
+ /**
+ * Set Series Data Values.
+ *
+ * @param array $dataValues
+ *
+ * @return $this
+ */
+ public function setDataValues($dataValues): static
+ {
+ $this->dataValues = Functions::flattenArray($dataValues);
+ $this->pointCount = count($dataValues);
+
+ return $this;
+ }
+
+ public function refresh(Worksheet $worksheet, bool $flatten = true): void
+ {
+ if ($this->dataSource !== null) {
+ $calcEngine = Calculation::getInstance($worksheet->getParent());
+ $newDataValues = Calculation::unwrapResult(
+ $calcEngine->_calculateFormulaValue(
+ '=' . $this->dataSource,
+ null,
+ $worksheet->getCell('A1')
+ )
+ );
+ if ($flatten) {
+ $this->dataValues = Functions::flattenArray($newDataValues);
+ foreach ($this->dataValues as &$dataValue) {
+ if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
+ $dataValue = 0.0;
+ }
+ }
+ unset($dataValue);
+ } else {
+ [$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);
+ } else {
+ $newArray = array_values(array_shift($newDataValues));
+ foreach ($newArray as $i => $newDataSet) {
+ $newArray[$i] = [$newDataSet];
+ }
+
+ foreach ($newDataValues as $newDataSet) {
+ $i = 0;
+ foreach ($newDataSet as $newDataVal) {
+ array_unshift($newArray[$i++], $newDataVal);
+ }
+ }
+ $this->dataValues = $newArray;
+ }
+ }
+ $this->pointCount = count($this->dataValues);
+ }
+ }
+
+ public function getScatterLines(): bool
+ {
+ return $this->scatterLines;
+ }
+
+ public function setScatterLines(bool $scatterLines): self
+ {
+ $this->scatterLines = $scatterLines;
+
+ return $this;
+ }
+
+ public function getBubble3D(): bool
+ {
+ return $this->bubble3D;
+ }
+
+ public function setBubble3D(bool $bubble3D): self
+ {
+ $this->bubble3D = $bubble3D;
+
+ return $this;
+ }
+
+ /**
+ * Smooth Line. Must be specified for both DataSeries and DataSeriesValues.
+ *
+ * @var bool
+ */
+ private $smoothLine;
+
+ /**
+ * Get Smooth Line.
+ *
+ * @return bool
+ */
+ public function getSmoothLine()
+ {
+ return $this->smoothLine;
+ }
+
+ /**
+ * Set Smooth Line.
+ *
+ * @param bool $smoothLine
+ *
+ * @return $this
+ */
+ public function setSmoothLine($smoothLine): static
+ {
+ $this->smoothLine = $smoothLine;
+
+ return $this;
+ }
+
+ public function getLabelLayout(): ?Layout
+ {
+ return $this->labelLayout;
+ }
+
+ public function setLabelLayout(?Layout $labelLayout): self
+ {
+ $this->labelLayout = $labelLayout;
+
+ return $this;
+ }
+
+ public function setTrendLines(array $trendLines): self
+ {
+ $this->trendLines = $trendLines;
+
+ return $this;
+ }
+
+ public function getTrendLines(): array
+ {
+ return $this->trendLines;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ parent::__clone();
+ $this->markerFillColor = clone $this->markerFillColor;
+ $this->markerBorderColor = clone $this->markerBorderColor;
+ if (is_array($this->fillColor)) {
+ $fillColor = $this->fillColor;
+ $this->fillColor = [];
+ foreach ($fillColor as $color) {
+ $this->fillColor[] = clone $color;
+ }
+ } elseif ($this->fillColor instanceof ChartColor) {
+ $this->fillColor = clone $this->fillColor;
+ }
+ $this->labelLayout = ($this->labelLayout === null) ? null : clone $this->labelLayout;
+ $trendLines = $this->trendLines;
+ $this->trendLines = [];
+ foreach ($trendLines as $trendLine) {
+ $this->trendLines[] = clone $trendLine;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php
new file mode 100644
index 0000000..3f95b59
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Exception.php
@@ -0,0 +1,9 @@
+layoutTarget = $layout['layoutTarget'];
+ }
+ if (isset($layout['xMode'])) {
+ $this->xMode = $layout['xMode'];
+ }
+ if (isset($layout['yMode'])) {
+ $this->yMode = $layout['yMode'];
+ }
+ if (isset($layout['x'])) {
+ $this->xPos = (float) $layout['x'];
+ }
+ if (isset($layout['y'])) {
+ $this->yPos = (float) $layout['y'];
+ }
+ if (isset($layout['w'])) {
+ $this->width = (float) $layout['w'];
+ }
+ if (isset($layout['h'])) {
+ $this->height = (float) $layout['h'];
+ }
+ if (isset($layout['dLblPos'])) {
+ $this->dLblPos = (string) $layout['dLblPos'];
+ }
+ if (isset($layout['numFmtCode'])) {
+ $this->numFmtCode = (string) $layout['numFmtCode'];
+ }
+ $this->initBoolean($layout, 'showLegendKey');
+ $this->initBoolean($layout, 'showVal');
+ $this->initBoolean($layout, 'showCatName');
+ $this->initBoolean($layout, 'showSerName');
+ $this->initBoolean($layout, 'showPercent');
+ $this->initBoolean($layout, 'showBubbleSize');
+ $this->initBoolean($layout, 'showLeaderLines');
+ $this->initBoolean($layout, 'numFmtLinked');
+ $this->initColor($layout, 'labelFillColor');
+ $this->initColor($layout, 'labelBorderColor');
+ $labelFont = $layout['labelFont'] ?? null;
+ if ($labelFont instanceof Font) {
+ $this->labelFont = $labelFont;
+ }
+ $labelFontColor = $layout['labelFontColor'] ?? null;
+ if ($labelFontColor instanceof ChartColor) {
+ $this->setLabelFontColor($labelFontColor);
+ }
+ $labelEffects = $layout['labelEffects'] ?? null;
+ if ($labelEffects instanceof Properties) {
+ $this->labelEffects = $labelEffects;
+ }
+ }
+
+ private function initBoolean(array $layout, string $name): void
+ {
+ if (isset($layout[$name])) {
+ $this->$name = (bool) $layout[$name];
+ }
+ }
+
+ private function initColor(array $layout, string $name): void
+ {
+ if (isset($layout[$name]) && $layout[$name] instanceof ChartColor) {
+ $this->$name = $layout[$name];
+ }
+ }
+
+ /**
+ * Get Layout Target.
+ *
+ * @return ?string
+ */
+ public function getLayoutTarget()
+ {
+ return $this->layoutTarget;
+ }
+
+ /**
+ * Set Layout Target.
+ *
+ * @return $this
+ */
+ public function setLayoutTarget(?string $target): static
+ {
+ $this->layoutTarget = $target;
+
+ return $this;
+ }
+
+ /**
+ * Get X-Mode.
+ *
+ * @return ?string
+ */
+ public function getXMode()
+ {
+ return $this->xMode;
+ }
+
+ /**
+ * Set X-Mode.
+ *
+ * @param ?string $mode
+ *
+ * @return $this
+ */
+ public function setXMode($mode): static
+ {
+ $this->xMode = (string) $mode;
+
+ return $this;
+ }
+
+ /**
+ * Get Y-Mode.
+ *
+ * @return ?string
+ */
+ public function getYMode()
+ {
+ return $this->yMode;
+ }
+
+ /**
+ * Set Y-Mode.
+ *
+ * @param ?string $mode
+ *
+ * @return $this
+ */
+ public function setYMode($mode): static
+ {
+ $this->yMode = (string) $mode;
+
+ return $this;
+ }
+
+ /**
+ * Get X-Position.
+ *
+ * @return null|float|int
+ */
+ public function getXPosition()
+ {
+ return $this->xPos;
+ }
+
+ /**
+ * Set X-Position.
+ *
+ * @return $this
+ */
+ public function setXPosition(float $position): static
+ {
+ $this->xPos = $position;
+
+ return $this;
+ }
+
+ /**
+ * Get Y-Position.
+ *
+ * @return null|float
+ */
+ public function getYPosition()
+ {
+ return $this->yPos;
+ }
+
+ /**
+ * Set Y-Position.
+ *
+ * @return $this
+ */
+ public function setYPosition(float $position): static
+ {
+ $this->yPos = $position;
+
+ return $this;
+ }
+
+ /**
+ * Get Width.
+ *
+ * @return ?float
+ */
+ public function getWidth()
+ {
+ return $this->width;
+ }
+
+ /**
+ * Set Width.
+ *
+ * @return $this
+ */
+ public function setWidth(?float $width): static
+ {
+ $this->width = $width;
+
+ return $this;
+ }
+
+ /**
+ * Get Height.
+ *
+ * @return null|float
+ */
+ public function getHeight()
+ {
+ return $this->height;
+ }
+
+ /**
+ * Set Height.
+ *
+ * @return $this
+ */
+ public function setHeight(?float $height): static
+ {
+ $this->height = $height;
+
+ return $this;
+ }
+
+ public function getShowLegendKey(): ?bool
+ {
+ return $this->showLegendKey;
+ }
+
+ /**
+ * Set show legend key
+ * Specifies that legend keys should be shown in data labels.
+ */
+ public function setShowLegendKey(?bool $showLegendKey): self
+ {
+ $this->showLegendKey = $showLegendKey;
+
+ return $this;
+ }
+
+ public function getShowVal(): ?bool
+ {
+ return $this->showVal;
+ }
+
+ /**
+ * Set show val
+ * Specifies that the value should be shown in data labels.
+ */
+ public function setShowVal(?bool $showDataLabelValues): self
+ {
+ $this->showVal = $showDataLabelValues;
+
+ return $this;
+ }
+
+ public function getShowCatName(): ?bool
+ {
+ return $this->showCatName;
+ }
+
+ /**
+ * Set show cat name
+ * Specifies that the category name should be shown in data labels.
+ */
+ public function setShowCatName(?bool $showCategoryName): self
+ {
+ $this->showCatName = $showCategoryName;
+
+ return $this;
+ }
+
+ public function getShowSerName(): ?bool
+ {
+ return $this->showSerName;
+ }
+
+ /**
+ * Set show data series name.
+ * Specifies that the series name should be shown in data labels.
+ */
+ public function setShowSerName(?bool $showSeriesName): self
+ {
+ $this->showSerName = $showSeriesName;
+
+ return $this;
+ }
+
+ public function getShowPercent(): ?bool
+ {
+ return $this->showPercent;
+ }
+
+ /**
+ * Set show percentage.
+ * Specifies that the percentage should be shown in data labels.
+ */
+ public function setShowPercent(?bool $showPercentage): self
+ {
+ $this->showPercent = $showPercentage;
+
+ return $this;
+ }
+
+ public function getShowBubbleSize(): ?bool
+ {
+ return $this->showBubbleSize;
+ }
+
+ /**
+ * Set show bubble size.
+ * Specifies that the bubble size should be shown in data labels.
+ */
+ public function setShowBubbleSize(?bool $showBubbleSize): self
+ {
+ $this->showBubbleSize = $showBubbleSize;
+
+ return $this;
+ }
+
+ public function getShowLeaderLines(): ?bool
+ {
+ return $this->showLeaderLines;
+ }
+
+ /**
+ * Set show leader lines.
+ * Specifies that leader lines should be shown in data labels.
+ */
+ public function setShowLeaderLines(?bool $showLeaderLines): self
+ {
+ $this->showLeaderLines = $showLeaderLines;
+
+ return $this;
+ }
+
+ public function getLabelFillColor(): ?ChartColor
+ {
+ return $this->labelFillColor;
+ }
+
+ public function setLabelFillColor(?ChartColor $chartColor): self
+ {
+ $this->labelFillColor = $chartColor;
+
+ return $this;
+ }
+
+ public function getLabelBorderColor(): ?ChartColor
+ {
+ return $this->labelBorderColor;
+ }
+
+ public function setLabelBorderColor(?ChartColor $chartColor): self
+ {
+ $this->labelBorderColor = $chartColor;
+
+ return $this;
+ }
+
+ public function getLabelFont(): ?Font
+ {
+ return $this->labelFont;
+ }
+
+ public function getLabelEffects(): ?Properties
+ {
+ return $this->labelEffects;
+ }
+
+ public function getLabelFontColor(): ?ChartColor
+ {
+ if ($this->labelFont === null) {
+ return null;
+ }
+
+ return $this->labelFont->getChartColor();
+ }
+
+ public function setLabelFontColor(?ChartColor $chartColor): self
+ {
+ if ($this->labelFont === null) {
+ $this->labelFont = new Font();
+ $this->labelFont->setSize(null, true);
+ }
+ $this->labelFont->setChartColorFromObject($chartColor);
+
+ return $this;
+ }
+
+ public function getDLblPos(): string
+ {
+ return $this->dLblPos;
+ }
+
+ public function setDLblPos(string $dLblPos): self
+ {
+ $this->dLblPos = $dLblPos;
+
+ return $this;
+ }
+
+ public function getNumFmtCode(): string
+ {
+ return $this->numFmtCode;
+ }
+
+ public function setNumFmtCode(string $numFmtCode): self
+ {
+ $this->numFmtCode = $numFmtCode;
+
+ return $this;
+ }
+
+ public function getNumFmtLinked(): bool
+ {
+ return $this->numFmtLinked;
+ }
+
+ public function setNumFmtLinked(bool $numFmtLinked): self
+ {
+ $this->numFmtLinked = $numFmtLinked;
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->labelFillColor = ($this->labelFillColor === null) ? null : clone $this->labelFillColor;
+ $this->labelBorderColor = ($this->labelBorderColor === null) ? null : clone $this->labelBorderColor;
+ $this->labelFont = ($this->labelFont === null) ? null : clone $this->labelFont;
+ $this->labelEffects = ($this->labelEffects === null) ? null : clone $this->labelEffects;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
new file mode 100644
index 0000000..44c098d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
@@ -0,0 +1,189 @@
+ self::POSITION_BOTTOM,
+ self::XL_LEGEND_POSITION_CORNER => self::POSITION_TOPRIGHT,
+ self::XL_LEGEND_POSITION_CUSTOM => '??',
+ self::XL_LEGEND_POSITION_LEFT => self::POSITION_LEFT,
+ self::XL_LEGEND_POSITION_RIGHT => self::POSITION_RIGHT,
+ self::XL_LEGEND_POSITION_TOP => self::POSITION_TOP,
+ ];
+
+ /**
+ * Legend position.
+ *
+ * @var string
+ */
+ private $position = self::POSITION_RIGHT;
+
+ /**
+ * Allow overlay of other elements?
+ *
+ * @var bool
+ */
+ private $overlay = true;
+
+ /**
+ * Legend Layout.
+ */
+ private ?Layout $layout;
+
+ private GridLines $borderLines;
+
+ private ChartColor $fillColor;
+
+ private ?AxisText $legendText = null;
+
+ /**
+ * Create a new Legend.
+ *
+ * @param string $position
+ * @param bool $overlay
+ */
+ public function __construct($position = self::POSITION_RIGHT, ?Layout $layout = null, $overlay = false)
+ {
+ $this->setPosition($position);
+ $this->layout = $layout;
+ $this->setOverlay($overlay);
+ $this->borderLines = new GridLines();
+ $this->fillColor = new ChartColor();
+ }
+
+ public function getFillColor(): ChartColor
+ {
+ return $this->fillColor;
+ }
+
+ /**
+ * Get legend position as an excel string value.
+ *
+ * @return string
+ */
+ public function getPosition()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Get legend position using an excel string value.
+ *
+ * @param string $position see self::POSITION_*
+ */
+ public function setPosition($position): bool
+ {
+ if (!in_array($position, self::POSITION_XLREF)) {
+ return false;
+ }
+
+ $this->position = $position;
+
+ return true;
+ }
+
+ /**
+ * Get legend position as an Excel internal numeric value.
+ */
+ public function getPositionXL(): false|int
+ {
+ return array_search($this->position, self::POSITION_XLREF);
+ }
+
+ /**
+ * Set legend position using an Excel internal numeric value.
+ *
+ * @param int $positionXL see self::XL_LEGEND_POSITION_*
+ */
+ public function setPositionXL($positionXL): bool
+ {
+ if (!isset(self::POSITION_XLREF[$positionXL])) {
+ return false;
+ }
+
+ $this->position = self::POSITION_XLREF[$positionXL];
+
+ return true;
+ }
+
+ /**
+ * Get allow overlay of other elements?
+ *
+ * @return bool
+ */
+ public function getOverlay()
+ {
+ return $this->overlay;
+ }
+
+ /**
+ * Set allow overlay of other elements?
+ *
+ * @param bool $overlay
+ */
+ public function setOverlay($overlay): void
+ {
+ $this->overlay = $overlay;
+ }
+
+ /**
+ * Get Layout.
+ *
+ * @return ?Layout
+ */
+ public function getLayout()
+ {
+ return $this->layout;
+ }
+
+ public function getLegendText(): ?AxisText
+ {
+ return $this->legendText;
+ }
+
+ public function setLegendText(?AxisText $legendText): self
+ {
+ $this->legendText = $legendText;
+
+ return $this;
+ }
+
+ public function getBorderLines(): GridLines
+ {
+ return $this->borderLines;
+ }
+
+ public function setBorderLines(GridLines $borderLines): self
+ {
+ $this->borderLines = $borderLines;
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->layout = ($this->layout === null) ? null : clone $this->layout;
+ $this->legendText = ($this->legendText === null) ? null : clone $this->legendText;
+ $this->borderLines = clone $this->borderLines;
+ $this->fillColor = clone $this->fillColor;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
new file mode 100644
index 0000000..5064d5a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
@@ -0,0 +1,217 @@
+layout = $layout;
+ $this->plotSeries = $plotSeries;
+ }
+
+ public function getLayout(): ?Layout
+ {
+ return $this->layout;
+ }
+
+ /**
+ * Get Number of Plot Groups.
+ */
+ public function getPlotGroupCount(): int
+ {
+ return count($this->plotSeries);
+ }
+
+ /**
+ * Get Number of Plot Series.
+ *
+ * @return int
+ */
+ public function getPlotSeriesCount(): int|float
+ {
+ $seriesCount = 0;
+ foreach ($this->plotSeries as $plot) {
+ $seriesCount += $plot->getPlotSeriesCount();
+ }
+
+ return $seriesCount;
+ }
+
+ /**
+ * Get Plot Series.
+ *
+ * @return DataSeries[]
+ */
+ public function getPlotGroup()
+ {
+ return $this->plotSeries;
+ }
+
+ /**
+ * Get Plot Series by Index.
+ *
+ * @return DataSeries
+ */
+ public function getPlotGroupByIndex(mixed $index)
+ {
+ return $this->plotSeries[$index];
+ }
+
+ /**
+ * Set Plot Series.
+ *
+ * @param DataSeries[] $plotSeries
+ *
+ * @return $this
+ */
+ public function setPlotSeries(array $plotSeries): static
+ {
+ $this->plotSeries = $plotSeries;
+
+ return $this;
+ }
+
+ public function refresh(Worksheet $worksheet): void
+ {
+ foreach ($this->plotSeries as $plotSeries) {
+ $plotSeries->refresh($worksheet);
+ }
+ }
+
+ public function setNoFill(bool $noFill): self
+ {
+ $this->noFill = $noFill;
+
+ return $this;
+ }
+
+ public function getNoFill(): bool
+ {
+ return $this->noFill;
+ }
+
+ public function setGradientFillProperties(array $gradientFillStops, ?float $gradientFillAngle): self
+ {
+ $this->gradientFillStops = $gradientFillStops;
+ $this->gradientFillAngle = $gradientFillAngle;
+
+ return $this;
+ }
+
+ /**
+ * Get gradientFillAngle.
+ */
+ public function getGradientFillAngle(): ?float
+ {
+ return $this->gradientFillAngle;
+ }
+
+ /**
+ * Get gradientFillStops.
+ *
+ * @return array
+ */
+ public function getGradientFillStops()
+ {
+ return $this->gradientFillStops;
+ }
+
+ private ?int $gapWidth = null;
+
+ /** @var bool */
+ private $useUpBars = false;
+
+ /** @var bool */
+ private $useDownBars = false;
+
+ public function getGapWidth(): ?int
+ {
+ return $this->gapWidth;
+ }
+
+ public function setGapWidth(?int $gapWidth): self
+ {
+ $this->gapWidth = $gapWidth;
+
+ return $this;
+ }
+
+ public function getUseUpBars(): bool
+ {
+ return $this->useUpBars;
+ }
+
+ public function setUseUpBars(bool $useUpBars): self
+ {
+ $this->useUpBars = $useUpBars;
+
+ return $this;
+ }
+
+ public function getUseDownBars(): bool
+ {
+ return $this->useDownBars;
+ }
+
+ public function setUseDownBars(bool $useDownBars): self
+ {
+ $this->useDownBars = $useDownBars;
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->layout = ($this->layout === null) ? null : clone $this->layout;
+ $plotSeries = $this->plotSeries;
+ $this->plotSeries = [];
+ foreach ($plotSeries as $series) {
+ $this->plotSeries[] = clone $series;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
new file mode 100644
index 0000000..8074df6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
@@ -0,0 +1,970 @@
+ null,
+ ];
+
+ /** @var array */
+ protected $shadowProperties = self::PRESETS_OPTIONS[0];
+
+ protected ChartColor $shadowColor;
+
+ public function __construct()
+ {
+ $this->lineColor = new ChartColor();
+ $this->glowColor = new ChartColor();
+ $this->shadowColor = new ChartColor();
+ $this->shadowColor->setType(ChartColor::EXCEL_COLOR_TYPE_STANDARD);
+ $this->shadowColor->setValue('black');
+ $this->shadowColor->setAlpha(40);
+ }
+
+ /**
+ * Get Object State.
+ *
+ * @return bool
+ */
+ public function getObjectState()
+ {
+ return $this->objectState;
+ }
+
+ /**
+ * Change Object State to True.
+ *
+ * @return $this
+ */
+ public function activateObject()
+ {
+ $this->objectState = true;
+
+ return $this;
+ }
+
+ public static function pointsToXml(float $width): string
+ {
+ return (string) (int) ($width * self::POINTS_WIDTH_MULTIPLIER);
+ }
+
+ public static function xmlToPoints(string $width): float
+ {
+ return ((float) $width) / self::POINTS_WIDTH_MULTIPLIER;
+ }
+
+ public static function angleToXml(float $angle): string
+ {
+ return (string) (int) ($angle * self::ANGLE_MULTIPLIER);
+ }
+
+ public static function xmlToAngle(string $angle): float
+ {
+ return ((float) $angle) / self::ANGLE_MULTIPLIER;
+ }
+
+ public static function tenthOfPercentToXml(float $value): string
+ {
+ return (string) (int) ($value * self::PERCENTAGE_MULTIPLIER);
+ }
+
+ public static function xmlToTenthOfPercent(string $value): float
+ {
+ return ((float) $value) / self::PERCENTAGE_MULTIPLIER;
+ }
+
+ /**
+ * @param null|float|int|string $alpha
+ */
+ protected function setColorProperties(?string $color, $alpha, ?string $colorType): array
+ {
+ return [
+ 'type' => $colorType,
+ 'value' => $color,
+ 'alpha' => ($alpha === null) ? null : (int) $alpha,
+ ];
+ }
+
+ protected const PRESETS_OPTIONS = [
+ //NONE
+ 0 => [
+ 'presets' => self::SHADOW_PRESETS_NOSHADOW,
+ 'effect' => null,
+ //'color' => [
+ // 'type' => ChartColor::EXCEL_COLOR_TYPE_STANDARD,
+ // 'value' => 'black',
+ // 'alpha' => 40,
+ //],
+ 'size' => [
+ 'sx' => null,
+ 'sy' => null,
+ 'kx' => null,
+ 'ky' => null,
+ ],
+ 'blur' => null,
+ 'direction' => null,
+ 'distance' => null,
+ 'algn' => null,
+ 'rotWithShape' => null,
+ ],
+ //OUTER
+ 1 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 2700000 / self::ANGLE_MULTIPLIER,
+ 'algn' => 'tl',
+ 'rotWithShape' => '0',
+ ],
+ 2 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 5400000 / self::ANGLE_MULTIPLIER,
+ 'algn' => 't',
+ 'rotWithShape' => '0',
+ ],
+ 3 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 8100000 / self::ANGLE_MULTIPLIER,
+ 'algn' => 'tr',
+ 'rotWithShape' => '0',
+ ],
+ 4 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'algn' => 'l',
+ 'rotWithShape' => '0',
+ ],
+ 5 => [
+ 'effect' => 'outerShdw',
+ 'size' => [
+ 'sx' => 102000 / self::PERCENTAGE_MULTIPLIER,
+ 'sy' => 102000 / self::PERCENTAGE_MULTIPLIER,
+ ],
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'algn' => 'ctr',
+ 'rotWithShape' => '0',
+ ],
+ 6 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 10800000 / self::ANGLE_MULTIPLIER,
+ 'algn' => 'r',
+ 'rotWithShape' => '0',
+ ],
+ 7 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 18900000 / self::ANGLE_MULTIPLIER,
+ 'algn' => 'bl',
+ 'rotWithShape' => '0',
+ ],
+ 8 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 16200000 / self::ANGLE_MULTIPLIER,
+ 'rotWithShape' => '0',
+ ],
+ 9 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 38100 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 13500000 / self::ANGLE_MULTIPLIER,
+ 'algn' => 'br',
+ 'rotWithShape' => '0',
+ ],
+ //INNER
+ 10 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 2700000 / self::ANGLE_MULTIPLIER,
+ ],
+ 11 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 5400000 / self::ANGLE_MULTIPLIER,
+ ],
+ 12 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 8100000 / self::ANGLE_MULTIPLIER,
+ ],
+ 13 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ ],
+ 14 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 114300 / self::POINTS_WIDTH_MULTIPLIER,
+ ],
+ 15 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 10800000 / self::ANGLE_MULTIPLIER,
+ ],
+ 16 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 18900000 / self::ANGLE_MULTIPLIER,
+ ],
+ 17 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 16200000 / self::ANGLE_MULTIPLIER,
+ ],
+ 18 => [
+ 'effect' => 'innerShdw',
+ 'blur' => 63500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 50800 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 13500000 / self::ANGLE_MULTIPLIER,
+ ],
+ //perspective
+ 19 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 152400 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 317500 / self::POINTS_WIDTH_MULTIPLIER,
+ 'size' => [
+ 'sx' => 90000 / self::PERCENTAGE_MULTIPLIER,
+ 'sy' => -19000 / self::PERCENTAGE_MULTIPLIER,
+ ],
+ 'direction' => 5400000 / self::ANGLE_MULTIPLIER,
+ 'rotWithShape' => '0',
+ ],
+ 20 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 76200 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 18900000 / self::ANGLE_MULTIPLIER,
+ 'size' => [
+ 'sy' => 23000 / self::PERCENTAGE_MULTIPLIER,
+ 'kx' => -1200000 / self::ANGLE_MULTIPLIER,
+ ],
+ 'algn' => 'bl',
+ 'rotWithShape' => '0',
+ ],
+ 21 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 76200 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 13500000 / self::ANGLE_MULTIPLIER,
+ 'size' => [
+ 'sy' => 23000 / self::PERCENTAGE_MULTIPLIER,
+ 'kx' => 1200000 / self::ANGLE_MULTIPLIER,
+ ],
+ 'algn' => 'br',
+ 'rotWithShape' => '0',
+ ],
+ 22 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 76200 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 12700 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 2700000 / self::ANGLE_MULTIPLIER,
+ 'size' => [
+ 'sy' => -23000 / self::PERCENTAGE_MULTIPLIER,
+ 'kx' => -800400 / self::ANGLE_MULTIPLIER,
+ ],
+ 'algn' => 'bl',
+ 'rotWithShape' => '0',
+ ],
+ 23 => [
+ 'effect' => 'outerShdw',
+ 'blur' => 76200 / self::POINTS_WIDTH_MULTIPLIER,
+ 'distance' => 12700 / self::POINTS_WIDTH_MULTIPLIER,
+ 'direction' => 8100000 / self::ANGLE_MULTIPLIER,
+ 'size' => [
+ 'sy' => -23000 / self::PERCENTAGE_MULTIPLIER,
+ 'kx' => 800400 / self::ANGLE_MULTIPLIER,
+ ],
+ 'algn' => 'br',
+ 'rotWithShape' => '0',
+ ],
+ ];
+
+ protected function getShadowPresetsMap(int $presetsOption): array
+ {
+ return self::PRESETS_OPTIONS[$presetsOption] ?? self::PRESETS_OPTIONS[0];
+ }
+
+ /**
+ * Get value of array element.
+ *
+ * @return mixed
+ */
+ protected function getArrayElementsValue(mixed $properties, mixed $elements)
+ {
+ $reference = &$properties;
+ if (!is_array($elements)) {
+ return $reference[$elements];
+ }
+
+ foreach ($elements as $keys) {
+ $reference = &$reference[$keys];
+ }
+
+ return $reference;
+ }
+
+ /**
+ * Set Glow Properties.
+ *
+ * @param float $size
+ * @param ?string $colorValue
+ * @param ?int $colorAlpha
+ * @param ?string $colorType
+ */
+ public function setGlowProperties($size, $colorValue = null, $colorAlpha = null, $colorType = null): void
+ {
+ $this
+ ->activateObject()
+ ->setGlowSize($size);
+ $this->glowColor->setColorPropertiesArray(
+ [
+ 'value' => $colorValue,
+ 'type' => $colorType,
+ 'alpha' => $colorAlpha,
+ ]
+ );
+ }
+
+ /**
+ * Get Glow Property.
+ *
+ * @param array|string $property
+ *
+ * @return null|array|float|int|string
+ */
+ public function getGlowProperty($property)
+ {
+ $retVal = null;
+ if ($property === 'size') {
+ $retVal = $this->glowSize;
+ } elseif ($property === 'color') {
+ $retVal = [
+ 'value' => $this->glowColor->getColorProperty('value'),
+ 'type' => $this->glowColor->getColorProperty('type'),
+ 'alpha' => $this->glowColor->getColorProperty('alpha'),
+ ];
+ } elseif (is_array($property) && count($property) >= 2 && $property[0] === 'color') {
+ $retVal = $this->glowColor->getColorProperty($property[1]);
+ }
+
+ return $retVal;
+ }
+
+ /**
+ * Get Glow Color Property.
+ *
+ * @param string $propertyName
+ *
+ * @return null|int|string
+ */
+ public function getGlowColor($propertyName)
+ {
+ return $this->glowColor->getColorProperty($propertyName);
+ }
+
+ public function getGlowColorObject(): ChartColor
+ {
+ return $this->glowColor;
+ }
+
+ /**
+ * Get Glow Size.
+ *
+ * @return ?float
+ */
+ public function getGlowSize()
+ {
+ return $this->glowSize;
+ }
+
+ /**
+ * Set Glow Size.
+ *
+ * @param ?float $size
+ *
+ * @return $this
+ */
+ protected function setGlowSize($size)
+ {
+ $this->glowSize = $size;
+
+ return $this;
+ }
+
+ /**
+ * Set Soft Edges Size.
+ *
+ * @param ?float $size
+ */
+ public function setSoftEdges($size): void
+ {
+ if ($size !== null) {
+ $this->activateObject();
+ $this->softEdges['size'] = $size;
+ }
+ }
+
+ /**
+ * Get Soft Edges Size.
+ *
+ * @return string
+ */
+ public function getSoftEdgesSize()
+ {
+ return $this->softEdges['size'];
+ }
+
+ public function setShadowProperty(string $propertyName, mixed $value): self
+ {
+ $this->activateObject();
+ if ($propertyName === 'color' && is_array($value)) {
+ $this->shadowColor->setColorPropertiesArray($value);
+ } else {
+ $this->shadowProperties[$propertyName] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set Shadow Properties.
+ *
+ * @param int $presets
+ * @param string $colorValue
+ * @param string $colorType
+ * @param null|float|int|string $colorAlpha
+ * @param null|float $blur
+ * @param null|int $angle
+ * @param null|float $distance
+ */
+ public function setShadowProperties($presets, $colorValue = null, $colorType = null, $colorAlpha = null, $blur = null, $angle = null, $distance = null): void
+ {
+ $this->activateObject()->setShadowPresetsProperties((int) $presets);
+ if ($presets === 0) {
+ $this->shadowColor->setType(ChartColor::EXCEL_COLOR_TYPE_STANDARD);
+ $this->shadowColor->setValue('black');
+ $this->shadowColor->setAlpha(40);
+ }
+ if ($colorValue !== null) {
+ $this->shadowColor->setValue($colorValue);
+ }
+ if ($colorType !== null) {
+ $this->shadowColor->setType($colorType);
+ }
+ if (is_numeric($colorAlpha)) {
+ $this->shadowColor->setAlpha((int) $colorAlpha);
+ }
+ $this
+ ->setShadowBlur($blur)
+ ->setShadowAngle($angle)
+ ->setShadowDistance($distance);
+ }
+
+ /**
+ * Set Shadow Presets Properties.
+ *
+ * @return $this
+ */
+ protected function setShadowPresetsProperties(int $presets)
+ {
+ $this->shadowProperties['presets'] = $presets;
+ $this->setShadowPropertiesMapValues($this->getShadowPresetsMap($presets));
+
+ return $this;
+ }
+
+ protected const SHADOW_ARRAY_KEYS = ['size', 'color'];
+
+ /**
+ * Set Shadow Properties Values.
+ *
+ * @return $this
+ */
+ protected function setShadowPropertiesMapValues(array $propertiesMap, ?array &$reference = null)
+ {
+ $base_reference = $reference;
+ foreach ($propertiesMap as $property_key => $property_val) {
+ if (is_array($property_val)) {
+ if (in_array($property_key, self::SHADOW_ARRAY_KEYS, true)) {
+ $reference = &$this->shadowProperties[$property_key];
+ $this->setShadowPropertiesMapValues($property_val, $reference);
+ }
+ } else {
+ if ($base_reference === null) {
+ $this->shadowProperties[$property_key] = $property_val;
+ } else {
+ $reference[$property_key] = $property_val;
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set Shadow Blur.
+ *
+ * @param ?float $blur
+ *
+ * @return $this
+ */
+ protected function setShadowBlur($blur)
+ {
+ if ($blur !== null) {
+ $this->shadowProperties['blur'] = $blur;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set Shadow Angle.
+ *
+ * @param null|float|int|string $angle
+ *
+ * @return $this
+ */
+ protected function setShadowAngle($angle)
+ {
+ if (is_numeric($angle)) {
+ $this->shadowProperties['direction'] = $angle;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set Shadow Distance.
+ *
+ * @param ?float $distance
+ *
+ * @return $this
+ */
+ protected function setShadowDistance($distance)
+ {
+ if ($distance !== null) {
+ $this->shadowProperties['distance'] = $distance;
+ }
+
+ return $this;
+ }
+
+ public function getShadowColorObject(): ChartColor
+ {
+ return $this->shadowColor;
+ }
+
+ /**
+ * Get Shadow Property.
+ *
+ * @param string|string[] $elements
+ *
+ * @return array|string
+ */
+ public function getShadowProperty($elements)
+ {
+ if ($elements === 'color') {
+ return [
+ 'value' => $this->shadowColor->getValue(),
+ 'type' => $this->shadowColor->getType(),
+ 'alpha' => $this->shadowColor->getAlpha(),
+ ];
+ }
+
+ return $this->getArrayElementsValue($this->shadowProperties, $elements);
+ }
+
+ public function getShadowArray(): array
+ {
+ $array = $this->shadowProperties;
+ if ($this->getShadowColorObject()->isUsable()) {
+ $array['color'] = $this->getShadowProperty('color');
+ }
+
+ return $array;
+ }
+
+ protected ChartColor $lineColor;
+
+ /** @var array */
+ protected $lineStyleProperties = [
+ 'width' => null, //'9525',
+ 'compound' => '', //self::LINE_STYLE_COMPOUND_SIMPLE,
+ 'dash' => '', //self::LINE_STYLE_DASH_SOLID,
+ 'cap' => '', //self::LINE_STYLE_CAP_FLAT,
+ 'join' => '', //self::LINE_STYLE_JOIN_BEVEL,
+ 'arrow' => [
+ 'head' => [
+ 'type' => '', //self::LINE_STYLE_ARROW_TYPE_NOARROW,
+ 'size' => '', //self::LINE_STYLE_ARROW_SIZE_5,
+ 'w' => '',
+ 'len' => '',
+ ],
+ 'end' => [
+ 'type' => '', //self::LINE_STYLE_ARROW_TYPE_NOARROW,
+ 'size' => '', //self::LINE_STYLE_ARROW_SIZE_8,
+ 'w' => '',
+ 'len' => '',
+ ],
+ ],
+ ];
+
+ public function copyLineStyles(self $otherProperties): void
+ {
+ $this->lineStyleProperties = $otherProperties->lineStyleProperties;
+ $this->lineColor = $otherProperties->lineColor;
+ $this->glowSize = $otherProperties->glowSize;
+ $this->glowColor = $otherProperties->glowColor;
+ $this->softEdges = $otherProperties->softEdges;
+ $this->shadowProperties = $otherProperties->shadowProperties;
+ }
+
+ public function getLineColor(): ChartColor
+ {
+ return $this->lineColor;
+ }
+
+ /**
+ * Set Line Color Properties.
+ *
+ * @param ?int $alpha
+ */
+ public function setLineColorProperties(?string $value, $alpha = null, ?string $colorType = null): void
+ {
+ $this->activateObject();
+ $this->lineColor->setColorPropertiesArray(
+ $this->setColorProperties(
+ $value,
+ $alpha,
+ $colorType
+ )
+ );
+ }
+
+ /**
+ * Get Line Color Property.
+ *
+ * @param string $propertyName
+ *
+ * @return null|int|string
+ */
+ public function getLineColorProperty($propertyName)
+ {
+ return $this->lineColor->getColorProperty($propertyName);
+ }
+
+ /**
+ * Set Line Style Properties.
+ *
+ * @param null|float|int|string $lineWidth
+ * @param string $compoundType
+ * @param string $dashType
+ * @param string $capType
+ * @param string $joinType
+ * @param string $headArrowType
+ * @param null|int|string $headArrowSize index into ARROW_SIZES array
+ * @param string $endArrowType
+ * @param null|int|string $endArrowSize index into ARROW_SIZES array
+ * @param string $headArrowWidth
+ * @param string $headArrowLength
+ * @param string $endArrowWidth
+ * @param string $endArrowLength
+ */
+ public function setLineStyleProperties($lineWidth = null, $compoundType = '', $dashType = '', $capType = '', $joinType = '', $headArrowType = '', $headArrowSize = '', $endArrowType = '', $endArrowSize = '', $headArrowWidth = '', $headArrowLength = '', $endArrowWidth = '', $endArrowLength = ''): void
+ {
+ $this->activateObject();
+ if (is_numeric($lineWidth)) {
+ $this->lineStyleProperties['width'] = $lineWidth;
+ }
+ if ($compoundType !== '') {
+ $this->lineStyleProperties['compound'] = $compoundType;
+ }
+ if ($dashType !== '') {
+ $this->lineStyleProperties['dash'] = $dashType;
+ }
+ if ($capType !== '') {
+ $this->lineStyleProperties['cap'] = $capType;
+ }
+ if ($joinType !== '') {
+ $this->lineStyleProperties['join'] = $joinType;
+ }
+ if ($headArrowType !== '') {
+ $this->lineStyleProperties['arrow']['head']['type'] = $headArrowType;
+ }
+ if (isset(self::ARROW_SIZES[$headArrowSize])) {
+ $this->lineStyleProperties['arrow']['head']['size'] = $headArrowSize;
+ $this->lineStyleProperties['arrow']['head']['w'] = self::ARROW_SIZES[$headArrowSize]['w'];
+ $this->lineStyleProperties['arrow']['head']['len'] = self::ARROW_SIZES[$headArrowSize]['len'];
+ }
+ if ($endArrowType !== '') {
+ $this->lineStyleProperties['arrow']['end']['type'] = $endArrowType;
+ }
+ if (isset(self::ARROW_SIZES[$endArrowSize])) {
+ $this->lineStyleProperties['arrow']['end']['size'] = $endArrowSize;
+ $this->lineStyleProperties['arrow']['end']['w'] = self::ARROW_SIZES[$endArrowSize]['w'];
+ $this->lineStyleProperties['arrow']['end']['len'] = self::ARROW_SIZES[$endArrowSize]['len'];
+ }
+ if ($headArrowWidth !== '') {
+ $this->lineStyleProperties['arrow']['head']['w'] = $headArrowWidth;
+ }
+ if ($headArrowLength !== '') {
+ $this->lineStyleProperties['arrow']['head']['len'] = $headArrowLength;
+ }
+ if ($endArrowWidth !== '') {
+ $this->lineStyleProperties['arrow']['end']['w'] = $endArrowWidth;
+ }
+ if ($endArrowLength !== '') {
+ $this->lineStyleProperties['arrow']['end']['len'] = $endArrowLength;
+ }
+ }
+
+ public function getLineStyleArray(): array
+ {
+ return $this->lineStyleProperties;
+ }
+
+ public function setLineStyleArray(array $lineStyleProperties = []): self
+ {
+ $this->activateObject();
+ $this->lineStyleProperties['width'] = $lineStyleProperties['width'] ?? null;
+ $this->lineStyleProperties['compound'] = $lineStyleProperties['compound'] ?? '';
+ $this->lineStyleProperties['dash'] = $lineStyleProperties['dash'] ?? '';
+ $this->lineStyleProperties['cap'] = $lineStyleProperties['cap'] ?? '';
+ $this->lineStyleProperties['join'] = $lineStyleProperties['join'] ?? '';
+ $this->lineStyleProperties['arrow']['head']['type'] = $lineStyleProperties['arrow']['head']['type'] ?? '';
+ $this->lineStyleProperties['arrow']['head']['size'] = $lineStyleProperties['arrow']['head']['size'] ?? '';
+ $this->lineStyleProperties['arrow']['head']['w'] = $lineStyleProperties['arrow']['head']['w'] ?? '';
+ $this->lineStyleProperties['arrow']['head']['len'] = $lineStyleProperties['arrow']['head']['len'] ?? '';
+ $this->lineStyleProperties['arrow']['end']['type'] = $lineStyleProperties['arrow']['end']['type'] ?? '';
+ $this->lineStyleProperties['arrow']['end']['size'] = $lineStyleProperties['arrow']['end']['size'] ?? '';
+ $this->lineStyleProperties['arrow']['end']['w'] = $lineStyleProperties['arrow']['end']['w'] ?? '';
+ $this->lineStyleProperties['arrow']['end']['len'] = $lineStyleProperties['arrow']['end']['len'] ?? '';
+
+ return $this;
+ }
+
+ public function setLineStyleProperty(string $propertyName, mixed $value): self
+ {
+ $this->activateObject();
+ $this->lineStyleProperties[$propertyName] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get Line Style Property.
+ *
+ * @param array|string $elements
+ *
+ * @return string
+ */
+ public function getLineStyleProperty($elements)
+ {
+ return $this->getArrayElementsValue($this->lineStyleProperties, $elements);
+ }
+
+ protected const ARROW_SIZES = [
+ 1 => ['w' => 'sm', 'len' => 'sm'],
+ 2 => ['w' => 'sm', 'len' => 'med'],
+ 3 => ['w' => 'sm', 'len' => 'lg'],
+ 4 => ['w' => 'med', 'len' => 'sm'],
+ 5 => ['w' => 'med', 'len' => 'med'],
+ 6 => ['w' => 'med', 'len' => 'lg'],
+ 7 => ['w' => 'lg', 'len' => 'sm'],
+ 8 => ['w' => 'lg', 'len' => 'med'],
+ 9 => ['w' => 'lg', 'len' => 'lg'],
+ ];
+
+ /**
+ * Get Line Style Arrow Size.
+ *
+ * @param int $arraySelector
+ * @param string $arrayKaySelector
+ *
+ * @return string
+ */
+ protected function getLineStyleArrowSize($arraySelector, $arrayKaySelector)
+ {
+ return self::ARROW_SIZES[$arraySelector][$arrayKaySelector] ?? '';
+ }
+
+ /**
+ * Get Line Style Arrow Parameters.
+ *
+ * @param string $arrowSelector
+ * @param string $propertySelector
+ *
+ * @return string
+ */
+ public function getLineStyleArrowParameters($arrowSelector, $propertySelector)
+ {
+ return $this->getLineStyleArrowSize($this->lineStyleProperties['arrow'][$arrowSelector]['size'], $propertySelector);
+ }
+
+ /**
+ * Get Line Style Arrow Width.
+ *
+ * @param string $arrow
+ *
+ * @return string
+ */
+ public function getLineStyleArrowWidth($arrow)
+ {
+ return $this->getLineStyleProperty(['arrow', $arrow, 'w']);
+ }
+
+ /**
+ * Get Line Style Arrow Excel Length.
+ *
+ * @param string $arrow
+ *
+ * @return string
+ */
+ public function getLineStyleArrowLength($arrow)
+ {
+ return $this->getLineStyleProperty(['arrow', $arrow, 'len']);
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->lineColor = clone $this->lineColor;
+ $this->glowColor = clone $this->glowColor;
+ $this->shadowColor = clone $this->shadowColor;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
new file mode 100644
index 0000000..ff14cd7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
@@ -0,0 +1,22 @@
+graph = null;
+ $this->chart = $chart;
+
+ self::$markSet = [
+ 'diamond' => MARK_DIAMOND,
+ 'square' => MARK_SQUARE,
+ 'triangle' => MARK_UTRIANGLE,
+ 'x' => MARK_X,
+ 'star' => MARK_STAR,
+ 'dot' => MARK_FILLEDCIRCLE,
+ 'dash' => MARK_DTRIANGLE,
+ 'circle' => MARK_CIRCLE,
+ 'plus' => MARK_CROSS,
+ ];
+ }
+
+ private function getGraphWidth(): float
+ {
+ return $this->chart->getRenderedWidth() ?? self::DEFAULT_WIDTH;
+ }
+
+ private function getGraphHeight(): float
+ {
+ return $this->chart->getRenderedHeight() ?? self::DEFAULT_HEIGHT;
+ }
+
+ /**
+ * This method should be overriden in descendants to do real JpGraph library initialization.
+ */
+ abstract protected static function init(): void;
+
+ private function formatPointMarker($seriesPlot, $markerID)
+ {
+ $plotMarkKeys = array_keys(self::$markSet);
+ if ($markerID === null) {
+ // Use default plot marker (next marker in the series)
+ self::$plotMark %= count(self::$markSet);
+ $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
+ } elseif ($markerID !== 'none') {
+ // Use specified plot marker (if it exists)
+ if (isset(self::$markSet[$markerID])) {
+ $seriesPlot->mark->SetType(self::$markSet[$markerID]);
+ } else {
+ // If the specified plot marker doesn't exist, use default plot marker (next marker in the series)
+ self::$plotMark %= count(self::$markSet);
+ $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
+ }
+ } else {
+ // Hide plot marker
+ $seriesPlot->mark->Hide();
+ }
+ $seriesPlot->mark->SetColor(self::$colourSet[self::$plotColour]);
+ $seriesPlot->mark->SetFillColor(self::$colourSet[self::$plotColour]);
+ $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
+
+ return $seriesPlot;
+ }
+
+ private function formatDataSetLabels(int $groupID, array $datasetLabels, $rotation = '')
+ {
+ $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode() ?? '';
+ // Retrieve any label formatting code
+ $datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
+
+ $testCurrentIndex = 0;
+ foreach ($datasetLabels as $i => $datasetLabel) {
+ if (is_array($datasetLabel)) {
+ if ($rotation == 'bar') {
+ $datasetLabels[$i] = implode(' ', $datasetLabel);
+ } else {
+ $datasetLabel = array_reverse($datasetLabel);
+ $datasetLabels[$i] = implode("\n", $datasetLabel);
+ }
+ } else {
+ // Format labels according to any formatting code
+ if ($datasetLabelFormatCode !== null) {
+ $datasetLabels[$i] = NumberFormat::toFormattedString($datasetLabel, $datasetLabelFormatCode);
+ }
+ }
+ ++$testCurrentIndex;
+ }
+
+ return $datasetLabels;
+ }
+
+ private function percentageSumCalculation(int $groupID, $seriesCount)
+ {
+ $sumValues = [];
+ // Adjust our values to a percentage value across all series in the group
+ for ($i = 0; $i < $seriesCount; ++$i) {
+ if ($i == 0) {
+ $sumValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+ } else {
+ $nextValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+ foreach ($nextValues as $k => $value) {
+ if (isset($sumValues[$k])) {
+ $sumValues[$k] += $value;
+ } else {
+ $sumValues[$k] = $value;
+ }
+ }
+ }
+ }
+
+ return $sumValues;
+ }
+
+ private function percentageAdjustValues(array $dataValues, array $sumValues)
+ {
+ foreach ($dataValues as $k => $dataValue) {
+ $dataValues[$k] = $dataValue / $sumValues[$k] * 100;
+ }
+
+ return $dataValues;
+ }
+
+ private function getCaption($captionElement)
+ {
+ // Read any caption
+ $caption = ($captionElement !== null) ? $captionElement->getCaption() : null;
+ // Test if we have a title caption to display
+ if ($caption !== null) {
+ // If we do, it could be a plain string or an array
+ if (is_array($caption)) {
+ // Implode an array to a plain string
+ $caption = implode('', $caption);
+ }
+ }
+
+ return $caption;
+ }
+
+ private function renderTitle(): void
+ {
+ $title = $this->getCaption($this->chart->getTitle());
+ if ($title !== null) {
+ $this->graph->title->Set($title);
+ }
+ }
+
+ private function renderLegend(): void
+ {
+ $legend = $this->chart->getLegend();
+ if ($legend !== null) {
+ $legendPosition = $legend->getPosition();
+ switch ($legendPosition) {
+ case 'r':
+ $this->graph->legend->SetPos(0.01, 0.5, 'right', 'center'); // right
+ $this->graph->legend->SetColumns(1);
+
+ break;
+ case 'l':
+ $this->graph->legend->SetPos(0.01, 0.5, 'left', 'center'); // left
+ $this->graph->legend->SetColumns(1);
+
+ break;
+ case 't':
+ $this->graph->legend->SetPos(0.5, 0.01, 'center', 'top'); // top
+
+ break;
+ case 'b':
+ $this->graph->legend->SetPos(0.5, 0.99, 'center', 'bottom'); // bottom
+
+ break;
+ default:
+ $this->graph->legend->SetPos(0.01, 0.01, 'right', 'top'); // top-right
+ $this->graph->legend->SetColumns(1);
+
+ break;
+ }
+ } else {
+ $this->graph->legend->Hide();
+ }
+ }
+
+ private function renderCartesianPlotArea(string $type = 'textlin'): void
+ {
+ $this->graph = new Graph($this->getGraphWidth(), $this->getGraphHeight());
+ $this->graph->SetScale($type);
+
+ $this->renderTitle();
+
+ // Rotate for bar rather than column chart
+ $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotDirection();
+ $reverse = $rotation == 'bar';
+
+ $xAxisLabel = $this->chart->getXAxisLabel();
+ if ($xAxisLabel !== null) {
+ $title = $this->getCaption($xAxisLabel);
+ if ($title !== null) {
+ $this->graph->xaxis->SetTitle($title, 'center');
+ $this->graph->xaxis->title->SetMargin(35);
+ if ($reverse) {
+ $this->graph->xaxis->title->SetAngle(90);
+ $this->graph->xaxis->title->SetMargin(90);
+ }
+ }
+ }
+
+ $yAxisLabel = $this->chart->getYAxisLabel();
+ if ($yAxisLabel !== null) {
+ $title = $this->getCaption($yAxisLabel);
+ if ($title !== null) {
+ $this->graph->yaxis->SetTitle($title, 'center');
+ if ($reverse) {
+ $this->graph->yaxis->title->SetAngle(0);
+ $this->graph->yaxis->title->SetMargin(-55);
+ }
+ }
+ }
+ }
+
+ private function renderPiePlotArea(): void
+ {
+ $this->graph = new PieGraph($this->getGraphWidth(), $this->getGraphHeight());
+
+ $this->renderTitle();
+ }
+
+ private function renderRadarPlotArea(): void
+ {
+ $this->graph = new RadarGraph($this->getGraphWidth(), $this->getGraphHeight());
+ $this->graph->SetScale('lin');
+
+ $this->renderTitle();
+ }
+
+ private function renderPlotLine(int $groupID, bool $filled = false, bool $combination = false): void
+ {
+ $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
+
+ $index = array_keys($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder())[0];
+ $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointCount();
+ if ($labelCount > 0) {
+ $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+ $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels);
+ $this->graph->xaxis->SetTickLabels($datasetLabels);
+ }
+
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+ $seriesPlots = [];
+ if ($grouping == 'percentStacked') {
+ $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
+ } else {
+ $sumValues = [];
+ }
+
+ // Loop through each data series in turn
+ for ($i = 0; $i < $seriesCount; ++$i) {
+ $index = array_keys($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder())[$i];
+ $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getDataValues();
+ $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointMarker();
+
+ if ($grouping == 'percentStacked') {
+ $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
+ }
+
+ // Fill in any missing values in the $dataValues array
+ $testCurrentIndex = 0;
+ foreach ($dataValues as $k => $dataValue) {
+ while ($k != $testCurrentIndex) {
+ $dataValues[$testCurrentIndex] = null;
+ ++$testCurrentIndex;
+ }
+ ++$testCurrentIndex;
+ }
+
+ $seriesPlot = new LinePlot($dataValues);
+ if ($combination) {
+ $seriesPlot->SetBarCenter();
+ }
+
+ if ($filled) {
+ $seriesPlot->SetFilled(true);
+ $seriesPlot->SetColor('black');
+ $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
+ } else {
+ // Set the appropriate plot marker
+ $this->formatPointMarker($seriesPlot, $marker);
+ }
+ $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($index)->getDataValue();
+ $seriesPlot->SetLegend($dataLabel);
+
+ $seriesPlots[] = $seriesPlot;
+ }
+
+ if ($grouping == 'standard') {
+ $groupPlot = $seriesPlots;
+ } else {
+ $groupPlot = new AccLinePlot($seriesPlots);
+ }
+ $this->graph->Add($groupPlot);
+ }
+
+ private function renderPlotBar(int $groupID, ?string $dimensions = '2d'): void
+ {
+ $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection();
+ // Rotate for bar rather than column chart
+ if (($groupID == 0) && ($rotation == 'bar')) {
+ $this->graph->Set90AndMargin();
+ }
+ $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
+
+ $index = array_keys($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder())[0];
+ $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getPointCount();
+ if ($labelCount > 0) {
+ $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+ $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels, $rotation);
+ // Rotate for bar rather than column chart
+ if ($rotation == 'bar') {
+ $datasetLabels = array_reverse($datasetLabels);
+ $this->graph->yaxis->SetPos('max');
+ $this->graph->yaxis->SetLabelAlign('center', 'top');
+ $this->graph->yaxis->SetLabelSide(SIDE_RIGHT);
+ }
+ $this->graph->xaxis->SetTickLabels($datasetLabels);
+ }
+
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+ $seriesPlots = [];
+ if ($grouping == 'percentStacked') {
+ $sumValues = $this->percentageSumCalculation($groupID, $seriesCount);
+ } else {
+ $sumValues = [];
+ }
+
+ // Loop through each data series in turn
+ for ($j = 0; $j < $seriesCount; ++$j) {
+ $index = array_keys($this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder())[$j];
+ $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($index)->getDataValues();
+ if ($grouping == 'percentStacked') {
+ $dataValues = $this->percentageAdjustValues($dataValues, $sumValues);
+ }
+
+ // Fill in any missing values in the $dataValues array
+ $testCurrentIndex = 0;
+ foreach ($dataValues as $k => $dataValue) {
+ while ($k != $testCurrentIndex) {
+ $dataValues[$testCurrentIndex] = null;
+ ++$testCurrentIndex;
+ }
+ ++$testCurrentIndex;
+ }
+
+ // Reverse the $dataValues order for bar rather than column chart
+ if ($rotation == 'bar') {
+ $dataValues = array_reverse($dataValues);
+ }
+ $seriesPlot = new BarPlot($dataValues);
+ $seriesPlot->SetColor('black');
+ $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
+ if ($dimensions == '3d') {
+ $seriesPlot->SetShadow();
+ }
+ if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) {
+ $dataLabel = '';
+ } else {
+ $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue();
+ }
+ $seriesPlot->SetLegend($dataLabel);
+
+ $seriesPlots[] = $seriesPlot;
+ }
+ // Reverse the plot order for bar rather than column chart
+ if (($rotation == 'bar') && ($grouping != 'percentStacked')) {
+ $seriesPlots = array_reverse($seriesPlots);
+ }
+
+ if ($grouping == 'clustered') {
+ $groupPlot = new GroupBarPlot($seriesPlots);
+ } elseif ($grouping == 'standard') {
+ $groupPlot = new GroupBarPlot($seriesPlots);
+ } else {
+ $groupPlot = new AccBarPlot($seriesPlots);
+ if ($dimensions == '3d') {
+ $groupPlot->SetShadow();
+ }
+ }
+
+ $this->graph->Add($groupPlot);
+ }
+
+ private function renderPlotScatter(int $groupID, bool $bubble): void
+ {
+ $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+
+ // Loop through each data series in turn
+ for ($i = 0; $i < $seriesCount; ++$i) {
+ $plotCategoryByIndex = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i);
+ if ($plotCategoryByIndex === false) {
+ $plotCategoryByIndex = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0);
+ }
+ $dataValuesY = $plotCategoryByIndex->getDataValues();
+ $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+
+ $redoDataValuesY = true;
+ if ($bubble) {
+ if (!$bubbleSize) {
+ $bubbleSize = '10';
+ }
+ $redoDataValuesY = false;
+ foreach ($dataValuesY as $dataValueY) {
+ if (!is_int($dataValueY) && !is_float($dataValueY)) {
+ $redoDataValuesY = true;
+
+ break;
+ }
+ }
+ }
+ if ($redoDataValuesY) {
+ foreach ($dataValuesY as $k => $dataValueY) {
+ $dataValuesY[$k] = $k;
+ }
+ }
+
+ $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY);
+ if ($scatterStyle == 'lineMarker') {
+ $seriesPlot->SetLinkPoints();
+ $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
+ } elseif ($scatterStyle == 'smoothMarker') {
+ $spline = new Spline($dataValuesY, $dataValuesX);
+ [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * $this->getGraphWidth() / 20);
+ $lplot = new LinePlot($splineDataX, $splineDataY);
+ $lplot->SetColor(self::$colourSet[self::$plotColour]);
+
+ $this->graph->Add($lplot);
+ }
+
+ if ($bubble) {
+ $this->formatPointMarker($seriesPlot, 'dot');
+ $seriesPlot->mark->SetColor('black');
+ $seriesPlot->mark->SetSize($bubbleSize);
+ } else {
+ $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
+ $this->formatPointMarker($seriesPlot, $marker);
+ }
+ $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
+ $seriesPlot->SetLegend($dataLabel);
+
+ $this->graph->Add($seriesPlot);
+ }
+ }
+
+ private function renderPlotRadar(int $groupID): void
+ {
+ $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+
+ // Loop through each data series in turn
+ for ($i = 0; $i < $seriesCount; ++$i) {
+ $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
+ $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+ $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
+
+ $dataValues = [];
+ foreach ($dataValuesY as $k => $dataValueY) {
+ $dataValues[$k] = is_array($dataValueY) ? implode(' ', array_reverse($dataValueY)) : $dataValueY;
+ }
+ $tmp = array_shift($dataValues);
+ $dataValues[] = $tmp;
+ $tmp = array_shift($dataValuesX);
+ $dataValuesX[] = $tmp;
+
+ $this->graph->SetTitles(array_reverse($dataValues));
+
+ $seriesPlot = new RadarPlot(array_reverse($dataValuesX));
+
+ $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
+ $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
+ if ($radarStyle == 'filled') {
+ $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]);
+ }
+ $this->formatPointMarker($seriesPlot, $marker);
+ $seriesPlot->SetLegend($dataLabel);
+
+ $this->graph->Add($seriesPlot);
+ }
+ }
+
+ private function renderPlotContour(int $groupID): void
+ {
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+
+ $dataValues = [];
+ // Loop through each data series in turn
+ for ($i = 0; $i < $seriesCount; ++$i) {
+ $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
+
+ $dataValues[$i] = $dataValuesX;
+ }
+ $seriesPlot = new ContourPlot($dataValues);
+
+ $this->graph->Add($seriesPlot);
+ }
+
+ private function renderPlotStock(int $groupID): void
+ {
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+ $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder();
+
+ $dataValues = [];
+ // Loop through each data series in turn and build the plot arrays
+ foreach ($plotOrder as $i => $v) {
+ $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v);
+ if ($dataValuesX === false) {
+ continue;
+ }
+ $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v)->getDataValues();
+ foreach ($dataValuesX as $j => $dataValueX) {
+ $dataValues[$plotOrder[$i]][$j] = $dataValueX;
+ }
+ }
+ if (empty($dataValues)) {
+ return;
+ }
+
+ $dataValuesPlot = [];
+ // Flatten the plot arrays to a single dimensional array to work with jpgraph
+ $jMax = count($dataValues[0]);
+ for ($j = 0; $j < $jMax; ++$j) {
+ for ($i = 0; $i < $seriesCount; ++$i) {
+ $dataValuesPlot[] = $dataValues[$i][$j] ?? null;
+ }
+ }
+
+ // Set the x-axis labels
+ $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
+ if ($labelCount > 0) {
+ $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+ $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels);
+ $this->graph->xaxis->SetTickLabels($datasetLabels);
+ }
+
+ $seriesPlot = new StockPlot($dataValuesPlot);
+ $seriesPlot->SetWidth(20);
+
+ $this->graph->Add($seriesPlot);
+ }
+
+ private function renderAreaChart($groupCount): void
+ {
+ $this->renderCartesianPlotArea();
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $this->renderPlotLine($i, true, false);
+ }
+ }
+
+ private function renderLineChart($groupCount): void
+ {
+ $this->renderCartesianPlotArea();
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $this->renderPlotLine($i, false, false);
+ }
+ }
+
+ private function renderBarChart($groupCount, ?string $dimensions = '2d'): void
+ {
+ $this->renderCartesianPlotArea();
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $this->renderPlotBar($i, $dimensions);
+ }
+ }
+
+ private function renderScatterChart($groupCount): void
+ {
+ $this->renderCartesianPlotArea('linlin');
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $this->renderPlotScatter($i, false);
+ }
+ }
+
+ private function renderBubbleChart($groupCount): void
+ {
+ $this->renderCartesianPlotArea('linlin');
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $this->renderPlotScatter($i, true);
+ }
+ }
+
+ private function renderPieChart($groupCount, ?string $dimensions = '2d', bool $doughnut = false, bool $multiplePlots = false): void
+ {
+ $this->renderPiePlotArea();
+
+ $iLimit = ($multiplePlots) ? $groupCount : 1;
+ for ($groupID = 0; $groupID < $iLimit; ++$groupID) {
+ $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
+ $datasetLabels = [];
+ if ($groupID == 0) {
+ $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
+ if ($labelCount > 0) {
+ $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
+ $datasetLabels = $this->formatDataSetLabels($groupID, $datasetLabels);
+ }
+ }
+
+ $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
+ // For pie charts, we only display the first series: doughnut charts generally display all series
+ $jLimit = ($multiplePlots) ? $seriesCount : 1;
+ // Loop through each data series in turn
+ for ($j = 0; $j < $jLimit; ++$j) {
+ $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
+
+ // Fill in any missing values in the $dataValues array
+ $testCurrentIndex = 0;
+ foreach ($dataValues as $k => $dataValue) {
+ while ($k != $testCurrentIndex) {
+ $dataValues[$testCurrentIndex] = null;
+ ++$testCurrentIndex;
+ }
+ ++$testCurrentIndex;
+ }
+
+ if ($dimensions == '3d') {
+ $seriesPlot = new PiePlot3D($dataValues);
+ } else {
+ if ($doughnut) {
+ $seriesPlot = new PiePlotC($dataValues);
+ } else {
+ $seriesPlot = new PiePlot($dataValues);
+ }
+ }
+
+ if ($multiplePlots) {
+ $seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4));
+ }
+
+ if ($doughnut && method_exists($seriesPlot, 'SetMidColor')) {
+ $seriesPlot->SetMidColor('white');
+ }
+
+ $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
+ if (count($datasetLabels) > 0) {
+ $seriesPlot->SetLabels(array_fill(0, count($datasetLabels), ''));
+ }
+ if ($dimensions != '3d') {
+ $seriesPlot->SetGuideLines(false);
+ }
+ if ($j == 0) {
+ if ($exploded) {
+ $seriesPlot->ExplodeAll();
+ }
+ $seriesPlot->SetLegends($datasetLabels);
+ }
+
+ $this->graph->Add($seriesPlot);
+ }
+ }
+ }
+
+ private function renderRadarChart($groupCount): void
+ {
+ $this->renderRadarPlotArea();
+
+ for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
+ $this->renderPlotRadar($groupID);
+ }
+ }
+
+ private function renderStockChart($groupCount): void
+ {
+ $this->renderCartesianPlotArea('intint');
+
+ for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
+ $this->renderPlotStock($groupID);
+ }
+ }
+
+ private function renderContourChart($groupCount): void
+ {
+ $this->renderCartesianPlotArea('intint');
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $this->renderPlotContour($i);
+ }
+ }
+
+ private function renderCombinationChart($groupCount, $outputDestination): bool
+ {
+ $this->renderCartesianPlotArea();
+
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $dimensions = null;
+ $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
+ switch ($chartType) {
+ case 'area3DChart':
+ case 'areaChart':
+ $this->renderPlotLine($i, true, true);
+
+ break;
+ case 'bar3DChart':
+ $dimensions = '3d';
+ // no break
+ case 'barChart':
+ $this->renderPlotBar($i, $dimensions);
+
+ break;
+ case 'line3DChart':
+ case 'lineChart':
+ $this->renderPlotLine($i, false, true);
+
+ break;
+ case 'scatterChart':
+ $this->renderPlotScatter($i, false);
+
+ break;
+ case 'bubbleChart':
+ $this->renderPlotScatter($i, true);
+
+ break;
+ default:
+ $this->graph = null;
+
+ return false;
+ }
+ }
+
+ $this->renderLegend();
+
+ $this->graph->Stroke($outputDestination);
+
+ return true;
+ }
+
+ public function render($outputDestination)
+ {
+ self::$plotColour = 0;
+
+ $groupCount = $this->chart->getPlotArea()->getPlotGroupCount();
+
+ $dimensions = null;
+ if ($groupCount == 1) {
+ $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType();
+ } else {
+ $chartTypes = [];
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $chartTypes[] = $this->chart->getPlotArea()->getPlotGroupByIndex($i)->getPlotType();
+ }
+ $chartTypes = array_unique($chartTypes);
+ if (count($chartTypes) == 1) {
+ $chartType = array_pop($chartTypes);
+ } elseif (count($chartTypes) == 0) {
+ echo 'Chart is not yet implemented ';
+
+ return false;
+ } else {
+ return $this->renderCombinationChart($groupCount, $outputDestination);
+ }
+ }
+
+ switch ($chartType) {
+ case 'area3DChart':
+ $dimensions = '3d';
+ // no break
+ case 'areaChart':
+ $this->renderAreaChart($groupCount);
+
+ break;
+ case 'bar3DChart':
+ $dimensions = '3d';
+ // no break
+ case 'barChart':
+ $this->renderBarChart($groupCount, $dimensions);
+
+ break;
+ case 'line3DChart':
+ $dimensions = '3d';
+ // no break
+ case 'lineChart':
+ $this->renderLineChart($groupCount);
+
+ break;
+ case 'pie3DChart':
+ $dimensions = '3d';
+ // no break
+ case 'pieChart':
+ $this->renderPieChart($groupCount, $dimensions, false, false);
+
+ break;
+ case 'doughnut3DChart':
+ $dimensions = '3d';
+ // no break
+ case 'doughnutChart':
+ $this->renderPieChart($groupCount, $dimensions, true, true);
+
+ break;
+ case 'scatterChart':
+ $this->renderScatterChart($groupCount);
+
+ break;
+ case 'bubbleChart':
+ $this->renderBubbleChart($groupCount);
+
+ break;
+ case 'radarChart':
+ $this->renderRadarChart($groupCount);
+
+ break;
+ case 'surface3DChart':
+ case 'surfaceChart':
+ $this->renderContourChart($groupCount);
+
+ break;
+ case 'stockChart':
+ $this->renderStockChart($groupCount);
+
+ break;
+ default:
+ echo $chartType . ' is not yet implemented ';
+
+ return false;
+ }
+ $this->renderLegend();
+
+ $this->graph->Stroke($outputDestination);
+
+ return true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php
new file mode 100644
index 0000000..9697920
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php
@@ -0,0 +1,38 @@
+caption = $caption;
+ $this->layout = $layout;
+ $this->setOverlay($overlay);
+ }
+
+ /**
+ * Get caption.
+ *
+ * @return null|array|RichText|string may be null
+ */
+ public function getCaption()
+ {
+ return $this->caption;
+ }
+
+ public function getCaptionText(?Spreadsheet $spreadsheet = null): string
+ {
+ if ($spreadsheet !== null) {
+ $caption = $this->getCalculatedTitle($spreadsheet);
+ if ($caption !== null) {
+ return $caption;
+ }
+ }
+ $caption = $this->caption;
+ if ($caption === null) {
+ return '';
+ }
+ if (is_string($caption)) {
+ return $caption;
+ }
+ if ($caption instanceof RichText) {
+ return $caption->getPlainText();
+ }
+ $retVal = '';
+ foreach ($caption as $textx) {
+ /** @var RichText|string */
+ $text = $textx;
+ if ($text instanceof RichText) {
+ $retVal .= $text->getPlainText();
+ } else {
+ $retVal .= $text;
+ }
+ }
+
+ return $retVal;
+ }
+
+ /**
+ * Set caption.
+ *
+ * @param null|array|RichText|string $caption may be null
+ *
+ * @return $this
+ */
+ public function setCaption($caption): static
+ {
+ $this->caption = $caption;
+
+ return $this;
+ }
+
+ /**
+ * Get allow overlay of other elements?
+ *
+ * @return bool
+ */
+ public function getOverlay()
+ {
+ return $this->overlay;
+ }
+
+ /**
+ * Set allow overlay of other elements?
+ *
+ * @param bool $overlay
+ */
+ public function setOverlay($overlay): static
+ {
+ $this->overlay = $overlay;
+
+ return $this;
+ }
+
+ public function getLayout(): ?Layout
+ {
+ return $this->layout;
+ }
+
+ public function setCellReference(string $cellReference): self
+ {
+ $this->cellReference = $cellReference;
+
+ return $this;
+ }
+
+ public function getCellReference(): string
+ {
+ return $this->cellReference;
+ }
+
+ public function getCalculatedTitle(?Spreadsheet $spreadsheet): ?string
+ {
+ preg_match(self::TITLE_CELL_REFERENCE, $this->cellReference, $matches);
+ if (count($matches) === 0 || $spreadsheet === null) {
+ return null;
+ }
+ $sheetName = preg_replace("/^'(.*)'$/", '$1', $matches[1]) ?? '';
+
+ return $spreadsheet->getSheetByName($sheetName)?->getCell($matches[2] . $matches[3])?->getFormattedValue();
+ }
+
+ public function getFont(): ?Font
+ {
+ return $this->font;
+ }
+
+ public function setFont(?Font $font): self
+ {
+ $this->font = $font;
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->layout = ($this->layout === null) ? null : clone $this->layout;
+ $this->font = ($this->font === null) ? null : clone $this->font;
+ if (is_array($this->caption)) {
+ $captions = $this->caption;
+ $this->caption = [];
+ foreach ($captions as $caption) {
+ $this->caption[] = is_object($caption) ? (clone $caption) : $caption;
+ }
+ } else {
+ $this->caption = is_object($this->caption) ? (clone $this->caption) : $this->caption;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php
new file mode 100644
index 0000000..7ea2bde
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php
@@ -0,0 +1,224 @@
+setTrendLineProperties(
+ $trendLineType,
+ $order,
+ $period,
+ $dispRSqr,
+ $dispEq,
+ $backward,
+ $forward,
+ $intercept,
+ $name
+ );
+ }
+
+ public function getTrendLineType(): string
+ {
+ return $this->trendLineType;
+ }
+
+ public function setTrendLineType(string $trendLineType): self
+ {
+ $this->trendLineType = $trendLineType;
+
+ return $this;
+ }
+
+ public function getOrder(): int
+ {
+ return $this->order;
+ }
+
+ public function setOrder(int $order): self
+ {
+ $this->order = $order;
+
+ return $this;
+ }
+
+ public function getPeriod(): int
+ {
+ return $this->period;
+ }
+
+ public function setPeriod(int $period): self
+ {
+ $this->period = $period;
+
+ return $this;
+ }
+
+ public function getDispRSqr(): bool
+ {
+ return $this->dispRSqr;
+ }
+
+ public function setDispRSqr(bool $dispRSqr): self
+ {
+ $this->dispRSqr = $dispRSqr;
+
+ return $this;
+ }
+
+ public function getDispEq(): bool
+ {
+ return $this->dispEq;
+ }
+
+ public function setDispEq(bool $dispEq): self
+ {
+ $this->dispEq = $dispEq;
+
+ return $this;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function getBackward(): float
+ {
+ return $this->backward;
+ }
+
+ public function setBackward(float $backward): self
+ {
+ $this->backward = $backward;
+
+ return $this;
+ }
+
+ public function getForward(): float
+ {
+ return $this->forward;
+ }
+
+ public function setForward(float $forward): self
+ {
+ $this->forward = $forward;
+
+ return $this;
+ }
+
+ public function getIntercept(): float
+ {
+ return $this->intercept;
+ }
+
+ public function setIntercept(float $intercept): self
+ {
+ $this->intercept = $intercept;
+
+ return $this;
+ }
+
+ public function setTrendLineProperties(
+ ?string $trendLineType = null,
+ ?int $order = 0,
+ ?int $period = 0,
+ ?bool $dispRSqr = false,
+ ?bool $dispEq = false,
+ ?float $backward = null,
+ ?float $forward = null,
+ ?float $intercept = null,
+ ?string $name = null
+ ): self {
+ if (!empty($trendLineType)) {
+ $this->setTrendLineType($trendLineType);
+ }
+ if ($order !== null) {
+ $this->setOrder($order);
+ }
+ if ($period !== null) {
+ $this->setPeriod($period);
+ }
+ if ($dispRSqr !== null) {
+ $this->setDispRSqr($dispRSqr);
+ }
+ if ($dispEq !== null) {
+ $this->setDispEq($dispEq);
+ }
+ if ($backward !== null) {
+ $this->setBackward($backward);
+ }
+ if ($forward !== null) {
+ $this->setForward($forward);
+ }
+ if ($intercept !== null) {
+ $this->setIntercept($intercept);
+ }
+ if ($name !== null) {
+ $this->setName($name);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
new file mode 100644
index 0000000..9137bea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
@@ -0,0 +1,482 @@
+parent = $parent;
+ $this->cache = $cache;
+ $this->cachePrefix = $this->getUniqueID();
+ }
+
+ /**
+ * Return the parent worksheet for this cell collection.
+ *
+ * @return null|Worksheet
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Whether the collection holds a cell for the given coordinate.
+ *
+ * @param string $cellCoordinate Coordinate of the cell to check
+ */
+ public function has($cellCoordinate): bool
+ {
+ return ($cellCoordinate === $this->currentCoordinate) || isset($this->index[$cellCoordinate]);
+ }
+
+ /**
+ * Add or update a cell in the collection.
+ *
+ * @param Cell $cell Cell to update
+ */
+ public function update(Cell $cell): Cell
+ {
+ return $this->add($cell->getCoordinate(), $cell);
+ }
+
+ /**
+ * Delete a cell in cache identified by coordinate.
+ *
+ * @param string $cellCoordinate Coordinate of the cell to delete
+ */
+ public function delete(string $cellCoordinate): void
+ {
+ if ($cellCoordinate === $this->currentCoordinate && $this->currentCell !== null) {
+ $this->currentCell->detach();
+ $this->currentCoordinate = null;
+ $this->currentCell = null;
+ $this->currentCellIsDirty = false;
+ }
+
+ unset($this->index[$cellCoordinate]);
+
+ // Delete the entry from cache
+ $this->cache->delete($this->cachePrefix . $cellCoordinate);
+ }
+
+ /**
+ * Get a list of all cell coordinates currently held in the collection.
+ *
+ * @return string[]
+ */
+ public function getCoordinates(): array
+ {
+ return array_keys($this->index);
+ }
+
+ /**
+ * Get a sorted list of all cell coordinates currently held in the collection by row and column.
+ *
+ * @return string[]
+ */
+ public function getSortedCoordinates(): array
+ {
+ asort($this->index);
+
+ return array_keys($this->index);
+ }
+
+ /**
+ * Get a sorted list of all cell coordinates currently held in the collection by index (16384*row+column).
+ *
+ * @return int[]
+ */
+ public function getSortedCoordinatesInt(): array
+ {
+ asort($this->index);
+
+ return array_values($this->index);
+ }
+
+ /**
+ * Return the cell coordinate of the currently active cell object.
+ *
+ * @return null|string
+ */
+ public function getCurrentCoordinate()
+ {
+ return $this->currentCoordinate;
+ }
+
+ /**
+ * Return the column coordinate of the currently active cell object.
+ */
+ public function getCurrentColumn(): string
+ {
+ $column = 0;
+ $row = '';
+ sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row);
+
+ return (string) $column;
+ }
+
+ /**
+ * Return the row coordinate of the currently active cell object.
+ */
+ public function getCurrentRow(): int
+ {
+ $column = 0;
+ $row = '';
+ sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row);
+
+ return (int) $row;
+ }
+
+ /**
+ * Get highest worksheet column and highest row that have cell records.
+ *
+ * @return array Highest column name and highest row number
+ */
+ public function getHighestRowAndColumn(): array
+ {
+ // Lookup highest column and highest row
+ $maxRow = $maxColumn = 1;
+ foreach ($this->index as $coordinate) {
+ $row = (int) floor(($coordinate - 1) / self::MAX_COLUMN_ID) + 1;
+ $maxRow = ($maxRow > $row) ? $maxRow : $row;
+ $column = ($coordinate % self::MAX_COLUMN_ID) ?: self::MAX_COLUMN_ID;
+ $maxColumn = ($maxColumn > $column) ? $maxColumn : $column;
+ }
+
+ return [
+ 'row' => $maxRow,
+ 'column' => Coordinate::stringFromColumnIndex($maxColumn),
+ ];
+ }
+
+ /**
+ * Get highest worksheet column.
+ *
+ * @param null|int|string $row Return the highest column for the specified row,
+ * or the highest column of any row if no row number is passed
+ *
+ * @return string Highest column name
+ */
+ public function getHighestColumn($row = null)
+ {
+ if ($row === null) {
+ return $this->getHighestRowAndColumn()['column'];
+ }
+
+ $row = (int) $row;
+ if ($row <= 0) {
+ throw new PhpSpreadsheetException('Row number must be a positive integer');
+ }
+
+ $maxColumn = 1;
+ $toRow = $row * self::MAX_COLUMN_ID;
+ $fromRow = --$row * self::MAX_COLUMN_ID;
+ foreach ($this->index as $coordinate) {
+ if ($coordinate < $fromRow || $coordinate >= $toRow) {
+ continue;
+ }
+ $column = ($coordinate % self::MAX_COLUMN_ID) ?: self::MAX_COLUMN_ID;
+ $maxColumn = $maxColumn > $column ? $maxColumn : $column;
+ }
+
+ return Coordinate::stringFromColumnIndex($maxColumn);
+ }
+
+ /**
+ * Get highest worksheet row.
+ *
+ * @param null|string $column Return the highest row for the specified column,
+ * or the highest row of any column if no column letter is passed
+ *
+ * @return int Highest row number
+ */
+ public function getHighestRow($column = null)
+ {
+ if ($column === null) {
+ return $this->getHighestRowAndColumn()['row'];
+ }
+
+ $maxRow = 1;
+ $columnIndex = Coordinate::columnIndexFromString($column);
+ foreach ($this->index as $coordinate) {
+ if ($coordinate % self::MAX_COLUMN_ID !== $columnIndex) {
+ continue;
+ }
+ $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
+ $maxRow = ($maxRow > $row) ? $maxRow : $row;
+ }
+
+ return $maxRow;
+ }
+
+ /**
+ * Generate a unique ID for cache referencing.
+ *
+ * @return string Unique Reference
+ */
+ private function getUniqueID(): string
+ {
+ $cacheType = Settings::getCache();
+
+ return ($cacheType instanceof Memory\SimpleCache1 || $cacheType instanceof Memory\SimpleCache3)
+ ? random_bytes(7) . ':'
+ : uniqid('phpspreadsheet.', true) . '.';
+ }
+
+ /**
+ * Clone the cell collection.
+ */
+ public function cloneCellCollection(Worksheet $worksheet): static
+ {
+ $this->storeCurrentCell();
+ $newCollection = clone $this;
+
+ $newCollection->parent = $worksheet;
+ $newCollection->cachePrefix = $newCollection->getUniqueID();
+
+ foreach ($this->index as $key => $value) {
+ $newCollection->index[$key] = $value;
+ $stored = $newCollection->cache->set(
+ $newCollection->cachePrefix . $key,
+ clone $this->cache->get($this->cachePrefix . $key)
+ );
+ if ($stored === false) {
+ $this->destructIfNeeded($newCollection, 'Failed to copy cells in cache');
+ }
+ }
+
+ return $newCollection;
+ }
+
+ /**
+ * Remove a row, deleting all cells in that row.
+ *
+ * @param int|string $row Row number to remove
+ */
+ public function removeRow($row): void
+ {
+ $this->storeCurrentCell();
+ $row = (int) $row;
+ if ($row <= 0) {
+ throw new PhpSpreadsheetException('Row number must be a positive integer');
+ }
+
+ $toRow = $row * self::MAX_COLUMN_ID;
+ $fromRow = --$row * self::MAX_COLUMN_ID;
+ foreach ($this->index as $coordinate) {
+ if ($coordinate >= $fromRow && $coordinate < $toRow) {
+ $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
+ $column = Coordinate::stringFromColumnIndex($coordinate % self::MAX_COLUMN_ID);
+ $this->delete("{$column}{$row}");
+ }
+ }
+ }
+
+ /**
+ * Remove a column, deleting all cells in that column.
+ *
+ * @param string $column Column ID to remove
+ */
+ public function removeColumn($column): void
+ {
+ $this->storeCurrentCell();
+
+ $columnIndex = Coordinate::columnIndexFromString($column);
+ foreach ($this->index as $coordinate) {
+ if ($coordinate % self::MAX_COLUMN_ID === $columnIndex) {
+ $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
+ $column = Coordinate::stringFromColumnIndex($coordinate % self::MAX_COLUMN_ID);
+ $this->delete("{$column}{$row}");
+ }
+ }
+ }
+
+ /**
+ * Store cell data in cache for the current cell object if it's "dirty",
+ * and the 'nullify' the current cell object.
+ */
+ private function storeCurrentCell(): void
+ {
+ if ($this->currentCellIsDirty && isset($this->currentCoordinate, $this->currentCell)) {
+ $this->currentCell->detach();
+
+ $stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell);
+ if ($stored === false) {
+ $this->destructIfNeeded($this, "Failed to store cell {$this->currentCoordinate} in cache");
+ }
+ $this->currentCellIsDirty = false;
+ }
+
+ $this->currentCoordinate = null;
+ $this->currentCell = null;
+ }
+
+ private function destructIfNeeded(self $cells, string $message): void
+ {
+ $cells->__destruct();
+
+ throw new PhpSpreadsheetException($message);
+ }
+
+ /**
+ * Add or update a cell identified by its coordinate into the collection.
+ *
+ * @param string $cellCoordinate Coordinate of the cell to update
+ * @param Cell $cell Cell to update
+ */
+ public function add($cellCoordinate, Cell $cell): Cell
+ {
+ if ($cellCoordinate !== $this->currentCoordinate) {
+ $this->storeCurrentCell();
+ }
+ $column = 0;
+ $row = '';
+ sscanf($cellCoordinate, '%[A-Z]%d', $column, $row);
+ $this->index[$cellCoordinate] = (--$row * self::MAX_COLUMN_ID) + Coordinate::columnIndexFromString((string) $column);
+
+ $this->currentCoordinate = $cellCoordinate;
+ $this->currentCell = $cell;
+ $this->currentCellIsDirty = true;
+
+ return $cell;
+ }
+
+ /**
+ * Get cell at a specific coordinate.
+ *
+ * @param string $cellCoordinate Coordinate of the cell
+ *
+ * @return null|Cell Cell that was found, or null if not found
+ */
+ public function get(string $cellCoordinate)
+ {
+ if ($cellCoordinate === $this->currentCoordinate) {
+ return $this->currentCell;
+ }
+ $this->storeCurrentCell();
+
+ // Return null if requested entry doesn't exist in collection
+ if ($this->has($cellCoordinate) === false) {
+ return null;
+ }
+
+ // Check if the entry that has been requested actually exists in the cache
+ $cell = $this->cache->get($this->cachePrefix . $cellCoordinate);
+ if ($cell === null) {
+ throw new PhpSpreadsheetException("Cell entry {$cellCoordinate} no longer exists in cache. This probably means that the cache was cleared by someone else.");
+ }
+
+ // Set current entry to the requested entry
+ $this->currentCoordinate = $cellCoordinate;
+ $this->currentCell = $cell;
+ // Re-attach this as the cell's parent
+ $this->currentCell->attach($this);
+
+ // Return requested entry
+ return $this->currentCell;
+ }
+
+ /**
+ * Clear the cell collection and disconnect from our parent.
+ */
+ public function unsetWorksheetCells(): void
+ {
+ if ($this->currentCell !== null) {
+ $this->currentCell->detach();
+ $this->currentCell = null;
+ $this->currentCoordinate = null;
+ }
+
+ // Flush the cache
+ $this->__destruct();
+
+ $this->index = [];
+
+ // detach ourself from the worksheet, so that it can then delete this object successfully
+ $this->parent = null;
+ }
+
+ /**
+ * Destroy this cell collection.
+ */
+ public function __destruct()
+ {
+ $this->cache->deleteMultiple($this->getAllCacheKeys());
+ $this->parent = null;
+ }
+
+ /**
+ * Returns all known cache keys.
+ *
+ * @return Generator|string[]
+ */
+ private function getAllCacheKeys()
+ {
+ foreach ($this->index as $coordinate => $value) {
+ yield $this->cachePrefix . $coordinate;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
new file mode 100644
index 0000000..b3833bd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
@@ -0,0 +1,20 @@
+cache = [];
+
+ return true;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return bool
+ */
+ public function delete($key)
+ {
+ unset($this->cache[$key]);
+
+ return true;
+ }
+
+ /**
+ * @param iterable $keys
+ *
+ * @return bool
+ */
+ public function deleteMultiple($keys)
+ {
+ foreach ($keys as $key) {
+ $this->delete($key);
+ }
+
+ return true;
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if ($this->has($key)) {
+ return $this->cache[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * @param iterable $keys
+ * @param mixed $default
+ *
+ * @return iterable
+ */
+ public function getMultiple($keys, $default = null)
+ {
+ $results = [];
+ foreach ($keys as $key) {
+ $results[$key] = $this->get($key, $default);
+ }
+
+ return $results;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return bool
+ */
+ public function has($key)
+ {
+ return array_key_exists($key, $this->cache);
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $value
+ * @param null|DateInterval|int $ttl
+ *
+ * @return bool
+ */
+ public function set($key, $value, $ttl = null)
+ {
+ $this->cache[$key] = $value;
+
+ return true;
+ }
+
+ /**
+ * @param iterable $values
+ * @param null|DateInterval|int $ttl
+ *
+ * @return bool
+ */
+ public function setMultiple($values, $ttl = null)
+ {
+ foreach ($values as $key => $value) {
+ $this->set($key, $value);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
new file mode 100644
index 0000000..34f6447
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
@@ -0,0 +1,109 @@
+cache = [];
+
+ return true;
+ }
+
+ /**
+ * @param string $key
+ */
+ public function delete($key): bool
+ {
+ unset($this->cache[$key]);
+
+ return true;
+ }
+
+ /**
+ * @param iterable $keys
+ */
+ public function deleteMultiple($keys): bool
+ {
+ foreach ($keys as $key) {
+ $this->delete($key);
+ }
+
+ return true;
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $default
+ */
+ public function get($key, $default = null): mixed
+ {
+ if ($this->has($key)) {
+ return $this->cache[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * @param iterable $keys
+ * @param mixed $default
+ */
+ public function getMultiple($keys, $default = null): iterable
+ {
+ $results = [];
+ foreach ($keys as $key) {
+ $results[$key] = $this->get($key, $default);
+ }
+
+ return $results;
+ }
+
+ /**
+ * @param string $key
+ */
+ public function has($key): bool
+ {
+ return array_key_exists($key, $this->cache);
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $value
+ * @param null|DateInterval|int $ttl
+ */
+ public function set($key, $value, $ttl = null): bool
+ {
+ $this->cache[$key] = $value;
+
+ return true;
+ }
+
+ /**
+ * @param iterable $values
+ * @param null|DateInterval|int $ttl
+ */
+ public function setMultiple($values, $ttl = null): bool
+ {
+ foreach ($values as $key => $value) {
+ $this->set($key, $value);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
new file mode 100644
index 0000000..f61e172
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
@@ -0,0 +1,345 @@
+author = 'Author';
+ $this->text = new RichText();
+ $this->fillColor = new Color('FFFFFFE1');
+ $this->alignment = Alignment::HORIZONTAL_GENERAL;
+ $this->backgroundImage = new Drawing();
+ }
+
+ /**
+ * Get Author.
+ */
+ public function getAuthor(): string
+ {
+ return $this->author;
+ }
+
+ /**
+ * Set Author.
+ */
+ public function setAuthor(string $author): self
+ {
+ $this->author = $author;
+
+ return $this;
+ }
+
+ /**
+ * Get Rich text comment.
+ */
+ public function getText(): RichText
+ {
+ return $this->text;
+ }
+
+ /**
+ * Set Rich text comment.
+ */
+ public function setText(RichText $text): self
+ {
+ $this->text = $text;
+
+ return $this;
+ }
+
+ /**
+ * Get comment width (CSS style, i.e. XXpx or YYpt).
+ */
+ public function getWidth(): string
+ {
+ return $this->width;
+ }
+
+ /**
+ * Set comment width (CSS style, i.e. XXpx or YYpt). Default unit is pt.
+ */
+ public function setWidth(string $width): self
+ {
+ $width = new Size($width);
+ if ($width->valid()) {
+ $this->width = (string) $width;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get comment height (CSS style, i.e. XXpx or YYpt).
+ */
+ public function getHeight(): string
+ {
+ return $this->height;
+ }
+
+ /**
+ * Set comment height (CSS style, i.e. XXpx or YYpt). Default unit is pt.
+ */
+ public function setHeight(string $height): self
+ {
+ $height = new Size($height);
+ if ($height->valid()) {
+ $this->height = (string) $height;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get left margin (CSS style, i.e. XXpx or YYpt).
+ */
+ public function getMarginLeft(): string
+ {
+ return $this->marginLeft;
+ }
+
+ /**
+ * Set left margin (CSS style, i.e. XXpx or YYpt). Default unit is pt.
+ */
+ public function setMarginLeft(string $margin): self
+ {
+ $margin = new Size($margin);
+ if ($margin->valid()) {
+ $this->marginLeft = (string) $margin;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get top margin (CSS style, i.e. XXpx or YYpt).
+ */
+ public function getMarginTop(): string
+ {
+ return $this->marginTop;
+ }
+
+ /**
+ * Set top margin (CSS style, i.e. XXpx or YYpt). Default unit is pt.
+ */
+ public function setMarginTop(string $margin): self
+ {
+ $margin = new Size($margin);
+ if ($margin->valid()) {
+ $this->marginTop = (string) $margin;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Is the comment visible by default?
+ */
+ public function getVisible(): bool
+ {
+ return $this->visible;
+ }
+
+ /**
+ * Set comment default visibility.
+ */
+ public function setVisible(bool $visibility): self
+ {
+ $this->visible = $visibility;
+
+ return $this;
+ }
+
+ /**
+ * Set fill color.
+ */
+ public function setFillColor(Color $color): self
+ {
+ $this->fillColor = $color;
+
+ return $this;
+ }
+
+ /**
+ * Get fill color.
+ */
+ public function getFillColor(): Color
+ {
+ return $this->fillColor;
+ }
+
+ /**
+ * Set Alignment.
+ */
+ public function setAlignment(string $alignment): self
+ {
+ $this->alignment = $alignment;
+
+ return $this;
+ }
+
+ /**
+ * Get Alignment.
+ */
+ public function getAlignment(): string
+ {
+ return $this->alignment;
+ }
+
+ /**
+ * Get hash code.
+ */
+ public function getHashCode(): string
+ {
+ return md5(
+ $this->author
+ . $this->text->getHashCode()
+ . $this->width
+ . $this->height
+ . $this->marginLeft
+ . $this->marginTop
+ . ($this->visible ? 1 : 0)
+ . $this->fillColor->getHashCode()
+ . $this->alignment
+ . ($this->hasBackgroundImage() ? $this->backgroundImage->getHashCode() : '')
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ /**
+ * Convert to string.
+ */
+ public function __toString(): string
+ {
+ return $this->text->getPlainText();
+ }
+
+ /**
+ * Check is background image exists.
+ */
+ public function hasBackgroundImage(): bool
+ {
+ $path = $this->backgroundImage->getPath();
+
+ if (empty($path)) {
+ return false;
+ }
+
+ return getimagesize($path) !== false;
+ }
+
+ /**
+ * Returns background image.
+ */
+ public function getBackgroundImage(): Drawing
+ {
+ return $this->backgroundImage;
+ }
+
+ /**
+ * Sets background image.
+ */
+ public function setBackgroundImage(Drawing $objDrawing): self
+ {
+ if (!array_key_exists($objDrawing->getType(), Drawing::IMAGE_TYPES_CONVERTION_MAP)) {
+ throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
+ }
+ $this->backgroundImage = $objDrawing;
+
+ return $this;
+ }
+
+ /**
+ * Sets size of comment as size of background image.
+ */
+ public function setSizeAsBackgroundImage(): self
+ {
+ if ($this->hasBackgroundImage()) {
+ $this->setWidth(SharedDrawing::pixelsToPoints($this->backgroundImage->getWidth()) . 'pt');
+ $this->setHeight(SharedDrawing::pixelsToPoints($this->backgroundImage->getHeight()) . 'pt');
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php
new file mode 100644
index 0000000..686e56e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php
@@ -0,0 +1,269 @@
+worksheet).
+ */
+ protected bool $localOnly;
+
+ /**
+ * Scope.
+ */
+ protected ?Worksheet $scope;
+
+ /**
+ * Whether this is a named range or a named formula.
+ */
+ protected bool $isFormula;
+
+ /**
+ * Create a new Defined Name.
+ */
+ public function __construct(
+ string $name,
+ ?Worksheet $worksheet = null,
+ ?string $value = null,
+ bool $localOnly = false,
+ ?Worksheet $scope = null
+ ) {
+ if ($worksheet === null) {
+ $worksheet = $scope;
+ }
+
+ // Set local members
+ $this->name = $name;
+ $this->worksheet = $worksheet;
+ $this->value = (string) $value;
+ $this->localOnly = $localOnly;
+ // If local only, then the scope will be set to worksheet unless a scope is explicitly set
+ $this->scope = ($localOnly === true) ? (($scope === null) ? $worksheet : $scope) : null;
+ // If the range string contains characters that aren't associated with the range definition (A-Z,1-9
+ // for cell references, and $, or the range operators (colon comma or space), quotes and ! for
+ // worksheet names
+ // then this is treated as a named formula, and not a named range
+ $this->isFormula = self::testIfFormula($this->value);
+ }
+
+ public function __destruct()
+ {
+ $this->worksheet = null;
+ $this->scope = null;
+ }
+
+ /**
+ * Create a new defined name, either a range or a formula.
+ */
+ public static function createInstance(
+ string $name,
+ ?Worksheet $worksheet = null,
+ ?string $value = null,
+ bool $localOnly = false,
+ ?Worksheet $scope = null
+ ): self {
+ $value = (string) $value;
+ $isFormula = self::testIfFormula($value);
+ if ($isFormula) {
+ return new NamedFormula($name, $worksheet, $value, $localOnly, $scope);
+ }
+
+ return new NamedRange($name, $worksheet, $value, $localOnly, $scope);
+ }
+
+ public static function testIfFormula(string $value): bool
+ {
+ if (str_starts_with($value, '=')) {
+ $value = substr($value, 1);
+ }
+
+ if (is_numeric($value)) {
+ return true;
+ }
+
+ $segMatcher = false;
+ foreach (explode("'", $value) as $subVal) {
+ // Only test in alternate array entries (the non-quoted blocks)
+ $segMatcher = $segMatcher === false;
+ if (
+ $segMatcher
+ && (preg_match('/' . self::REGEXP_IDENTIFY_FORMULA . '/miu', $subVal))
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get name.
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set name.
+ */
+ public function setName(string $name): self
+ {
+ if (!empty($name)) {
+ // Old title
+ $oldTitle = $this->name;
+
+ // Re-attach
+ if ($this->worksheet !== null) {
+ $this->worksheet->getParentOrThrow()->removeNamedRange($this->name, $this->worksheet);
+ }
+ $this->name = $name;
+
+ if ($this->worksheet !== null) {
+ $this->worksheet->getParentOrThrow()->addDefinedName($this);
+ }
+
+ if ($this->worksheet !== null) {
+ // New title
+ $newTitle = $this->name;
+ ReferenceHelper::getInstance()->updateNamedFormulae($this->worksheet->getParentOrThrow(), $oldTitle, $newTitle);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get worksheet.
+ */
+ public function getWorksheet(): ?Worksheet
+ {
+ return $this->worksheet;
+ }
+
+ /**
+ * Set worksheet.
+ */
+ public function setWorksheet(?Worksheet $worksheet): self
+ {
+ $this->worksheet = $worksheet;
+
+ return $this;
+ }
+
+ /**
+ * Get range or formula value.
+ */
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set range or formula value.
+ */
+ public function setValue(string $value): self
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get localOnly.
+ */
+ public function getLocalOnly(): bool
+ {
+ return $this->localOnly;
+ }
+
+ /**
+ * Set localOnly.
+ */
+ public function setLocalOnly(bool $localScope): self
+ {
+ $this->localOnly = $localScope;
+ $this->scope = $localScope ? $this->worksheet : null;
+
+ return $this;
+ }
+
+ /**
+ * Get scope.
+ */
+ public function getScope(): ?Worksheet
+ {
+ return $this->scope;
+ }
+
+ /**
+ * Set scope.
+ */
+ public function setScope(?Worksheet $worksheet): self
+ {
+ $this->scope = $worksheet;
+ $this->localOnly = $worksheet !== null;
+
+ return $this;
+ }
+
+ /**
+ * Identify whether this is a named range or a named formula.
+ */
+ public function isFormula(): bool
+ {
+ return $this->isFormula;
+ }
+
+ /**
+ * Resolve a named range to a regular cell range or formula.
+ */
+ public static function resolveName(string $definedName, Worksheet $worksheet, string $sheetName = ''): ?self
+ {
+ if ($sheetName === '') {
+ $worksheet2 = $worksheet;
+ } else {
+ $worksheet2 = $worksheet->getParentOrThrow()->getSheetByName($sheetName);
+ if ($worksheet2 === null) {
+ return null;
+ }
+ }
+
+ return $worksheet->getParentOrThrow()->getDefinedName($definedName, $worksheet2);
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
new file mode 100644
index 0000000..4ec9a2b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
@@ -0,0 +1,537 @@
+lastModifiedBy = $this->creator;
+ $this->created = self::intOrFloatTimestamp(null);
+ $this->modified = $this->created;
+ }
+
+ /**
+ * Get Creator.
+ */
+ public function getCreator(): string
+ {
+ return $this->creator;
+ }
+
+ /**
+ * Set Creator.
+ *
+ * @return $this
+ */
+ public function setCreator(string $creator): self
+ {
+ $this->creator = $creator;
+
+ return $this;
+ }
+
+ /**
+ * Get Last Modified By.
+ */
+ public function getLastModifiedBy(): string
+ {
+ return $this->lastModifiedBy;
+ }
+
+ /**
+ * Set Last Modified By.
+ *
+ * @return $this
+ */
+ public function setLastModifiedBy(string $modifiedBy): self
+ {
+ $this->lastModifiedBy = $modifiedBy;
+
+ return $this;
+ }
+
+ /**
+ * @param null|float|int|string $timestamp
+ *
+ * @return float|int
+ */
+ private static function intOrFloatTimestamp($timestamp)
+ {
+ if ($timestamp === null) {
+ $timestamp = (float) (new DateTime())->format('U');
+ } elseif (is_string($timestamp)) {
+ if (is_numeric($timestamp)) {
+ $timestamp = (float) $timestamp;
+ } else {
+ $timestamp = (string) preg_replace('/[.][0-9]*$/', '', $timestamp);
+ $timestamp = (string) preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp);
+ $timestamp = (string) preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp);
+ $timestamp = (float) (new DateTime($timestamp))->format('U');
+ }
+ }
+
+ return IntOrFloat::evaluate($timestamp);
+ }
+
+ /**
+ * Get Created.
+ *
+ * @return float|int
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * Set Created.
+ *
+ * @param null|float|int|string $timestamp
+ *
+ * @return $this
+ */
+ public function setCreated($timestamp): self
+ {
+ $this->created = self::intOrFloatTimestamp($timestamp);
+
+ return $this;
+ }
+
+ /**
+ * Get Modified.
+ *
+ * @return float|int
+ */
+ public function getModified()
+ {
+ return $this->modified;
+ }
+
+ /**
+ * Set Modified.
+ *
+ * @param null|float|int|string $timestamp
+ *
+ * @return $this
+ */
+ public function setModified($timestamp): self
+ {
+ $this->modified = self::intOrFloatTimestamp($timestamp);
+
+ return $this;
+ }
+
+ /**
+ * Get Title.
+ */
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+
+ /**
+ * Set Title.
+ *
+ * @return $this
+ */
+ public function setTitle(string $title): self
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ /**
+ * Get Description.
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set Description.
+ *
+ * @return $this
+ */
+ public function setDescription(string $description): self
+ {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ /**
+ * Get Subject.
+ */
+ public function getSubject(): string
+ {
+ return $this->subject;
+ }
+
+ /**
+ * Set Subject.
+ *
+ * @return $this
+ */
+ public function setSubject(string $subject): self
+ {
+ $this->subject = $subject;
+
+ return $this;
+ }
+
+ /**
+ * Get Keywords.
+ */
+ public function getKeywords(): string
+ {
+ return $this->keywords;
+ }
+
+ /**
+ * Set Keywords.
+ *
+ * @return $this
+ */
+ public function setKeywords(string $keywords): self
+ {
+ $this->keywords = $keywords;
+
+ return $this;
+ }
+
+ /**
+ * Get Category.
+ */
+ public function getCategory(): string
+ {
+ return $this->category;
+ }
+
+ /**
+ * Set Category.
+ *
+ * @return $this
+ */
+ public function setCategory(string $category): self
+ {
+ $this->category = $category;
+
+ return $this;
+ }
+
+ /**
+ * Get Company.
+ */
+ public function getCompany(): string
+ {
+ return $this->company;
+ }
+
+ /**
+ * Set Company.
+ *
+ * @return $this
+ */
+ public function setCompany(string $company): self
+ {
+ $this->company = $company;
+
+ return $this;
+ }
+
+ /**
+ * Get Manager.
+ */
+ public function getManager(): string
+ {
+ return $this->manager;
+ }
+
+ /**
+ * Set Manager.
+ *
+ * @return $this
+ */
+ public function setManager(string $manager): self
+ {
+ $this->manager = $manager;
+
+ return $this;
+ }
+
+ /**
+ * Get a List of Custom Property Names.
+ *
+ * @return string[]
+ */
+ public function getCustomProperties(): array
+ {
+ return array_keys($this->customProperties);
+ }
+
+ /**
+ * Check if a Custom Property is defined.
+ */
+ public function isCustomPropertySet(string $propertyName): bool
+ {
+ return array_key_exists($propertyName, $this->customProperties);
+ }
+
+ /**
+ * Get a Custom Property Value.
+ *
+ * @return mixed
+ */
+ public function getCustomPropertyValue(string $propertyName)
+ {
+ if (isset($this->customProperties[$propertyName])) {
+ return $this->customProperties[$propertyName]['value'];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a Custom Property Type.
+ *
+ * @return null|string
+ */
+ public function getCustomPropertyType(string $propertyName)
+ {
+ return $this->customProperties[$propertyName]['type'] ?? null;
+ }
+
+ private function identifyPropertyType(mixed $propertyValue): string
+ {
+ if (is_float($propertyValue)) {
+ return self::PROPERTY_TYPE_FLOAT;
+ }
+ if (is_int($propertyValue)) {
+ return self::PROPERTY_TYPE_INTEGER;
+ }
+ if (is_bool($propertyValue)) {
+ return self::PROPERTY_TYPE_BOOLEAN;
+ }
+
+ return self::PROPERTY_TYPE_STRING;
+ }
+
+ /**
+ * Set a Custom Property.
+ *
+ * @param string $propertyType
+ * 'i' : Integer
+ * 'f' : Floating Point
+ * 's' : String
+ * 'd' : Date/Time
+ * 'b' : Boolean
+ *
+ * @return $this
+ */
+ public function setCustomProperty(string $propertyName, mixed $propertyValue = '', $propertyType = null): self
+ {
+ if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
+ $propertyType = $this->identifyPropertyType($propertyValue);
+ }
+
+ if (!is_object($propertyValue)) {
+ $this->customProperties[$propertyName] = [
+ 'value' => self::convertProperty($propertyValue, $propertyType),
+ 'type' => $propertyType,
+ ];
+ }
+
+ return $this;
+ }
+
+ private const PROPERTY_TYPE_ARRAY = [
+ 'i' => self::PROPERTY_TYPE_INTEGER, // Integer
+ 'i1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Signed Integer
+ 'i2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Signed Integer
+ 'i4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Signed Integer
+ 'i8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Signed Integer
+ 'int' => self::PROPERTY_TYPE_INTEGER, // Integer
+ 'ui1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Unsigned Integer
+ 'ui2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Unsigned Integer
+ 'ui4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Unsigned Integer
+ 'ui8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Unsigned Integer
+ 'uint' => self::PROPERTY_TYPE_INTEGER, // Unsigned Integer
+ 'f' => self::PROPERTY_TYPE_FLOAT, // Real Number
+ 'r4' => self::PROPERTY_TYPE_FLOAT, // 4-Byte Real Number
+ 'r8' => self::PROPERTY_TYPE_FLOAT, // 8-Byte Real Number
+ 'decimal' => self::PROPERTY_TYPE_FLOAT, // Decimal
+ 's' => self::PROPERTY_TYPE_STRING, // String
+ 'empty' => self::PROPERTY_TYPE_STRING, // Empty
+ 'null' => self::PROPERTY_TYPE_STRING, // Null
+ 'lpstr' => self::PROPERTY_TYPE_STRING, // LPSTR
+ 'lpwstr' => self::PROPERTY_TYPE_STRING, // LPWSTR
+ 'bstr' => self::PROPERTY_TYPE_STRING, // Basic String
+ 'd' => self::PROPERTY_TYPE_DATE, // Date and Time
+ 'date' => self::PROPERTY_TYPE_DATE, // Date and Time
+ 'filetime' => self::PROPERTY_TYPE_DATE, // File Time
+ 'b' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
+ 'bool' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
+ ];
+
+ private const SPECIAL_TYPES = [
+ 'empty' => '',
+ 'null' => null,
+ ];
+
+ /**
+ * Convert property to form desired by Excel.
+ *
+ * @return mixed
+ */
+ public static function convertProperty(mixed $propertyValue, string $propertyType)
+ {
+ return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
+ }
+
+ /**
+ * Convert property to form desired by Excel.
+ *
+ * @return mixed
+ */
+ private static function convertProperty2(mixed $propertyValue, string $type)
+ {
+ $propertyType = self::convertPropertyType($type);
+ switch ($propertyType) {
+ case self::PROPERTY_TYPE_INTEGER:
+ $intValue = (int) $propertyValue;
+
+ return ($type[0] === 'u') ? abs($intValue) : $intValue;
+ case self::PROPERTY_TYPE_FLOAT:
+ return (float) $propertyValue;
+ case self::PROPERTY_TYPE_DATE:
+ return self::intOrFloatTimestamp($propertyValue);
+ case self::PROPERTY_TYPE_BOOLEAN:
+ return is_bool($propertyValue) ? $propertyValue : ($propertyValue === 'true');
+ default: // includes string
+ return $propertyValue;
+ }
+ }
+
+ public static function convertPropertyType(string $propertyType): string
+ {
+ return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;
+ }
+
+ public function getHyperlinkBase(): string
+ {
+ return $this->hyperlinkBase;
+ }
+
+ public function setHyperlinkBase(string $hyperlinkBase): self
+ {
+ $this->hyperlinkBase = $hyperlinkBase;
+
+ return $this;
+ }
+
+ public function getViewport(): string
+ {
+ return $this->viewport;
+ }
+
+ public const SUGGESTED_VIEWPORT = 'width=device-width, initial-scale=1';
+
+ public function setViewport(string $viewport): self
+ {
+ $this->viewport = $viewport;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
new file mode 100644
index 0000000..b542718
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
@@ -0,0 +1,146 @@
+lockRevision
+ || $this->lockStructure
+ || $this->lockWindows;
+ }
+
+ public function getLockRevision(): bool
+ {
+ return $this->lockRevision;
+ }
+
+ public function setLockRevision(?bool $locked): self
+ {
+ if ($locked !== null) {
+ $this->lockRevision = $locked;
+ }
+
+ return $this;
+ }
+
+ public function getLockStructure(): bool
+ {
+ return $this->lockStructure;
+ }
+
+ public function setLockStructure(?bool $locked): self
+ {
+ if ($locked !== null) {
+ $this->lockStructure = $locked;
+ }
+
+ return $this;
+ }
+
+ public function getLockWindows(): bool
+ {
+ return $this->lockWindows;
+ }
+
+ public function setLockWindows(?bool $locked): self
+ {
+ if ($locked !== null) {
+ $this->lockWindows = $locked;
+ }
+
+ return $this;
+ }
+
+ public function getRevisionsPassword(): string
+ {
+ return $this->revisionsPassword;
+ }
+
+ /**
+ * Set RevisionsPassword.
+ *
+ * @param bool $alreadyHashed If the password has already been hashed, set this to true
+ *
+ * @return $this
+ */
+ public function setRevisionsPassword(?string $password, bool $alreadyHashed = false): static
+ {
+ if ($password !== null) {
+ if (!$alreadyHashed) {
+ $password = PasswordHasher::hashPassword($password);
+ }
+ $this->revisionsPassword = $password;
+ }
+
+ return $this;
+ }
+
+ public function getWorkbookPassword(): string
+ {
+ return $this->workbookPassword;
+ }
+
+ /**
+ * Set WorkbookPassword.
+ *
+ * @param bool $alreadyHashed If the password has already been hashed, set this to true
+ *
+ * @return $this
+ */
+ public function setWorkbookPassword(?string $password, bool $alreadyHashed = false): static
+ {
+ if ($password !== null) {
+ if (!$alreadyHashed) {
+ $password = PasswordHasher::hashPassword($password);
+ }
+ $this->workbookPassword = $password;
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
new file mode 100644
index 0000000..3491580
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
@@ -0,0 +1,9 @@
+
+ */
+ protected $items = [];
+
+ /**
+ * HashTable key map.
+ *
+ * @var array
+ */
+ protected $keyMap = [];
+
+ /**
+ * Create a new HashTable.
+ *
+ * @param T[] $source Optional source array to create HashTable from
+ */
+ public function __construct($source = null)
+ {
+ if ($source !== null) {
+ // Create HashTable
+ $this->addFromSource($source);
+ }
+ }
+
+ /**
+ * Add HashTable items from source.
+ *
+ * @param T[] $source Source array to create HashTable from
+ */
+ public function addFromSource(?array $source = null): void
+ {
+ // Check if an array was passed
+ if ($source === null) {
+ return;
+ }
+
+ foreach ($source as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Add HashTable item.
+ *
+ * @param T $source Item to add
+ */
+ public function add(IComparable $source): void
+ {
+ $hash = $source->getHashCode();
+ if (!isset($this->items[$hash])) {
+ $this->items[$hash] = $source;
+ $this->keyMap[count($this->items) - 1] = $hash;
+ }
+ }
+
+ /**
+ * Remove HashTable item.
+ *
+ * @param T $source Item to remove
+ */
+ public function remove(IComparable $source): void
+ {
+ $hash = $source->getHashCode();
+ if (isset($this->items[$hash])) {
+ unset($this->items[$hash]);
+
+ $deleteKey = -1;
+ foreach ($this->keyMap as $key => $value) {
+ if ($deleteKey >= 0) {
+ $this->keyMap[$key - 1] = $value;
+ }
+
+ if ($value == $hash) {
+ $deleteKey = $key;
+ }
+ }
+ unset($this->keyMap[count($this->keyMap) - 1]);
+ }
+ }
+
+ /**
+ * Clear HashTable.
+ */
+ public function clear(): void
+ {
+ $this->items = [];
+ $this->keyMap = [];
+ }
+
+ /**
+ * Count.
+ */
+ public function count(): int
+ {
+ return count($this->items);
+ }
+
+ /**
+ * Get index for hash code.
+ */
+ public function getIndexForHashCode(string $hashCode): false|int
+ {
+ return array_search($hashCode, $this->keyMap, true);
+ }
+
+ /**
+ * Get by index.
+ *
+ * @return null|T
+ */
+ public function getByIndex(int $index)
+ {
+ if (isset($this->keyMap[$index])) {
+ return $this->getByHashCode($this->keyMap[$index]);
+ }
+
+ return null;
+ }
+
+ /**
+ * Get by hashcode.
+ *
+ * @return null|T
+ */
+ public function getByHashCode(string $hashCode)
+ {
+ if (isset($this->items[$hashCode])) {
+ return $this->items[$hashCode];
+ }
+
+ return null;
+ }
+
+ /**
+ * HashTable to array.
+ *
+ * @return T[]
+ */
+ public function toArray()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ // each member of this class is an array
+ if (is_array($value)) {
+ $array1 = $value;
+ foreach ($array1 as $key1 => $value1) {
+ if (is_object($value1)) {
+ $array1[$key1] = clone $value1;
+ }
+ }
+ $this->$key = $array1;
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php
new file mode 100644
index 0000000..7afb803
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php
@@ -0,0 +1,113 @@
+ 96.0 / 2.54,
+ self::UOM_MILLIMETERS => 96.0 / 25.4,
+ self::UOM_INCHES => 96.0,
+ self::UOM_PIXELS => 1.0,
+ self::UOM_POINTS => 96.0 / 72,
+ self::UOM_PICA => 96.0 * 12 / 72,
+ ];
+
+ /**
+ * Based on a standard column width of 8.54 units in MS Excel.
+ */
+ const RELATIVE_UNITS = [
+ 'em' => 10.0 / 8.54,
+ 'ex' => 10.0 / 8.54,
+ 'ch' => 10.0 / 8.54,
+ 'rem' => 10.0 / 8.54,
+ 'vw' => 8.54,
+ 'vh' => 8.54,
+ 'vmin' => 8.54,
+ 'vmax' => 8.54,
+ '%' => 8.54 / 100,
+ ];
+
+ /**
+ * @var float|int If this is a width, then size is measured in pixels (if is set)
+ * or in Excel's default column width units if $unit is null.
+ * If this is a height, then size is measured in pixels ()
+ * or in points () if $unit is null.
+ */
+ protected float|int $size;
+
+ /**
+ * @var null|string
+ */
+ protected $unit;
+
+ /**
+ * Phpstan bug has been fixed; this function allows us to
+ * pass Phpstan whether fixed or not.
+ */
+ private static function stanBugFixed(mixed $value): array
+ {
+ return is_array($value) ? $value : [null, null];
+ }
+
+ public function __construct(string $dimension)
+ {
+ [$size, $unit] = self::stanBugFixed(sscanf($dimension, '%[1234567890.]%s'));
+ $unit = strtolower(trim($unit ?? ''));
+ $size = (float) $size;
+
+ // If a UoM is specified, then convert the size to pixels for internal storage
+ if (isset(self::ABSOLUTE_UNITS[$unit])) {
+ $size *= self::ABSOLUTE_UNITS[$unit];
+ $this->unit = self::UOM_PIXELS;
+ } elseif (isset(self::RELATIVE_UNITS[$unit])) {
+ $size *= self::RELATIVE_UNITS[$unit];
+ $size = round($size, 4);
+ }
+
+ $this->size = $size;
+ }
+
+ public function width(): float
+ {
+ return (float) ($this->unit === null)
+ ? $this->size
+ : round(Drawing::pixelsToCellDimension((int) $this->size, new Font(false)), 4);
+ }
+
+ public function height(): float
+ {
+ return (float) ($this->unit === null)
+ ? $this->size
+ : $this->toUnit(self::UOM_POINTS);
+ }
+
+ public function toUnit(string $unitOfMeasure): float
+ {
+ $unitOfMeasure = strtolower($unitOfMeasure);
+ if (!array_key_exists($unitOfMeasure, self::ABSOLUTE_UNITS)) {
+ throw new Exception("{$unitOfMeasure} is not a vaid unit of measure");
+ }
+
+ $size = $this->size;
+ if ($this->unit === null) {
+ $size = Drawing::cellDimensionToPixels($size, new Font(false));
+ }
+
+ return $size / self::ABSOLUTE_UNITS[$unitOfMeasure];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php
new file mode 100644
index 0000000..4e98c79
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php
@@ -0,0 +1,101 @@
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'csv' => 'text/csv',
+ 'html' => 'text/html',
+ 'pdf' => 'application/pdf',
+ ];
+
+ public function __construct(string $folder, string $filename, ?string $filetype = null)
+ {
+ if ((is_dir($folder) === false) || (is_readable($folder) === false)) {
+ throw new Exception("Folder {$folder} is not accessable");
+ }
+ $filepath = "{$folder}/{$filename}";
+ $this->filepath = (string) realpath($filepath);
+ $this->filename = basename($filepath);
+ if ((file_exists($this->filepath) === false) || (is_readable($this->filepath) === false)) {
+ throw new Exception("{$this->filename} not found, or cannot be read");
+ }
+
+ $filetype ??= pathinfo($filename, PATHINFO_EXTENSION);
+ if (array_key_exists(strtolower($filetype), self::CONTENT_TYPES) === false) {
+ throw new Exception("Invalid filetype: {$filetype} cannot be downloaded");
+ }
+ $this->filetype = strtolower($filetype);
+ }
+
+ public function download(): void
+ {
+ $this->headers();
+
+ readfile($this->filepath);
+ }
+
+ public function headers(): void
+ {
+ // I cannot tell what this ob_clean is paired with.
+ // I have never seen a problem with it, but someone has - issue 3739.
+ // Perhaps it should be removed altogether,
+ // but making it conditional seems harmless, and safer.
+ if ((int) ob_get_length() > 0) {
+ ob_clean();
+ }
+
+ $this->contentType();
+ $this->contentDisposition();
+ $this->cacheHeaders();
+ $this->fileSize();
+
+ flush();
+ }
+
+ protected function contentType(): void
+ {
+ header('Content-Type: ' . self::CONTENT_TYPES[$this->filetype]);
+ }
+
+ protected function contentDisposition(): void
+ {
+ header('Content-Disposition: attachment;filename="' . $this->filename . '"');
+ }
+
+ protected function cacheHeaders(): void
+ {
+ header('Cache-Control: max-age=0');
+ // If you're serving to IE 9, then the following may be needed
+ header('Cache-Control: max-age=1');
+
+ // If you're serving to IE over SSL, then the following may be needed
+ header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
+ header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
+ header('Pragma: public'); // HTTP/1.0
+ }
+
+ protected function fileSize(): void
+ {
+ header('Content-Length: ' . filesize($this->filepath));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php
new file mode 100644
index 0000000..d05197c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php
@@ -0,0 +1,46 @@
+ 'f0f8ff',
+ 'antiquewhite' => 'faebd7',
+ 'antiquewhite1' => 'ffefdb',
+ 'antiquewhite2' => 'eedfcc',
+ 'antiquewhite3' => 'cdc0b0',
+ 'antiquewhite4' => '8b8378',
+ 'aqua' => '00ffff',
+ 'aquamarine1' => '7fffd4',
+ 'aquamarine2' => '76eec6',
+ 'aquamarine4' => '458b74',
+ 'azure1' => 'f0ffff',
+ 'azure2' => 'e0eeee',
+ 'azure3' => 'c1cdcd',
+ 'azure4' => '838b8b',
+ 'beige' => 'f5f5dc',
+ 'bisque1' => 'ffe4c4',
+ 'bisque2' => 'eed5b7',
+ 'bisque3' => 'cdb79e',
+ 'bisque4' => '8b7d6b',
+ 'black' => '000000',
+ 'blanchedalmond' => 'ffebcd',
+ 'blue' => '0000ff',
+ 'blue1' => '0000ff',
+ 'blue2' => '0000ee',
+ 'blue4' => '00008b',
+ 'blueviolet' => '8a2be2',
+ 'brown' => 'a52a2a',
+ 'brown1' => 'ff4040',
+ 'brown2' => 'ee3b3b',
+ 'brown3' => 'cd3333',
+ 'brown4' => '8b2323',
+ 'burlywood' => 'deb887',
+ 'burlywood1' => 'ffd39b',
+ 'burlywood2' => 'eec591',
+ 'burlywood3' => 'cdaa7d',
+ 'burlywood4' => '8b7355',
+ 'cadetblue' => '5f9ea0',
+ 'cadetblue1' => '98f5ff',
+ 'cadetblue2' => '8ee5ee',
+ 'cadetblue3' => '7ac5cd',
+ 'cadetblue4' => '53868b',
+ 'chartreuse1' => '7fff00',
+ 'chartreuse2' => '76ee00',
+ 'chartreuse3' => '66cd00',
+ 'chartreuse4' => '458b00',
+ 'chocolate' => 'd2691e',
+ 'chocolate1' => 'ff7f24',
+ 'chocolate2' => 'ee7621',
+ 'chocolate3' => 'cd661d',
+ 'coral' => 'ff7f50',
+ 'coral1' => 'ff7256',
+ 'coral2' => 'ee6a50',
+ 'coral3' => 'cd5b45',
+ 'coral4' => '8b3e2f',
+ 'cornflowerblue' => '6495ed',
+ 'cornsilk1' => 'fff8dc',
+ 'cornsilk2' => 'eee8cd',
+ 'cornsilk3' => 'cdc8b1',
+ 'cornsilk4' => '8b8878',
+ 'cyan1' => '00ffff',
+ 'cyan2' => '00eeee',
+ 'cyan3' => '00cdcd',
+ 'cyan4' => '008b8b',
+ 'darkgoldenrod' => 'b8860b',
+ 'darkgoldenrod1' => 'ffb90f',
+ 'darkgoldenrod2' => 'eead0e',
+ 'darkgoldenrod3' => 'cd950c',
+ 'darkgoldenrod4' => '8b6508',
+ 'darkgreen' => '006400',
+ 'darkkhaki' => 'bdb76b',
+ 'darkolivegreen' => '556b2f',
+ 'darkolivegreen1' => 'caff70',
+ 'darkolivegreen2' => 'bcee68',
+ 'darkolivegreen3' => 'a2cd5a',
+ 'darkolivegreen4' => '6e8b3d',
+ 'darkorange' => 'ff8c00',
+ 'darkorange1' => 'ff7f00',
+ 'darkorange2' => 'ee7600',
+ 'darkorange3' => 'cd6600',
+ 'darkorange4' => '8b4500',
+ 'darkorchid' => '9932cc',
+ 'darkorchid1' => 'bf3eff',
+ 'darkorchid2' => 'b23aee',
+ 'darkorchid3' => '9a32cd',
+ 'darkorchid4' => '68228b',
+ 'darksalmon' => 'e9967a',
+ 'darkseagreen' => '8fbc8f',
+ 'darkseagreen1' => 'c1ffc1',
+ 'darkseagreen2' => 'b4eeb4',
+ 'darkseagreen3' => '9bcd9b',
+ 'darkseagreen4' => '698b69',
+ 'darkslateblue' => '483d8b',
+ 'darkslategray' => '2f4f4f',
+ 'darkslategray1' => '97ffff',
+ 'darkslategray2' => '8deeee',
+ 'darkslategray3' => '79cdcd',
+ 'darkslategray4' => '528b8b',
+ 'darkturquoise' => '00ced1',
+ 'darkviolet' => '9400d3',
+ 'deeppink1' => 'ff1493',
+ 'deeppink2' => 'ee1289',
+ 'deeppink3' => 'cd1076',
+ 'deeppink4' => '8b0a50',
+ 'deepskyblue1' => '00bfff',
+ 'deepskyblue2' => '00b2ee',
+ 'deepskyblue3' => '009acd',
+ 'deepskyblue4' => '00688b',
+ 'dimgray' => '696969',
+ 'dodgerblue1' => '1e90ff',
+ 'dodgerblue2' => '1c86ee',
+ 'dodgerblue3' => '1874cd',
+ 'dodgerblue4' => '104e8b',
+ 'firebrick' => 'b22222',
+ 'firebrick1' => 'ff3030',
+ 'firebrick2' => 'ee2c2c',
+ 'firebrick3' => 'cd2626',
+ 'firebrick4' => '8b1a1a',
+ 'floralwhite' => 'fffaf0',
+ 'forestgreen' => '228b22',
+ 'fuchsia' => 'ff00ff',
+ 'gainsboro' => 'dcdcdc',
+ 'ghostwhite' => 'f8f8ff',
+ 'gold1' => 'ffd700',
+ 'gold2' => 'eec900',
+ 'gold3' => 'cdad00',
+ 'gold4' => '8b7500',
+ 'goldenrod' => 'daa520',
+ 'goldenrod1' => 'ffc125',
+ 'goldenrod2' => 'eeb422',
+ 'goldenrod3' => 'cd9b1d',
+ 'goldenrod4' => '8b6914',
+ 'gray' => 'bebebe',
+ 'gray1' => '030303',
+ 'gray10' => '1a1a1a',
+ 'gray11' => '1c1c1c',
+ 'gray12' => '1f1f1f',
+ 'gray13' => '212121',
+ 'gray14' => '242424',
+ 'gray15' => '262626',
+ 'gray16' => '292929',
+ 'gray17' => '2b2b2b',
+ 'gray18' => '2e2e2e',
+ 'gray19' => '303030',
+ 'gray2' => '050505',
+ 'gray20' => '333333',
+ 'gray21' => '363636',
+ 'gray22' => '383838',
+ 'gray23' => '3b3b3b',
+ 'gray24' => '3d3d3d',
+ 'gray25' => '404040',
+ 'gray26' => '424242',
+ 'gray27' => '454545',
+ 'gray28' => '474747',
+ 'gray29' => '4a4a4a',
+ 'gray3' => '080808',
+ 'gray30' => '4d4d4d',
+ 'gray31' => '4f4f4f',
+ 'gray32' => '525252',
+ 'gray33' => '545454',
+ 'gray34' => '575757',
+ 'gray35' => '595959',
+ 'gray36' => '5c5c5c',
+ 'gray37' => '5e5e5e',
+ 'gray38' => '616161',
+ 'gray39' => '636363',
+ 'gray4' => '0a0a0a',
+ 'gray40' => '666666',
+ 'gray41' => '696969',
+ 'gray42' => '6b6b6b',
+ 'gray43' => '6e6e6e',
+ 'gray44' => '707070',
+ 'gray45' => '737373',
+ 'gray46' => '757575',
+ 'gray47' => '787878',
+ 'gray48' => '7a7a7a',
+ 'gray49' => '7d7d7d',
+ 'gray5' => '0d0d0d',
+ 'gray50' => '7f7f7f',
+ 'gray51' => '828282',
+ 'gray52' => '858585',
+ 'gray53' => '878787',
+ 'gray54' => '8a8a8a',
+ 'gray55' => '8c8c8c',
+ 'gray56' => '8f8f8f',
+ 'gray57' => '919191',
+ 'gray58' => '949494',
+ 'gray59' => '969696',
+ 'gray6' => '0f0f0f',
+ 'gray60' => '999999',
+ 'gray61' => '9c9c9c',
+ 'gray62' => '9e9e9e',
+ 'gray63' => 'a1a1a1',
+ 'gray64' => 'a3a3a3',
+ 'gray65' => 'a6a6a6',
+ 'gray66' => 'a8a8a8',
+ 'gray67' => 'ababab',
+ 'gray68' => 'adadad',
+ 'gray69' => 'b0b0b0',
+ 'gray7' => '121212',
+ 'gray70' => 'b3b3b3',
+ 'gray71' => 'b5b5b5',
+ 'gray72' => 'b8b8b8',
+ 'gray73' => 'bababa',
+ 'gray74' => 'bdbdbd',
+ 'gray75' => 'bfbfbf',
+ 'gray76' => 'c2c2c2',
+ 'gray77' => 'c4c4c4',
+ 'gray78' => 'c7c7c7',
+ 'gray79' => 'c9c9c9',
+ 'gray8' => '141414',
+ 'gray80' => 'cccccc',
+ 'gray81' => 'cfcfcf',
+ 'gray82' => 'd1d1d1',
+ 'gray83' => 'd4d4d4',
+ 'gray84' => 'd6d6d6',
+ 'gray85' => 'd9d9d9',
+ 'gray86' => 'dbdbdb',
+ 'gray87' => 'dedede',
+ 'gray88' => 'e0e0e0',
+ 'gray89' => 'e3e3e3',
+ 'gray9' => '171717',
+ 'gray90' => 'e5e5e5',
+ 'gray91' => 'e8e8e8',
+ 'gray92' => 'ebebeb',
+ 'gray93' => 'ededed',
+ 'gray94' => 'f0f0f0',
+ 'gray95' => 'f2f2f2',
+ 'gray97' => 'f7f7f7',
+ 'gray98' => 'fafafa',
+ 'gray99' => 'fcfcfc',
+ 'green' => '00ff00',
+ 'green1' => '00ff00',
+ 'green2' => '00ee00',
+ 'green3' => '00cd00',
+ 'green4' => '008b00',
+ 'greenyellow' => 'adff2f',
+ 'honeydew1' => 'f0fff0',
+ 'honeydew2' => 'e0eee0',
+ 'honeydew3' => 'c1cdc1',
+ 'honeydew4' => '838b83',
+ 'hotpink' => 'ff69b4',
+ 'hotpink1' => 'ff6eb4',
+ 'hotpink2' => 'ee6aa7',
+ 'hotpink3' => 'cd6090',
+ 'hotpink4' => '8b3a62',
+ 'indianred' => 'cd5c5c',
+ 'indianred1' => 'ff6a6a',
+ 'indianred2' => 'ee6363',
+ 'indianred3' => 'cd5555',
+ 'indianred4' => '8b3a3a',
+ 'ivory1' => 'fffff0',
+ 'ivory2' => 'eeeee0',
+ 'ivory3' => 'cdcdc1',
+ 'ivory4' => '8b8b83',
+ 'khaki' => 'f0e68c',
+ 'khaki1' => 'fff68f',
+ 'khaki2' => 'eee685',
+ 'khaki3' => 'cdc673',
+ 'khaki4' => '8b864e',
+ 'lavender' => 'e6e6fa',
+ 'lavenderblush1' => 'fff0f5',
+ 'lavenderblush2' => 'eee0e5',
+ 'lavenderblush3' => 'cdc1c5',
+ 'lavenderblush4' => '8b8386',
+ 'lawngreen' => '7cfc00',
+ 'lemonchiffon1' => 'fffacd',
+ 'lemonchiffon2' => 'eee9bf',
+ 'lemonchiffon3' => 'cdc9a5',
+ 'lemonchiffon4' => '8b8970',
+ 'light' => 'eedd82',
+ 'lightblue' => 'add8e6',
+ 'lightblue1' => 'bfefff',
+ 'lightblue2' => 'b2dfee',
+ 'lightblue3' => '9ac0cd',
+ 'lightblue4' => '68838b',
+ 'lightcoral' => 'f08080',
+ 'lightcyan1' => 'e0ffff',
+ 'lightcyan2' => 'd1eeee',
+ 'lightcyan3' => 'b4cdcd',
+ 'lightcyan4' => '7a8b8b',
+ 'lightgoldenrod1' => 'ffec8b',
+ 'lightgoldenrod2' => 'eedc82',
+ 'lightgoldenrod3' => 'cdbe70',
+ 'lightgoldenrod4' => '8b814c',
+ 'lightgoldenrodyellow' => 'fafad2',
+ 'lightgray' => 'd3d3d3',
+ 'lightpink' => 'ffb6c1',
+ 'lightpink1' => 'ffaeb9',
+ 'lightpink2' => 'eea2ad',
+ 'lightpink3' => 'cd8c95',
+ 'lightpink4' => '8b5f65',
+ 'lightsalmon1' => 'ffa07a',
+ 'lightsalmon2' => 'ee9572',
+ 'lightsalmon3' => 'cd8162',
+ 'lightsalmon4' => '8b5742',
+ 'lightseagreen' => '20b2aa',
+ 'lightskyblue' => '87cefa',
+ 'lightskyblue1' => 'b0e2ff',
+ 'lightskyblue2' => 'a4d3ee',
+ 'lightskyblue3' => '8db6cd',
+ 'lightskyblue4' => '607b8b',
+ 'lightslateblue' => '8470ff',
+ 'lightslategray' => '778899',
+ 'lightsteelblue' => 'b0c4de',
+ 'lightsteelblue1' => 'cae1ff',
+ 'lightsteelblue2' => 'bcd2ee',
+ 'lightsteelblue3' => 'a2b5cd',
+ 'lightsteelblue4' => '6e7b8b',
+ 'lightyellow1' => 'ffffe0',
+ 'lightyellow2' => 'eeeed1',
+ 'lightyellow3' => 'cdcdb4',
+ 'lightyellow4' => '8b8b7a',
+ 'lime' => '00ff00',
+ 'limegreen' => '32cd32',
+ 'linen' => 'faf0e6',
+ 'magenta' => 'ff00ff',
+ 'magenta2' => 'ee00ee',
+ 'magenta3' => 'cd00cd',
+ 'magenta4' => '8b008b',
+ 'maroon' => 'b03060',
+ 'maroon1' => 'ff34b3',
+ 'maroon2' => 'ee30a7',
+ 'maroon3' => 'cd2990',
+ 'maroon4' => '8b1c62',
+ 'medium' => '66cdaa',
+ 'mediumaquamarine' => '66cdaa',
+ 'mediumblue' => '0000cd',
+ 'mediumorchid' => 'ba55d3',
+ 'mediumorchid1' => 'e066ff',
+ 'mediumorchid2' => 'd15fee',
+ 'mediumorchid3' => 'b452cd',
+ 'mediumorchid4' => '7a378b',
+ 'mediumpurple' => '9370db',
+ 'mediumpurple1' => 'ab82ff',
+ 'mediumpurple2' => '9f79ee',
+ 'mediumpurple3' => '8968cd',
+ 'mediumpurple4' => '5d478b',
+ 'mediumseagreen' => '3cb371',
+ 'mediumslateblue' => '7b68ee',
+ 'mediumspringgreen' => '00fa9a',
+ 'mediumturquoise' => '48d1cc',
+ 'mediumvioletred' => 'c71585',
+ 'midnightblue' => '191970',
+ 'mintcream' => 'f5fffa',
+ 'mistyrose1' => 'ffe4e1',
+ 'mistyrose2' => 'eed5d2',
+ 'mistyrose3' => 'cdb7b5',
+ 'mistyrose4' => '8b7d7b',
+ 'moccasin' => 'ffe4b5',
+ 'navajowhite1' => 'ffdead',
+ 'navajowhite2' => 'eecfa1',
+ 'navajowhite3' => 'cdb38b',
+ 'navajowhite4' => '8b795e',
+ 'navy' => '000080',
+ 'navyblue' => '000080',
+ 'oldlace' => 'fdf5e6',
+ 'olive' => '808000',
+ 'olivedrab' => '6b8e23',
+ 'olivedrab1' => 'c0ff3e',
+ 'olivedrab2' => 'b3ee3a',
+ 'olivedrab4' => '698b22',
+ 'orange' => 'ffa500',
+ 'orange1' => 'ffa500',
+ 'orange2' => 'ee9a00',
+ 'orange3' => 'cd8500',
+ 'orange4' => '8b5a00',
+ 'orangered1' => 'ff4500',
+ 'orangered2' => 'ee4000',
+ 'orangered3' => 'cd3700',
+ 'orangered4' => '8b2500',
+ 'orchid' => 'da70d6',
+ 'orchid1' => 'ff83fa',
+ 'orchid2' => 'ee7ae9',
+ 'orchid3' => 'cd69c9',
+ 'orchid4' => '8b4789',
+ 'pale' => 'db7093',
+ 'palegoldenrod' => 'eee8aa',
+ 'palegreen' => '98fb98',
+ 'palegreen1' => '9aff9a',
+ 'palegreen2' => '90ee90',
+ 'palegreen3' => '7ccd7c',
+ 'palegreen4' => '548b54',
+ 'paleturquoise' => 'afeeee',
+ 'paleturquoise1' => 'bbffff',
+ 'paleturquoise2' => 'aeeeee',
+ 'paleturquoise3' => '96cdcd',
+ 'paleturquoise4' => '668b8b',
+ 'palevioletred' => 'db7093',
+ 'palevioletred1' => 'ff82ab',
+ 'palevioletred2' => 'ee799f',
+ 'palevioletred3' => 'cd6889',
+ 'palevioletred4' => '8b475d',
+ 'papayawhip' => 'ffefd5',
+ 'peachpuff1' => 'ffdab9',
+ 'peachpuff2' => 'eecbad',
+ 'peachpuff3' => 'cdaf95',
+ 'peachpuff4' => '8b7765',
+ 'pink' => 'ffc0cb',
+ 'pink1' => 'ffb5c5',
+ 'pink2' => 'eea9b8',
+ 'pink3' => 'cd919e',
+ 'pink4' => '8b636c',
+ 'plum' => 'dda0dd',
+ 'plum1' => 'ffbbff',
+ 'plum2' => 'eeaeee',
+ 'plum3' => 'cd96cd',
+ 'plum4' => '8b668b',
+ 'powderblue' => 'b0e0e6',
+ 'purple' => 'a020f0',
+ 'rebeccapurple' => '663399',
+ 'purple1' => '9b30ff',
+ 'purple2' => '912cee',
+ 'purple3' => '7d26cd',
+ 'purple4' => '551a8b',
+ 'red' => 'ff0000',
+ 'red1' => 'ff0000',
+ 'red2' => 'ee0000',
+ 'red3' => 'cd0000',
+ 'red4' => '8b0000',
+ 'rosybrown' => 'bc8f8f',
+ 'rosybrown1' => 'ffc1c1',
+ 'rosybrown2' => 'eeb4b4',
+ 'rosybrown3' => 'cd9b9b',
+ 'rosybrown4' => '8b6969',
+ 'royalblue' => '4169e1',
+ 'royalblue1' => '4876ff',
+ 'royalblue2' => '436eee',
+ 'royalblue3' => '3a5fcd',
+ 'royalblue4' => '27408b',
+ 'saddlebrown' => '8b4513',
+ 'salmon' => 'fa8072',
+ 'salmon1' => 'ff8c69',
+ 'salmon2' => 'ee8262',
+ 'salmon3' => 'cd7054',
+ 'salmon4' => '8b4c39',
+ 'sandybrown' => 'f4a460',
+ 'seagreen1' => '54ff9f',
+ 'seagreen2' => '4eee94',
+ 'seagreen3' => '43cd80',
+ 'seagreen4' => '2e8b57',
+ 'seashell1' => 'fff5ee',
+ 'seashell2' => 'eee5de',
+ 'seashell3' => 'cdc5bf',
+ 'seashell4' => '8b8682',
+ 'sienna' => 'a0522d',
+ 'sienna1' => 'ff8247',
+ 'sienna2' => 'ee7942',
+ 'sienna3' => 'cd6839',
+ 'sienna4' => '8b4726',
+ 'silver' => 'c0c0c0',
+ 'skyblue' => '87ceeb',
+ 'skyblue1' => '87ceff',
+ 'skyblue2' => '7ec0ee',
+ 'skyblue3' => '6ca6cd',
+ 'skyblue4' => '4a708b',
+ 'slateblue' => '6a5acd',
+ 'slateblue1' => '836fff',
+ 'slateblue2' => '7a67ee',
+ 'slateblue3' => '6959cd',
+ 'slateblue4' => '473c8b',
+ 'slategray' => '708090',
+ 'slategray1' => 'c6e2ff',
+ 'slategray2' => 'b9d3ee',
+ 'slategray3' => '9fb6cd',
+ 'slategray4' => '6c7b8b',
+ 'snow1' => 'fffafa',
+ 'snow2' => 'eee9e9',
+ 'snow3' => 'cdc9c9',
+ 'snow4' => '8b8989',
+ 'springgreen1' => '00ff7f',
+ 'springgreen2' => '00ee76',
+ 'springgreen3' => '00cd66',
+ 'springgreen4' => '008b45',
+ 'steelblue' => '4682b4',
+ 'steelblue1' => '63b8ff',
+ 'steelblue2' => '5cacee',
+ 'steelblue3' => '4f94cd',
+ 'steelblue4' => '36648b',
+ 'tan' => 'd2b48c',
+ 'tan1' => 'ffa54f',
+ 'tan2' => 'ee9a49',
+ 'tan3' => 'cd853f',
+ 'tan4' => '8b5a2b',
+ 'teal' => '008080',
+ 'thistle' => 'd8bfd8',
+ 'thistle1' => 'ffe1ff',
+ 'thistle2' => 'eed2ee',
+ 'thistle3' => 'cdb5cd',
+ 'thistle4' => '8b7b8b',
+ 'tomato1' => 'ff6347',
+ 'tomato2' => 'ee5c42',
+ 'tomato3' => 'cd4f39',
+ 'tomato4' => '8b3626',
+ 'turquoise' => '40e0d0',
+ 'turquoise1' => '00f5ff',
+ 'turquoise2' => '00e5ee',
+ 'turquoise3' => '00c5cd',
+ 'turquoise4' => '00868b',
+ 'violet' => 'ee82ee',
+ 'violetred' => 'd02090',
+ 'violetred1' => 'ff3e96',
+ 'violetred2' => 'ee3a8c',
+ 'violetred3' => 'cd3278',
+ 'violetred4' => '8b2252',
+ 'wheat' => 'f5deb3',
+ 'wheat1' => 'ffe7ba',
+ 'wheat2' => 'eed8ae',
+ 'wheat3' => 'cdba96',
+ 'wheat4' => '8b7e66',
+ 'white' => 'ffffff',
+ 'whitesmoke' => 'f5f5f5',
+ 'yellow' => 'ffff00',
+ 'yellow1' => 'ffff00',
+ 'yellow2' => 'eeee00',
+ 'yellow3' => 'cdcd00',
+ 'yellow4' => '8b8b00',
+ 'yellowgreen' => '9acd32',
+ ];
+
+ /** @var ?string */
+ private $face;
+
+ /** @var ?string */
+ private $size;
+
+ /** @var ?string */
+ private $color;
+
+ /** @var bool */
+ private $bold = false;
+
+ /** @var bool */
+ private $italic = false;
+
+ /** @var bool */
+ private $underline = false;
+
+ /** @var bool */
+ private $superscript = false;
+
+ /** @var bool */
+ private $subscript = false;
+
+ /** @var bool */
+ private $strikethrough = false;
+
+ /** @var callable[] */
+ private $startTagCallbacks = [
+ 'font' => [self::class, 'startFontTag'],
+ 'b' => [self::class, 'startBoldTag'],
+ 'strong' => [self::class, 'startBoldTag'],
+ 'i' => [self::class, 'startItalicTag'],
+ 'em' => [self::class, 'startItalicTag'],
+ 'u' => [self::class, 'startUnderlineTag'],
+ 'ins' => [self::class, 'startUnderlineTag'],
+ 'del' => [self::class, 'startStrikethruTag'],
+ 'sup' => [self::class, 'startSuperscriptTag'],
+ 'sub' => [self::class, 'startSubscriptTag'],
+ ];
+
+ /** @var callable[] */
+ private $endTagCallbacks = [
+ 'font' => [self::class, 'endFontTag'],
+ 'b' => [self::class, 'endBoldTag'],
+ 'strong' => [self::class, 'endBoldTag'],
+ 'i' => [self::class, 'endItalicTag'],
+ 'em' => [self::class, 'endItalicTag'],
+ 'u' => [self::class, 'endUnderlineTag'],
+ 'ins' => [self::class, 'endUnderlineTag'],
+ 'del' => [self::class, 'endStrikethruTag'],
+ 'sup' => [self::class, 'endSuperscriptTag'],
+ 'sub' => [self::class, 'endSubscriptTag'],
+ 'br' => [self::class, 'breakTag'],
+ 'p' => [self::class, 'breakTag'],
+ 'h1' => [self::class, 'breakTag'],
+ 'h2' => [self::class, 'breakTag'],
+ 'h3' => [self::class, 'breakTag'],
+ 'h4' => [self::class, 'breakTag'],
+ 'h5' => [self::class, 'breakTag'],
+ 'h6' => [self::class, 'breakTag'],
+ ];
+
+ /** @var array */
+ private $stack = [];
+
+ public string $stringData = '';
+
+ /**
+ * @var RichText
+ */
+ private $richTextObject;
+
+ private function initialise(): void
+ {
+ $this->face = $this->size = $this->color = null;
+ $this->bold = $this->italic = $this->underline = $this->superscript = $this->subscript = $this->strikethrough = false;
+
+ $this->stack = [];
+
+ $this->stringData = '';
+ }
+
+ /**
+ * Parse HTML formatting and return the resulting RichText.
+ *
+ * @return RichText
+ */
+ public function toRichTextObject(string $html)
+ {
+ $this->initialise();
+
+ // Create a new DOM object
+ $dom = new DOMDocument();
+ // Load the HTML file into the DOM object
+ // Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
+ $prefix = '';
+ @$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+ // Discard excess white space
+ $dom->preserveWhiteSpace = false;
+
+ $this->richTextObject = new RichText();
+ $this->parseElements($dom);
+
+ // Clean any further spurious whitespace
+ $this->cleanWhitespace();
+
+ return $this->richTextObject;
+ }
+
+ private function cleanWhitespace(): void
+ {
+ foreach ($this->richTextObject->getRichTextElements() as $key => $element) {
+ $text = $element->getText();
+ // Trim any leading spaces on the first run
+ if ($key == 0) {
+ $text = ltrim($text);
+ }
+ // Trim any spaces immediately after a line break
+ $text = (string) preg_replace('/\n */mu', "\n", $text);
+ $element->setText($text);
+ }
+ }
+
+ private function buildTextRun(): void
+ {
+ $text = $this->stringData;
+ if (trim($text) === '') {
+ return;
+ }
+
+ $richtextRun = $this->richTextObject->createTextRun($this->stringData);
+ $font = $richtextRun->getFont();
+ if ($font !== null) {
+ if ($this->face) {
+ $font->setName($this->face);
+ }
+ if ($this->size) {
+ $font->setSize($this->size);
+ }
+ if ($this->color) {
+ $font->setColor(new Color('ff' . $this->color));
+ }
+ if ($this->bold) {
+ $font->setBold(true);
+ }
+ if ($this->italic) {
+ $font->setItalic(true);
+ }
+ if ($this->underline) {
+ $font->setUnderline(Font::UNDERLINE_SINGLE);
+ }
+ if ($this->superscript) {
+ $font->setSuperscript(true);
+ }
+ if ($this->subscript) {
+ $font->setSubscript(true);
+ }
+ if ($this->strikethrough) {
+ $font->setStrikethrough(true);
+ }
+ }
+ $this->stringData = '';
+ }
+
+ private function rgbToColour(string $rgbValue): string
+ {
+ preg_match_all('/\d+/', $rgbValue, $values);
+ foreach ($values[0] as &$value) {
+ $value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT);
+ }
+
+ return implode('', $values[0]);
+ }
+
+ public static function colourNameLookup(string $colorName): string
+ {
+ return self::COLOUR_MAP[$colorName] ?? '';
+ }
+
+ protected function startFontTag(DOMElement $tag): void
+ {
+ $attrs = $tag->attributes;
+ if ($attrs !== null) {
+ foreach ($attrs as $attribute) {
+ $attributeName = strtolower($attribute->name);
+ $attributeValue = $attribute->value;
+
+ if ($attributeName == 'color') {
+ if (preg_match('/rgb\s*\(/', $attributeValue)) {
+ $this->$attributeName = $this->rgbToColour($attributeValue);
+ } elseif (str_starts_with(trim($attributeValue), '#')) {
+ $this->$attributeName = ltrim($attributeValue, '#');
+ } else {
+ $this->$attributeName = static::colourNameLookup($attributeValue);
+ }
+ } else {
+ $this->$attributeName = $attributeValue;
+ }
+ }
+ }
+ }
+
+ protected function endFontTag(): void
+ {
+ $this->face = $this->size = $this->color = null;
+ }
+
+ protected function startBoldTag(): void
+ {
+ $this->bold = true;
+ }
+
+ protected function endBoldTag(): void
+ {
+ $this->bold = false;
+ }
+
+ protected function startItalicTag(): void
+ {
+ $this->italic = true;
+ }
+
+ protected function endItalicTag(): void
+ {
+ $this->italic = false;
+ }
+
+ protected function startUnderlineTag(): void
+ {
+ $this->underline = true;
+ }
+
+ protected function endUnderlineTag(): void
+ {
+ $this->underline = false;
+ }
+
+ protected function startSubscriptTag(): void
+ {
+ $this->subscript = true;
+ }
+
+ protected function endSubscriptTag(): void
+ {
+ $this->subscript = false;
+ }
+
+ protected function startSuperscriptTag(): void
+ {
+ $this->superscript = true;
+ }
+
+ protected function endSuperscriptTag(): void
+ {
+ $this->superscript = false;
+ }
+
+ protected function startStrikethruTag(): void
+ {
+ $this->strikethrough = true;
+ }
+
+ protected function endStrikethruTag(): void
+ {
+ $this->strikethrough = false;
+ }
+
+ public function breakTag(): void
+ {
+ $this->stringData .= "\n";
+ }
+
+ private function parseTextNode(DOMText $textNode): void
+ {
+ $domText = (string) preg_replace(
+ '/\s+/u',
+ ' ',
+ str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '')
+ );
+ $this->stringData .= $domText;
+ $this->buildTextRun();
+ }
+
+ public function addStartTagCallback(string $tag, callable $callback): void
+ {
+ $this->startTagCallbacks[$tag] = $callback;
+ }
+
+ public function addEndTagCallback(string $tag, callable $callback): void
+ {
+ $this->endTagCallbacks[$tag] = $callback;
+ }
+
+ /** @param callable[] $callbacks */
+ private function handleCallback(DOMElement $element, string $callbackTag, array $callbacks): void
+ {
+ if (isset($callbacks[$callbackTag])) {
+ $elementHandler = $callbacks[$callbackTag];
+ if (is_callable($elementHandler)) {
+ call_user_func($elementHandler, $element, $this);
+ }
+ }
+ }
+
+ private function parseElementNode(DOMElement $element): void
+ {
+ $callbackTag = strtolower($element->nodeName);
+ $this->stack[] = $callbackTag;
+
+ $this->handleCallback($element, $callbackTag, $this->startTagCallbacks);
+
+ $this->parseElements($element);
+ array_pop($this->stack);
+
+ $this->handleCallback($element, $callbackTag, $this->endTagCallbacks);
+ }
+
+ private function parseElements(DOMNode $element): void
+ {
+ foreach ($element->childNodes as $child) {
+ if ($child instanceof DOMText) {
+ $this->parseTextNode($child);
+ } elseif ($child instanceof DOMElement) {
+ $this->parseElementNode($child);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
new file mode 100644
index 0000000..2f7d95b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
@@ -0,0 +1,308 @@
+getScriptFilename() === 'index';
+ }
+
+ /**
+ * Return the page title.
+ */
+ public function getPageTitle(): string
+ {
+ return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
+ }
+
+ /**
+ * Return the page heading.
+ */
+ public function getPageHeading(): string
+ {
+ return $this->isIndex() ? '' : '' . str_replace('_', ' ', $this->getScriptFilename()) . ' ';
+ }
+
+ /**
+ * Returns an array of all known samples.
+ *
+ * @return string[][] [$name => $path]
+ */
+ public function getSamples(): array
+ {
+ // Populate samples
+ $baseDir = realpath(__DIR__ . '/../../../samples');
+ if ($baseDir === false) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('realpath returned false');
+ // @codeCoverageIgnoreEnd
+ }
+ $directory = new RecursiveDirectoryIterator($baseDir);
+ $iterator = new RecursiveIteratorIterator($directory);
+ $regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);
+
+ $files = [];
+ foreach ($regex as $file) {
+ $file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
+ if (is_array($file)) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('str_replace returned array');
+ // @codeCoverageIgnoreEnd
+ }
+ $info = pathinfo($file);
+ $category = str_replace('_', ' ', $info['dirname'] ?? '');
+ $name = str_replace('_', ' ', (string) preg_replace('/(|\.php)/', '', $info['filename']));
+ if (!in_array($category, ['.', 'boostrap', 'templates'])) {
+ if (!isset($files[$category])) {
+ $files[$category] = [];
+ }
+ $files[$category][$name] = $file;
+ }
+ }
+
+ // Sort everything
+ ksort($files);
+ foreach ($files as &$f) {
+ asort($f);
+ }
+
+ return $files;
+ }
+
+ /**
+ * Write documents.
+ *
+ * @param string[] $writers
+ */
+ public function write(Spreadsheet $spreadsheet, string $filename, array $writers = ['Xlsx', 'Xls'], bool $withCharts = false, ?callable $writerCallback = null, bool $resetActiveSheet = true): void
+ {
+ // Set active sheet index to the first sheet, so Excel opens this as the first sheet
+ if ($resetActiveSheet) {
+ $spreadsheet->setActiveSheetIndex(0);
+ }
+
+ // Write documents
+ foreach ($writers as $writerType) {
+ $path = $this->getFilename($filename, mb_strtolower($writerType));
+ $writer = IOFactory::createWriter($spreadsheet, $writerType);
+ $writer->setIncludeCharts($withCharts);
+ if ($writerCallback !== null) {
+ $writerCallback($writer);
+ }
+ $callStartTime = microtime(true);
+ $writer->save($path);
+ $this->logWrite($writer, $path, $callStartTime);
+ if ($this->isCli() === false) {
+ // @codeCoverageIgnoreStart
+ echo 'Download ' . basename($path) . ' ';
+ // @codeCoverageIgnoreEnd
+ }
+ }
+
+ $this->logEndingNotes();
+ }
+
+ protected function isDirOrMkdir(string $folder): bool
+ {
+ return \is_dir($folder) || \mkdir($folder);
+ }
+
+ /**
+ * Returns the temporary directory and make sure it exists.
+ */
+ public function getTemporaryFolder(): string
+ {
+ $tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
+ if (!$this->isDirOrMkdir($tempFolder)) {
+ throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder));
+ }
+
+ return $tempFolder;
+ }
+
+ /**
+ * Returns the filename that should be used for sample output.
+ *
+ * @param string $filename
+ */
+ public function getFilename($filename, string $extension = 'xlsx'): string
+ {
+ $originalExtension = pathinfo($filename, PATHINFO_EXTENSION);
+
+ return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename));
+ }
+
+ /**
+ * Return a random temporary file name.
+ */
+ public function getTemporaryFilename(string $extension = 'xlsx'): string
+ {
+ $temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
+ if ($temporaryFilename === false) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('tempnam returned false');
+ // @codeCoverageIgnoreEnd
+ }
+ unlink($temporaryFilename);
+
+ return $temporaryFilename . '.' . $extension;
+ }
+
+ public function log(string $message): void
+ {
+ $eol = $this->isCli() ? PHP_EOL : ' ';
+ echo($this->isCli() ? date('H:i:s ') : '') . $message . $eol;
+ }
+
+ /**
+ * Render chart as part of running chart samples in browser.
+ * Charts are not rendered in unit tests, which are command line.
+ *
+ * @codeCoverageIgnore
+ */
+ public function renderChart(Chart $chart, string $fileName, ?Spreadsheet $spreadsheet = null): void
+ {
+ if ($this->isCli() === true) {
+ return;
+ }
+ Settings::setChartRenderer(MtJpGraphRenderer::class);
+
+ $fileName = $this->getFilename($fileName, 'png');
+ $title = $chart->getTitle();
+ $caption = null;
+ if ($title !== null) {
+ $calculatedTitle = $title->getCalculatedTitle($spreadsheet);
+ if ($calculatedTitle !== null) {
+ $caption = $title->getCaption();
+ $title->setCaption($calculatedTitle);
+ }
+ }
+
+ try {
+ $chart->render($fileName);
+ $this->log('Rendered image: ' . $fileName);
+ $imageData = @file_get_contents($fileName);
+ if ($imageData !== false) {
+ echo '';
+ } else {
+ $this->log('Unable to open chart' . PHP_EOL);
+ }
+ } catch (Throwable $e) {
+ $this->log('Error rendering chart: ' . $e->getMessage() . PHP_EOL);
+ }
+ if (isset($title, $caption)) {
+ $title->setCaption($caption);
+ }
+ Settings::unsetChartRenderer();
+ }
+
+ public function titles(string $category, string $functionName, ?string $description = null): void
+ {
+ $this->log(sprintf('%s Functions:', $category));
+ $description === null
+ ? $this->log(sprintf('Function: %s()', rtrim($functionName, '()')))
+ : $this->log(sprintf('Function: %s() - %s.', rtrim($functionName, '()'), rtrim($description, '.')));
+ }
+
+ public function displayGrid(array $matrix): void
+ {
+ $renderer = new TextGrid($matrix, $this->isCli());
+ echo $renderer->render();
+ }
+
+ public function logCalculationResult(
+ Worksheet $worksheet,
+ string $functionName,
+ string $formulaCell,
+ ?string $descriptionCell = null
+ ): void {
+ if ($descriptionCell !== null) {
+ $this->log($worksheet->getCell($descriptionCell)->getValue());
+ }
+ $this->log($worksheet->getCell($formulaCell)->getValue());
+ $this->log(sprintf('%s() Result is ', $functionName) . $worksheet->getCell($formulaCell)->getCalculatedValue());
+ }
+
+ /**
+ * Log ending notes.
+ */
+ public function logEndingNotes(): void
+ {
+ // Do not show execution time for index
+ $this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB');
+ }
+
+ /**
+ * Log a line about the write operation.
+ *
+ * @param string $path
+ * @param float $callStartTime
+ */
+ public function logWrite(IWriter $writer, $path, $callStartTime): void
+ {
+ $callEndTime = microtime(true);
+ $callTime = $callEndTime - $callStartTime;
+ $reflection = new ReflectionClass($writer);
+ $format = $reflection->getShortName();
+
+ $codePath = $this->isCli() ? $path : "$path";
+ $message = "Write {$format} format to {$codePath} in " . sprintf('%.4f', $callTime) . ' seconds';
+
+ $this->log($message);
+ }
+
+ /**
+ * Log a line about the read operation.
+ *
+ * @param string $format
+ * @param string $path
+ * @param float $callStartTime
+ */
+ public function logRead($format, $path, $callStartTime): void
+ {
+ $callEndTime = microtime(true);
+ $callTime = $callEndTime - $callStartTime;
+ $message = "Read {$format} format from {$path} in " . sprintf('%.4f', $callTime) . ' seconds';
+
+ $this->log($message);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
new file mode 100644
index 0000000..575ed89
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
@@ -0,0 +1,45 @@
+\d*\.?\d+)(?Ppt|px|em)?$/i';
+
+ protected bool $valid;
+
+ protected string $size = '';
+
+ protected string $unit = '';
+
+ public function __construct(string $size)
+ {
+ $this->valid = (bool) preg_match(self::REGEXP_SIZE_VALIDATION, $size, $matches);
+ if ($this->valid) {
+ $this->size = $matches['size'];
+ $this->unit = $matches['unit'] ?? 'pt';
+ }
+ }
+
+ public function valid(): bool
+ {
+ return $this->valid;
+ }
+
+ public function size(): string
+ {
+ return $this->size;
+ }
+
+ public function unit(): string
+ {
+ return $this->unit;
+ }
+
+ public function __toString(): string
+ {
+ return $this->size . $this->unit;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php
new file mode 100644
index 0000000..8088560
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php
@@ -0,0 +1,127 @@
+rows = array_keys($matrix);
+ $this->columns = array_keys($matrix[$this->rows[0]]);
+
+ $matrix = array_values($matrix);
+ array_walk(
+ $matrix,
+ function (&$row): void {
+ $row = array_values($row);
+ }
+ );
+
+ $this->matrix = $matrix;
+ $this->isCli = $isCli;
+ }
+
+ public function render(): string
+ {
+ $this->gridDisplay = $this->isCli ? '' : '';
+
+ $maxRow = max($this->rows);
+ $maxRowLength = mb_strlen((string) $maxRow) + 1;
+ $columnWidths = $this->getColumnWidths();
+
+ $this->renderColumnHeader($maxRowLength, $columnWidths);
+ $this->renderRows($maxRowLength, $columnWidths);
+ $this->renderFooter($maxRowLength, $columnWidths);
+
+ $this->gridDisplay .= $this->isCli ? '' : ' ';
+
+ return $this->gridDisplay;
+ }
+
+ private function renderRows(int $maxRowLength, array $columnWidths): void
+ {
+ foreach ($this->matrix as $row => $rowData) {
+ $this->gridDisplay .= '|' . str_pad((string) $this->rows[$row], $maxRowLength, ' ', STR_PAD_LEFT) . ' ';
+ $this->renderCells($rowData, $columnWidths);
+ $this->gridDisplay .= '|' . PHP_EOL;
+ }
+ }
+
+ private function renderCells(array $rowData, array $columnWidths): void
+ {
+ foreach ($rowData as $column => $cell) {
+ $displayCell = ($this->isCli) ? (string) $cell : htmlentities((string) $cell);
+ $this->gridDisplay .= '| ';
+ $this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($cell ?? '') + 1);
+ }
+ }
+
+ private function renderColumnHeader(int $maxRowLength, array $columnWidths): void
+ {
+ $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
+ foreach ($this->columns as $column => $reference) {
+ $this->gridDisplay .= '+-' . str_repeat('-', $columnWidths[$column] + 1);
+ }
+ $this->gridDisplay .= '+' . PHP_EOL;
+
+ $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
+ foreach ($this->columns as $column => $reference) {
+ $this->gridDisplay .= '| ' . str_pad((string) $reference, $columnWidths[$column] + 1, ' ');
+ }
+ $this->gridDisplay .= '|' . PHP_EOL;
+
+ $this->renderFooter($maxRowLength, $columnWidths);
+ }
+
+ private function renderFooter(int $maxRowLength, array $columnWidths): void
+ {
+ $this->gridDisplay .= '+' . str_repeat('-', $maxRowLength + 1);
+ foreach ($this->columns as $column => $reference) {
+ $this->gridDisplay .= '+-';
+ $this->gridDisplay .= str_pad((string) '', $columnWidths[$column] + 1, '-');
+ }
+ $this->gridDisplay .= '+' . PHP_EOL;
+ }
+
+ private function getColumnWidths(): array
+ {
+ $columnCount = count($this->matrix, COUNT_RECURSIVE) / count($this->matrix);
+ $columnWidths = [];
+ for ($column = 0; $column < $columnCount; ++$column) {
+ $columnWidths[] = $this->getColumnWidth(array_column($this->matrix, $column));
+ }
+
+ return $columnWidths;
+ }
+
+ private function getColumnWidth(array $columnData): int
+ {
+ $columnWidth = 0;
+ $columnData = array_values($columnData);
+
+ foreach ($columnData as $columnValue) {
+ if (is_string($columnValue)) {
+ $columnWidth = max($columnWidth, mb_strlen($columnValue));
+ } elseif (is_bool($columnValue)) {
+ $columnWidth = max($columnWidth, mb_strlen($columnValue ? 'TRUE' : 'FALSE'));
+ }
+
+ $columnWidth = max($columnWidth, mb_strlen((string) $columnWidth));
+ }
+
+ return $columnWidth;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
new file mode 100644
index 0000000..c215847
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
@@ -0,0 +1,13 @@
+ Reader\Xlsx::class,
+ self::READER_XLS => Reader\Xls::class,
+ self::READER_XML => Reader\Xml::class,
+ self::READER_ODS => Reader\Ods::class,
+ self::READER_SLK => Reader\Slk::class,
+ self::READER_GNUMERIC => Reader\Gnumeric::class,
+ self::READER_HTML => Reader\Html::class,
+ self::READER_CSV => Reader\Csv::class,
+ ];
+
+ /** @var string[] */
+ private static $writers = [
+ self::WRITER_XLS => Writer\Xls::class,
+ self::WRITER_XLSX => Writer\Xlsx::class,
+ self::WRITER_ODS => Writer\Ods::class,
+ self::WRITER_CSV => Writer\Csv::class,
+ self::WRITER_HTML => Writer\Html::class,
+ 'Tcpdf' => Writer\Pdf\Tcpdf::class,
+ 'Dompdf' => Writer\Pdf\Dompdf::class,
+ 'Mpdf' => Writer\Pdf\Mpdf::class,
+ ];
+
+ /**
+ * Create Writer\IWriter.
+ */
+ public static function createWriter(Spreadsheet $spreadsheet, string $writerType): IWriter
+ {
+ if (!isset(self::$writers[$writerType])) {
+ throw new Writer\Exception("No writer found for type $writerType");
+ }
+
+ // Instantiate writer
+ /** @var IWriter */
+ $className = self::$writers[$writerType];
+
+ return new $className($spreadsheet);
+ }
+
+ /**
+ * Create IReader.
+ */
+ public static function createReader(string $readerType): IReader
+ {
+ if (!isset(self::$readers[$readerType])) {
+ throw new Reader\Exception("No reader found for type $readerType");
+ }
+
+ // Instantiate reader
+ /** @var IReader */
+ $className = self::$readers[$readerType];
+
+ return new $className();
+ }
+
+ /**
+ * Loads Spreadsheet from file using automatic Reader\IReader resolution.
+ *
+ * @param string $filename The name of the spreadsheet file
+ * @param int $flags the optional second parameter flags may be used to identify specific elements
+ * that should be loaded, but which won't be loaded by default, using these values:
+ * IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file.
+ * IReader::READ_DATA_ONLY - Read cell values only, not formatting or merge structure.
+ * IReader::IGNORE_EMPTY_CELLS - Don't load empty cells into the model.
+ * @param string[] $readers An array of Readers to use to identify the file type. By default, load() will try
+ * all possible Readers until it finds a match; but this allows you to pass in a
+ * list of Readers so it will only try the subset that you specify here.
+ * Values in this list can be any of the constant values defined in the set
+ * IOFactory::READER_*.
+ */
+ public static function load(string $filename, int $flags = 0, ?array $readers = null): Spreadsheet
+ {
+ $reader = self::createReaderForFile($filename, $readers);
+
+ return $reader->load($filename, $flags);
+ }
+
+ /**
+ * Identify file type using automatic IReader resolution.
+ */
+ public static function identify(string $filename, ?array $readers = null): string
+ {
+ $reader = self::createReaderForFile($filename, $readers);
+ $className = $reader::class;
+ $classType = explode('\\', $className);
+ unset($reader);
+
+ return array_pop($classType);
+ }
+
+ /**
+ * Create Reader\IReader for file using automatic IReader resolution.
+ *
+ * @param string[] $readers An array of Readers to use to identify the file type. By default, load() will try
+ * all possible Readers until it finds a match; but this allows you to pass in a
+ * list of Readers so it will only try the subset that you specify here.
+ * Values in this list can be any of the constant values defined in the set
+ * IOFactory::READER_*.
+ */
+ public static function createReaderForFile(string $filename, ?array $readers = null): IReader
+ {
+ File::assertFile($filename);
+
+ $testReaders = self::$readers;
+ if ($readers !== null) {
+ $readers = array_map('strtoupper', $readers);
+ $testReaders = array_filter(
+ self::$readers,
+ fn (string $readerType): bool => in_array(strtoupper($readerType), $readers, true),
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ // First, lucky guess by inspecting file extension
+ $guessedReader = self::getReaderTypeFromExtension($filename);
+ if (($guessedReader !== null) && array_key_exists($guessedReader, $testReaders)) {
+ $reader = self::createReader($guessedReader);
+
+ // Let's see if we are lucky
+ if ($reader->canRead($filename)) {
+ return $reader;
+ }
+ }
+
+ // If we reach here then "lucky guess" didn't give any result
+ // Try walking through all the options in self::$readers (or the selected subset)
+ foreach ($testReaders as $readerType => $class) {
+ // Ignore our original guess, we know that won't work
+ if ($readerType !== $guessedReader) {
+ $reader = self::createReader($readerType);
+ if ($reader->canRead($filename)) {
+ return $reader;
+ }
+ }
+ }
+
+ throw new Reader\Exception('Unable to identify a reader for this file');
+ }
+
+ /**
+ * Guess a reader type from the file extension, if any.
+ */
+ private static function getReaderTypeFromExtension(string $filename): ?string
+ {
+ $pathinfo = pathinfo($filename);
+ if (!isset($pathinfo['extension'])) {
+ return null;
+ }
+
+ return match (strtolower($pathinfo['extension'])) {
+ // Excel (OfficeOpenXML) Spreadsheet
+ 'xlsx',
+ // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
+ 'xlsm',
+ // Excel (OfficeOpenXML) Template
+ 'xltx',
+ // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
+ 'xltm' => 'Xlsx',
+ // Excel (BIFF) Spreadsheet
+ 'xls',
+ // Excel (BIFF) Template
+ 'xlt' => 'Xls',
+ // Open/Libre Offic Calc
+ 'ods',
+ // Open/Libre Offic Calc Template
+ 'ots' => 'Ods',
+ 'slk' => 'Slk',
+ // Excel 2003 SpreadSheetML
+ 'xml' => 'Xml',
+ 'gnumeric' => 'Gnumeric',
+ 'htm', 'html' => 'Html',
+ // Do nothing
+ // We must not try to use CSV reader since it loads
+ // all files including Excel files etc.
+ 'csv' => null,
+ default => null,
+ };
+ }
+
+ /**
+ * Register a writer with its type and class name.
+ */
+ public static function registerWriter(string $writerType, string $writerClass): void
+ {
+ if (!is_a($writerClass, IWriter::class, true)) {
+ throw new Writer\Exception('Registered writers must implement ' . IWriter::class);
+ }
+
+ self::$writers[$writerType] = $writerClass;
+ }
+
+ /**
+ * Register a reader with its type and class name.
+ */
+ public static function registerReader(string $readerType, string $readerClass): void
+ {
+ if (!is_a($readerClass, IReader::class, true)) {
+ throw new Reader\Exception('Registered readers must implement ' . IReader::class);
+ }
+
+ self::$readers[$readerType] = $readerClass;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php
new file mode 100644
index 0000000..500151f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php
@@ -0,0 +1,45 @@
+value;
+ }
+
+ /**
+ * Set the formula value.
+ */
+ public function setFormula(string $formula): self
+ {
+ if (!empty($formula)) {
+ $this->value = $formula;
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
new file mode 100644
index 0000000..819ddea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
@@ -0,0 +1,55 @@
+value;
+ }
+
+ /**
+ * Set the range value.
+ */
+ public function setRange(string $range): self
+ {
+ if (!empty($range)) {
+ $this->value = $range;
+ }
+
+ return $this;
+ }
+
+ public function getCellsInRange(): array
+ {
+ $range = $this->value;
+ if (str_starts_with($range, '=')) {
+ $range = substr($range, 1);
+ }
+
+ return Coordinate::extractAllCellReferencesInRange($range);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
new file mode 100644
index 0000000..b8e6a81
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
@@ -0,0 +1,231 @@
+readFilter = new DefaultReadFilter();
+ }
+
+ public function getReadDataOnly()
+ {
+ return $this->readDataOnly;
+ }
+
+ public function setReadDataOnly($readCellValuesOnly)
+ {
+ $this->readDataOnly = (bool) $readCellValuesOnly;
+
+ return $this;
+ }
+
+ public function getReadEmptyCells()
+ {
+ return $this->readEmptyCells;
+ }
+
+ public function setReadEmptyCells($readEmptyCells)
+ {
+ $this->readEmptyCells = (bool) $readEmptyCells;
+
+ return $this;
+ }
+
+ public function getIncludeCharts()
+ {
+ return $this->includeCharts;
+ }
+
+ public function setIncludeCharts($includeCharts)
+ {
+ $this->includeCharts = (bool) $includeCharts;
+
+ return $this;
+ }
+
+ public function getLoadSheetsOnly()
+ {
+ return $this->loadSheetsOnly;
+ }
+
+ public function setLoadSheetsOnly($sheetList)
+ {
+ if ($sheetList === null) {
+ return $this->setLoadAllSheets();
+ }
+
+ $this->loadSheetsOnly = is_array($sheetList) ? $sheetList : [$sheetList];
+
+ return $this;
+ }
+
+ public function setLoadAllSheets()
+ {
+ $this->loadSheetsOnly = null;
+
+ return $this;
+ }
+
+ public function getReadFilter()
+ {
+ return $this->readFilter;
+ }
+
+ public function setReadFilter(IReadFilter $readFilter)
+ {
+ $this->readFilter = $readFilter;
+
+ return $this;
+ }
+
+ public function getSecurityScanner(): ?XmlScanner
+ {
+ return $this->securityScanner;
+ }
+
+ public function getSecurityScannerOrThrow(): XmlScanner
+ {
+ if ($this->securityScanner === null) {
+ throw new ReaderException('Security scanner is unexpectedly null');
+ }
+
+ return $this->securityScanner;
+ }
+
+ protected function processFlags(int $flags): void
+ {
+ if (((bool) ($flags & self::LOAD_WITH_CHARTS)) === true) {
+ $this->setIncludeCharts(true);
+ }
+ if (((bool) ($flags & self::READ_DATA_ONLY)) === true) {
+ $this->setReadDataOnly(true);
+ }
+ if (((bool) ($flags & self::SKIP_EMPTY_CELLS) || (bool) ($flags & self::IGNORE_EMPTY_CELLS)) === true) {
+ $this->setReadEmptyCells(false);
+ }
+ }
+
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ throw new PhpSpreadsheetException('Reader classes must implement their own loadSpreadsheetFromFile() method');
+ }
+
+ /**
+ * Loads Spreadsheet from file.
+ *
+ * @param int $flags the optional second parameter flags may be used to identify specific elements
+ * that should be loaded, but which won't be loaded by default, using these values:
+ * IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file
+ */
+ public function load(string $filename, int $flags = 0): Spreadsheet
+ {
+ $this->processFlags($flags);
+
+ try {
+ return $this->loadSpreadsheetFromFile($filename);
+ } catch (ReaderException $e) {
+ throw $e;
+ }
+ }
+
+ /**
+ * Open file for reading.
+ */
+ protected function openFile(string $filename): void
+ {
+ $fileHandle = false;
+ if ($filename) {
+ File::assertFile($filename);
+
+ // Open file
+ $fileHandle = fopen($filename, 'rb');
+ }
+ if ($fileHandle === false) {
+ throw new ReaderException('Could not open file ' . $filename . ' for reading.');
+ }
+
+ $this->fileHandle = $fileHandle;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ throw new PhpSpreadsheetException('Reader classes must implement their own listWorksheetInfo() method');
+ }
+
+ /**
+ * Returns names of the worksheets from a file,
+ * possibly without parsing the whole file to a Spreadsheet object.
+ * Readers will often have a more efficient method with which
+ * they can override this method.
+ */
+ public function listWorksheetNames(string $filename): array
+ {
+ $returnArray = [];
+ $info = $this->listWorksheetInfo($filename);
+ foreach ($info as $infoArray) {
+ if (isset($infoArray['worksheetName'])) {
+ $returnArray[] = $infoArray['worksheetName'];
+ }
+ }
+
+ return $returnArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
new file mode 100644
index 0000000..a93bbcf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
@@ -0,0 +1,667 @@
+inputEncoding = $encoding;
+
+ return $this;
+ }
+
+ public function getInputEncoding(): string
+ {
+ return $this->inputEncoding;
+ }
+
+ public function setFallbackEncoding(string $fallbackEncoding): self
+ {
+ $this->fallbackEncoding = $fallbackEncoding;
+
+ return $this;
+ }
+
+ public function getFallbackEncoding(): string
+ {
+ return $this->fallbackEncoding;
+ }
+
+ /**
+ * Move filepointer past any BOM marker.
+ */
+ protected function skipBOM(): void
+ {
+ rewind($this->fileHandle);
+
+ if (fgets($this->fileHandle, self::UTF8_BOM_LEN + 1) !== self::UTF8_BOM) {
+ rewind($this->fileHandle);
+ }
+ }
+
+ /**
+ * Identify any separator that is explicitly set in the file.
+ */
+ protected function checkSeparator(): void
+ {
+ $line = fgets($this->fileHandle);
+ if ($line === false) {
+ return;
+ }
+
+ if ((strlen(trim($line, "\r\n")) == 5) && (stripos($line, 'sep=') === 0)) {
+ $this->delimiter = substr($line, 4, 1);
+
+ return;
+ }
+
+ $this->skipBOM();
+ }
+
+ /**
+ * Infer the separator if it isn't explicitly set in the file or specified by the user.
+ */
+ protected function inferSeparator(): void
+ {
+ if ($this->delimiter !== null) {
+ return;
+ }
+
+ $inferenceEngine = new Delimiter($this->fileHandle, $this->escapeCharacter, $this->enclosure);
+
+ // If number of lines is 0, nothing to infer : fall back to the default
+ if ($inferenceEngine->linesCounted() === 0) {
+ $this->delimiter = $inferenceEngine->getDefaultDelimiter();
+ $this->skipBOM();
+
+ return;
+ }
+
+ $this->delimiter = $inferenceEngine->infer();
+
+ // If no delimiter could be detected, fall back to the default
+ if ($this->delimiter === null) {
+ $this->delimiter = $inferenceEngine->getDefaultDelimiter();
+ }
+
+ $this->skipBOM();
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ // Open file
+ $this->openFileOrMemory($filename);
+ $fileHandle = $this->fileHandle;
+
+ // Skip BOM, if any
+ $this->skipBOM();
+ $this->checkSeparator();
+ $this->inferSeparator();
+
+ $worksheetInfo = [];
+ $worksheetInfo[0]['worksheetName'] = 'Worksheet';
+ $worksheetInfo[0]['lastColumnLetter'] = 'A';
+ $worksheetInfo[0]['lastColumnIndex'] = 0;
+ $worksheetInfo[0]['totalRows'] = 0;
+ $worksheetInfo[0]['totalColumns'] = 0;
+ $delimiter = $this->delimiter ?? '';
+
+ // Loop through each line of the file in turn
+ $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
+ while (is_array($rowData)) {
+ ++$worksheetInfo[0]['totalRows'];
+ $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
+ $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
+ }
+
+ $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
+ $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
+
+ // Close file
+ fclose($fileHandle);
+
+ return $worksheetInfo;
+ }
+
+ /**
+ * Loads Spreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+
+ // Load into this instance
+ return $this->loadIntoExisting($filename, $spreadsheet);
+ }
+
+ /**
+ * Loads Spreadsheet from string.
+ */
+ public function loadSpreadsheetFromString(string $contents): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+
+ // Load into this instance
+ return $this->loadStringOrFile('data://text/plain,' . urlencode($contents), $spreadsheet, true);
+ }
+
+ private function openFileOrMemory(string $filename): void
+ {
+ // Open file
+ $fhandle = $this->canRead($filename);
+ if (!$fhandle) {
+ throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+ }
+ if ($this->inputEncoding === self::GUESS_ENCODING) {
+ $this->inputEncoding = self::guessEncoding($filename, $this->fallbackEncoding);
+ }
+ $this->openFile($filename);
+ if ($this->inputEncoding !== 'UTF-8') {
+ fclose($this->fileHandle);
+ $entireFile = file_get_contents($filename);
+ $fileHandle = fopen('php://memory', 'r+b');
+ if ($fileHandle !== false && $entireFile !== false) {
+ $this->fileHandle = $fileHandle;
+ $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
+ fwrite($this->fileHandle, $data);
+ $this->skipBOM();
+ }
+ }
+ }
+
+ public function setTestAutoDetect(bool $value): self
+ {
+ $this->testAutodetect = $value;
+
+ return $this;
+ }
+
+ private function setAutoDetect(?string $value): ?string
+ {
+ $retVal = null;
+ if ($value !== null && $this->testAutodetect) {
+ $retVal2 = @ini_set('auto_detect_line_endings', $value);
+ if (is_string($retVal2)) {
+ $retVal = $retVal2;
+ }
+ }
+
+ return $retVal;
+ }
+
+ public function castFormattedNumberToNumeric(
+ bool $castFormattedNumberToNumeric,
+ bool $preserveNumericFormatting = false
+ ): void {
+ $this->castFormattedNumberToNumeric = $castFormattedNumberToNumeric;
+ $this->preserveNumericFormatting = $preserveNumericFormatting;
+ }
+
+ /**
+ * Open data uri for reading.
+ */
+ private function openDataUri(string $filename): void
+ {
+ $fileHandle = fopen($filename, 'rb');
+ if ($fileHandle === false) {
+ // @codeCoverageIgnoreStart
+ throw new ReaderException('Could not open file ' . $filename . ' for reading.');
+ // @codeCoverageIgnoreEnd
+ }
+
+ $this->fileHandle = $fileHandle;
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+ */
+ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
+ {
+ return $this->loadStringOrFile($filename, $spreadsheet, false);
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+ */
+ private function loadStringOrFile(string $filename, Spreadsheet $spreadsheet, bool $dataUri): Spreadsheet
+ {
+ // Deprecated in Php8.1
+ $iniset = $this->setAutoDetect('1');
+
+ // Open file
+ if ($dataUri) {
+ $this->openDataUri($filename);
+ } else {
+ $this->openFileOrMemory($filename);
+ }
+ $fileHandle = $this->fileHandle;
+
+ // Skip BOM, if any
+ $this->skipBOM();
+ $this->checkSeparator();
+ $this->inferSeparator();
+
+ // Create new PhpSpreadsheet object
+ while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
+ $spreadsheet->createSheet();
+ }
+ $sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+ if ($this->sheetNameIsFileName) {
+ $sheet->setTitle(substr(basename($filename, '.csv'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
+ }
+
+ // Set our starting row based on whether we're in contiguous mode or not
+ $currentRow = 1;
+ $outRow = 0;
+
+ // Loop through each line of the file in turn
+ $delimiter = $this->delimiter ?? '';
+ $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
+ $valueBinder = Cell::getValueBinder();
+ $preserveBooleanString = method_exists($valueBinder, 'getBooleanConversion') && $valueBinder->getBooleanConversion();
+ $this->getTrue = Calculation::getTRUE();
+ $this->getFalse = Calculation::getFALSE();
+ $this->thousandsSeparator = StringHelper::getThousandsSeparator();
+ $this->decimalSeparator = StringHelper::getDecimalSeparator();
+ while (is_array($rowData)) {
+ $noOutputYet = true;
+ $columnLetter = 'A';
+ foreach ($rowData as $rowDatum) {
+ if ($preserveBooleanString) {
+ $rowDatum = $rowDatum ?? '';
+ } else {
+ $this->convertBoolean($rowDatum);
+ }
+ $numberFormatMask = $this->castFormattedNumberToNumeric ? $this->convertFormattedNumber($rowDatum) : '';
+ if (($rowDatum !== '' || $this->preserveNullString) && $this->readFilter->readCell($columnLetter, $currentRow)) {
+ if ($this->contiguous) {
+ if ($noOutputYet) {
+ $noOutputYet = false;
+ ++$outRow;
+ }
+ } else {
+ $outRow = $currentRow;
+ }
+ // Set basic styling for the value (Note that this could be overloaded by styling in a value binder)
+ if ($numberFormatMask !== '') {
+ $sheet->getStyle($columnLetter . $outRow)
+ ->getNumberFormat()
+ ->setFormatCode($numberFormatMask);
+ }
+ // Set cell value
+ $sheet->getCell($columnLetter . $outRow)->setValue($rowDatum);
+ }
+ ++$columnLetter;
+ }
+ $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
+ ++$currentRow;
+ }
+
+ // Close file
+ fclose($fileHandle);
+
+ $this->setAutoDetect($iniset);
+
+ // Return
+ return $spreadsheet;
+ }
+
+ /**
+ * Convert string true/false to boolean, and null to null-string.
+ */
+ private function convertBoolean(mixed &$rowDatum): void
+ {
+ if (is_string($rowDatum)) {
+ if (strcasecmp($this->getTrue, $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) {
+ $rowDatum = true;
+ } elseif (strcasecmp($this->getFalse, $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) {
+ $rowDatum = false;
+ }
+ } else {
+ $rowDatum = $rowDatum ?? '';
+ }
+ }
+
+ /**
+ * Convert numeric strings to int or float values.
+ */
+ private function convertFormattedNumber(mixed &$rowDatum): string
+ {
+ $numberFormatMask = '';
+ if ($this->castFormattedNumberToNumeric === true && is_string($rowDatum)) {
+ $numeric = str_replace(
+ [$this->thousandsSeparator, $this->decimalSeparator],
+ ['', '.'],
+ $rowDatum
+ );
+
+ if (is_numeric($numeric)) {
+ $decimalPos = strpos($rowDatum, $this->decimalSeparator);
+ if ($this->preserveNumericFormatting === true) {
+ $numberFormatMask = (str_contains($rowDatum, $this->thousandsSeparator))
+ ? '#,##0' : '0';
+ if ($decimalPos !== false) {
+ $decimals = strlen($rowDatum) - $decimalPos - 1;
+ $numberFormatMask .= '.' . str_repeat('0', min($decimals, 6));
+ }
+ }
+
+ $rowDatum = ($decimalPos !== false) ? (float) $numeric : (int) $numeric;
+ }
+ }
+
+ return $numberFormatMask;
+ }
+
+ public function getDelimiter(): ?string
+ {
+ return $this->delimiter;
+ }
+
+ public function setDelimiter(?string $delimiter): self
+ {
+ $this->delimiter = $delimiter;
+
+ return $this;
+ }
+
+ public function getEnclosure(): string
+ {
+ return $this->enclosure;
+ }
+
+ public function setEnclosure(string $enclosure): self
+ {
+ if ($enclosure == '') {
+ $enclosure = '"';
+ }
+ $this->enclosure = $enclosure;
+
+ return $this;
+ }
+
+ public function getSheetIndex(): int
+ {
+ return $this->sheetIndex;
+ }
+
+ public function setSheetIndex(int $indexValue): self
+ {
+ $this->sheetIndex = $indexValue;
+
+ return $this;
+ }
+
+ public function setContiguous(bool $contiguous): self
+ {
+ $this->contiguous = $contiguous;
+
+ return $this;
+ }
+
+ public function getContiguous(): bool
+ {
+ return $this->contiguous;
+ }
+
+ public function setEscapeCharacter(string $escapeCharacter): self
+ {
+ $this->escapeCharacter = $escapeCharacter;
+
+ return $this;
+ }
+
+ public function getEscapeCharacter(): string
+ {
+ return $this->escapeCharacter;
+ }
+
+ /**
+ * Can the current IReader read the file?
+ */
+ public function canRead(string $filename): bool
+ {
+ // Check if file exists
+ try {
+ $this->openFile($filename);
+ } catch (ReaderException) {
+ return false;
+ }
+
+ fclose($this->fileHandle);
+
+ // Trust file extension if any
+ $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+ if (in_array($extension, ['csv', 'tsv'])) {
+ return true;
+ }
+
+ // Attempt to guess mimetype
+ $type = mime_content_type($filename);
+ $supportedTypes = [
+ 'application/csv',
+ 'text/csv',
+ 'text/plain',
+ 'inode/x-empty',
+ ];
+
+ return in_array($type, $supportedTypes, true);
+ }
+
+ private static function guessEncodingTestNoBom(string &$encoding, string &$contents, string $compare, string $setEncoding): void
+ {
+ if ($encoding === '') {
+ $pos = strpos($contents, $compare);
+ if ($pos !== false && $pos % strlen($compare) === 0) {
+ $encoding = $setEncoding;
+ }
+ }
+ }
+
+ private static function guessEncodingNoBom(string $filename): string
+ {
+ $encoding = '';
+ $contents = file_get_contents($filename);
+ self::guessEncodingTestNoBom($encoding, $contents, self::UTF32BE_LF, 'UTF-32BE');
+ self::guessEncodingTestNoBom($encoding, $contents, self::UTF32LE_LF, 'UTF-32LE');
+ self::guessEncodingTestNoBom($encoding, $contents, self::UTF16BE_LF, 'UTF-16BE');
+ self::guessEncodingTestNoBom($encoding, $contents, self::UTF16LE_LF, 'UTF-16LE');
+ if ($encoding === '' && preg_match('//u', $contents) === 1) {
+ $encoding = 'UTF-8';
+ }
+
+ return $encoding;
+ }
+
+ private static function guessEncodingTestBom(string &$encoding, string $first4, string $compare, string $setEncoding): void
+ {
+ if ($encoding === '') {
+ if (str_starts_with($first4, $compare)) {
+ $encoding = $setEncoding;
+ }
+ }
+ }
+
+ private static function guessEncodingBom(string $filename): string
+ {
+ $encoding = '';
+ $first4 = file_get_contents($filename, false, null, 0, 4);
+ if ($first4 !== false) {
+ self::guessEncodingTestBom($encoding, $first4, self::UTF8_BOM, 'UTF-8');
+ self::guessEncodingTestBom($encoding, $first4, self::UTF16BE_BOM, 'UTF-16BE');
+ self::guessEncodingTestBom($encoding, $first4, self::UTF32BE_BOM, 'UTF-32BE');
+ self::guessEncodingTestBom($encoding, $first4, self::UTF32LE_BOM, 'UTF-32LE');
+ self::guessEncodingTestBom($encoding, $first4, self::UTF16LE_BOM, 'UTF-16LE');
+ }
+
+ return $encoding;
+ }
+
+ public static function guessEncoding(string $filename, string $dflt = self::DEFAULT_FALLBACK_ENCODING): string
+ {
+ $encoding = self::guessEncodingBom($filename);
+ if ($encoding === '') {
+ $encoding = self::guessEncodingNoBom($filename);
+ }
+
+ return ($encoding === '') ? $dflt : $encoding;
+ }
+
+ public function setPreserveNullString(bool $value): self
+ {
+ $this->preserveNullString = $value;
+
+ return $this;
+ }
+
+ public function getPreserveNullString(): bool
+ {
+ return $this->preserveNullString;
+ }
+
+ public function setSheetNameIsFileName(bool $sheetNameIsFileName): self
+ {
+ $this->sheetNameIsFileName = $sheetNameIsFileName;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php
new file mode 100644
index 0000000..8721be1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php
@@ -0,0 +1,147 @@
+fileHandle = $fileHandle;
+ $this->escapeCharacter = $escapeCharacter;
+ $this->enclosure = $enclosure;
+
+ $this->countPotentialDelimiters();
+ }
+
+ public function getDefaultDelimiter(): string
+ {
+ return self::POTENTIAL_DELIMETERS[0];
+ }
+
+ public function linesCounted(): int
+ {
+ return $this->numberLines;
+ }
+
+ protected function countPotentialDelimiters(): void
+ {
+ $this->counts = array_fill_keys(self::POTENTIAL_DELIMETERS, []);
+ $delimiterKeys = array_flip(self::POTENTIAL_DELIMETERS);
+
+ // Count how many times each of the potential delimiters appears in each line
+ $this->numberLines = 0;
+ while (($line = $this->getNextLine()) !== false && (++$this->numberLines < 1000)) {
+ $this->countDelimiterValues($line, $delimiterKeys);
+ }
+ }
+
+ protected function countDelimiterValues(string $line, array $delimiterKeys): void
+ {
+ $splitString = str_split($line, 1);
+ if (is_array($splitString)) {
+ $distribution = array_count_values($splitString);
+ $countLine = array_intersect_key($distribution, $delimiterKeys);
+
+ foreach (self::POTENTIAL_DELIMETERS as $delimiter) {
+ $this->counts[$delimiter][] = $countLine[$delimiter] ?? 0;
+ }
+ }
+ }
+
+ public function infer(): ?string
+ {
+ // Calculate the mean square deviations for each delimiter
+ // (ignoring delimiters that haven't been found consistently)
+ $meanSquareDeviations = [];
+ $middleIdx = floor(($this->numberLines - 1) / 2);
+
+ foreach (self::POTENTIAL_DELIMETERS as $delimiter) {
+ $series = $this->counts[$delimiter];
+ sort($series);
+
+ $median = ($this->numberLines % 2)
+ ? $series[$middleIdx]
+ : ($series[$middleIdx] + $series[$middleIdx + 1]) / 2;
+
+ if ($median === 0) {
+ continue;
+ }
+
+ $meanSquareDeviations[$delimiter] = array_reduce(
+ $series,
+ fn ($sum, $value): int|float => $sum + ($value - $median) ** 2
+ ) / count($series);
+ }
+
+ // ... and pick the delimiter with the smallest mean square deviation
+ // (in case of ties, the order in potentialDelimiters is respected)
+ $min = INF;
+ foreach (self::POTENTIAL_DELIMETERS as $delimiter) {
+ if (!isset($meanSquareDeviations[$delimiter])) {
+ continue;
+ }
+
+ if ($meanSquareDeviations[$delimiter] < $min) {
+ $min = $meanSquareDeviations[$delimiter];
+ $this->delimiter = $delimiter;
+ }
+ }
+
+ return $this->delimiter;
+ }
+
+ /**
+ * Get the next full line from the file.
+ *
+ * @return false|string
+ */
+ public function getNextLine()
+ {
+ $line = '';
+ $enclosure = ($this->escapeCharacter === '' ? ''
+ : ('(?escapeCharacter, '/') . ')'))
+ . preg_quote($this->enclosure, '/');
+
+ do {
+ // Get the next line in the file
+ $newLine = fgets($this->fileHandle);
+
+ // Return false if there is no next line
+ if ($newLine === false) {
+ return false;
+ }
+
+ // Add the new line to the line passed in
+ $line = $line . $newLine;
+
+ // Drop everything that is enclosed to avoid counting false positives in enclosures
+ $line = (string) preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
+
+ // See if we have any enclosures left in the line
+ // if we still have an enclosure then we need to read the next line as well
+ } while (preg_match('/(' . $enclosure . ')/', $line) > 0);
+
+ return ($line !== '') ? $line : false;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
new file mode 100644
index 0000000..bfabb07
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
@@ -0,0 +1,18 @@
+ [
+ '10' => DataType::TYPE_NULL,
+ '20' => DataType::TYPE_BOOL,
+ '30' => DataType::TYPE_NUMERIC, // Integer doesn't exist in Excel
+ '40' => DataType::TYPE_NUMERIC, // Float
+ '50' => DataType::TYPE_ERROR,
+ '60' => DataType::TYPE_STRING,
+ //'70': // Cell Range
+ //'80': // Array
+ ],
+ ];
+
+ /**
+ * Create a new Gnumeric.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->referenceHelper = ReferenceHelper::getInstance();
+ $this->securityScanner = XmlScanner::getInstance($this);
+ }
+
+ /**
+ * Can the current IReader read the file?
+ */
+ public function canRead(string $filename): bool
+ {
+ $data = null;
+ if (File::testFileNoThrow($filename)) {
+ $data = $this->gzfileGetContents($filename);
+ if (!str_contains($data, self::NAMESPACE_GNM)) {
+ $data = '';
+ }
+ }
+
+ return !empty($data);
+ }
+
+ private static function matchXml(XMLReader $xml, string $expectedLocalName): bool
+ {
+ return $xml->namespaceURI === self::NAMESPACE_GNM
+ && $xml->localName === $expectedLocalName
+ && $xml->nodeType === XMLReader::ELEMENT;
+ }
+
+ /**
+ * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
+ */
+ public function listWorksheetNames(string $filename): array
+ {
+ File::assertFile($filename);
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an invalid Gnumeric file.');
+ }
+
+ $xml = new XMLReader();
+ $contents = $this->gzfileGetContents($filename);
+ $xml->xml($contents, null, Settings::getLibXmlLoaderOptions());
+ $xml->setParserProperty(2, true);
+
+ $worksheetNames = [];
+ while ($xml->read()) {
+ if (self::matchXml($xml, 'SheetName')) {
+ $xml->read(); // Move onto the value node
+ $worksheetNames[] = (string) $xml->value;
+ } elseif (self::matchXml($xml, 'Sheets')) {
+ // break out of the loop once we've got our sheet names rather than parse the entire file
+ break;
+ }
+ }
+
+ return $worksheetNames;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ File::assertFile($filename);
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an invalid Gnumeric file.');
+ }
+
+ $xml = new XMLReader();
+ $contents = $this->gzfileGetContents($filename);
+ $xml->xml($contents, null, Settings::getLibXmlLoaderOptions());
+ $xml->setParserProperty(2, true);
+
+ $worksheetInfo = [];
+ while ($xml->read()) {
+ if (self::matchXml($xml, 'Sheet')) {
+ $tmpInfo = [
+ 'worksheetName' => '',
+ 'lastColumnLetter' => 'A',
+ 'lastColumnIndex' => 0,
+ 'totalRows' => 0,
+ 'totalColumns' => 0,
+ ];
+
+ while ($xml->read()) {
+ if (self::matchXml($xml, 'Name')) {
+ $xml->read(); // Move onto the value node
+ $tmpInfo['worksheetName'] = (string) $xml->value;
+ } elseif (self::matchXml($xml, 'MaxCol')) {
+ $xml->read(); // Move onto the value node
+ $tmpInfo['lastColumnIndex'] = (int) $xml->value;
+ $tmpInfo['totalColumns'] = (int) $xml->value + 1;
+ } elseif (self::matchXml($xml, 'MaxRow')) {
+ $xml->read(); // Move onto the value node
+ $tmpInfo['totalRows'] = (int) $xml->value + 1;
+
+ break;
+ }
+ }
+ $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+ $worksheetInfo[] = $tmpInfo;
+ }
+ }
+
+ return $worksheetInfo;
+ }
+
+ /**
+ * @param string $filename
+ */
+ private function gzfileGetContents($filename): string
+ {
+ $data = '';
+ $contents = @file_get_contents($filename);
+ if ($contents !== false) {
+ if (str_starts_with($contents, "\x1f\x8b")) {
+ // Check if gzlib functions are available
+ if (function_exists('gzdecode')) {
+ $contents = @gzdecode($contents);
+ if ($contents !== false) {
+ $data = $contents;
+ }
+ }
+ } else {
+ $data = $contents;
+ }
+ }
+ if ($data !== '') {
+ $data = $this->getSecurityScannerOrThrow()->scan($data);
+ }
+
+ return $data;
+ }
+
+ public static function gnumericMappings(): array
+ {
+ return array_merge(self::$mappings, Styles::$mappings);
+ }
+
+ private function processComments(SimpleXMLElement $sheet): void
+ {
+ if ((!$this->readDataOnly) && (isset($sheet->Objects))) {
+ foreach ($sheet->Objects->children(self::NAMESPACE_GNM) as $key => $comment) {
+ $commentAttributes = $comment->attributes();
+ // Only comment objects are handled at the moment
+ if ($commentAttributes && $commentAttributes->Text) {
+ $this->spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)
+ ->setAuthor((string) $commentAttributes->Author)
+ ->setText($this->parseRichText((string) $commentAttributes->Text));
+ }
+ }
+ }
+ }
+
+ private static function testSimpleXml(mixed $value): SimpleXMLElement
+ {
+ return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement(' ');
+ }
+
+ /**
+ * Loads Spreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+ $spreadsheet->removeSheetByIndex(0);
+
+ // Load into this instance
+ return $this->loadIntoExisting($filename, $spreadsheet);
+ }
+
+ /**
+ * Loads from file into Spreadsheet instance.
+ */
+ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
+ {
+ $this->spreadsheet = $spreadsheet;
+ File::assertFile($filename);
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an invalid Gnumeric file.');
+ }
+
+ $gFileData = $this->gzfileGetContents($filename);
+
+ $xml2 = simplexml_load_string($gFileData, 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
+ $xml = self::testSimpleXml($xml2);
+
+ $gnmXML = $xml->children(self::NAMESPACE_GNM);
+ (new Properties($this->spreadsheet))->readProperties($xml, $gnmXML);
+
+ $worksheetID = 0;
+ foreach ($gnmXML->Sheets->Sheet as $sheetOrNull) {
+ $sheet = self::testSimpleXml($sheetOrNull);
+ $worksheetName = (string) $sheet->Name;
+ if (is_array($this->loadSheetsOnly) && !in_array($worksheetName, $this->loadSheetsOnly, true)) {
+ continue;
+ }
+
+ $maxRow = $maxCol = 0;
+
+ // Create new Worksheet
+ $this->spreadsheet->createSheet();
+ $this->spreadsheet->setActiveSheetIndex($worksheetID);
+ // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
+ // cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
+ // name in line with the formula, not the reverse
+ $this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
+
+ $visibility = $sheet->attributes()['Visibility'] ?? 'GNM_SHEET_VISIBILITY_VISIBLE';
+ if ((string) $visibility !== 'GNM_SHEET_VISIBILITY_VISIBLE') {
+ $this->spreadsheet->getActiveSheet()->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
+ }
+
+ if (!$this->readDataOnly) {
+ (new PageSetup($this->spreadsheet))
+ ->printInformation($sheet)
+ ->sheetMargins($sheet);
+ }
+
+ foreach ($sheet->Cells->Cell as $cellOrNull) {
+ $cell = self::testSimpleXml($cellOrNull);
+ $cellAttributes = self::testSimpleXml($cell->attributes());
+ $row = (int) $cellAttributes->Row + 1;
+ $column = (int) $cellAttributes->Col;
+
+ $maxRow = max($maxRow, $row);
+ $maxCol = max($maxCol, $column);
+
+ $column = Coordinate::stringFromColumnIndex($column + 1);
+
+ // Read cell?
+ if ($this->getReadFilter() !== null) {
+ if (!$this->getReadFilter()->readCell($column, $row, $worksheetName)) {
+ continue;
+ }
+ }
+
+ $this->loadCell($cell, $worksheetName, $cellAttributes, $column, $row);
+ }
+
+ if ($sheet->Styles !== null) {
+ (new Styles($this->spreadsheet, $this->readDataOnly))->read($sheet, $maxRow, $maxCol);
+ }
+
+ $this->processComments($sheet);
+ $this->processColumnWidths($sheet, $maxCol);
+ $this->processRowHeights($sheet, $maxRow);
+ $this->processMergedCells($sheet);
+ $this->processAutofilter($sheet);
+
+ $this->setSelectedCells($sheet);
+ ++$worksheetID;
+ }
+
+ $this->processDefinedNames($gnmXML);
+
+ $this->setSelectedSheet($gnmXML);
+
+ // Return
+ return $this->spreadsheet;
+ }
+
+ private function setSelectedSheet(SimpleXMLElement $gnmXML): void
+ {
+ if (isset($gnmXML->UIData)) {
+ $attributes = self::testSimpleXml($gnmXML->UIData->attributes());
+ $selectedSheet = (int) $attributes['SelectedTab'];
+ $this->spreadsheet->setActiveSheetIndex($selectedSheet);
+ }
+ }
+
+ private function setSelectedCells(?SimpleXMLElement $sheet): void
+ {
+ if ($sheet !== null && isset($sheet->Selections)) {
+ foreach ($sheet->Selections as $selection) {
+ $startCol = (int) ($selection->StartCol ?? 0);
+ $startRow = (int) ($selection->StartRow ?? 0) + 1;
+ $endCol = (int) ($selection->EndCol ?? $startCol);
+ $endRow = (int) ($selection->endRow ?? 0) + 1;
+
+ $startColumn = Coordinate::stringFromColumnIndex($startCol + 1);
+ $endColumn = Coordinate::stringFromColumnIndex($endCol + 1);
+
+ $startCell = "{$startColumn}{$startRow}";
+ $endCell = "{$endColumn}{$endRow}";
+ $selectedRange = $startCell . (($endCell !== $startCell) ? ':' . $endCell : '');
+ $this->spreadsheet->getActiveSheet()->setSelectedCell($selectedRange);
+
+ break;
+ }
+ }
+ }
+
+ private function processMergedCells(?SimpleXMLElement $sheet): void
+ {
+ // Handle Merged Cells in this worksheet
+ if ($sheet !== null && isset($sheet->MergedRegions)) {
+ foreach ($sheet->MergedRegions->Merge as $mergeCells) {
+ if (str_contains((string) $mergeCells, ':')) {
+ $this->spreadsheet->getActiveSheet()->mergeCells($mergeCells, Worksheet::MERGE_CELL_CONTENT_HIDE);
+ }
+ }
+ }
+ }
+
+ private function processAutofilter(?SimpleXMLElement $sheet): void
+ {
+ if ($sheet !== null && isset($sheet->Filters)) {
+ foreach ($sheet->Filters->Filter as $autofilter) {
+ if ($autofilter !== null) {
+ $attributes = $autofilter->attributes();
+ if (isset($attributes['Area'])) {
+ $this->spreadsheet->getActiveSheet()->setAutoFilter((string) $attributes['Area']);
+ }
+ }
+ }
+ }
+ }
+
+ private function setColumnWidth(int $whichColumn, float $defaultWidth): void
+ {
+ $columnDimension = $this->spreadsheet->getActiveSheet()
+ ->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1));
+ if ($columnDimension !== null) {
+ $columnDimension->setWidth($defaultWidth);
+ }
+ }
+
+ private function setColumnInvisible(int $whichColumn): void
+ {
+ $columnDimension = $this->spreadsheet->getActiveSheet()
+ ->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1));
+ if ($columnDimension !== null) {
+ $columnDimension->setVisible(false);
+ }
+ }
+
+ private function processColumnLoop(int $whichColumn, int $maxCol, ?SimpleXMLElement $columnOverride, float $defaultWidth): int
+ {
+ $columnOverride = self::testSimpleXml($columnOverride);
+ $columnAttributes = self::testSimpleXml($columnOverride->attributes());
+ $column = $columnAttributes['No'];
+ $columnWidth = ((float) $columnAttributes['Unit']) / 5.4;
+ $hidden = (isset($columnAttributes['Hidden'])) && ((string) $columnAttributes['Hidden'] == '1');
+ $columnCount = (int) ($columnAttributes['Count'] ?? 1);
+ while ($whichColumn < $column) {
+ $this->setColumnWidth($whichColumn, $defaultWidth);
+ ++$whichColumn;
+ }
+ while (($whichColumn < ($column + $columnCount)) && ($whichColumn <= $maxCol)) {
+ $this->setColumnWidth($whichColumn, $columnWidth);
+ if ($hidden) {
+ $this->setColumnInvisible($whichColumn);
+ }
+ ++$whichColumn;
+ }
+
+ return $whichColumn;
+ }
+
+ private function processColumnWidths(?SimpleXMLElement $sheet, int $maxCol): void
+ {
+ if ((!$this->readDataOnly) && $sheet !== null && (isset($sheet->Cols))) {
+ // Column Widths
+ $defaultWidth = 0;
+ $columnAttributes = $sheet->Cols->attributes();
+ if ($columnAttributes !== null) {
+ $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4;
+ }
+ $whichColumn = 0;
+ foreach ($sheet->Cols->ColInfo as $columnOverride) {
+ $whichColumn = $this->processColumnLoop($whichColumn, $maxCol, $columnOverride, $defaultWidth);
+ }
+ while ($whichColumn <= $maxCol) {
+ $this->setColumnWidth($whichColumn, $defaultWidth);
+ ++$whichColumn;
+ }
+ }
+ }
+
+ private function setRowHeight(int $whichRow, float $defaultHeight): void
+ {
+ $rowDimension = $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow);
+ if ($rowDimension !== null) {
+ $rowDimension->setRowHeight($defaultHeight);
+ }
+ }
+
+ private function setRowInvisible(int $whichRow): void
+ {
+ $rowDimension = $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow);
+ if ($rowDimension !== null) {
+ $rowDimension->setVisible(false);
+ }
+ }
+
+ private function processRowLoop(int $whichRow, int $maxRow, ?SimpleXMLElement $rowOverride, float $defaultHeight): int
+ {
+ $rowOverride = self::testSimpleXml($rowOverride);
+ $rowAttributes = self::testSimpleXml($rowOverride->attributes());
+ $row = $rowAttributes['No'];
+ $rowHeight = (float) $rowAttributes['Unit'];
+ $hidden = (isset($rowAttributes['Hidden'])) && ((string) $rowAttributes['Hidden'] == '1');
+ $rowCount = (int) ($rowAttributes['Count'] ?? 1);
+ while ($whichRow < $row) {
+ ++$whichRow;
+ $this->setRowHeight($whichRow, $defaultHeight);
+ }
+ while (($whichRow < ($row + $rowCount)) && ($whichRow < $maxRow)) {
+ ++$whichRow;
+ $this->setRowHeight($whichRow, $rowHeight);
+ if ($hidden) {
+ $this->setRowInvisible($whichRow);
+ }
+ }
+
+ return $whichRow;
+ }
+
+ private function processRowHeights(?SimpleXMLElement $sheet, int $maxRow): void
+ {
+ if ((!$this->readDataOnly) && $sheet !== null && (isset($sheet->Rows))) {
+ // Row Heights
+ $defaultHeight = 0;
+ $rowAttributes = $sheet->Rows->attributes();
+ if ($rowAttributes !== null) {
+ $defaultHeight = (float) $rowAttributes['DefaultSizePts'];
+ }
+ $whichRow = 0;
+
+ foreach ($sheet->Rows->RowInfo as $rowOverride) {
+ $whichRow = $this->processRowLoop($whichRow, $maxRow, $rowOverride, $defaultHeight);
+ }
+ // never executed, I can't figure out any circumstances
+ // under which it would be executed, and, even if
+ // such exist, I'm not convinced this is needed.
+ //while ($whichRow < $maxRow) {
+ // ++$whichRow;
+ // $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow)->setRowHeight($defaultHeight);
+ //}
+ }
+ }
+
+ private function processDefinedNames(?SimpleXMLElement $gnmXML): void
+ {
+ // Loop through definedNames (global named ranges)
+ if ($gnmXML !== null && isset($gnmXML->Names)) {
+ foreach ($gnmXML->Names->Name as $definedName) {
+ $name = (string) $definedName->name;
+ $value = (string) $definedName->value;
+ if (stripos($value, '#REF!') !== false || empty($value)) {
+ continue;
+ }
+
+ [$worksheetName] = Worksheet::extractSheetTitle($value, true);
+ $worksheetName = trim($worksheetName, "'");
+ $worksheet = $this->spreadsheet->getSheetByName($worksheetName);
+ // Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet
+ if ($worksheet !== null) {
+ $this->spreadsheet->addDefinedName(DefinedName::createInstance($name, $worksheet, $value));
+ }
+ }
+ }
+ }
+
+ private function parseRichText(string $is): RichText
+ {
+ $value = new RichText();
+ $value->createText($is);
+
+ return $value;
+ }
+
+ private function loadCell(
+ SimpleXMLElement $cell,
+ string $worksheetName,
+ SimpleXMLElement $cellAttributes,
+ string $column,
+ int $row
+ ): void {
+ $ValueType = $cellAttributes->ValueType;
+ $ExprID = (string) $cellAttributes->ExprID;
+ $type = DataType::TYPE_FORMULA;
+ if ($ExprID > '') {
+ if (((string) $cell) > '') {
+ $this->expressions[$ExprID] = [
+ 'column' => $cellAttributes->Col,
+ 'row' => $cellAttributes->Row,
+ 'formula' => (string) $cell,
+ ];
+ } else {
+ $expression = $this->expressions[$ExprID];
+
+ $cell = $this->referenceHelper->updateFormulaReferences(
+ $expression['formula'],
+ 'A1',
+ $cellAttributes->Col - $expression['column'],
+ $cellAttributes->Row - $expression['row'],
+ $worksheetName
+ );
+ }
+ $type = DataType::TYPE_FORMULA;
+ } else {
+ $vtype = (string) $ValueType;
+ if (array_key_exists($vtype, self::$mappings['dataType'])) {
+ $type = self::$mappings['dataType'][$vtype];
+ }
+ if ($vtype === '20') { // Boolean
+ $cell = $cell == 'TRUE';
+ }
+ }
+
+ $this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type);
+ if (isset($cellAttributes->ValueFormat)) {
+ $this->spreadsheet->getActiveSheet()->getCell($column . $row)
+ ->getStyle()->getNumberFormat()
+ ->setFormatCode((string) $cellAttributes->ValueFormat);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
new file mode 100644
index 0000000..f12b742
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
@@ -0,0 +1,147 @@
+spreadsheet = $spreadsheet;
+ }
+
+ public function printInformation(SimpleXMLElement $sheet): self
+ {
+ if (isset($sheet->PrintInformation, $sheet->PrintInformation[0])) {
+ $printInformation = $sheet->PrintInformation[0];
+ $setup = $this->spreadsheet->getActiveSheet()->getPageSetup();
+
+ $attributes = $printInformation->Scale->attributes();
+ if (isset($attributes['percentage'])) {
+ $setup->setScale((int) $attributes['percentage']);
+ }
+ $pageOrder = (string) $printInformation->order;
+ if ($pageOrder === 'r_then_d') {
+ $setup->setPageOrder(WorksheetPageSetup::PAGEORDER_OVER_THEN_DOWN);
+ } elseif ($pageOrder === 'd_then_r') {
+ $setup->setPageOrder(WorksheetPageSetup::PAGEORDER_DOWN_THEN_OVER);
+ }
+ $orientation = (string) $printInformation->orientation;
+ if ($orientation !== '') {
+ $setup->setOrientation($orientation);
+ }
+ $attributes = $printInformation->hcenter->attributes();
+ if (isset($attributes['value'])) {
+ $setup->setHorizontalCentered((bool) (string) $attributes['value']);
+ }
+ $attributes = $printInformation->vcenter->attributes();
+ if (isset($attributes['value'])) {
+ $setup->setVerticalCentered((bool) (string) $attributes['value']);
+ }
+ }
+
+ return $this;
+ }
+
+ public function sheetMargins(SimpleXMLElement $sheet): self
+ {
+ if (isset($sheet->PrintInformation, $sheet->PrintInformation->Margins)) {
+ $marginSet = [
+ // Default Settings
+ 'top' => 0.75,
+ 'header' => 0.3,
+ 'left' => 0.7,
+ 'right' => 0.7,
+ 'bottom' => 0.75,
+ 'footer' => 0.3,
+ ];
+
+ $marginSet = $this->buildMarginSet($sheet, $marginSet);
+ $this->adjustMargins($marginSet);
+ }
+
+ return $this;
+ }
+
+ private function buildMarginSet(SimpleXMLElement $sheet, array $marginSet): array
+ {
+ foreach ($sheet->PrintInformation->Margins->children(Gnumeric::NAMESPACE_GNM) as $key => $margin) {
+ $marginAttributes = $margin->attributes();
+ $marginSize = ($marginAttributes['Points']) ?? 72; // Default is 72pt
+ // Convert value in points to inches
+ $marginSize = PageMargins::fromPoints((float) $marginSize);
+ $marginSet[$key] = $marginSize;
+ }
+
+ return $marginSet;
+ }
+
+ private function adjustMargins(array $marginSet): void
+ {
+ foreach ($marginSet as $key => $marginSize) {
+ // Gnumeric is quirky in the way it displays the header/footer values:
+ // header is actually the sum of top and header; footer is actually the sum of bottom and footer
+ // then top is actually the header value, and bottom is actually the footer value
+ switch ($key) {
+ case 'left':
+ case 'right':
+ $this->sheetMargin($key, $marginSize);
+
+ break;
+ case 'top':
+ $this->sheetMargin($key, $marginSet['header'] ?? 0);
+
+ break;
+ case 'bottom':
+ $this->sheetMargin($key, $marginSet['footer'] ?? 0);
+
+ break;
+ case 'header':
+ $this->sheetMargin($key, ($marginSet['top'] ?? 0) - $marginSize);
+
+ break;
+ case 'footer':
+ $this->sheetMargin($key, ($marginSet['bottom'] ?? 0) - $marginSize);
+
+ break;
+ }
+ }
+ }
+
+ private function sheetMargin(string $key, float $marginSize): void
+ {
+ switch ($key) {
+ case 'top':
+ $this->spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize);
+
+ break;
+ case 'bottom':
+ $this->spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize);
+
+ break;
+ case 'left':
+ $this->spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize);
+
+ break;
+ case 'right':
+ $this->spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize);
+
+ break;
+ case 'header':
+ $this->spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize);
+
+ break;
+ case 'footer':
+ $this->spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize);
+
+ break;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
new file mode 100644
index 0000000..a60b453
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
@@ -0,0 +1,161 @@
+spreadsheet = $spreadsheet;
+ }
+
+ private function docPropertiesOld(SimpleXMLElement $gnmXML): void
+ {
+ $docProps = $this->spreadsheet->getProperties();
+ foreach ($gnmXML->Summary->Item as $summaryItem) {
+ $propertyName = $summaryItem->name;
+ $propertyValue = $summaryItem->{'val-string'};
+ switch ($propertyName) {
+ case 'title':
+ $docProps->setTitle(trim($propertyValue));
+
+ break;
+ case 'comments':
+ $docProps->setDescription(trim($propertyValue));
+
+ break;
+ case 'keywords':
+ $docProps->setKeywords(trim($propertyValue));
+
+ break;
+ case 'category':
+ $docProps->setCategory(trim($propertyValue));
+
+ break;
+ case 'manager':
+ $docProps->setManager(trim($propertyValue));
+
+ break;
+ case 'author':
+ $docProps->setCreator(trim($propertyValue));
+ $docProps->setLastModifiedBy(trim($propertyValue));
+
+ break;
+ case 'company':
+ $docProps->setCompany(trim($propertyValue));
+
+ break;
+ }
+ }
+ }
+
+ private function docPropertiesDC(SimpleXMLElement $officePropertyDC): void
+ {
+ $docProps = $this->spreadsheet->getProperties();
+ foreach ($officePropertyDC as $propertyName => $propertyValue) {
+ $propertyValue = trim((string) $propertyValue);
+ switch ($propertyName) {
+ case 'title':
+ $docProps->setTitle($propertyValue);
+
+ break;
+ case 'subject':
+ $docProps->setSubject($propertyValue);
+
+ break;
+ case 'creator':
+ $docProps->setCreator($propertyValue);
+ $docProps->setLastModifiedBy($propertyValue);
+
+ break;
+ case 'date':
+ $creationDate = $propertyValue;
+ $docProps->setModified($creationDate);
+
+ break;
+ case 'description':
+ $docProps->setDescription($propertyValue);
+
+ break;
+ }
+ }
+ }
+
+ private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta): void
+ {
+ $docProps = $this->spreadsheet->getProperties();
+ foreach ($officePropertyMeta as $propertyName => $propertyValue) {
+ if ($propertyValue !== null) {
+ $attributes = $propertyValue->attributes(Gnumeric::NAMESPACE_META);
+ $propertyValue = trim((string) $propertyValue);
+ switch ($propertyName) {
+ case 'keyword':
+ $docProps->setKeywords($propertyValue);
+
+ break;
+ case 'initial-creator':
+ $docProps->setCreator($propertyValue);
+ $docProps->setLastModifiedBy($propertyValue);
+
+ break;
+ case 'creation-date':
+ $creationDate = $propertyValue;
+ $docProps->setCreated($creationDate);
+
+ break;
+ case 'user-defined':
+ if ($attributes) {
+ [, $attrName] = explode(':', (string) $attributes['name']);
+ $this->userDefinedProperties($attrName, $propertyValue);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ private function userDefinedProperties(string $attrName, string $propertyValue): void
+ {
+ $docProps = $this->spreadsheet->getProperties();
+ switch ($attrName) {
+ case 'publisher':
+ $docProps->setCompany($propertyValue);
+
+ break;
+ case 'category':
+ $docProps->setCategory($propertyValue);
+
+ break;
+ case 'manager':
+ $docProps->setManager($propertyValue);
+
+ break;
+ }
+ }
+
+ public function readProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML): void
+ {
+ $officeXML = $xml->children(Gnumeric::NAMESPACE_OFFICE);
+ if (!empty($officeXML)) {
+ $officeDocXML = $officeXML->{'document-meta'};
+ $officeDocMetaXML = $officeDocXML->meta;
+
+ foreach ($officeDocMetaXML as $officePropertyData) {
+ $officePropertyDC = $officePropertyData->children(Gnumeric::NAMESPACE_DC);
+ $this->docPropertiesDC($officePropertyDC);
+
+ $officePropertyMeta = $officePropertyData->children(Gnumeric::NAMESPACE_META);
+ $this->docPropertiesMeta($officePropertyMeta);
+ }
+ } elseif (isset($gnmXML->Summary)) {
+ $this->docPropertiesOld($gnmXML);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
new file mode 100644
index 0000000..4b75165
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
@@ -0,0 +1,274 @@
+ [
+ '0' => Border::BORDER_NONE,
+ '1' => Border::BORDER_THIN,
+ '2' => Border::BORDER_MEDIUM,
+ '3' => Border::BORDER_SLANTDASHDOT,
+ '4' => Border::BORDER_DASHED,
+ '5' => Border::BORDER_THICK,
+ '6' => Border::BORDER_DOUBLE,
+ '7' => Border::BORDER_DOTTED,
+ '8' => Border::BORDER_MEDIUMDASHED,
+ '9' => Border::BORDER_DASHDOT,
+ '10' => Border::BORDER_MEDIUMDASHDOT,
+ '11' => Border::BORDER_DASHDOTDOT,
+ '12' => Border::BORDER_MEDIUMDASHDOTDOT,
+ '13' => Border::BORDER_MEDIUMDASHDOTDOT,
+ ],
+ 'fillType' => [
+ '1' => Fill::FILL_SOLID,
+ '2' => Fill::FILL_PATTERN_DARKGRAY,
+ '3' => Fill::FILL_PATTERN_MEDIUMGRAY,
+ '4' => Fill::FILL_PATTERN_LIGHTGRAY,
+ '5' => Fill::FILL_PATTERN_GRAY125,
+ '6' => Fill::FILL_PATTERN_GRAY0625,
+ '7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
+ '8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe
+ '9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe
+ '10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe
+ '11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
+ '12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
+ '13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
+ '14' => Fill::FILL_PATTERN_LIGHTVERTICAL,
+ '15' => Fill::FILL_PATTERN_LIGHTUP,
+ '16' => Fill::FILL_PATTERN_LIGHTDOWN,
+ '17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
+ '18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
+ ],
+ 'horizontal' => [
+ '1' => Alignment::HORIZONTAL_GENERAL,
+ '2' => Alignment::HORIZONTAL_LEFT,
+ '4' => Alignment::HORIZONTAL_RIGHT,
+ '8' => Alignment::HORIZONTAL_CENTER,
+ '16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+ '32' => Alignment::HORIZONTAL_JUSTIFY,
+ '64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+ ],
+ 'underline' => [
+ '1' => Font::UNDERLINE_SINGLE,
+ '2' => Font::UNDERLINE_DOUBLE,
+ '3' => Font::UNDERLINE_SINGLEACCOUNTING,
+ '4' => Font::UNDERLINE_DOUBLEACCOUNTING,
+ ],
+ 'vertical' => [
+ '1' => Alignment::VERTICAL_TOP,
+ '2' => Alignment::VERTICAL_BOTTOM,
+ '4' => Alignment::VERTICAL_CENTER,
+ '8' => Alignment::VERTICAL_JUSTIFY,
+ ],
+ ];
+
+ public function __construct(Spreadsheet $spreadsheet, bool $readDataOnly)
+ {
+ $this->spreadsheet = $spreadsheet;
+ $this->readDataOnly = $readDataOnly;
+ }
+
+ public function read(SimpleXMLElement $sheet, int $maxRow, int $maxCol): void
+ {
+ if ($sheet->Styles->StyleRegion !== null) {
+ $this->readStyles($sheet->Styles->StyleRegion, $maxRow, $maxCol);
+ }
+ }
+
+ private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void
+ {
+ foreach ($styleRegion as $style) {
+ $styleAttributes = $style->attributes();
+ if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) {
+ $cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow);
+
+ $styleAttributes = $style->Style->attributes();
+
+ $styleArray = [];
+ // We still set the number format mask for date/time values, even if readDataOnly is true
+ // so that we can identify whether a float is a float or a date value
+ $formatCode = $styleAttributes ? (string) $styleAttributes['Format'] : null;
+ if ($formatCode && Date::isDateTimeFormatCode($formatCode)) {
+ $styleArray['numberFormat']['formatCode'] = $formatCode;
+ }
+ if ($this->readDataOnly === false && $styleAttributes !== null) {
+ // If readDataOnly is false, we set all formatting information
+ $styleArray['numberFormat']['formatCode'] = $formatCode;
+ $styleArray = $this->readStyle($styleArray, $styleAttributes, $style);
+ }
+ $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
+ }
+ }
+ }
+
+ private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void
+ {
+ if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) {
+ $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
+ $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
+ } elseif (isset($srssb->Diagonal)) {
+ $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
+ $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
+ } elseif (isset($srssb->{'Rev-Diagonal'})) {
+ $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes());
+ $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
+ }
+ }
+
+ private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void
+ {
+ $ucDirection = ucfirst($direction);
+ if (isset($srssb->$ucDirection)) {
+ $styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes());
+ }
+ }
+
+ private function calcRotation(SimpleXMLElement $styleAttributes): int
+ {
+ $rotation = (int) $styleAttributes->Rotation;
+ if ($rotation >= 270 && $rotation <= 360) {
+ $rotation -= 360;
+ }
+ $rotation = (abs($rotation) > 90) ? 0 : $rotation;
+
+ return $rotation;
+ }
+
+ private static function addStyle(array &$styleArray, string $key, string $value): void
+ {
+ if (array_key_exists($value, self::$mappings[$key])) {
+ $styleArray[$key] = self::$mappings[$key][$value];
+ }
+ }
+
+ private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void
+ {
+ if (array_key_exists($value, self::$mappings[$key])) {
+ $styleArray[$key1][$key] = self::$mappings[$key][$value];
+ }
+ }
+
+ private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array
+ {
+ $styleArray = [];
+ if ($borderAttributes !== null) {
+ if (isset($borderAttributes['Color'])) {
+ $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
+ }
+
+ self::addStyle($styleArray, 'borderStyle', (string) $borderAttributes['Style']);
+ }
+
+ return $styleArray;
+ }
+
+ private static function parseGnumericColour(string $gnmColour): string
+ {
+ [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
+ $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
+ $gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
+ $gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
+
+ return $gnmR . $gnmG . $gnmB;
+ }
+
+ private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void
+ {
+ $RGB = self::parseGnumericColour((string) $styleAttributes['Fore']);
+ $styleArray['font']['color']['rgb'] = $RGB;
+ $RGB = self::parseGnumericColour((string) $styleAttributes['Back']);
+ $shade = (string) $styleAttributes['Shade'];
+ if (($RGB !== '000000') || ($shade !== '0')) {
+ $RGB2 = self::parseGnumericColour((string) $styleAttributes['PatternColor']);
+ if ($shade === '1') {
+ $styleArray['fill']['startColor']['rgb'] = $RGB;
+ $styleArray['fill']['endColor']['rgb'] = $RGB2;
+ } else {
+ $styleArray['fill']['endColor']['rgb'] = $RGB;
+ $styleArray['fill']['startColor']['rgb'] = $RGB2;
+ }
+ self::addStyle2($styleArray, 'fill', 'fillType', $shade);
+ }
+ }
+
+ private function readStyleRange(SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string
+ {
+ $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1);
+ $startRow = $styleAttributes['startRow'] + 1;
+
+ $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol'];
+ $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1);
+
+ $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']);
+ $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
+
+ return $cellRange;
+ }
+
+ private function readStyle(array $styleArray, SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array
+ {
+ self::addStyle2($styleArray, 'alignment', 'horizontal', (string) $styleAttributes['HAlign']);
+ self::addStyle2($styleArray, 'alignment', 'vertical', (string) $styleAttributes['VAlign']);
+ $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1';
+ $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes);
+ $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1';
+ $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
+
+ $this->addColors($styleArray, $styleAttributes);
+
+ $fontAttributes = $style->Style->Font->attributes();
+ if ($fontAttributes !== null) {
+ $styleArray['font']['name'] = (string) $style->Style->Font;
+ $styleArray['font']['size'] = (int) ($fontAttributes['Unit']);
+ $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1';
+ $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1';
+ $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1';
+ self::addStyle2($styleArray, 'font', 'underline', (string) $fontAttributes['Underline']);
+
+ switch ($fontAttributes['Script']) {
+ case '1':
+ $styleArray['font']['superscript'] = true;
+
+ break;
+ case '-1':
+ $styleArray['font']['subscript'] = true;
+
+ break;
+ }
+ }
+
+ if (isset($style->Style->StyleBorder)) {
+ $srssb = $style->Style->StyleBorder;
+ $this->addBorderStyle($srssb, $styleArray, 'top');
+ $this->addBorderStyle($srssb, $styleArray, 'bottom');
+ $this->addBorderStyle($srssb, $styleArray, 'left');
+ $this->addBorderStyle($srssb, $styleArray, 'right');
+ $this->addBorderDiagonal($srssb, $styleArray);
+ }
+ // TO DO
+ /*
+ if (isset($style->Style->HyperLink)) {
+ $hyperlink = $style->Style->HyperLink->attributes();
+ }
+ */
+
+ return $styleArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
new file mode 100644
index 0000000..6fcf31d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
@@ -0,0 +1,1173 @@
+ [
+ 'font' => [
+ 'bold' => true,
+ 'size' => 24,
+ ],
+ ], // Bold, 24pt
+ 'h2' => [
+ 'font' => [
+ 'bold' => true,
+ 'size' => 18,
+ ],
+ ], // Bold, 18pt
+ 'h3' => [
+ 'font' => [
+ 'bold' => true,
+ 'size' => 13.5,
+ ],
+ ], // Bold, 13.5pt
+ 'h4' => [
+ 'font' => [
+ 'bold' => true,
+ 'size' => 12,
+ ],
+ ], // Bold, 12pt
+ 'h5' => [
+ 'font' => [
+ 'bold' => true,
+ 'size' => 10,
+ ],
+ ], // Bold, 10pt
+ 'h6' => [
+ 'font' => [
+ 'bold' => true,
+ 'size' => 7.5,
+ ],
+ ], // Bold, 7.5pt
+ 'a' => [
+ 'font' => [
+ 'underline' => true,
+ 'color' => [
+ 'argb' => Color::COLOR_BLUE,
+ ],
+ ],
+ ], // Blue underlined
+ 'hr' => [
+ 'borders' => [
+ 'bottom' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => [
+ Color::COLOR_BLACK,
+ ],
+ ],
+ ],
+ ], // Bottom border
+ 'strong' => [
+ 'font' => [
+ 'bold' => true,
+ ],
+ ], // Bold
+ 'b' => [
+ 'font' => [
+ 'bold' => true,
+ ],
+ ], // Bold
+ 'i' => [
+ 'font' => [
+ 'italic' => true,
+ ],
+ ], // Italic
+ 'em' => [
+ 'font' => [
+ 'italic' => true,
+ ],
+ ], // Italic
+ ];
+
+ /** @var array */
+ protected $rowspan = [];
+
+ /**
+ * Create a new HTML Reader instance.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->securityScanner = XmlScanner::getInstance($this);
+ }
+
+ /**
+ * Validate that the current file is an HTML file.
+ */
+ public function canRead(string $filename): bool
+ {
+ // Check if file exists
+ try {
+ $this->openFile($filename);
+ } catch (Exception) {
+ return false;
+ }
+
+ $beginning = $this->readBeginning();
+ $startWithTag = self::startsWithTag($beginning);
+ $containsTags = self::containsTags($beginning);
+ $endsWithTag = self::endsWithTag($this->readEnding());
+
+ fclose($this->fileHandle);
+
+ return $startWithTag && $containsTags && $endsWithTag;
+ }
+
+ private function readBeginning(): string
+ {
+ fseek($this->fileHandle, 0);
+
+ return (string) fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
+ }
+
+ private function readEnding(): string
+ {
+ $meta = stream_get_meta_data($this->fileHandle);
+ $filename = $meta['uri'];
+
+ $size = (int) filesize($filename);
+ if ($size === 0) {
+ return '';
+ }
+
+ $blockSize = self::TEST_SAMPLE_SIZE;
+ if ($size < $blockSize) {
+ $blockSize = $size;
+ }
+
+ fseek($this->fileHandle, $size - $blockSize);
+
+ return (string) fread($this->fileHandle, $blockSize);
+ }
+
+ private static function startsWithTag(string $data): bool
+ {
+ return str_starts_with(trim($data), '<');
+ }
+
+ private static function endsWithTag(string $data): bool
+ {
+ return str_ends_with(trim($data), '>');
+ }
+
+ private static function containsTags(string $data): bool
+ {
+ return strlen($data) !== strlen(strip_tags($data));
+ }
+
+ /**
+ * Loads Spreadsheet from file.
+ */
+ public function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+
+ // Load into this instance
+ return $this->loadIntoExisting($filename, $spreadsheet);
+ }
+
+ /**
+ * Set input encoding.
+ *
+ * @param string $inputEncoding Input encoding, eg: 'ANSI'
+ *
+ * @return $this
+ *
+ * @codeCoverageIgnore
+ *
+ * @deprecated no use is made of this property
+ */
+ public function setInputEncoding($inputEncoding): static
+ {
+ $this->inputEncoding = $inputEncoding;
+
+ return $this;
+ }
+
+ /**
+ * Get input encoding.
+ *
+ * @return string
+ *
+ * @codeCoverageIgnore
+ *
+ * @deprecated no use is made of this property
+ */
+ public function getInputEncoding()
+ {
+ return $this->inputEncoding;
+ }
+
+ // Data Array used for testing only, should write to Spreadsheet object on completion of tests
+
+ /** @var array */
+ protected $dataArray = [];
+
+ /** @var int */
+ protected $tableLevel = 0;
+
+ /** @var array */
+ protected $nestedColumn = ['A'];
+
+ protected function setTableStartColumn(string $column): string
+ {
+ if ($this->tableLevel == 0) {
+ $column = 'A';
+ }
+ ++$this->tableLevel;
+ $this->nestedColumn[$this->tableLevel] = $column;
+
+ return $this->nestedColumn[$this->tableLevel];
+ }
+
+ protected function getTableStartColumn(): string
+ {
+ return $this->nestedColumn[$this->tableLevel];
+ }
+
+ protected function releaseTableStartColumn(): string
+ {
+ --$this->tableLevel;
+
+ return array_pop($this->nestedColumn);
+ }
+
+ /**
+ * Flush cell.
+ *
+ * @param int|string $row
+ */
+ protected function flushCell(Worksheet $sheet, string $column, $row, mixed &$cellContent, array $attributeArray): void
+ {
+ if (is_string($cellContent)) {
+ // Simple String content
+ if (trim($cellContent) > '') {
+ // Only actually write it if there's content in the string
+ // Write to worksheet to be done here...
+ // ... we return the cell, so we can mess about with styles more easily
+
+ // Set cell value explicitly if there is data-type attribute
+ if (isset($attributeArray['data-type'])) {
+ $datatype = $attributeArray['data-type'];
+ if (in_array($datatype, [DataType::TYPE_STRING, DataType::TYPE_STRING2, DataType::TYPE_INLINE])) {
+ //Prevent to Excel treat string with beginning equal sign or convert big numbers to scientific number
+ if (str_starts_with($cellContent, '=')) {
+ $sheet->getCell($column . $row)
+ ->getStyle()
+ ->setQuotePrefix(true);
+ }
+ }
+
+ //catching the Exception and ignoring the invalid data types
+ try {
+ $sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
+ } catch (\PhpOffice\PhpSpreadsheet\Exception) {
+ $sheet->setCellValue($column . $row, $cellContent);
+ }
+ } else {
+ $sheet->setCellValue($column . $row, $cellContent);
+ }
+ $this->dataArray[$row][$column] = $cellContent;
+ }
+ } else {
+ // We have a Rich Text run
+ // TODO
+ $this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent;
+ }
+ $cellContent = (string) '';
+ }
+
+ private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void
+ {
+ $attributeArray = [];
+ foreach ($child->attributes as $attribute) {
+ $attributeArray[$attribute->name] = $attribute->value;
+ }
+
+ if ($child->nodeName === 'body') {
+ $row = 1;
+ $column = 'A';
+ $cellContent = '';
+ $this->tableLevel = 0;
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ } else {
+ $this->processDomElementTitle($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementTitle(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'title') {
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ $sheet->setTitle($cellContent, true, true);
+ $cellContent = '';
+ } else {
+ $this->processDomElementSpanEtc($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private const SPAN_ETC = ['span', 'div', 'font', 'i', 'em', 'strong', 'b'];
+
+ private function processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if (in_array((string) $child->nodeName, self::SPAN_ETC, true)) {
+ if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
+ $sheet->getComment($column . $row)
+ ->getText()
+ ->createTextRun($child->textContent);
+ } else {
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ }
+
+ if (isset($this->formats[$child->nodeName])) {
+ $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+ }
+ } else {
+ $this->processDomElementHr($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'hr') {
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+ ++$row;
+ if (isset($this->formats[$child->nodeName])) {
+ $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+ }
+ ++$row;
+ }
+ // fall through to br
+ $this->processDomElementBr($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+
+ private function processDomElementBr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'br' || $child->nodeName === 'hr') {
+ if ($this->tableLevel > 0) {
+ // 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, $attributeArray);
+ ++$row;
+ }
+ } else {
+ $this->processDomElementA($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementA(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'a') {
+ foreach ($attributeArray as $attributeName => $attributeValue) {
+ switch ($attributeName) {
+ case 'href':
+ $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue);
+ if (isset($this->formats[$child->nodeName])) {
+ $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+ }
+
+ break;
+ case 'class':
+ if ($attributeValue === 'comment-indicator') {
+ break; // Ignore - it's just a red square.
+ }
+ }
+ }
+ // no idea why this should be needed
+ //$cellContent .= ' ';
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ } else {
+ $this->processDomElementH1Etc($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private const H1_ETC = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'p'];
+
+ private function processDomElementH1Etc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if (in_array((string) $child->nodeName, self::H1_ETC, true)) {
+ if ($this->tableLevel > 0) {
+ // If we're inside a table, replace with a \n
+ $cellContent .= $cellContent ? "\n" : '';
+ $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ } else {
+ if ($cellContent > '') {
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+ ++$row;
+ }
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+
+ if (isset($this->formats[$child->nodeName])) {
+ $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+ }
+
+ ++$row;
+ $column = 'A';
+ }
+ } else {
+ $this->processDomElementLi($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementLi(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'li') {
+ if ($this->tableLevel > 0) {
+ // If we're inside a table, replace with a \n
+ $cellContent .= $cellContent ? "\n" : '';
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ } else {
+ if ($cellContent > '') {
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+ }
+ ++$row;
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+ $column = 'A';
+ }
+ } else {
+ $this->processDomElementImg($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementImg(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'img') {
+ $this->insertImage($sheet, $column, $row, $attributeArray);
+ } else {
+ $this->processDomElementTable($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private string $currentColumn = 'A';
+
+ private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'table') {
+ $this->currentColumn = 'A';
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+ $column = $this->setTableStartColumn($column);
+ if ($this->tableLevel > 1 && $row > 1) {
+ --$row;
+ }
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ $column = $this->releaseTableStartColumn();
+ if ($this->tableLevel > 1) {
+ ++$column;
+ } else {
+ ++$row;
+ }
+ } else {
+ $this->processDomElementTr($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementTr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName === 'col') {
+ $this->applyInlineStyle($sheet, -1, $this->currentColumn, $attributeArray);
+ ++$this->currentColumn;
+ } elseif ($child->nodeName === 'tr') {
+ $column = $this->getTableStartColumn();
+ $cellContent = '';
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+ if (isset($attributeArray['height'])) {
+ $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
+ }
+
+ ++$row;
+ } else {
+ $this->processDomElementThTdOther($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementThTdOther(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ if ($child->nodeName !== 'td' && $child->nodeName !== 'th') {
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+ } else {
+ $this->processDomElementThTd($sheet, $row, $column, $cellContent, $child, $attributeArray);
+ }
+ }
+
+ private function processDomElementBgcolor(Worksheet $sheet, int $row, string $column, array $attributeArray): void
+ {
+ if (isset($attributeArray['bgcolor'])) {
+ $sheet->getStyle("$column$row")->applyFromArray(
+ [
+ 'fill' => [
+ 'fillType' => Fill::FILL_SOLID,
+ 'color' => ['rgb' => $this->getStyleColor($attributeArray['bgcolor'])],
+ ],
+ ]
+ );
+ }
+ }
+
+ private function processDomElementWidth(Worksheet $sheet, string $column, array $attributeArray): void
+ {
+ if (isset($attributeArray['width'])) {
+ $sheet->getColumnDimension($column)->setWidth((new CssDimension($attributeArray['width']))->width());
+ }
+ }
+
+ private function processDomElementHeight(Worksheet $sheet, int $row, array $attributeArray): void
+ {
+ if (isset($attributeArray['height'])) {
+ $sheet->getRowDimension($row)->setRowHeight((new CssDimension($attributeArray['height']))->height());
+ }
+ }
+
+ private function processDomElementAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void
+ {
+ if (isset($attributeArray['align'])) {
+ $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
+ }
+ }
+
+ private function processDomElementVAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void
+ {
+ if (isset($attributeArray['valign'])) {
+ $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
+ }
+ }
+
+ private function processDomElementDataFormat(Worksheet $sheet, int $row, string $column, array $attributeArray): void
+ {
+ if (isset($attributeArray['data-format'])) {
+ $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
+ }
+ }
+
+ private function processDomElementThTd(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
+ {
+ while (isset($this->rowspan[$column . $row])) {
+ ++$column;
+ }
+ $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+ // apply inline style
+ $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
+
+ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
+
+ $this->processDomElementBgcolor($sheet, $row, $column, $attributeArray);
+ $this->processDomElementWidth($sheet, $column, $attributeArray);
+ $this->processDomElementHeight($sheet, $row, $attributeArray);
+ $this->processDomElementAlign($sheet, $row, $column, $attributeArray);
+ $this->processDomElementVAlign($sheet, $row, $column, $attributeArray);
+ $this->processDomElementDataFormat($sheet, $row, $column, $attributeArray);
+
+ if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
+ //create merging rowspan and colspan
+ $columnTo = $column;
+ for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
+ ++$columnTo;
+ }
+ $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
+ foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
+ $this->rowspan[$value] = true;
+ }
+ $sheet->mergeCells($range);
+ $column = $columnTo;
+ } elseif (isset($attributeArray['rowspan'])) {
+ //create merging rowspan
+ $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
+ foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
+ $this->rowspan[$value] = true;
+ }
+ $sheet->mergeCells($range);
+ } elseif (isset($attributeArray['colspan'])) {
+ //create merging colspan
+ $columnTo = $column;
+ for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
+ ++$columnTo;
+ }
+ $sheet->mergeCells($column . $row . ':' . $columnTo . $row);
+ $column = $columnTo;
+ }
+
+ ++$column;
+ }
+
+ protected function processDomElement(DOMNode $element, Worksheet $sheet, int &$row, string &$column, string &$cellContent): void
+ {
+ foreach ($element->childNodes as $child) {
+ if ($child instanceof DOMText) {
+ $domText = (string) preg_replace('/\s+/u', ' ', trim($child->nodeValue ?? ''));
+ if (is_string($cellContent)) {
+ // simply append the text if the cell content is a plain text string
+ $cellContent .= $domText;
+ }
+ // but if we have a rich text run instead, we need to append it correctly
+ // TODO
+ } elseif ($child instanceof DOMElement) {
+ $this->processDomElementBody($sheet, $row, $column, $cellContent, $child);
+ }
+ }
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+ */
+ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
+ {
+ // Validate
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an Invalid HTML file.');
+ }
+
+ // Create a new DOM object
+ $dom = new DOMDocument();
+
+ // Reload the HTML file into the DOM object
+ try {
+ $convert = $this->getSecurityScannerOrThrow()->scanFile($filename);
+ $lowend = "\u{80}";
+ $highend = "\u{10ffff}";
+ $regexp = "/[$lowend-$highend]/u";
+ /** @var callable */
+ $callback = [self::class, 'replaceNonAscii'];
+ $convert = preg_replace_callback($regexp, $callback, $convert);
+ $loaded = ($convert === null) ? false : $dom->loadHTML($convert);
+ } catch (Throwable $e) {
+ $loaded = false;
+ }
+ if ($loaded === false) {
+ throw new Exception('Failed to load ' . $filename . ' as a DOM Document', 0, $e ?? null);
+ }
+ self::loadProperties($dom, $spreadsheet);
+
+ return $this->loadDocument($dom, $spreadsheet);
+ }
+
+ private static function loadProperties(DOMDocument $dom, Spreadsheet $spreadsheet): void
+ {
+ $properties = $spreadsheet->getProperties();
+ foreach ($dom->getElementsByTagName('meta') as $meta) {
+ $metaContent = (string) $meta->getAttribute('content');
+ if ($metaContent !== '') {
+ $metaName = (string) $meta->getAttribute('name');
+ switch ($metaName) {
+ case 'author':
+ $properties->setCreator($metaContent);
+
+ break;
+ case 'category':
+ $properties->setCategory($metaContent);
+
+ break;
+ case 'company':
+ $properties->setCompany($metaContent);
+
+ break;
+ case 'created':
+ $properties->setCreated($metaContent);
+
+ break;
+ case 'description':
+ $properties->setDescription($metaContent);
+
+ break;
+ case 'keywords':
+ $properties->setKeywords($metaContent);
+
+ break;
+ case 'lastModifiedBy':
+ $properties->setLastModifiedBy($metaContent);
+
+ break;
+ case 'manager':
+ $properties->setManager($metaContent);
+
+ break;
+ case 'modified':
+ $properties->setModified($metaContent);
+
+ break;
+ case 'subject':
+ $properties->setSubject($metaContent);
+
+ break;
+ case 'title':
+ $properties->setTitle($metaContent);
+
+ break;
+ case 'viewport':
+ $properties->setViewport($metaContent);
+
+ break;
+ default:
+ if (preg_match('/^custom[.](bool|date|float|int|string)[.](.+)$/', $metaName, $matches) === 1) {
+ match ($matches[1]) {
+ 'bool' => $properties->setCustomProperty($matches[2], (bool) $metaContent, Properties::PROPERTY_TYPE_BOOLEAN),
+ 'float' => $properties->setCustomProperty($matches[2], (float) $metaContent, Properties::PROPERTY_TYPE_FLOAT),
+ 'int' => $properties->setCustomProperty($matches[2], (int) $metaContent, Properties::PROPERTY_TYPE_INTEGER),
+ 'date' => $properties->setCustomProperty($matches[2], $metaContent, Properties::PROPERTY_TYPE_DATE),
+ // string
+ default => $properties->setCustomProperty($matches[2], $metaContent, Properties::PROPERTY_TYPE_STRING),
+ };
+ }
+ }
+ }
+ }
+ if (!empty($dom->baseURI)) {
+ $properties->setHyperlinkBase($dom->baseURI);
+ }
+ }
+
+ private static function replaceNonAscii(array $matches): string
+ {
+ return '' . mb_ord($matches[0], 'UTF-8') . ';';
+ }
+
+ /**
+ * Spreadsheet from content.
+ *
+ * @param string $content
+ */
+ public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
+ {
+ // Create a new DOM object
+ $dom = new DOMDocument();
+
+ // Reload the HTML file into the DOM object
+ try {
+ $convert = $this->getSecurityScannerOrThrow()->scan($content);
+ $lowend = "\u{80}";
+ $highend = "\u{10ffff}";
+ $regexp = "/[$lowend-$highend]/u";
+ /** @var callable */
+ $callback = [self::class, 'replaceNonAscii'];
+ $convert = preg_replace_callback($regexp, $callback, $convert);
+ $loaded = ($convert === null) ? false : $dom->loadHTML($convert);
+ } catch (Throwable $e) {
+ $loaded = false;
+ }
+ if ($loaded === false) {
+ throw new Exception('Failed to load content as a DOM Document', 0, $e ?? null);
+ }
+ $spreadsheet = $spreadsheet ?? new Spreadsheet();
+ self::loadProperties($dom, $spreadsheet);
+
+ return $this->loadDocument($dom, $spreadsheet);
+ }
+
+ /**
+ * Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
+ */
+ 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($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
+
+ // Return
+ return $spreadsheet;
+ }
+
+ /**
+ * Get sheet index.
+ *
+ * @return int
+ */
+ public function getSheetIndex()
+ {
+ return $this->sheetIndex;
+ }
+
+ /**
+ * Set sheet index.
+ *
+ * @param int $sheetIndex Sheet index
+ *
+ * @return $this
+ */
+ public function setSheetIndex($sheetIndex): static
+ {
+ $this->sheetIndex = $sheetIndex;
+
+ return $this;
+ }
+
+ /**
+ * Apply inline css inline style.
+ *
+ * NOTES :
+ * Currently only intended for td & th element,
+ * and only takes 'background-color' and 'color'; property with HEX color
+ *
+ * TODO :
+ * - Implement to other propertie, such as border
+ *
+ * @param int $row
+ */
+ private function applyInlineStyle(Worksheet &$sheet, $row, string $column, array $attributeArray): void
+ {
+ if (!isset($attributeArray['style'])) {
+ return;
+ }
+
+ if ($row <= 0 || $column === '') {
+ $cellStyle = new Style();
+ } elseif (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
+ $columnTo = $column;
+ for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
+ ++$columnTo;
+ }
+ $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
+ $cellStyle = $sheet->getStyle($range);
+ } elseif (isset($attributeArray['rowspan'])) {
+ $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
+ $cellStyle = $sheet->getStyle($range);
+ } elseif (isset($attributeArray['colspan'])) {
+ $columnTo = $column;
+ for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
+ ++$columnTo;
+ }
+ $range = $column . $row . ':' . $columnTo . $row;
+ $cellStyle = $sheet->getStyle($range);
+ } else {
+ $cellStyle = $sheet->getStyle($column . $row);
+ }
+
+ // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
+ $styles = explode(';', $attributeArray['style']);
+ foreach ($styles as $st) {
+ $value = explode(':', $st);
+ $styleName = isset($value[0]) ? trim($value[0]) : null;
+ $styleValue = isset($value[1]) ? trim($value[1]) : null;
+ $styleValueString = (string) $styleValue;
+
+ if (!$styleName) {
+ continue;
+ }
+
+ switch ($styleName) {
+ case 'background':
+ case 'background-color':
+ $styleColor = $this->getStyleColor($styleValueString);
+
+ if (!$styleColor) {
+ continue 2;
+ }
+
+ $cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
+
+ break;
+ case 'color':
+ $styleColor = $this->getStyleColor($styleValueString);
+
+ if (!$styleColor) {
+ continue 2;
+ }
+
+ $cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
+
+ break;
+
+ case 'border':
+ $this->setBorderStyle($cellStyle, $styleValueString, 'allBorders');
+
+ break;
+
+ case 'border-top':
+ $this->setBorderStyle($cellStyle, $styleValueString, 'top');
+
+ break;
+
+ case 'border-bottom':
+ $this->setBorderStyle($cellStyle, $styleValueString, 'bottom');
+
+ break;
+
+ case 'border-left':
+ $this->setBorderStyle($cellStyle, $styleValueString, 'left');
+
+ break;
+
+ case 'border-right':
+ $this->setBorderStyle($cellStyle, $styleValueString, 'right');
+
+ break;
+
+ case 'font-size':
+ $cellStyle->getFont()->setSize(
+ (float) $styleValue
+ );
+
+ break;
+
+ case 'font-weight':
+ if ($styleValue === 'bold' || $styleValue >= 500) {
+ $cellStyle->getFont()->setBold(true);
+ }
+
+ break;
+
+ case 'font-style':
+ if ($styleValue === 'italic') {
+ $cellStyle->getFont()->setItalic(true);
+ }
+
+ break;
+
+ case 'font-family':
+ $cellStyle->getFont()->setName(str_replace('\'', '', $styleValueString));
+
+ break;
+
+ case 'text-decoration':
+ switch ($styleValue) {
+ case 'underline':
+ $cellStyle->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
+
+ break;
+ case 'line-through':
+ $cellStyle->getFont()->setStrikethrough(true);
+
+ break;
+ }
+
+ break;
+
+ case 'text-align':
+ $cellStyle->getAlignment()->setHorizontal($styleValueString);
+
+ break;
+
+ case 'vertical-align':
+ $cellStyle->getAlignment()->setVertical($styleValueString);
+
+ break;
+
+ case 'width':
+ if ($column !== '') {
+ $sheet->getColumnDimension($column)->setWidth(
+ (new CssDimension($styleValue ?? ''))->width()
+ );
+ }
+
+ break;
+
+ case 'height':
+ if ($row > 0) {
+ $sheet->getRowDimension($row)->setRowHeight(
+ (new CssDimension($styleValue ?? ''))->height()
+ );
+ }
+
+ break;
+
+ case 'word-wrap':
+ $cellStyle->getAlignment()->setWrapText(
+ $styleValue === 'break-word'
+ );
+
+ break;
+
+ case 'text-indent':
+ $cellStyle->getAlignment()->setIndent(
+ (int) str_replace(['px'], '', $styleValueString)
+ );
+
+ break;
+ }
+ }
+ }
+
+ /**
+ * Check if has #, so we can get clean hex.
+ */
+ public function getStyleColor(mixed $value): string
+ {
+ $value = (string) $value;
+ if (str_starts_with($value, '#')) {
+ return substr($value, 1);
+ }
+
+ return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup($value);
+ }
+
+ private function insertImage(Worksheet $sheet, string $column, int $row, array $attributes): void
+ {
+ if (!isset($attributes['src'])) {
+ return;
+ }
+
+ $src = urldecode($attributes['src']);
+ $width = isset($attributes['width']) ? (float) $attributes['width'] : null;
+ $height = isset($attributes['height']) ? (float) $attributes['height'] : null;
+ $name = $attributes['alt'] ?? null;
+
+ $drawing = new Drawing();
+ $drawing->setPath($src);
+ $drawing->setWorksheet($sheet);
+ $drawing->setCoordinates($column . $row);
+ $drawing->setOffsetX(0);
+ $drawing->setOffsetY(10);
+ $drawing->setResizeProportional(true);
+
+ if ($name) {
+ $drawing->setName($name);
+ }
+
+ if ($width) {
+ $drawing->setWidth((int) $width);
+ }
+
+ if ($height) {
+ $drawing->setHeight((int) $height);
+ }
+
+ $sheet->getColumnDimension($column)->setWidth(
+ $drawing->getWidth() / 6
+ );
+
+ $sheet->getRowDimension($row)->setRowHeight(
+ $drawing->getHeight() * 0.9
+ );
+ }
+
+ private const BORDER_MAPPINGS = [
+ 'dash-dot' => Border::BORDER_DASHDOT,
+ 'dash-dot-dot' => Border::BORDER_DASHDOTDOT,
+ 'dashed' => Border::BORDER_DASHED,
+ 'dotted' => Border::BORDER_DOTTED,
+ 'double' => Border::BORDER_DOUBLE,
+ 'hair' => Border::BORDER_HAIR,
+ 'medium' => Border::BORDER_MEDIUM,
+ 'medium-dashed' => Border::BORDER_MEDIUMDASHED,
+ 'medium-dash-dot' => Border::BORDER_MEDIUMDASHDOT,
+ 'medium-dash-dot-dot' => Border::BORDER_MEDIUMDASHDOTDOT,
+ 'none' => Border::BORDER_NONE,
+ 'slant-dash-dot' => Border::BORDER_SLANTDASHDOT,
+ 'solid' => Border::BORDER_THIN,
+ 'thick' => Border::BORDER_THICK,
+ ];
+
+ public static function getBorderMappings(): array
+ {
+ return self::BORDER_MAPPINGS;
+ }
+
+ /**
+ * Map html border style to PhpSpreadsheet border style.
+ *
+ * @param string $style
+ */
+ public function getBorderStyle($style): ?string
+ {
+ return self::BORDER_MAPPINGS[$style] ?? null;
+ }
+
+ private function setBorderStyle(Style $cellStyle, string $styleValue, string $type): void
+ {
+ if (trim($styleValue) === Border::BORDER_NONE) {
+ $borderStyle = Border::BORDER_NONE;
+ $color = null;
+ } else {
+ $borderArray = explode(' ', $styleValue);
+ $borderCount = count($borderArray);
+ if ($borderCount >= 3) {
+ $borderStyle = $borderArray[1];
+ $color = $borderArray[2];
+ } else {
+ $borderStyle = $borderArray[0];
+ $color = $borderArray[1] ?? null;
+ }
+ }
+
+ $cellStyle->applyFromArray([
+ 'borders' => [
+ $type => [
+ 'borderStyle' => $this->getBorderStyle($borderStyle),
+ 'color' => ['rgb' => $this->getStyleColor($color)],
+ ],
+ ],
+ ]);
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ $info = [];
+ $spreadsheet = new Spreadsheet();
+ $this->loadIntoExisting($filename, $spreadsheet);
+ foreach ($spreadsheet->getAllSheets() as $sheet) {
+ $newEntry = ['worksheetName' => $sheet->getTitle()];
+ $newEntry['lastColumnLetter'] = $sheet->getHighestDataColumn();
+ $newEntry['lastColumnIndex'] = Coordinate::columnIndexFromString($sheet->getHighestDataColumn()) - 1;
+ $newEntry['totalRows'] = $sheet->getHighestDataRow();
+ $newEntry['totalColumns'] = $newEntry['lastColumnIndex'] + 1;
+ $info[] = $newEntry;
+ }
+ $spreadsheet->disconnectWorksheets();
+
+ return $info;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
new file mode 100644
index 0000000..9f68a7f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
@@ -0,0 +1,17 @@
+securityScanner = XmlScanner::getInstance($this);
+ }
+
+ /**
+ * Can the current IReader read the file?
+ */
+ public function canRead(string $filename): bool
+ {
+ $mimeType = 'UNKNOWN';
+
+ // Load file
+
+ if (File::testFileNoThrow($filename, '')) {
+ $zip = new ZipArchive();
+ if ($zip->open($filename) === true) {
+ // check if it is an OOXML archive
+ $stat = $zip->statName('mimetype');
+ if (!empty($stat) && ($stat['size'] <= 255)) {
+ $mimeType = $zip->getFromName($stat['name']);
+ } elseif ($zip->statName('META-INF/manifest.xml')) {
+ $xml = simplexml_load_string(
+ $this->getSecurityScannerOrThrow()->scan($zip->getFromName('META-INF/manifest.xml')),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+ if ($xml !== false) {
+ $namespacesContent = $xml->getNamespaces(true);
+ if (isset($namespacesContent['manifest'])) {
+ $manifest = $xml->children($namespacesContent['manifest']);
+ foreach ($manifest as $manifestDataSet) {
+ $manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
+ if ($manifestAttributes && $manifestAttributes->{'full-path'} == '/') {
+ $mimeType = (string) $manifestAttributes->{'media-type'};
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ $zip->close();
+ }
+ }
+
+ return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet';
+ }
+
+ /**
+ * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
+ *
+ * @return string[]
+ */
+ public function listWorksheetNames(string $filename): array
+ {
+ File::assertFile($filename, self::INITIAL_FILE);
+
+ $worksheetNames = [];
+
+ $xml = new XMLReader();
+ $xml->xml(
+ $this->getSecurityScannerOrThrow()->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
+ null,
+ Settings::getLibXmlLoaderOptions()
+ );
+ $xml->setParserProperty(2, true);
+
+ // Step into the first level of content of the XML
+ $xml->read();
+ while ($xml->read()) {
+ // Quickly jump through to the office:body node
+ while (self::getXmlName($xml) !== 'office:body') {
+ if ($xml->isEmptyElement) {
+ $xml->read();
+ } else {
+ $xml->next();
+ }
+ }
+ // Now read each node until we find our first table:table node
+ while ($xml->read()) {
+ $xmlName = self::getXmlName($xml);
+ if ($xmlName == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
+ // Loop through each table:table node reading the table:name attribute for each worksheet name
+ do {
+ $worksheetName = $xml->getAttribute('table:name');
+ if (!empty($worksheetName)) {
+ $worksheetNames[] = $worksheetName;
+ }
+ $xml->next();
+ } while (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
+ }
+ }
+ }
+
+ return $worksheetNames;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ File::assertFile($filename, self::INITIAL_FILE);
+
+ $worksheetInfo = [];
+
+ $xml = new XMLReader();
+ $xml->xml(
+ $this->getSecurityScannerOrThrow()->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
+ null,
+ Settings::getLibXmlLoaderOptions()
+ );
+ $xml->setParserProperty(2, true);
+
+ // Step into the first level of content of the XML
+ $xml->read();
+ while ($xml->read()) {
+ // Quickly jump through to the office:body node
+ while (self::getXmlName($xml) !== 'office:body') {
+ if ($xml->isEmptyElement) {
+ $xml->read();
+ } else {
+ $xml->next();
+ }
+ }
+ // Now read each node until we find our first table:table node
+ while ($xml->read()) {
+ if (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
+ $worksheetNames[] = $xml->getAttribute('table:name');
+
+ $tmpInfo = [
+ 'worksheetName' => $xml->getAttribute('table:name'),
+ 'lastColumnLetter' => 'A',
+ 'lastColumnIndex' => 0,
+ 'totalRows' => 0,
+ 'totalColumns' => 0,
+ ];
+
+ // Loop through each child node of the table:table element reading
+ $currCells = 0;
+ do {
+ $xml->read();
+ if (self::getXmlName($xml) == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) {
+ $rowspan = $xml->getAttribute('table:number-rows-repeated');
+ $rowspan = empty($rowspan) ? 1 : $rowspan;
+ $tmpInfo['totalRows'] += $rowspan;
+ $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+ $currCells = 0;
+ // Step into the row
+ $xml->read();
+ do {
+ $doread = true;
+ if (self::getXmlName($xml) == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
+ if (!$xml->isEmptyElement) {
+ ++$currCells;
+ $xml->next();
+ $doread = false;
+ }
+ } elseif (self::getXmlName($xml) == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
+ $mergeSize = $xml->getAttribute('table:number-columns-repeated');
+ $currCells += (int) $mergeSize;
+ }
+ if ($doread) {
+ $xml->read();
+ }
+ } while (self::getXmlName($xml) != 'table:table-row');
+ }
+ } while (self::getXmlName($xml) != 'table:table');
+
+ $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+ $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
+ $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+ $worksheetInfo[] = $tmpInfo;
+ }
+ }
+ }
+
+ return $worksheetInfo;
+ }
+
+ /**
+ * Counteract Phpstan caching.
+ *
+ * @phpstan-impure
+ */
+ private static function getXmlName(XMLReader $xml): string
+ {
+ return $xml->name;
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+ $spreadsheet->removeSheetByIndex(0);
+
+ // Load into this instance
+ return $this->loadIntoExisting($filename, $spreadsheet);
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+ */
+ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
+ {
+ File::assertFile($filename, self::INITIAL_FILE);
+
+ $zip = new ZipArchive();
+ $zip->open($filename);
+
+ // Meta
+
+ $xml = @simplexml_load_string(
+ $this->getSecurityScannerOrThrow()->scan($zip->getFromName('meta.xml')),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+ if ($xml === false) {
+ throw new Exception('Unable to read data from {$pFilename}');
+ }
+
+ $namespacesMeta = $xml->getNamespaces(true);
+
+ (new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
+
+ // Styles
+
+ $dom = new DOMDocument('1.01', 'UTF-8');
+ $dom->loadXML(
+ $this->getSecurityScannerOrThrow()->scan($zip->getFromName('styles.xml')),
+ Settings::getLibXmlLoaderOptions()
+ );
+
+ $pageSettings = new PageSettings($dom);
+
+ // Main Content
+
+ $dom = new DOMDocument('1.01', 'UTF-8');
+ $dom->loadXML(
+ $this->getSecurityScannerOrThrow()->scan($zip->getFromName(self::INITIAL_FILE)),
+ Settings::getLibXmlLoaderOptions()
+ );
+
+ $officeNs = (string) $dom->lookupNamespaceUri('office');
+ $tableNs = (string) $dom->lookupNamespaceUri('table');
+ $textNs = (string) $dom->lookupNamespaceUri('text');
+ $xlinkNs = (string) $dom->lookupNamespaceUri('xlink');
+ $styleNs = (string) $dom->lookupNamespaceUri('style');
+
+ $pageSettings->readStyleCrossReferences($dom);
+
+ $autoFilterReader = new AutoFilter($spreadsheet, $tableNs);
+ $definedNameReader = new DefinedNames($spreadsheet, $tableNs);
+ $columnWidths = [];
+ $automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
+ $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
+ foreach ($automaticStyles as $automaticStyle) {
+ $styleName = $automaticStyle->getAttributeNS($styleNs, 'name');
+ $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
+ if ($styleFamily === 'table-column') {
+ $tcprops = $automaticStyle->getElementsByTagNameNS($styleNs, 'table-column-properties');
+ if ($tcprops !== null) {
+ $tcprop = $tcprops->item(0);
+ if ($tcprop !== null) {
+ $columnWidth = $tcprop->getAttributeNs($styleNs, 'column-width');
+ $columnWidths[$styleName] = $columnWidth;
+ }
+ }
+ }
+ }
+
+ // Content
+ $item0 = $dom->getElementsByTagNameNS($officeNs, 'body')->item(0);
+ $spreadsheets = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($officeNs, 'spreadsheet');
+
+ foreach ($spreadsheets as $workbookData) {
+ /** @var DOMElement $workbookData */
+ $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table');
+
+ $worksheetID = 0;
+ foreach ($tables as $worksheetDataSet) {
+ /** @var DOMElement $worksheetDataSet */
+ $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name');
+
+ // Check loadSheetsOnly
+ if (
+ $this->loadSheetsOnly !== null
+ && $worksheetName
+ && !in_array($worksheetName, $this->loadSheetsOnly)
+ ) {
+ continue;
+ }
+
+ $worksheetStyleName = $worksheetDataSet->getAttributeNS($tableNs, 'style-name');
+
+ // Create sheet
+ $spreadsheet->createSheet();
+ $spreadsheet->setActiveSheetIndex($worksheetID);
+
+ if ($worksheetName || is_numeric($worksheetName)) {
+ // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
+ // formula cells... during the load, all formulae should be correct, and we're simply
+ // bringing the worksheet name in line with the formula, not the reverse
+ $spreadsheet->getActiveSheet()->setTitle((string) $worksheetName, false, false);
+ }
+
+ // Go through every child of table element
+ $rowID = 1;
+ $tableColumnIndex = 1;
+ foreach ($worksheetDataSet->childNodes as $childNode) {
+ /** @var DOMElement $childNode */
+
+ // Filter elements which are not under the "table" ns
+ if ($childNode->namespaceURI != $tableNs) {
+ continue;
+ }
+
+ $key = $childNode->nodeName;
+
+ // Remove ns from node name
+ if (str_contains($key, ':')) {
+ $keyChunks = explode(':', $key);
+ $key = array_pop($keyChunks);
+ }
+
+ switch ($key) {
+ case 'table-header-rows':
+ /// TODO :: Figure this out. This is only a partial implementation I guess.
+ // ($rowData it's not used at all and I'm not sure that PHPExcel
+ // has an API for this)
+
+// foreach ($rowData as $keyRowData => $cellData) {
+// $rowData = $cellData;
+// break;
+// }
+ break;
+ case 'table-column':
+ if ($childNode->hasAttributeNS($tableNs, 'number-columns-repeated')) {
+ $rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-columns-repeated');
+ } else {
+ $rowRepeats = 1;
+ }
+ $tableStyleName = $childNode->getAttributeNS($tableNs, 'style-name');
+ if (isset($columnWidths[$tableStyleName])) {
+ $columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
+ $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex);
+ for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0; --$rowRepeats2) {
+ $spreadsheet->getActiveSheet()
+ ->getColumnDimension($tableColumnString)
+ ->setWidth($columnWidth->toUnit('cm'), 'cm');
+ ++$tableColumnString;
+ }
+ }
+ $tableColumnIndex += $rowRepeats;
+
+ break;
+ case 'table-row':
+ if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) {
+ $rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
+ } else {
+ $rowRepeats = 1;
+ }
+
+ $columnID = 'A';
+ /** @var DOMElement $cellData */
+ foreach ($childNode->childNodes as $cellData) {
+ if ($this->getReadFilter() !== null) {
+ if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
+ if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
+ $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
+ } else {
+ $colRepeats = 1;
+ }
+
+ for ($i = 0; $i < $colRepeats; ++$i) {
+ ++$columnID;
+ }
+
+ continue;
+ }
+ }
+
+ // Initialize variables
+ $formatting = $hyperlink = null;
+ $hasCalculatedValue = false;
+ $cellDataFormula = '';
+
+ if ($cellData->hasAttributeNS($tableNs, 'formula')) {
+ $cellDataFormula = $cellData->getAttributeNS($tableNs, 'formula');
+ $hasCalculatedValue = true;
+ }
+
+ // Annotations
+ $annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation');
+
+ if ($annotation->length > 0 && $annotation->item(0) !== null) {
+ $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p');
+
+ if ($textNode->length > 0 && $textNode->item(0) !== null) {
+ $text = $this->scanElementForText($textNode->item(0));
+
+ $spreadsheet->getActiveSheet()
+ ->getComment($columnID . $rowID)
+ ->setText($this->parseRichText($text));
+// ->setAuthor( $author )
+ }
+ }
+
+ // Content
+
+ /** @var DOMElement[] $paragraphs */
+ $paragraphs = [];
+
+ foreach ($cellData->childNodes as $item) {
+ /** @var DOMElement $item */
+
+ // Filter text:p elements
+ if ($item->nodeName == 'text:p') {
+ $paragraphs[] = $item;
+ }
+ }
+
+ if (count($paragraphs) > 0) {
+ // Consolidate if there are multiple p records (maybe with spans as well)
+ $dataArray = [];
+
+ // Text can have multiple text:p and within those, multiple text:span.
+ // text:p newlines, but text:span does not.
+ // Also, here we assume there is no text data is span fields are specified, since
+ // we have no way of knowing proper positioning anyway.
+
+ foreach ($paragraphs as $pData) {
+ $dataArray[] = $this->scanElementForText($pData);
+ }
+ $allCellDataText = implode("\n", $dataArray);
+
+ $type = $cellData->getAttributeNS($officeNs, 'value-type');
+
+ switch ($type) {
+ case 'string':
+ $type = DataType::TYPE_STRING;
+ $dataValue = $allCellDataText;
+
+ foreach ($paragraphs as $paragraph) {
+ $link = $paragraph->getElementsByTagNameNS($textNs, 'a');
+ if ($link->length > 0 && $link->item(0) !== null) {
+ $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href');
+ }
+ }
+
+ break;
+ case 'boolean':
+ $type = DataType::TYPE_BOOL;
+ $dataValue = ($allCellDataText == 'TRUE') ? true : false;
+
+ break;
+ case 'percentage':
+ $type = DataType::TYPE_NUMERIC;
+ $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
+
+ // percentage should always be float
+ //if (floor($dataValue) == $dataValue) {
+ // $dataValue = (int) $dataValue;
+ //}
+ $formatting = NumberFormat::FORMAT_PERCENTAGE_00;
+
+ break;
+ case 'currency':
+ $type = DataType::TYPE_NUMERIC;
+ $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
+
+ if (floor($dataValue) == $dataValue) {
+ $dataValue = (int) $dataValue;
+ }
+ $formatting = NumberFormat::FORMAT_CURRENCY_USD_INTEGER;
+
+ break;
+ case 'float':
+ $type = DataType::TYPE_NUMERIC;
+ $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
+
+ if (floor($dataValue) == $dataValue) {
+ if ($dataValue == (int) $dataValue) {
+ $dataValue = (int) $dataValue;
+ }
+ }
+
+ break;
+ case 'date':
+ $type = DataType::TYPE_NUMERIC;
+ $value = $cellData->getAttributeNS($officeNs, 'date-value');
+ $dataValue = Date::convertIsoDate($value);
+
+ if ($dataValue != floor($dataValue)) {
+ $formatting = NumberFormat::FORMAT_DATE_XLSX15
+ . ' '
+ . NumberFormat::FORMAT_DATE_TIME4;
+ } else {
+ $formatting = NumberFormat::FORMAT_DATE_XLSX15;
+ }
+
+ break;
+ case 'time':
+ $type = DataType::TYPE_NUMERIC;
+
+ $timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
+
+ $dataValue = Date::PHPToExcel(
+ strtotime(
+ '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS') ?? [])
+ )
+ );
+ $formatting = NumberFormat::FORMAT_DATE_TIME4;
+
+ break;
+ default:
+ $dataValue = null;
+ }
+ } else {
+ $type = DataType::TYPE_NULL;
+ $dataValue = null;
+ }
+
+ if ($hasCalculatedValue) {
+ $type = DataType::TYPE_FORMULA;
+ $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1);
+ $cellDataFormula = FormulaTranslator::convertToExcelFormulaValue($cellDataFormula);
+ }
+
+ if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
+ $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
+ } else {
+ $colRepeats = 1;
+ }
+
+ if ($type !== null) {
+ for ($i = 0; $i < $colRepeats; ++$i) {
+ if ($i > 0) {
+ ++$columnID;
+ }
+
+ if ($type !== DataType::TYPE_NULL) {
+ for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
+ $rID = $rowID + $rowAdjust;
+
+ $cell = $spreadsheet->getActiveSheet()
+ ->getCell($columnID . $rID);
+
+ // Set value
+ if ($hasCalculatedValue) {
+ $cell->setValueExplicit($cellDataFormula, $type);
+ } else {
+ $cell->setValueExplicit($dataValue, $type);
+ }
+
+ if ($hasCalculatedValue) {
+ $cell->setCalculatedValue($dataValue, $type === DataType::TYPE_NUMERIC);
+ }
+
+ // Set other properties
+ if ($formatting !== null) {
+ $spreadsheet->getActiveSheet()
+ ->getStyle($columnID . $rID)
+ ->getNumberFormat()
+ ->setFormatCode($formatting);
+ } else {
+ $spreadsheet->getActiveSheet()
+ ->getStyle($columnID . $rID)
+ ->getNumberFormat()
+ ->setFormatCode(NumberFormat::FORMAT_GENERAL);
+ }
+
+ if ($hyperlink !== null) {
+ if ($hyperlink[0] === '#') {
+ $hyperlink = 'sheet://' . substr($hyperlink, 1);
+ }
+ $cell->getHyperlink()
+ ->setUrl($hyperlink);
+ }
+ }
+ }
+ }
+ }
+
+ // Merged cells
+ $this->processMergedCells($cellData, $tableNs, $type, $columnID, $rowID, $spreadsheet);
+
+ ++$columnID;
+ }
+ $rowID += $rowRepeats;
+
+ break;
+ }
+ }
+ $pageSettings->setVisibilityForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
+ $pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
+ ++$worksheetID;
+ }
+
+ $autoFilterReader->read($workbookData);
+ $definedNameReader->read($workbookData);
+ }
+ $spreadsheet->setActiveSheetIndex(0);
+
+ if ($zip->locateName('settings.xml') !== false) {
+ $this->processSettings($zip, $spreadsheet);
+ }
+
+ // Return
+ return $spreadsheet;
+ }
+
+ private function processSettings(ZipArchive $zip, Spreadsheet $spreadsheet): void
+ {
+ $dom = new DOMDocument('1.01', 'UTF-8');
+ $dom->loadXML(
+ $this->getSecurityScannerOrThrow()->scan($zip->getFromName('settings.xml')),
+ Settings::getLibXmlLoaderOptions()
+ );
+ //$xlinkNs = $dom->lookupNamespaceUri('xlink');
+ $configNs = (string) $dom->lookupNamespaceUri('config');
+ //$oooNs = $dom->lookupNamespaceUri('ooo');
+ $officeNs = (string) $dom->lookupNamespaceUri('office');
+ $settings = $dom->getElementsByTagNameNS($officeNs, 'settings')
+ ->item(0);
+ if ($settings !== null) {
+ $this->lookForActiveSheet($settings, $spreadsheet, $configNs);
+ $this->lookForSelectedCells($settings, $spreadsheet, $configNs);
+ }
+ }
+
+ private function lookForActiveSheet(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
+ {
+ /** @var DOMElement $t */
+ foreach ($settings->getElementsByTagNameNS($configNs, 'config-item') as $t) {
+ if ($t->getAttributeNs($configNs, 'name') === 'ActiveTable') {
+ try {
+ $spreadsheet->setActiveSheetIndexByName($t->nodeValue ?? '');
+ } catch (Throwable) {
+ // do nothing
+ }
+
+ break;
+ }
+ }
+ }
+
+ private function lookForSelectedCells(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
+ {
+ /** @var DOMElement $t */
+ foreach ($settings->getElementsByTagNameNS($configNs, 'config-item-map-named') as $t) {
+ if ($t->getAttributeNs($configNs, 'name') === 'Tables') {
+ foreach ($t->getElementsByTagNameNS($configNs, 'config-item-map-entry') as $ws) {
+ $setRow = $setCol = '';
+ $wsname = $ws->getAttributeNs($configNs, 'name');
+ foreach ($ws->getElementsByTagNameNS($configNs, 'config-item') as $configItem) {
+ $attrName = $configItem->getAttributeNs($configNs, 'name');
+ if ($attrName === 'CursorPositionX') {
+ $setCol = $configItem->nodeValue;
+ }
+ if ($attrName === 'CursorPositionY') {
+ $setRow = $configItem->nodeValue;
+ }
+ }
+ $this->setSelected($spreadsheet, $wsname, "$setCol", "$setRow");
+ }
+
+ break;
+ }
+ }
+ }
+
+ private function setSelected(Spreadsheet $spreadsheet, string $wsname, string $setCol, string $setRow): void
+ {
+ if (is_numeric($setCol) && is_numeric($setRow)) {
+ $sheet = $spreadsheet->getSheetByName($wsname);
+ if ($sheet !== null) {
+ $sheet->setSelectedCells([(int) $setCol + 1, (int) $setRow + 1]);
+ }
+ }
+ }
+
+ /**
+ * Recursively scan element.
+ */
+ protected function scanElementForText(DOMNode $element): string
+ {
+ $str = '';
+ foreach ($element->childNodes as $child) {
+ /** @var DOMNode $child */
+ if ($child->nodeType == XML_TEXT_NODE) {
+ $str .= $child->nodeValue;
+ } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') {
+ // It's a space
+
+ // Multiple spaces?
+ $attributes = $child->attributes;
+ /** @var ?DOMAttr $cAttr */
+ $cAttr = ($attributes === null) ? null : $attributes->getNamedItem('c');
+ $multiplier = self::getMultiplier($cAttr);
+ $str .= str_repeat(' ', $multiplier);
+ }
+
+ if ($child->hasChildNodes()) {
+ $str .= $this->scanElementForText($child);
+ }
+ }
+
+ return $str;
+ }
+
+ private static function getMultiplier(?DOMAttr $cAttr): int
+ {
+ if ($cAttr) {
+ $multiplier = (int) $cAttr->nodeValue;
+ } else {
+ $multiplier = 1;
+ }
+
+ return $multiplier;
+ }
+
+ private function parseRichText(string $is): RichText
+ {
+ $value = new RichText();
+ $value->createText($is);
+
+ return $value;
+ }
+
+ private function processMergedCells(
+ DOMElement $cellData,
+ string $tableNs,
+ string $type,
+ string $columnID,
+ int $rowID,
+ Spreadsheet $spreadsheet
+ ): void {
+ if (
+ $cellData->hasAttributeNS($tableNs, 'number-columns-spanned')
+ || $cellData->hasAttributeNS($tableNs, 'number-rows-spanned')
+ ) {
+ if (($type !== DataType::TYPE_NULL) || ($this->readDataOnly === false)) {
+ $columnTo = $columnID;
+
+ if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) {
+ $columnIndex = Coordinate::columnIndexFromString($columnID);
+ $columnIndex += (int) $cellData->getAttributeNS($tableNs, 'number-columns-spanned');
+ $columnIndex -= 2;
+
+ $columnTo = Coordinate::stringFromColumnIndex($columnIndex + 1);
+ }
+
+ $rowTo = $rowID;
+
+ if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) {
+ $rowTo = $rowTo + (int) $cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1;
+ }
+
+ $cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
+ $spreadsheet->getActiveSheet()->mergeCells($cellRange, Worksheet::MERGE_CELL_CONTENT_HIDE);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php
new file mode 100644
index 0000000..1f5f975
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php
@@ -0,0 +1,45 @@
+readAutoFilters($workbookData);
+ }
+
+ protected function readAutoFilters(DOMElement $workbookData): void
+ {
+ $databases = $workbookData->getElementsByTagNameNS($this->tableNs, 'database-ranges');
+
+ foreach ($databases as $autofilters) {
+ foreach ($autofilters->childNodes as $autofilter) {
+ $autofilterRange = $this->getAttributeValue($autofilter, 'target-range-address');
+ if ($autofilterRange !== null) {
+ $baseAddress = FormulaTranslator::convertToExcelAddressValue($autofilterRange);
+ $this->spreadsheet->getActiveSheet()->setAutoFilter($baseAddress);
+ }
+ }
+ }
+ }
+
+ protected function getAttributeValue(?DOMNode $node, string $attributeName): ?string
+ {
+ if ($node !== null && $node->attributes !== null) {
+ $attribute = $node->attributes->getNamedItemNS(
+ $this->tableNs,
+ $attributeName
+ );
+
+ if ($attribute !== null) {
+ return $attribute->nodeValue;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php
new file mode 100644
index 0000000..c280c4e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php
@@ -0,0 +1,21 @@
+spreadsheet = $spreadsheet;
+ $this->tableNs = $tableNs;
+ }
+
+ abstract public function read(DOMElement $workbookData): void;
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php
new file mode 100644
index 0000000..a99e3ea
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php
@@ -0,0 +1,70 @@
+readDefinedRanges($workbookData);
+ $this->readDefinedExpressions($workbookData);
+ }
+
+ /**
+ * Read any Named Ranges that are defined in this spreadsheet.
+ */
+ protected function readDefinedRanges(DOMElement $workbookData): void
+ {
+ $namedRanges = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-range');
+ foreach ($namedRanges as $definedNameElement) {
+ $definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name');
+ $baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
+ $range = $definedNameElement->getAttributeNS($this->tableNs, 'cell-range-address');
+
+ /** @var non-empty-string $baseAddress */
+ $baseAddress = FormulaTranslator::convertToExcelAddressValue($baseAddress);
+ $range = FormulaTranslator::convertToExcelAddressValue($range);
+
+ $this->addDefinedName($baseAddress, $definedName, $range);
+ }
+ }
+
+ /**
+ * Read any Named Formulae that are defined in this spreadsheet.
+ */
+ protected function readDefinedExpressions(DOMElement $workbookData): void
+ {
+ $namedExpressions = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-expression');
+ foreach ($namedExpressions as $definedNameElement) {
+ $definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name');
+ $baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
+ $expression = $definedNameElement->getAttributeNS($this->tableNs, 'expression');
+
+ /** @var non-empty-string $baseAddress */
+ $baseAddress = FormulaTranslator::convertToExcelAddressValue($baseAddress);
+ $expression = substr($expression, strpos($expression, ':=') + 1);
+ $expression = FormulaTranslator::convertToExcelFormulaValue($expression);
+
+ $this->addDefinedName($baseAddress, $definedName, $expression);
+ }
+ }
+
+ /**
+ * Assess scope and store the Defined Name.
+ *
+ * @param non-empty-string $baseAddress
+ */
+ private function addDefinedName(string $baseAddress, string $definedName, string $value): void
+ {
+ [$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true);
+ $worksheet = $this->spreadsheet->getSheetByName($sheetReference);
+ // Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet
+ if ($worksheet !== null) {
+ $this->spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value));
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php
new file mode 100644
index 0000000..27862d7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php
@@ -0,0 +1,97 @@
+setDomNameSpaces($styleDom);
+ $this->readPageSettingStyles($styleDom);
+ $this->readStyleMasterLookup($styleDom);
+ }
+
+ private function setDomNameSpaces(DOMDocument $styleDom): void
+ {
+ $this->officeNs = (string) $styleDom->lookupNamespaceUri('office');
+ $this->stylesNs = (string) $styleDom->lookupNamespaceUri('style');
+ $this->stylesFo = (string) $styleDom->lookupNamespaceUri('fo');
+ $this->tableNs = (string) $styleDom->lookupNamespaceUri('table');
+ }
+
+ private function readPageSettingStyles(DOMDocument $styleDom): void
+ {
+ $item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
+ $styles = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'page-layout');
+
+ foreach ($styles as $styleSet) {
+ $styleName = $styleSet->getAttributeNS($this->stylesNs, 'name');
+ $pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')[0];
+ $styleOrientation = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-orientation');
+ $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to');
+ $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order');
+ $centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering');
+
+ $marginLeft = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-left');
+ $marginRight = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-right');
+ $marginTop = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-top');
+ $marginBottom = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-bottom');
+ $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')[0];
+ $headerProperties = $header->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0];
+ $marginHeader = isset($headerProperties) ? $headerProperties->getAttributeNS($this->stylesFo, 'min-height') : null;
+ $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')[0];
+ $footerProperties = $footer->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0];
+ $marginFooter = isset($footerProperties) ? $footerProperties->getAttributeNS($this->stylesFo, 'min-height') : null;
+
+ $this->pageLayoutStyles[$styleName] = (object) [
+ 'orientation' => $styleOrientation ?: PageSetup::ORIENTATION_DEFAULT,
+ 'scale' => $styleScale ?: 100,
+ 'printOrder' => $stylePrintOrder,
+ 'horizontalCentered' => $centered === 'horizontal' || $centered === 'both',
+ 'verticalCentered' => $centered === 'vertical' || $centered === 'both',
+ // margin size is already stored in inches, so no UOM conversion is required
+ 'marginLeft' => (float) ($marginLeft ?? 0.7),
+ 'marginRight' => (float) ($marginRight ?? 0.7),
+ 'marginTop' => (float) ($marginTop ?? 0.3),
+ 'marginBottom' => (float) ($marginBottom ?? 0.3),
+ 'marginHeader' => (float) ($marginHeader ?? 0.45),
+ 'marginFooter' => (float) ($marginFooter ?? 0.45),
+ ];
+ }
+ }
+
+ private function readStyleMasterLookup(DOMDocument $styleDom): void
+ {
+ $item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles')->item(0);
+ $styleMasterLookup = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'master-page');
+
+ foreach ($styleMasterLookup as $styleMasterSet) {
+ $styleMasterName = $styleMasterSet->getAttributeNS($this->stylesNs, 'name');
+ $pageLayoutName = $styleMasterSet->getAttributeNS($this->stylesNs, 'page-layout-name');
+ $this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName;
+ }
+ }
+
+ public function readStyleCrossReferences(DOMDocument $contentDom): void
+ {
+ $item0 = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
+ $styleXReferences = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'style');
+
+ foreach ($styleXReferences as $styleXreferenceSet) {
+ $styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name');
+ $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name');
+ $styleFamilyName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'family');
+ if (!empty($styleFamilyName) && $styleFamilyName === 'table') {
+ $styleVisibility = 'true';
+ foreach ($styleXreferenceSet->getElementsByTagNameNS($this->stylesNs, 'table-properties') as $tableProperties) {
+ $styleVisibility = $tableProperties->getAttributeNS($this->tableNs, 'display');
+ }
+ $this->tableStylesCrossReference[$styleXRefName] = $styleVisibility;
+ }
+ if (!empty($stylePageLayoutName)) {
+ $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName;
+ }
+ }
+ }
+
+ public function setVisibilityForWorksheet(Worksheet $worksheet, string $styleName): void
+ {
+ if (!array_key_exists($styleName, $this->tableStylesCrossReference)) {
+ return;
+ }
+
+ $worksheet->setSheetState(
+ $this->tableStylesCrossReference[$styleName] === 'false'
+ ? Worksheet::SHEETSTATE_HIDDEN
+ : Worksheet::SHEETSTATE_VISIBLE
+ );
+ }
+
+ public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void
+ {
+ if (!array_key_exists($styleName, $this->masterStylesCrossReference)) {
+ return;
+ }
+ $masterStyleName = $this->masterStylesCrossReference[$styleName];
+
+ if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) {
+ return;
+ }
+ $printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName];
+
+ if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) {
+ return;
+ }
+ $printSettings = $this->pageLayoutStyles[$printSettingsIndex];
+
+ $worksheet->getPageSetup()
+ ->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT)
+ ->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER)
+ ->setScale((int) trim($printSettings->scale, '%'))
+ ->setHorizontalCentered($printSettings->horizontalCentered)
+ ->setVerticalCentered($printSettings->verticalCentered);
+
+ $worksheet->getPageMargins()
+ ->setLeft($printSettings->marginLeft)
+ ->setRight($printSettings->marginRight)
+ ->setTop($printSettings->marginTop)
+ ->setBottom($printSettings->marginBottom)
+ ->setHeader($printSettings->marginHeader)
+ ->setFooter($printSettings->marginFooter);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
new file mode 100644
index 0000000..f565610
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
@@ -0,0 +1,136 @@
+spreadsheet = $spreadsheet;
+ }
+
+ public function load(SimpleXMLElement $xml, array $namespacesMeta): void
+ {
+ $docProps = $this->spreadsheet->getProperties();
+ $officeProperty = $xml->children($namespacesMeta['office']);
+ foreach ($officeProperty as $officePropertyData) {
+ if (isset($namespacesMeta['dc'])) {
+ $officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
+ $this->setCoreProperties($docProps, $officePropertiesDC);
+ }
+
+ $officePropertyMeta = null;
+ if (isset($namespacesMeta['dc'])) {
+ $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
+ }
+ $officePropertyMeta = $officePropertyMeta ?? [];
+ foreach ($officePropertyMeta as $propertyName => $propertyValue) {
+ $this->setMetaProperties($namespacesMeta, $propertyValue, $propertyName, $docProps);
+ }
+ }
+ }
+
+ private function setCoreProperties(DocumentProperties $docProps, SimpleXMLElement $officePropertyDC): void
+ {
+ foreach ($officePropertyDC as $propertyName => $propertyValue) {
+ $propertyValue = (string) $propertyValue;
+ switch ($propertyName) {
+ case 'title':
+ $docProps->setTitle($propertyValue);
+
+ break;
+ case 'subject':
+ $docProps->setSubject($propertyValue);
+
+ break;
+ case 'creator':
+ $docProps->setCreator($propertyValue);
+ $docProps->setLastModifiedBy($propertyValue);
+
+ break;
+ case 'date':
+ $docProps->setModified($propertyValue);
+
+ break;
+ case 'description':
+ $docProps->setDescription($propertyValue);
+
+ break;
+ }
+ }
+ }
+
+ private function setMetaProperties(
+ array $namespacesMeta,
+ SimpleXMLElement $propertyValue,
+ string $propertyName,
+ DocumentProperties $docProps
+ ): void {
+ $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
+ $propertyValue = (string) $propertyValue;
+ switch ($propertyName) {
+ case 'initial-creator':
+ $docProps->setCreator($propertyValue);
+
+ break;
+ case 'keyword':
+ $docProps->setKeywords($propertyValue);
+
+ break;
+ case 'creation-date':
+ $docProps->setCreated($propertyValue);
+
+ break;
+ case 'user-defined':
+ $name2 = (string) ($propertyValueAttributes['name'] ?? '');
+ if ($name2 === 'Company') {
+ $docProps->setCompany($propertyValue);
+ } elseif ($name2 === 'category') {
+ $docProps->setCategory($propertyValue);
+ } else {
+ $this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
+ }
+
+ break;
+ }
+ }
+
+ private function setUserDefinedProperty(mixed $propertyValueAttributes, string $propertyValue, DocumentProperties $docProps): void
+ {
+ $propertyValueName = '';
+ $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
+ foreach ($propertyValueAttributes as $key => $value) {
+ if ($key == 'name') {
+ $propertyValueName = (string) $value;
+ } elseif ($key == 'value-type') {
+ switch ($value) {
+ case 'date':
+ $propertyValue = DocumentProperties::convertProperty($propertyValue, 'date');
+ $propertyValueType = DocumentProperties::PROPERTY_TYPE_DATE;
+
+ break;
+ case 'boolean':
+ $propertyValue = DocumentProperties::convertProperty($propertyValue, 'bool');
+ $propertyValueType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
+
+ break;
+ case 'float':
+ $propertyValue = DocumentProperties::convertProperty($propertyValue, 'r4');
+ $propertyValueType = DocumentProperties::PROPERTY_TYPE_FLOAT;
+
+ break;
+ default:
+ $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
+ }
+ }
+ }
+
+ $docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
new file mode 100644
index 0000000..1fbddcb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
@@ -0,0 +1,97 @@
+pattern = $pattern;
+ }
+
+ public static function getInstance(Reader\IReader $reader): self
+ {
+ $pattern = ($reader instanceof Reader\Html) ? 'callback = $callback;
+ }
+
+ private static function forceString(mixed $arg): string
+ {
+ return is_string($arg) ? $arg : '';
+ }
+
+ private function toUtf8(string $xml): string
+ {
+ $pattern = '/encoding="(.*?)"/';
+ $result = preg_match($pattern, $xml, $matches);
+ $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+
+ if ($charset !== 'UTF-8') {
+ $xml = self::forceString(mb_convert_encoding($xml, 'UTF-8', $charset));
+
+ $result = preg_match($pattern, $xml, $matches);
+ $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+ if ($charset !== 'UTF-8') {
+ throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
+ }
+ }
+
+ return $xml;
+ }
+
+ /**
+ * Scan the XML for use of toUtf8($xml);
+
+ // Don't rely purely on libxml_disable_entity_loader()
+ $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
+
+ if (preg_match($pattern, $xml)) {
+ throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
+ }
+
+ if ($this->callback !== null) {
+ $xml = call_user_func($this->callback, $xml);
+ }
+
+ return $xml;
+ }
+
+ /**
+ * Scan theXML for use of scan(file_get_contents($filestream));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
new file mode 100644
index 0000000..275d097
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
@@ -0,0 +1,617 @@
+openFile($filename);
+ } catch (ReaderException) {
+ return false;
+ }
+
+ // Read sample data (first 2 KB will do)
+ $data = (string) fread($this->fileHandle, 2048);
+
+ // Count delimiters in file
+ $delimiterCount = substr_count($data, ';');
+ $hasDelimiter = $delimiterCount > 0;
+
+ // Analyze first line looking for ID; signature
+ $lines = explode("\n", $data);
+ $hasId = str_starts_with($lines[0], 'ID;P');
+
+ fclose($this->fileHandle);
+
+ return $hasDelimiter && $hasId;
+ }
+
+ private function canReadOrBust(string $filename): void
+ {
+ if (!$this->canRead($filename)) {
+ throw new ReaderException($filename . ' is an Invalid SYLK file.');
+ }
+ $this->openFile($filename);
+ }
+
+ /**
+ * Set input encoding.
+ *
+ * @deprecated no use is made of this property
+ *
+ * @param string $inputEncoding Input encoding, eg: 'ANSI'
+ *
+ * @return $this
+ *
+ * @codeCoverageIgnore
+ */
+ public function setInputEncoding($inputEncoding): static
+ {
+ $this->inputEncoding = $inputEncoding;
+
+ return $this;
+ }
+
+ /**
+ * Get input encoding.
+ *
+ * @deprecated no use is made of this property
+ *
+ * @return string
+ *
+ * @codeCoverageIgnore
+ */
+ public function getInputEncoding()
+ {
+ return $this->inputEncoding;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ // Open file
+ $this->canReadOrBust($filename);
+ $fileHandle = $this->fileHandle;
+ rewind($fileHandle);
+
+ $worksheetInfo = [];
+ $worksheetInfo[0]['worksheetName'] = basename($filename, '.slk');
+
+ // loop through one row (line) at a time in the file
+ $rowIndex = 0;
+ $columnIndex = 0;
+ while (($rowData = fgets($fileHandle)) !== false) {
+ $columnIndex = 0;
+
+ // convert SYLK encoded $rowData to UTF-8
+ $rowData = StringHelper::SYLKtoUTF8($rowData);
+
+ // explode each row at semicolons while taking into account that literal semicolon (;)
+ // is escaped like this (;;)
+ $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
+
+ $dataType = array_shift($rowData);
+ if ($dataType == 'B') {
+ foreach ($rowData as $rowDatum) {
+ switch ($rowDatum[0]) {
+ case 'X':
+ $columnIndex = (int) substr($rowDatum, 1) - 1;
+
+ break;
+ case 'Y':
+ $rowIndex = (int) substr($rowDatum, 1);
+
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ $worksheetInfo[0]['lastColumnIndex'] = $columnIndex;
+ $worksheetInfo[0]['totalRows'] = $rowIndex;
+ $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
+ $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
+
+ // Close file
+ fclose($fileHandle);
+
+ return $worksheetInfo;
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+
+ // Load into this instance
+ return $this->loadIntoExisting($filename, $spreadsheet);
+ }
+
+ private const COLOR_ARRAY = [
+ 'FF00FFFF', // 0 - cyan
+ 'FF000000', // 1 - black
+ 'FFFFFFFF', // 2 - white
+ 'FFFF0000', // 3 - red
+ 'FF00FF00', // 4 - green
+ 'FF0000FF', // 5 - blue
+ 'FFFFFF00', // 6 - yellow
+ 'FFFF00FF', // 7 - magenta
+ ];
+
+ private const FONT_STYLE_MAPPINGS = [
+ 'B' => 'bold',
+ 'I' => 'italic',
+ 'U' => 'underline',
+ ];
+
+ private function processFormula(string $rowDatum, bool &$hasCalculatedValue, string &$cellDataFormula, string $row, string $column): void
+ {
+ $cellDataFormula = '=' . substr($rowDatum, 1);
+ // Convert R1C1 style references to A1 style references (but only when not quoted)
+ $temp = explode('"', $cellDataFormula);
+ $key = false;
+ foreach ($temp as &$value) {
+ // Only count/replace in alternate array entries
+ $key = $key === false;
+ if ($key) {
+ preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
+ // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
+ // through the formula from left to right. Reversing means that we work right to left.through
+ // the formula
+ $cellReferences = array_reverse($cellReferences);
+ // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
+ // then modify the formula to use that new reference
+ foreach ($cellReferences as $cellReference) {
+ $rowReference = $cellReference[2][0];
+ // Empty R reference is the current row
+ if ($rowReference == '') {
+ $rowReference = $row;
+ }
+ // Bracketed R references are relative to the current row
+ if ($rowReference[0] == '[') {
+ $rowReference = (int) $row + (int) trim($rowReference, '[]');
+ }
+ $columnReference = $cellReference[4][0];
+ // Empty C reference is the current column
+ if ($columnReference == '') {
+ $columnReference = $column;
+ }
+ // Bracketed C references are relative to the current column
+ if ($columnReference[0] == '[') {
+ $columnReference = (int) $column + (int) trim($columnReference, '[]');
+ }
+ $A1CellReference = Coordinate::stringFromColumnIndex((int) $columnReference) . $rowReference;
+
+ $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
+ }
+ }
+ }
+ unset($value);
+ // Then rebuild the formula string
+ $cellDataFormula = implode('"', $temp);
+ $hasCalculatedValue = true;
+ }
+
+ private function processCRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void
+ {
+ // Read cell value data
+ $hasCalculatedValue = false;
+ $tryNumeric = false;
+ $cellDataFormula = $cellData = '';
+ $sharedColumn = $sharedRow = -1;
+ $sharedFormula = false;
+ foreach ($rowData as $rowDatum) {
+ switch ($rowDatum[0]) {
+ case 'X':
+ $column = substr($rowDatum, 1);
+
+ break;
+ case 'Y':
+ $row = substr($rowDatum, 1);
+
+ break;
+ case 'K':
+ $cellData = substr($rowDatum, 1);
+ $tryNumeric = is_numeric($cellData);
+
+ break;
+ case 'E':
+ $this->processFormula($rowDatum, $hasCalculatedValue, $cellDataFormula, $row, $column);
+
+ break;
+ case 'A':
+ $comment = substr($rowDatum, 1);
+ $columnLetter = Coordinate::stringFromColumnIndex((int) $column);
+ $spreadsheet->getActiveSheet()
+ ->getComment("$columnLetter$row")
+ ->getText()
+ ->createText($comment);
+
+ break;
+ case 'C':
+ $sharedColumn = (int) substr($rowDatum, 1);
+
+ break;
+ case 'R':
+ $sharedRow = (int) substr($rowDatum, 1);
+
+ break;
+ case 'S':
+ $sharedFormula = true;
+
+ break;
+ }
+ }
+ if ($sharedFormula === true && $sharedRow >= 0 && $sharedColumn >= 0) {
+ $thisCoordinate = Coordinate::stringFromColumnIndex((int) $column) . $row;
+ $sharedCoordinate = Coordinate::stringFromColumnIndex($sharedColumn) . $sharedRow;
+ $formula = $spreadsheet->getActiveSheet()->getCell($sharedCoordinate)->getValue();
+ $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setValue($formula);
+ $referenceHelper = ReferenceHelper::getInstance();
+ $newFormula = $referenceHelper->updateFormulaReferences($formula, 'A1', (int) $column - $sharedColumn, (int) $row - $sharedRow, '', true, false);
+ $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setValue($newFormula);
+ //$calc = $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->getCalculatedValue();
+ //$spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setCalculatedValue($calc);
+ $cellData = Calculation::unwrapResult($cellData);
+ $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setCalculatedValue($cellData, $tryNumeric);
+
+ return;
+ }
+ $columnLetter = Coordinate::stringFromColumnIndex((int) $column);
+ $cellData = Calculation::unwrapResult($cellData);
+
+ // Set cell value
+ $this->processCFinal($spreadsheet, $hasCalculatedValue, $cellDataFormula, $cellData, "$columnLetter$row", $tryNumeric);
+ }
+
+ private function processCFinal(Spreadsheet &$spreadsheet, bool $hasCalculatedValue, string $cellDataFormula, string $cellData, string $coordinate, bool $tryNumeric): void
+ {
+ // Set cell value
+ $spreadsheet->getActiveSheet()->getCell($coordinate)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
+ if ($hasCalculatedValue) {
+ $cellData = Calculation::unwrapResult($cellData);
+ $spreadsheet->getActiveSheet()->getCell($coordinate)->setCalculatedValue($cellData, $tryNumeric);
+ }
+ }
+
+ private function processFRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void
+ {
+ // Read cell formatting
+ $formatStyle = $columnWidth = '';
+ $startCol = $endCol = '';
+ $fontStyle = '';
+ $styleData = [];
+ foreach ($rowData as $rowDatum) {
+ switch ($rowDatum[0]) {
+ case 'C':
+ case 'X':
+ $column = substr($rowDatum, 1);
+
+ break;
+ case 'R':
+ case 'Y':
+ $row = substr($rowDatum, 1);
+
+ break;
+ case 'P':
+ $formatStyle = $rowDatum;
+
+ break;
+ case 'W':
+ [$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1));
+
+ break;
+ case 'S':
+ $this->styleSettings($rowDatum, $styleData, $fontStyle);
+
+ break;
+ }
+ }
+ $this->addFormats($spreadsheet, $formatStyle, $row, $column);
+ $this->addFonts($spreadsheet, $fontStyle, $row, $column);
+ $this->addStyle($spreadsheet, $styleData, $row, $column);
+ $this->addWidth($spreadsheet, $columnWidth, $startCol, $endCol);
+ }
+
+ private const STYLE_SETTINGS_FONT = ['D' => 'bold', 'I' => 'italic'];
+
+ private const STYLE_SETTINGS_BORDER = [
+ 'B' => 'bottom',
+ 'L' => 'left',
+ 'R' => 'right',
+ 'T' => 'top',
+ ];
+
+ private function styleSettings(string $rowDatum, array &$styleData, string &$fontStyle): void
+ {
+ $styleSettings = substr($rowDatum, 1);
+ $iMax = strlen($styleSettings);
+ for ($i = 0; $i < $iMax; ++$i) {
+ $char = $styleSettings[$i];
+ if (array_key_exists($char, self::STYLE_SETTINGS_FONT)) {
+ $styleData['font'][self::STYLE_SETTINGS_FONT[$char]] = true;
+ } elseif (array_key_exists($char, self::STYLE_SETTINGS_BORDER)) {
+ $styleData['borders'][self::STYLE_SETTINGS_BORDER[$char]]['borderStyle'] = Border::BORDER_THIN;
+ } elseif ($char == 'S') {
+ $styleData['fill']['fillType'] = Fill::FILL_PATTERN_GRAY125;
+ } elseif ($char == 'M') {
+ if (preg_match('/M([1-9]\\d*)/', $styleSettings, $matches)) {
+ $fontStyle = $matches[1];
+ }
+ }
+ }
+ }
+
+ private function addFormats(Spreadsheet &$spreadsheet, string $formatStyle, string $row, string $column): void
+ {
+ if ($formatStyle && $column > '' && $row > '') {
+ $columnLetter = Coordinate::stringFromColumnIndex((int) $column);
+ if (isset($this->formats[$formatStyle])) {
+ $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]);
+ }
+ }
+ }
+
+ private function addFonts(Spreadsheet &$spreadsheet, string $fontStyle, string $row, string $column): void
+ {
+ if ($fontStyle && $column > '' && $row > '') {
+ $columnLetter = Coordinate::stringFromColumnIndex((int) $column);
+ if (isset($this->fonts[$fontStyle])) {
+ $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->fonts[$fontStyle]);
+ }
+ }
+ }
+
+ private function addStyle(Spreadsheet &$spreadsheet, array $styleData, string $row, string $column): void
+ {
+ if ((!empty($styleData)) && $column > '' && $row > '') {
+ $columnLetter = Coordinate::stringFromColumnIndex((int) $column);
+ $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData);
+ }
+ }
+
+ private function addWidth(Spreadsheet $spreadsheet, string $columnWidth, string $startCol, string $endCol): void
+ {
+ if ($columnWidth > '') {
+ if ($startCol == $endCol) {
+ $startCol = Coordinate::stringFromColumnIndex((int) $startCol);
+ $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
+ } else {
+ $startCol = Coordinate::stringFromColumnIndex((int) $startCol);
+ $endCol = Coordinate::stringFromColumnIndex((int) $endCol);
+ $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
+ do {
+ $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth((float) $columnWidth);
+ } while ($startCol !== $endCol);
+ }
+ }
+ }
+
+ private function processPRecord(array $rowData, Spreadsheet &$spreadsheet): void
+ {
+ // Read shared styles
+ $formatArray = [];
+ $fromFormats = ['\-', '\ '];
+ $toFormats = ['-', ' '];
+ foreach ($rowData as $rowDatum) {
+ switch ($rowDatum[0]) {
+ case 'P':
+ $formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1));
+
+ break;
+ case 'E':
+ case 'F':
+ $formatArray['font']['name'] = substr($rowDatum, 1);
+
+ break;
+ case 'M':
+ $formatArray['font']['size'] = ((float) substr($rowDatum, 1)) / 20;
+
+ break;
+ case 'L':
+ $this->processPColors($rowDatum, $formatArray);
+
+ break;
+ case 'S':
+ $this->processPFontStyles($rowDatum, $formatArray);
+
+ break;
+ }
+ }
+ $this->processPFinal($spreadsheet, $formatArray);
+ }
+
+ private function processPColors(string $rowDatum, array &$formatArray): void
+ {
+ if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) {
+ $fontColor = $matches[1] % 8;
+ $formatArray['font']['color']['argb'] = self::COLOR_ARRAY[$fontColor];
+ }
+ }
+
+ private function processPFontStyles(string $rowDatum, array &$formatArray): void
+ {
+ $styleSettings = substr($rowDatum, 1);
+ $iMax = strlen($styleSettings);
+ for ($i = 0; $i < $iMax; ++$i) {
+ if (array_key_exists($styleSettings[$i], self::FONT_STYLE_MAPPINGS)) {
+ $formatArray['font'][self::FONT_STYLE_MAPPINGS[$styleSettings[$i]]] = true;
+ }
+ }
+ }
+
+ private function processPFinal(Spreadsheet &$spreadsheet, array $formatArray): void
+ {
+ if (array_key_exists('numberFormat', $formatArray)) {
+ $this->formats['P' . $this->format] = $formatArray;
+ ++$this->format;
+ } elseif (array_key_exists('font', $formatArray)) {
+ ++$this->fontcount;
+ $this->fonts[$this->fontcount] = $formatArray;
+ if ($this->fontcount === 1) {
+ $spreadsheet->getDefaultStyle()->applyFromArray($formatArray);
+ }
+ }
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
+ *
+ * @return Spreadsheet
+ */
+ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet)
+ {
+ // Open file
+ $this->canReadOrBust($filename);
+ $fileHandle = $this->fileHandle;
+ rewind($fileHandle);
+
+ // Create new Worksheets
+ while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
+ $spreadsheet->createSheet();
+ }
+ $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+ $spreadsheet->getActiveSheet()->setTitle(substr(basename($filename, '.slk'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
+
+ // Loop through file
+ $column = $row = '';
+
+ // loop through one row (line) at a time in the file
+ while (($rowDataTxt = fgets($fileHandle)) !== false) {
+ // convert SYLK encoded $rowData to UTF-8
+ $rowDataTxt = StringHelper::SYLKtoUTF8($rowDataTxt);
+
+ // explode each row at semicolons while taking into account that literal semicolon (;)
+ // is escaped like this (;;)
+ $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowDataTxt)))));
+
+ $dataType = array_shift($rowData);
+ if ($dataType == 'P') {
+ // Read shared styles
+ $this->processPRecord($rowData, $spreadsheet);
+ } elseif ($dataType == 'C') {
+ // Read cell value data
+ $this->processCRecord($rowData, $spreadsheet, $row, $column);
+ } elseif ($dataType == 'F') {
+ // Read cell formatting
+ $this->processFRecord($rowData, $spreadsheet, $row, $column);
+ } else {
+ $this->columnRowFromRowData($rowData, $column, $row);
+ }
+ }
+
+ // Close file
+ fclose($fileHandle);
+
+ // Return
+ return $spreadsheet;
+ }
+
+ private function columnRowFromRowData(array $rowData, string &$column, string &$row): void
+ {
+ foreach ($rowData as $rowDatum) {
+ $char0 = $rowDatum[0];
+ if ($char0 === 'X' || $char0 == 'C') {
+ $column = substr($rowDatum, 1);
+ } elseif ($char0 === 'Y' || $char0 == 'R') {
+ $row = substr($rowDatum, 1);
+ }
+ }
+ }
+
+ /**
+ * Get sheet index.
+ *
+ * @return int
+ */
+ public function getSheetIndex()
+ {
+ return $this->sheetIndex;
+ }
+
+ /**
+ * Set sheet index.
+ *
+ * @param int $sheetIndex Sheet index
+ *
+ * @return $this
+ */
+ public function setSheetIndex($sheetIndex): static
+ {
+ $this->sheetIndex = $sheetIndex;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
new file mode 100644
index 0000000..b18fd09
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
@@ -0,0 +1,7656 @@
+data.
+ *
+ * @var int
+ */
+ private $dataSize;
+
+ /**
+ * Current position in stream.
+ *
+ * @var int
+ */
+ private $pos;
+
+ /**
+ * Workbook to be returned by the reader.
+ *
+ * @var Spreadsheet
+ */
+ private $spreadsheet;
+
+ /**
+ * Worksheet that is currently being built by the reader.
+ *
+ * @var Worksheet
+ */
+ private $phpSheet;
+
+ /**
+ * BIFF version.
+ *
+ * @var int
+ */
+ private $version = 0;
+
+ /**
+ * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
+ * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
+ */
+ private string $codepage = '';
+
+ /**
+ * Shared formats.
+ *
+ * @var array
+ */
+ private $formats;
+
+ /**
+ * Shared fonts.
+ *
+ * @var Font[]
+ */
+ private $objFonts;
+
+ /**
+ * Color palette.
+ *
+ * @var array
+ */
+ private $palette;
+
+ /**
+ * Worksheets.
+ *
+ * @var array
+ */
+ private $sheets;
+
+ /**
+ * External books.
+ *
+ * @var array
+ */
+ private $externalBooks;
+
+ /**
+ * REF structures. Only applies to BIFF8.
+ *
+ * @var array
+ */
+ private $ref;
+
+ /**
+ * External names.
+ *
+ * @var array
+ */
+ private $externalNames;
+
+ /**
+ * Defined names.
+ *
+ * @var array
+ */
+ private $definedname;
+
+ /**
+ * Shared strings. Only applies to BIFF8.
+ *
+ * @var array
+ */
+ private $sst;
+
+ /**
+ * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
+ *
+ * @var bool
+ */
+ private $frozen;
+
+ /**
+ * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
+ *
+ * @var bool
+ */
+ private $isFitToPages;
+
+ /**
+ * Objects. One OBJ record contributes with one entry.
+ *
+ * @var array
+ */
+ private $objs;
+
+ /**
+ * Text Objects. One TXO record corresponds with one entry.
+ *
+ * @var array
+ */
+ private $textObjects;
+
+ /**
+ * Cell Annotations (BIFF8).
+ *
+ * @var array
+ */
+ private $cellNotes;
+
+ /**
+ * The combined MSODRAWINGGROUP data.
+ *
+ * @var string
+ */
+ private $drawingGroupData;
+
+ /**
+ * The combined MSODRAWING data (per sheet).
+ *
+ * @var string
+ */
+ private $drawingData;
+
+ /**
+ * Keep track of XF index.
+ *
+ * @var int
+ */
+ private $xfIndex;
+
+ /**
+ * Mapping of XF index (that is a cell XF) to final index in cellXf collection.
+ *
+ * @var array
+ */
+ private $mapCellXfIndex;
+
+ /**
+ * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection.
+ *
+ * @var array
+ */
+ private $mapCellStyleXfIndex;
+
+ /**
+ * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
+ *
+ * @var array
+ */
+ private $sharedFormulas;
+
+ /**
+ * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
+ * refers to a shared formula.
+ *
+ * @var array
+ */
+ private $sharedFormulaParts;
+
+ /**
+ * The type of encryption in use.
+ *
+ * @var int
+ */
+ private $encryption = 0;
+
+ /**
+ * The position in the stream after which contents are encrypted.
+ *
+ * @var int
+ */
+ private $encryptionStartPos = 0;
+
+ /**
+ * The current RC4 decryption object.
+ *
+ * @var ?Xls\RC4
+ */
+ private $rc4Key;
+
+ /**
+ * The position in the stream that the RC4 decryption object was left at.
+ *
+ * @var int
+ */
+ private $rc4Pos = 0;
+
+ /**
+ * The current MD5 context state.
+ * It is never set in the program, so code which uses it is suspect.
+ *
+ * @var string
+ */
+ private $md5Ctxt; // @phpstan-ignore-line
+
+ /**
+ * @var int
+ */
+ private $textObjRef;
+
+ /**
+ * @var string
+ */
+ private $baseCell;
+
+ /** @var bool */
+ private $activeSheetSet = false;
+
+ /**
+ * Create a new Xls Reader instance.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Can the current IReader read the file?
+ */
+ public function canRead(string $filename): bool
+ {
+ if (File::testFileNoThrow($filename) === false) {
+ return false;
+ }
+
+ try {
+ // Use ParseXL for the hard work.
+ $ole = new OLERead();
+
+ // get excel data
+ $ole->read($filename);
+ if ($ole->wrkbook === null) {
+ throw new Exception('The filename ' . $filename . ' is not recognised as a Spreadsheet file');
+ }
+
+ return true;
+ } catch (PhpSpreadsheetException) {
+ return false;
+ }
+ }
+
+ public function setCodepage(string $codepage): void
+ {
+ if (CodePage::validate($codepage) === false) {
+ throw new PhpSpreadsheetException('Unknown codepage: ' . $codepage);
+ }
+
+ $this->codepage = $codepage;
+ }
+
+ public function getCodepage(): string
+ {
+ return $this->codepage;
+ }
+
+ /**
+ * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
+ */
+ public function listWorksheetNames(string $filename): array
+ {
+ File::assertFile($filename);
+
+ $worksheetNames = [];
+
+ // Read the OLE file
+ $this->loadOLE($filename);
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->dataSize = strlen($this->data);
+
+ $this->pos = 0;
+ $this->sheets = [];
+
+ // Parse Workbook Global Substream
+ while ($this->pos < $this->dataSize) {
+ $code = self::getUInt2d($this->data, $this->pos);
+
+ match ($code) {
+ self::XLS_TYPE_BOF => $this->readBof(),
+ self::XLS_TYPE_SHEET => $this->readSheet(),
+ self::XLS_TYPE_EOF => $this->readDefault(),
+ self::XLS_TYPE_CODEPAGE => $this->readCodepage(),
+ default => $this->readDefault(),
+ };
+
+ if ($code === self::XLS_TYPE_EOF) {
+ break;
+ }
+ }
+
+ foreach ($this->sheets as $sheet) {
+ if ($sheet['sheetType'] != 0x00) {
+ // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
+ continue;
+ }
+
+ $worksheetNames[] = $sheet['name'];
+ }
+
+ return $worksheetNames;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ File::assertFile($filename);
+
+ $worksheetInfo = [];
+
+ // Read the OLE file
+ $this->loadOLE($filename);
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->dataSize = strlen($this->data);
+
+ // initialize
+ $this->pos = 0;
+ $this->sheets = [];
+
+ // Parse Workbook Global Substream
+ while ($this->pos < $this->dataSize) {
+ $code = self::getUInt2d($this->data, $this->pos);
+
+ match ($code) {
+ self::XLS_TYPE_BOF => $this->readBof(),
+ self::XLS_TYPE_SHEET => $this->readSheet(),
+ self::XLS_TYPE_EOF => $this->readDefault(),
+ self::XLS_TYPE_CODEPAGE => $this->readCodepage(),
+ default => $this->readDefault(),
+ };
+
+ if ($code === self::XLS_TYPE_EOF) {
+ break;
+ }
+ }
+
+ // Parse the individual sheets
+ foreach ($this->sheets as $sheet) {
+ if ($sheet['sheetType'] != 0x00) {
+ // 0x00: Worksheet
+ // 0x02: Chart
+ // 0x06: Visual Basic module
+ continue;
+ }
+
+ $tmpInfo = [];
+ $tmpInfo['worksheetName'] = $sheet['name'];
+ $tmpInfo['lastColumnLetter'] = 'A';
+ $tmpInfo['lastColumnIndex'] = 0;
+ $tmpInfo['totalRows'] = 0;
+ $tmpInfo['totalColumns'] = 0;
+
+ $this->pos = $sheet['offset'];
+
+ while ($this->pos <= $this->dataSize - 4) {
+ $code = self::getUInt2d($this->data, $this->pos);
+
+ switch ($code) {
+ case self::XLS_TYPE_RK:
+ case self::XLS_TYPE_LABELSST:
+ case self::XLS_TYPE_NUMBER:
+ case self::XLS_TYPE_FORMULA:
+ case self::XLS_TYPE_BOOLERR:
+ case self::XLS_TYPE_LABEL:
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ $rowIndex = self::getUInt2d($recordData, 0) + 1;
+ $columnIndex = self::getUInt2d($recordData, 2);
+
+ $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
+ $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
+
+ break;
+ case self::XLS_TYPE_BOF:
+ $this->readBof();
+
+ break;
+ case self::XLS_TYPE_EOF:
+ $this->readDefault();
+
+ break 2;
+ default:
+ $this->readDefault();
+
+ break;
+ }
+ }
+
+ $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+ $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
+
+ $worksheetInfo[] = $tmpInfo;
+ }
+
+ return $worksheetInfo;
+ }
+
+ /**
+ * Loads PhpSpreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Read the OLE file
+ $this->loadOLE($filename);
+
+ // Initialisations
+ $this->spreadsheet = new Spreadsheet();
+ $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
+ if (!$this->readDataOnly) {
+ $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
+ $this->spreadsheet->removeCellXfByIndex(0); // remove the default style
+ }
+
+ // Read the summary information stream (containing meta data)
+ $this->readSummaryInformation();
+
+ // Read the Additional document summary information stream (containing application-specific meta data)
+ $this->readDocumentSummaryInformation();
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->dataSize = strlen($this->data);
+
+ // initialize
+ $this->pos = 0;
+ $this->codepage = $this->codepage ?: CodePage::DEFAULT_CODE_PAGE;
+ $this->formats = [];
+ $this->objFonts = [];
+ $this->palette = [];
+ $this->sheets = [];
+ $this->externalBooks = [];
+ $this->ref = [];
+ $this->definedname = [];
+ $this->sst = [];
+ $this->drawingGroupData = '';
+ $this->xfIndex = 0;
+ $this->mapCellXfIndex = [];
+ $this->mapCellStyleXfIndex = [];
+
+ // Parse Workbook Global Substream
+ while ($this->pos < $this->dataSize) {
+ $code = self::getUInt2d($this->data, $this->pos);
+
+ match ($code) {
+ self::XLS_TYPE_BOF => $this->readBof(),
+ self::XLS_TYPE_FILEPASS => $this->readFilepass(),
+ self::XLS_TYPE_CODEPAGE => $this->readCodepage(),
+ self::XLS_TYPE_DATEMODE => $this->readDateMode(),
+ self::XLS_TYPE_FONT => $this->readFont(),
+ self::XLS_TYPE_FORMAT => $this->readFormat(),
+ self::XLS_TYPE_XF => $this->readXf(),
+ self::XLS_TYPE_XFEXT => $this->readXfExt(),
+ self::XLS_TYPE_STYLE => $this->readStyle(),
+ self::XLS_TYPE_PALETTE => $this->readPalette(),
+ self::XLS_TYPE_SHEET => $this->readSheet(),
+ self::XLS_TYPE_EXTERNALBOOK => $this->readExternalBook(),
+ self::XLS_TYPE_EXTERNNAME => $this->readExternName(),
+ self::XLS_TYPE_EXTERNSHEET => $this->readExternSheet(),
+ self::XLS_TYPE_DEFINEDNAME => $this->readDefinedName(),
+ self::XLS_TYPE_MSODRAWINGGROUP => $this->readMsoDrawingGroup(),
+ self::XLS_TYPE_SST => $this->readSst(),
+ self::XLS_TYPE_EOF => $this->readDefault(),
+ default => $this->readDefault(),
+ };
+
+ if ($code === self::XLS_TYPE_EOF) {
+ break;
+ }
+ }
+
+ // Resolve indexed colors for font, fill, and border colors
+ // Cannot be resolved already in XF record, because PALETTE record comes afterwards
+ if (!$this->readDataOnly) {
+ foreach ($this->objFonts as $objFont) {
+ if (isset($objFont->colorIndex)) {
+ $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
+ $objFont->getColor()->setRGB($color['rgb']);
+ }
+ }
+
+ foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
+ // fill start and end color
+ $fill = $objStyle->getFill();
+
+ if (isset($fill->startcolorIndex)) {
+ $startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
+ $fill->getStartColor()->setRGB($startColor['rgb']);
+ }
+ if (isset($fill->endcolorIndex)) {
+ $endColor = Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version);
+ $fill->getEndColor()->setRGB($endColor['rgb']);
+ }
+
+ // border colors
+ $top = $objStyle->getBorders()->getTop();
+ $right = $objStyle->getBorders()->getRight();
+ $bottom = $objStyle->getBorders()->getBottom();
+ $left = $objStyle->getBorders()->getLeft();
+ $diagonal = $objStyle->getBorders()->getDiagonal();
+
+ if (isset($top->colorIndex)) {
+ $borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version);
+ $top->getColor()->setRGB($borderTopColor['rgb']);
+ }
+ if (isset($right->colorIndex)) {
+ $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
+ $right->getColor()->setRGB($borderRightColor['rgb']);
+ }
+ if (isset($bottom->colorIndex)) {
+ $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
+ $bottom->getColor()->setRGB($borderBottomColor['rgb']);
+ }
+ if (isset($left->colorIndex)) {
+ $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
+ $left->getColor()->setRGB($borderLeftColor['rgb']);
+ }
+ if (isset($diagonal->colorIndex)) {
+ $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
+ $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
+ }
+ }
+ }
+
+ // treat MSODRAWINGGROUP records, workbook-level Escher
+ $escherWorkbook = null;
+ if (!$this->readDataOnly && $this->drawingGroupData) {
+ $escher = new Escher();
+ $reader = new Xls\Escher($escher);
+ $escherWorkbook = $reader->load($this->drawingGroupData);
+ }
+
+ // Parse the individual sheets
+ $this->activeSheetSet = false;
+ foreach ($this->sheets as $sheet) {
+ if ($sheet['sheetType'] != 0x00) {
+ // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
+ continue;
+ }
+
+ // check if sheet should be skipped
+ if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
+ continue;
+ }
+
+ // add sheet to PhpSpreadsheet object
+ $this->phpSheet = $this->spreadsheet->createSheet();
+ // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
+ // cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
+ // name in line with the formula, not the reverse
+ $this->phpSheet->setTitle($sheet['name'], false, false);
+ $this->phpSheet->setSheetState($sheet['sheetState']);
+
+ $this->pos = $sheet['offset'];
+
+ // Initialize isFitToPages. May change after reading SHEETPR record.
+ $this->isFitToPages = false;
+
+ // Initialize drawingData
+ $this->drawingData = '';
+
+ // Initialize objs
+ $this->objs = [];
+
+ // Initialize shared formula parts
+ $this->sharedFormulaParts = [];
+
+ // Initialize shared formulas
+ $this->sharedFormulas = [];
+
+ // Initialize text objs
+ $this->textObjects = [];
+
+ // Initialize cell annotations
+ $this->cellNotes = [];
+ $this->textObjRef = -1;
+
+ while ($this->pos <= $this->dataSize - 4) {
+ $code = self::getUInt2d($this->data, $this->pos);
+
+ switch ($code) {
+ case self::XLS_TYPE_BOF:
+ $this->readBof();
+
+ break;
+ case self::XLS_TYPE_PRINTGRIDLINES:
+ $this->readPrintGridlines();
+
+ break;
+ case self::XLS_TYPE_DEFAULTROWHEIGHT:
+ $this->readDefaultRowHeight();
+
+ break;
+ case self::XLS_TYPE_SHEETPR:
+ $this->readSheetPr();
+
+ break;
+ case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
+ $this->readHorizontalPageBreaks();
+
+ break;
+ case self::XLS_TYPE_VERTICALPAGEBREAKS:
+ $this->readVerticalPageBreaks();
+
+ break;
+ case self::XLS_TYPE_HEADER:
+ $this->readHeader();
+
+ break;
+ case self::XLS_TYPE_FOOTER:
+ $this->readFooter();
+
+ break;
+ case self::XLS_TYPE_HCENTER:
+ $this->readHcenter();
+
+ break;
+ case self::XLS_TYPE_VCENTER:
+ $this->readVcenter();
+
+ break;
+ case self::XLS_TYPE_LEFTMARGIN:
+ $this->readLeftMargin();
+
+ break;
+ case self::XLS_TYPE_RIGHTMARGIN:
+ $this->readRightMargin();
+
+ break;
+ case self::XLS_TYPE_TOPMARGIN:
+ $this->readTopMargin();
+
+ break;
+ case self::XLS_TYPE_BOTTOMMARGIN:
+ $this->readBottomMargin();
+
+ break;
+ case self::XLS_TYPE_PAGESETUP:
+ $this->readPageSetup();
+
+ break;
+ case self::XLS_TYPE_PROTECT:
+ $this->readProtect();
+
+ break;
+ case self::XLS_TYPE_SCENPROTECT:
+ $this->readScenProtect();
+
+ break;
+ case self::XLS_TYPE_OBJECTPROTECT:
+ $this->readObjectProtect();
+
+ break;
+ case self::XLS_TYPE_PASSWORD:
+ $this->readPassword();
+
+ break;
+ case self::XLS_TYPE_DEFCOLWIDTH:
+ $this->readDefColWidth();
+
+ break;
+ case self::XLS_TYPE_COLINFO:
+ $this->readColInfo();
+
+ break;
+ case self::XLS_TYPE_DIMENSION:
+ $this->readDefault();
+
+ break;
+ case self::XLS_TYPE_ROW:
+ $this->readRow();
+
+ break;
+ case self::XLS_TYPE_DBCELL:
+ $this->readDefault();
+
+ break;
+ case self::XLS_TYPE_RK:
+ $this->readRk();
+
+ break;
+ case self::XLS_TYPE_LABELSST:
+ $this->readLabelSst();
+
+ break;
+ case self::XLS_TYPE_MULRK:
+ $this->readMulRk();
+
+ break;
+ case self::XLS_TYPE_NUMBER:
+ $this->readNumber();
+
+ break;
+ case self::XLS_TYPE_FORMULA:
+ $this->readFormula();
+
+ break;
+ case self::XLS_TYPE_SHAREDFMLA:
+ $this->readSharedFmla();
+
+ break;
+ case self::XLS_TYPE_BOOLERR:
+ $this->readBoolErr();
+
+ break;
+ case self::XLS_TYPE_MULBLANK:
+ $this->readMulBlank();
+
+ break;
+ case self::XLS_TYPE_LABEL:
+ $this->readLabel();
+
+ break;
+ case self::XLS_TYPE_BLANK:
+ $this->readBlank();
+
+ break;
+ case self::XLS_TYPE_MSODRAWING:
+ $this->readMsoDrawing();
+
+ break;
+ case self::XLS_TYPE_OBJ:
+ $this->readObj();
+
+ break;
+ case self::XLS_TYPE_WINDOW2:
+ $this->readWindow2();
+
+ break;
+ case self::XLS_TYPE_PAGELAYOUTVIEW:
+ $this->readPageLayoutView();
+
+ break;
+ case self::XLS_TYPE_SCL:
+ $this->readScl();
+
+ break;
+ case self::XLS_TYPE_PANE:
+ $this->readPane();
+
+ break;
+ case self::XLS_TYPE_SELECTION:
+ $this->readSelection();
+
+ break;
+ case self::XLS_TYPE_MERGEDCELLS:
+ $this->readMergedCells();
+
+ break;
+ case self::XLS_TYPE_HYPERLINK:
+ $this->readHyperLink();
+
+ break;
+ case self::XLS_TYPE_DATAVALIDATIONS:
+ $this->readDataValidations();
+
+ break;
+ case self::XLS_TYPE_DATAVALIDATION:
+ $this->readDataValidation();
+
+ break;
+ case self::XLS_TYPE_CFHEADER:
+ $cellRangeAddresses = $this->readCFHeader();
+
+ break;
+ case self::XLS_TYPE_CFRULE:
+ $this->readCFRule($cellRangeAddresses ?? []);
+
+ break;
+ case self::XLS_TYPE_SHEETLAYOUT:
+ $this->readSheetLayout();
+
+ break;
+ case self::XLS_TYPE_SHEETPROTECTION:
+ $this->readSheetProtection();
+
+ break;
+ case self::XLS_TYPE_RANGEPROTECTION:
+ $this->readRangeProtection();
+
+ break;
+ case self::XLS_TYPE_NOTE:
+ $this->readNote();
+
+ break;
+ case self::XLS_TYPE_TXO:
+ $this->readTextObject();
+
+ break;
+ case self::XLS_TYPE_CONTINUE:
+ $this->readContinue();
+
+ break;
+ case self::XLS_TYPE_EOF:
+ $this->readDefault();
+
+ break 2;
+ default:
+ $this->readDefault();
+
+ break;
+ }
+ }
+
+ // treat MSODRAWING records, sheet-level Escher
+ if (!$this->readDataOnly && $this->drawingData) {
+ $escherWorksheet = new Escher();
+ $reader = new Xls\Escher($escherWorksheet);
+ $escherWorksheet = $reader->load($this->drawingData);
+
+ // get all spContainers in one long array, so they can be mapped to OBJ records
+ /** @var SpContainer[] */
+ $allSpContainers = method_exists($escherWorksheet, 'getDgContainer') ? $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers() : [];
+ }
+
+ // treat OBJ records
+ foreach ($this->objs as $n => $obj) {
+ // the first shape container never has a corresponding OBJ record, hence $n + 1
+ if (isset($allSpContainers[$n + 1])) {
+ $spContainer = $allSpContainers[$n + 1];
+
+ // we skip all spContainers that are a part of a group shape since we cannot yet handle those
+ if ($spContainer->getNestingLevel() > 1) {
+ continue;
+ }
+
+ // calculate the width and height of the shape
+ /** @var int $startRow */
+ [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
+ /** @var int $endRow */
+ [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
+
+ $startOffsetX = $spContainer->getStartOffsetX();
+ $startOffsetY = $spContainer->getStartOffsetY();
+ $endOffsetX = $spContainer->getEndOffsetX();
+ $endOffsetY = $spContainer->getEndOffsetY();
+
+ $width = SharedXls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
+ $height = SharedXls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
+
+ // calculate offsetX and offsetY of the shape
+ $offsetX = (int) ($startOffsetX * SharedXls::sizeCol($this->phpSheet, $startColumn) / 1024);
+ $offsetY = (int) ($startOffsetY * SharedXls::sizeRow($this->phpSheet, $startRow) / 256);
+
+ switch ($obj['otObjType']) {
+ case 0x19:
+ // Note
+ if (isset($this->cellNotes[$obj['idObjID']])) {
+ //$cellNote = $this->cellNotes[$obj['idObjID']];
+
+ if (isset($this->textObjects[$obj['idObjID']])) {
+ $textObject = $this->textObjects[$obj['idObjID']];
+ $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
+ }
+ }
+
+ break;
+ case 0x08:
+ // picture
+ // get index to BSE entry (1-based)
+ $BSEindex = $spContainer->getOPT(0x0104);
+
+ // If there is no BSE Index, we will fail here and other fields are not read.
+ // Fix by checking here.
+ // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
+ // More likely : a uncompatible picture
+ if (!$BSEindex) {
+ continue 2;
+ }
+
+ if ($escherWorkbook) {
+ $BSECollection = method_exists($escherWorkbook, 'getDggContainer') ? $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection() : [];
+ $BSE = $BSECollection[$BSEindex - 1];
+ $blipType = $BSE->getBlipType();
+
+ // need check because some blip types are not supported by Escher reader such as EMF
+ if ($blip = $BSE->getBlip()) {
+ $ih = imagecreatefromstring($blip->getData());
+ if ($ih !== false) {
+ $drawing = new MemoryDrawing();
+ $drawing->setImageResource($ih);
+
+ // width, height, offsetX, offsetY
+ $drawing->setResizeProportional(false);
+ $drawing->setWidth($width);
+ $drawing->setHeight($height);
+ $drawing->setOffsetX($offsetX);
+ $drawing->setOffsetY($offsetY);
+
+ switch ($blipType) {
+ case BSE::BLIPTYPE_JPEG:
+ $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
+ $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
+
+ break;
+ case BSE::BLIPTYPE_PNG:
+ imagealphablending($ih, false);
+ imagesavealpha($ih, true);
+ $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
+ $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
+
+ break;
+ }
+
+ $drawing->setWorksheet($this->phpSheet);
+ $drawing->setCoordinates($spContainer->getStartCoordinates());
+ }
+ }
+ }
+
+ break;
+ default:
+ // other object type
+ break;
+ }
+ }
+ }
+
+ // treat SHAREDFMLA records
+ if ($this->version == self::XLS_BIFF8) {
+ foreach ($this->sharedFormulaParts as $cell => $baseCell) {
+ /** @var int $row */
+ [$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);
+ }
+ }
+ }
+
+ if (!empty($this->cellNotes)) {
+ foreach ($this->cellNotes as $note => $noteDetails) {
+ if (!isset($noteDetails['objTextData'])) {
+ if (isset($this->textObjects[$note])) {
+ $textObject = $this->textObjects[$note];
+ $noteDetails['objTextData'] = $textObject;
+ } else {
+ $noteDetails['objTextData']['text'] = '';
+ }
+ }
+ $cellAddress = str_replace('$', '', $noteDetails['cellRef']);
+ $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
+ }
+ }
+ }
+ if ($this->activeSheetSet === false) {
+ $this->spreadsheet->setActiveSheetIndex(0);
+ }
+
+ // add the named ranges (defined names)
+ foreach ($this->definedname as $definedName) {
+ if ($definedName['isBuiltInName']) {
+ switch ($definedName['name']) {
+ case pack('C', 0x06):
+ // print area
+ // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
+ $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+
+ $extractedRanges = [];
+ $sheetName = '';
+ /** @var non-empty-string $range */
+ foreach ($ranges as $range) {
+ // $range should look like one of these
+ // Foo!$C$7:$J$66
+ // Bar!$A$1:$IV$2
+ $explodes = Worksheet::extractSheetTitle($range, true);
+ $sheetName = trim($explodes[0], "'");
+ if (!str_contains($explodes[1], ':')) {
+ $explodes[1] = $explodes[1] . ':' . $explodes[1];
+ }
+ $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
+ }
+ if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
+ $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
+ }
+
+ break;
+ case pack('C', 0x07):
+ // print titles (repeating rows)
+ // Assuming BIFF8, there are 3 cases
+ // 1. repeating rows
+ // formula looks like this: Sheet!$A$1:$IV$2
+ // rows 1-2 repeat
+ // 2. repeating columns
+ // formula looks like this: Sheet!$A$1:$B$65536
+ // columns A-B repeat
+ // 3. both repeating rows and repeating columns
+ // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
+ $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
+ foreach ($ranges as $range) {
+ // $range should look like this one of these
+ // Sheet!$A$1:$B$65536
+ // Sheet!$A$1:$IV$2
+ if (str_contains($range, '!')) {
+ $explodes = Worksheet::extractSheetTitle($range, true);
+ if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
+ $extractedRange = $explodes[1];
+ $extractedRange = str_replace('$', '', $extractedRange);
+
+ $coordinateStrings = explode(':', $extractedRange);
+ if (count($coordinateStrings) == 2) {
+ [$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
+ [$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
+
+ if ($firstColumn == 'A' && $lastColumn == 'IV') {
+ // then we have repeating rows
+ $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
+ } elseif ($firstRow == 1 && $lastRow == 65536) {
+ // then we have repeating columns
+ $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ } else {
+ // Extract range
+ /** @var non-empty-string $formula */
+ $formula = $definedName['formula'];
+ if (str_contains($formula, '!')) {
+ $explodes = Worksheet::extractSheetTitle($formula, true);
+ if (
+ ($docSheet = $this->spreadsheet->getSheetByName($explodes[0]))
+ || ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))
+ ) {
+ $extractedRange = $explodes[1];
+
+ $localOnly = ($definedName['scope'] === 0) ? false : true;
+
+ $scope = ($definedName['scope'] === 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
+
+ $this->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
+ }
+ }
+ // Named Value
+ // TODO Provide support for named values
+ }
+ }
+ $this->data = '';
+
+ return $this->spreadsheet;
+ }
+
+ /**
+ * Read record data from stream, decrypting as required.
+ *
+ * @param string $data Data stream to read from
+ * @param int $pos Position to start reading from
+ * @param int $len Record data length
+ *
+ * @return string Record data
+ */
+ private function readRecordData($data, int $pos, int $len): string
+ {
+ $data = substr($data, $pos, $len);
+
+ // File not encrypted, or record before encryption start point
+ if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
+ return $data;
+ }
+
+ $recordData = '';
+ if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
+ $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
+ $block = (int) floor($pos / self::REKEY_BLOCK);
+ $endBlock = (int) floor(($pos + $len) / self::REKEY_BLOCK);
+
+ // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
+ // at a point earlier in the current block, re-use it as we can save some time.
+ if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
+ $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
+ $step = $pos % self::REKEY_BLOCK;
+ } else {
+ $step = $pos - $this->rc4Pos;
+ }
+ $this->rc4Key->RC4(str_repeat("\0", $step));
+
+ // Decrypt record data (re-keying at the end of every block)
+ while ($block != $endBlock) {
+ $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
+ $recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
+ $data = substr($data, $step);
+ $pos += $step;
+ $len -= $step;
+ ++$block;
+ $this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
+ }
+ $recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
+
+ // Keep track of the position of this decryptor.
+ // We'll try and re-use it later if we can to speed things up
+ $this->rc4Pos = $pos + $len;
+ } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
+ throw new Exception('XOr encryption not supported');
+ }
+
+ return $recordData;
+ }
+
+ /**
+ * Use OLE reader to extract the relevant data streams from the OLE file.
+ */
+ private function loadOLE(string $filename): void
+ {
+ // OLE reader
+ $ole = new OLERead();
+ // get excel data,
+ $ole->read($filename);
+ // Get workbook data: workbook stream + sheet streams
+ $this->data = $ole->getStream($ole->wrkbook); // @phpstan-ignore-line
+ // Get summary information data
+ $this->summaryInformation = $ole->getStream($ole->summaryInformation);
+ // Get additional document summary information data
+ $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
+ }
+
+ /**
+ * Read summary information.
+ */
+ private function readSummaryInformation(): void
+ {
+ if (!isset($this->summaryInformation)) {
+ return;
+ }
+
+ // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+ // offset: 2; size: 2;
+ // offset: 4; size: 2; OS version
+ // offset: 6; size: 2; OS indicator
+ // offset: 8; size: 16
+ // offset: 24; size: 4; section count
+ //$secCount = self::getInt4d($this->summaryInformation, 24);
+
+ // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
+ // offset: 44; size: 4
+ $secOffset = self::getInt4d($this->summaryInformation, 44);
+
+ // section header
+ // offset: $secOffset; size: 4; section length
+ //$secLength = self::getInt4d($this->summaryInformation, $secOffset);
+
+ // offset: $secOffset+4; size: 4; property count
+ $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
+
+ // initialize code page (used to resolve string values)
+ $codePage = 'CP1252';
+
+ // offset: ($secOffset+8); size: var
+ // loop through property decarations and properties
+ for ($i = 0; $i < $countProperties; ++$i) {
+ // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
+ $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
+
+ // Use value of property id as appropriate
+ // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
+ $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
+
+ $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
+
+ // initialize property value
+ $value = null;
+
+ // extract property value based on property type
+ switch ($type) {
+ case 0x02: // 2 byte signed integer
+ $value = self::getUInt2d($this->summaryInformation, $secOffset + 4 + $offset);
+
+ break;
+ case 0x03: // 4 byte signed integer
+ $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
+
+ break;
+ case 0x13: // 4 byte unsigned integer
+ // not needed yet, fix later if necessary
+ break;
+ case 0x1E: // null-terminated string prepended by dword string length
+ $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
+ $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
+ $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
+ $value = rtrim($value);
+
+ break;
+ case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ // PHP-time
+ $value = OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
+
+ break;
+ case 0x47: // Clipboard format
+ // not needed yet, fix later if necessary
+ break;
+ }
+
+ switch ($id) {
+ case 0x01: // Code Page
+ $codePage = CodePage::numberToName((int) $value);
+
+ break;
+ case 0x02: // Title
+ $this->spreadsheet->getProperties()->setTitle("$value");
+
+ break;
+ case 0x03: // Subject
+ $this->spreadsheet->getProperties()->setSubject("$value");
+
+ break;
+ case 0x04: // Author (Creator)
+ $this->spreadsheet->getProperties()->setCreator("$value");
+
+ break;
+ case 0x05: // Keywords
+ $this->spreadsheet->getProperties()->setKeywords("$value");
+
+ break;
+ case 0x06: // Comments (Description)
+ $this->spreadsheet->getProperties()->setDescription("$value");
+
+ break;
+ case 0x07: // Template
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x08: // Last Saved By (LastModifiedBy)
+ $this->spreadsheet->getProperties()->setLastModifiedBy("$value");
+
+ break;
+ case 0x09: // Revision
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0A: // Total Editing Time
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0B: // Last Printed
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0C: // Created Date/Time
+ $this->spreadsheet->getProperties()->setCreated($value);
+
+ break;
+ case 0x0D: // Modified Date/Time
+ $this->spreadsheet->getProperties()->setModified($value);
+
+ break;
+ case 0x0E: // Number of Pages
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0F: // Number of Words
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x10: // Number of Characters
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x11: // Thumbnail
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x12: // Name of creating application
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x13: // Security
+ // Not supported by PhpSpreadsheet
+ break;
+ }
+ }
+ }
+
+ /**
+ * Read additional document summary information.
+ */
+ private function readDocumentSummaryInformation(): void
+ {
+ if (!isset($this->documentSummaryInformation)) {
+ return;
+ }
+
+ // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+ // offset: 2; size: 2;
+ // offset: 4; size: 2; OS version
+ // offset: 6; size: 2; OS indicator
+ // offset: 8; size: 16
+ // offset: 24; size: 4; section count
+ //$secCount = self::getInt4d($this->documentSummaryInformation, 24);
+
+ // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
+ // offset: 44; size: 4; first section offset
+ $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
+
+ // section header
+ // offset: $secOffset; size: 4; section length
+ //$secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
+
+ // offset: $secOffset+4; size: 4; property count
+ $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
+
+ // initialize code page (used to resolve string values)
+ $codePage = 'CP1252';
+
+ // offset: ($secOffset+8); size: var
+ // loop through property decarations and properties
+ for ($i = 0; $i < $countProperties; ++$i) {
+ // offset: ($secOffset+8) + (8 * $i); size: 4; property ID
+ $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
+
+ // Use value of property id as appropriate
+ // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
+ $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
+
+ $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
+
+ // initialize property value
+ $value = null;
+
+ // extract property value based on property type
+ switch ($type) {
+ case 0x02: // 2 byte signed integer
+ $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+
+ break;
+ case 0x03: // 4 byte signed integer
+ $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+
+ break;
+ case 0x0B: // Boolean
+ $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+ $value = ($value == 0 ? false : true);
+
+ break;
+ case 0x13: // 4 byte unsigned integer
+ // not needed yet, fix later if necessary
+ break;
+ case 0x1E: // null-terminated string prepended by dword string length
+ $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
+ $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
+ $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
+ $value = rtrim($value);
+
+ break;
+ case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ // PHP-Time
+ $value = OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
+
+ break;
+ case 0x47: // Clipboard format
+ // not needed yet, fix later if necessary
+ break;
+ }
+
+ switch ($id) {
+ case 0x01: // Code Page
+ $codePage = CodePage::numberToName((int) $value);
+
+ break;
+ case 0x02: // Category
+ $this->spreadsheet->getProperties()->setCategory("$value");
+
+ break;
+ case 0x03: // Presentation Target
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x04: // Bytes
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x05: // Lines
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x06: // Paragraphs
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x07: // Slides
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x08: // Notes
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x09: // Hidden Slides
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0A: // MM Clips
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0B: // Scale Crop
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0C: // Heading Pairs
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0D: // Titles of Parts
+ // Not supported by PhpSpreadsheet
+ break;
+ case 0x0E: // Manager
+ $this->spreadsheet->getProperties()->setManager("$value");
+
+ break;
+ case 0x0F: // Company
+ $this->spreadsheet->getProperties()->setCompany("$value");
+
+ break;
+ case 0x10: // Links up-to-date
+ // Not supported by PhpSpreadsheet
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
+ */
+ private function readDefault(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+ }
+
+ /**
+ * The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
+ * this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
+ */
+ private function readNote(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
+ if ($this->version == self::XLS_BIFF8) {
+ $noteObjID = self::getUInt2d($recordData, 6);
+ $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
+ $noteAuthor = $noteAuthor['value'];
+ $this->cellNotes[$noteObjID] = [
+ 'cellRef' => $cellAddress,
+ 'objectID' => $noteObjID,
+ 'author' => $noteAuthor,
+ ];
+ } else {
+ $extension = false;
+ if ($cellAddress == '$B$65536') {
+ // If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
+ // note from the previous cell annotation. We're not yet handling this, so annotations longer than the
+ // max 2048 bytes will probably throw a wobbly.
+ //$row = self::getUInt2d($recordData, 0);
+ $extension = true;
+ $arrayKeys = array_keys($this->phpSheet->getComments());
+ $cellAddress = array_pop($arrayKeys);
+ }
+
+ $cellAddress = str_replace('$', '', (string) $cellAddress);
+ //$noteLength = self::getUInt2d($recordData, 4);
+ $noteText = trim(substr($recordData, 6));
+
+ if ($extension) {
+ // Concatenate this extension with the currently set comment for the cell
+ $comment = $this->phpSheet->getComment($cellAddress);
+ $commentText = $comment->getText()->getPlainText();
+ $comment->setText($this->parseRichText($commentText . $noteText));
+ } else {
+ // Set comment for the cell
+ $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
+// ->setAuthor($author)
+ }
+ }
+ }
+
+ /**
+ * The TEXT Object record contains the text associated with a cell annotation.
+ */
+ private function readTextObject(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // recordData consists of an array of subrecords looking like this:
+ // grbit: 2 bytes; Option Flags
+ // rot: 2 bytes; rotation
+ // cchText: 2 bytes; length of the text (in the first continue record)
+ // cbRuns: 2 bytes; length of the formatting (in the second continue record)
+ // followed by the continuation records containing the actual text and formatting
+ $grbitOpts = self::getUInt2d($recordData, 0);
+ $rot = self::getUInt2d($recordData, 2);
+ //$cchText = self::getUInt2d($recordData, 10);
+ $cbRuns = self::getUInt2d($recordData, 12);
+ $text = $this->getSplicedRecordData();
+
+ $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1;
+ $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte);
+ // get 1 byte
+ $is16Bit = ord($text['recordData'][0]);
+ // it is possible to use a compressed format,
+ // which omits the high bytes of all characters, if they are all zero
+ if (($is16Bit & 0x01) === 0) {
+ $textStr = StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1');
+ } else {
+ $textStr = $this->decodeCodepage($textStr);
+ }
+
+ $this->textObjects[$this->textObjRef] = [
+ 'text' => $textStr,
+ 'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
+ 'alignment' => $grbitOpts,
+ 'rotation' => $rot,
+ ];
+ }
+
+ /**
+ * Read BOF.
+ */
+ private function readBof(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = substr($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 2; size: 2; type of the following data
+ $substreamType = self::getUInt2d($recordData, 2);
+
+ switch ($substreamType) {
+ case self::XLS_WORKBOOKGLOBALS:
+ $version = self::getUInt2d($recordData, 0);
+ if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
+ throw new Exception('Cannot read this Excel file. Version is too old.');
+ }
+ $this->version = $version;
+
+ break;
+ case self::XLS_WORKSHEET:
+ // do not use this version information for anything
+ // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
+ break;
+ default:
+ // substream, e.g. chart
+ // just skip the entire substream
+ do {
+ $code = self::getUInt2d($this->data, $this->pos);
+ $this->readDefault();
+ } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
+
+ break;
+ }
+ }
+
+ /**
+ * FILEPASS.
+ *
+ * This record is part of the File Protection Block. It
+ * contains information about the read/write password of the
+ * file. All record contents following this record will be
+ * encrypted.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ *
+ * The decryption functions and objects used from here on in
+ * are based on the source of Spreadsheet-ParseExcel:
+ * https://metacpan.org/release/Spreadsheet-ParseExcel
+ */
+ private function readFilepass(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+
+ if ($length != 54) {
+ throw new Exception('Unexpected file pass record length');
+ }
+
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
+ throw new Exception('Decryption password incorrect');
+ }
+
+ $this->encryption = self::MS_BIFF_CRYPTO_RC4;
+
+ // Decryption required from the record after next onwards
+ $this->encryptionStartPos = $this->pos + self::getUInt2d($this->data, $this->pos + 2);
+ }
+
+ /**
+ * Make an RC4 decryptor for the given block.
+ *
+ * @param int $block Block for which to create decrypto
+ * @param string $valContext MD5 context state
+ */
+ private function makeKey(int $block, $valContext): Xls\RC4
+ {
+ $pwarray = str_repeat("\0", 64);
+
+ for ($i = 0; $i < 5; ++$i) {
+ $pwarray[$i] = $valContext[$i];
+ }
+
+ $pwarray[5] = chr($block & 0xFF);
+ $pwarray[6] = chr(($block >> 8) & 0xFF);
+ $pwarray[7] = chr(($block >> 16) & 0xFF);
+ $pwarray[8] = chr(($block >> 24) & 0xFF);
+
+ $pwarray[9] = "\x80";
+ $pwarray[56] = "\x48";
+
+ $md5 = new Xls\MD5();
+ $md5->add($pwarray);
+
+ $s = $md5->getContext();
+
+ return new Xls\RC4($s);
+ }
+
+ /**
+ * Verify RC4 file password.
+ *
+ * @param string $password Password to check
+ * @param string $docid Document id
+ * @param string $salt_data Salt data
+ * @param string $hashedsalt_data Hashed salt data
+ * @param string $valContext Set to the MD5 context of the value
+ *
+ * @return bool Success
+ */
+ private function verifyPassword(string $password, string $docid, string $salt_data, string $hashedsalt_data, &$valContext): bool
+ {
+ $pwarray = str_repeat("\0", 64);
+
+ $iMax = strlen($password);
+ for ($i = 0; $i < $iMax; ++$i) {
+ $o = ord(substr($password, $i, 1));
+ $pwarray[2 * $i] = chr($o & 0xFF);
+ $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xFF);
+ }
+ $pwarray[2 * $i] = chr(0x80);
+ $pwarray[56] = chr(($i << 4) & 0xFF);
+
+ $md5 = new Xls\MD5();
+ $md5->add($pwarray);
+
+ $mdContext1 = $md5->getContext();
+
+ $offset = 0;
+ $keyoffset = 0;
+ $tocopy = 5;
+
+ $md5->reset();
+
+ while ($offset != 16) {
+ if ((64 - $offset) < 5) {
+ $tocopy = 64 - $offset;
+ }
+ for ($i = 0; $i <= $tocopy; ++$i) {
+ $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
+ }
+ $offset += $tocopy;
+
+ if ($offset == 64) {
+ $md5->add($pwarray);
+ $keyoffset = $tocopy;
+ $tocopy = 5 - $tocopy;
+ $offset = 0;
+
+ continue;
+ }
+
+ $keyoffset = 0;
+ $tocopy = 5;
+ for ($i = 0; $i < 16; ++$i) {
+ $pwarray[$offset + $i] = $docid[$i];
+ }
+ $offset += 16;
+ }
+
+ $pwarray[16] = "\x80";
+ for ($i = 0; $i < 47; ++$i) {
+ $pwarray[17 + $i] = "\0";
+ }
+ $pwarray[56] = "\x80";
+ $pwarray[57] = "\x0a";
+
+ $md5->add($pwarray);
+ $valContext = $md5->getContext();
+
+ $key = $this->makeKey(0, $valContext);
+
+ $salt = $key->RC4($salt_data);
+ $hashedsalt = $key->RC4($hashedsalt_data);
+
+ $salt .= "\x80" . str_repeat("\0", 47);
+ $salt[56] = "\x80";
+
+ $md5->reset();
+ $md5->add($salt);
+ $mdContext2 = $md5->getContext();
+
+ return $mdContext2 == $hashedsalt;
+ }
+
+ /**
+ * CODEPAGE.
+ *
+ * This record stores the text encoding used to write byte
+ * strings, stored as MS Windows code page identifier.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readCodepage(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; code page identifier
+ $codepage = self::getUInt2d($recordData, 0);
+
+ $this->codepage = CodePage::numberToName($codepage);
+ }
+
+ /**
+ * DATEMODE.
+ *
+ * This record specifies the base date for displaying date
+ * values. All dates are stored as count of days past this
+ * base date. In BIFF2-BIFF4 this record is part of the
+ * Calculation Settings Block. In BIFF5-BIFF8 it is
+ * stored in the Workbook Globals Substream.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readDateMode(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
+ Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
+ if (ord($recordData[0]) == 1) {
+ Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
+ }
+ }
+
+ /**
+ * Read a FONT record.
+ */
+ private function readFont(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ $objFont = new Font();
+
+ // offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
+ $size = self::getUInt2d($recordData, 0);
+ $objFont->setSize($size / 20);
+
+ // offset: 2; size: 2; option flags
+ // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
+ // bit: 1; mask 0x0002; italic
+ $isItalic = (0x0002 & self::getUInt2d($recordData, 2)) >> 1;
+ if ($isItalic) {
+ $objFont->setItalic(true);
+ }
+
+ // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
+ // bit: 3; mask 0x0008; strikethrough
+ $isStrike = (0x0008 & self::getUInt2d($recordData, 2)) >> 3;
+ if ($isStrike) {
+ $objFont->setStrikethrough(true);
+ }
+
+ // offset: 4; size: 2; colour index
+ $colorIndex = self::getUInt2d($recordData, 4);
+ $objFont->colorIndex = $colorIndex;
+
+ // offset: 6; size: 2; font weight
+ $weight = self::getUInt2d($recordData, 6);
+ switch ($weight) {
+ case 0x02BC:
+ $objFont->setBold(true);
+
+ break;
+ }
+
+ // offset: 8; size: 2; escapement type
+ $escapement = self::getUInt2d($recordData, 8);
+ CellFont::escapement($objFont, $escapement);
+
+ // offset: 10; size: 1; underline type
+ $underlineType = ord($recordData[10]);
+ CellFont::underline($objFont, $underlineType);
+
+ // offset: 11; size: 1; font family
+ // offset: 12; size: 1; character set
+ // offset: 13; size: 1; not used
+ // offset: 14; size: var; font name
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringShort(substr($recordData, 14));
+ } else {
+ $string = $this->readByteStringShort(substr($recordData, 14));
+ }
+ $objFont->setName($string['value']);
+
+ $this->objFonts[] = $objFont;
+ }
+ }
+
+ /**
+ * FORMAT.
+ *
+ * This record contains information about a number format.
+ * All FORMAT records occur together in a sequential list.
+ *
+ * In BIFF2-BIFF4 other records referencing a FORMAT record
+ * contain a zero-based index into this list. From BIFF5 on
+ * the FORMAT record contains the index itself that will be
+ * used by other records.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readFormat(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ $indexCode = self::getUInt2d($recordData, 0);
+
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringLong(substr($recordData, 2));
+ } else {
+ // BIFF7
+ $string = $this->readByteStringShort(substr($recordData, 2));
+ }
+
+ $formatString = $string['value'];
+ // Apache Open Office sets wrong case writing to xls - issue 2239
+ if ($formatString === 'GENERAL') {
+ $formatString = NumberFormat::FORMAT_GENERAL;
+ }
+ $this->formats[$indexCode] = $formatString;
+ }
+ }
+
+ /**
+ * XF - Extended Format.
+ *
+ * This record contains formatting information for cells, rows, columns or styles.
+ * According to https://support.microsoft.com/en-us/help/147732 there are always at least 15 cell style XF
+ * and 1 cell XF.
+ * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
+ * and XF record 15 is a cell XF
+ * We only read the first cell style XF and skip the remaining cell style XF records
+ * We read all cell XF records.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readXf(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ $objStyle = new Style();
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; Index to FONT record
+ if (self::getUInt2d($recordData, 0) < 4) {
+ $fontIndex = self::getUInt2d($recordData, 0);
+ } else {
+ // this has to do with that index 4 is omitted in all BIFF versions for some strange reason
+ // check the OpenOffice documentation of the FONT record
+ $fontIndex = self::getUInt2d($recordData, 0) - 1;
+ }
+ if (isset($this->objFonts[$fontIndex])) {
+ $objStyle->setFont($this->objFonts[$fontIndex]);
+ }
+
+ // offset: 2; size: 2; Index to FORMAT record
+ $numberFormatIndex = self::getUInt2d($recordData, 2);
+ if (isset($this->formats[$numberFormatIndex])) {
+ // then we have user-defined format code
+ $numberFormat = ['formatCode' => $this->formats[$numberFormatIndex]];
+ } elseif (($code = NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
+ // then we have built-in format code
+ $numberFormat = ['formatCode' => $code];
+ } else {
+ // we set the general format code
+ $numberFormat = ['formatCode' => NumberFormat::FORMAT_GENERAL];
+ }
+ $objStyle->getNumberFormat()->setFormatCode($numberFormat['formatCode']);
+
+ // offset: 4; size: 2; XF type, cell protection, and parent style XF
+ // bit 2-0; mask 0x0007; XF_TYPE_PROT
+ $xfTypeProt = self::getUInt2d($recordData, 4);
+ // bit 0; mask 0x01; 1 = cell is locked
+ $isLocked = (0x01 & $xfTypeProt) >> 0;
+ $objStyle->getProtection()->setLocked($isLocked ? Protection::PROTECTION_INHERIT : Protection::PROTECTION_UNPROTECTED);
+
+ // bit 1; mask 0x02; 1 = Formula is hidden
+ $isHidden = (0x02 & $xfTypeProt) >> 1;
+ $objStyle->getProtection()->setHidden($isHidden ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED);
+
+ // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
+ $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
+
+ // offset: 6; size: 1; Alignment and text break
+ // bit 2-0, mask 0x07; horizontal alignment
+ $horAlign = (0x07 & ord($recordData[6])) >> 0;
+ Xls\Style\CellAlignment::horizontal($objStyle->getAlignment(), $horAlign);
+
+ // bit 3, mask 0x08; wrap text
+ $wrapText = (0x08 & ord($recordData[6])) >> 3;
+ Xls\Style\CellAlignment::wrap($objStyle->getAlignment(), $wrapText);
+
+ // bit 6-4, mask 0x70; vertical alignment
+ $vertAlign = (0x70 & ord($recordData[6])) >> 4;
+ Xls\Style\CellAlignment::vertical($objStyle->getAlignment(), $vertAlign);
+
+ if ($this->version == self::XLS_BIFF8) {
+ // offset: 7; size: 1; XF_ROTATION: Text rotation angle
+ $angle = ord($recordData[7]);
+ $rotation = 0;
+ if ($angle <= 90) {
+ $rotation = $angle;
+ } elseif ($angle <= 180) {
+ $rotation = 90 - $angle;
+ } elseif ($angle == Alignment::TEXTROTATION_STACK_EXCEL) {
+ $rotation = Alignment::TEXTROTATION_STACK_PHPSPREADSHEET;
+ }
+ $objStyle->getAlignment()->setTextRotation($rotation);
+
+ // offset: 8; size: 1; Indentation, shrink to cell size, and text direction
+ // bit: 3-0; mask: 0x0F; indent level
+ $indent = (0x0F & ord($recordData[8])) >> 0;
+ $objStyle->getAlignment()->setIndent($indent);
+
+ // bit: 4; mask: 0x10; 1 = shrink content to fit into cell
+ $shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
+ switch ($shrinkToFit) {
+ case 0:
+ $objStyle->getAlignment()->setShrinkToFit(false);
+
+ break;
+ case 1:
+ $objStyle->getAlignment()->setShrinkToFit(true);
+
+ break;
+ }
+
+ // offset: 9; size: 1; Flags used for attribute groups
+
+ // offset: 10; size: 4; Cell border lines and background area
+ // bit: 3-0; mask: 0x0000000F; left style
+ if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
+ $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
+ }
+ // bit: 7-4; mask: 0x000000F0; right style
+ if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
+ $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
+ }
+ // bit: 11-8; mask: 0x00000F00; top style
+ if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
+ $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
+ }
+ // bit: 15-12; mask: 0x0000F000; bottom style
+ if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
+ $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
+ }
+ // bit: 22-16; mask: 0x007F0000; left color
+ $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
+
+ // bit: 29-23; mask: 0x3F800000; right color
+ $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
+
+ // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
+ $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
+
+ // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
+ $diagonalUp = ((int) 0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
+
+ if ($diagonalUp === false) {
+ if ($diagonalDown === false) {
+ $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
+ } else {
+ $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+ }
+ } elseif ($diagonalDown === false) {
+ $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
+ } else {
+ $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
+ }
+
+ // offset: 14; size: 4;
+ // bit: 6-0; mask: 0x0000007F; top color
+ $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
+
+ // bit: 13-7; mask: 0x00003F80; bottom color
+ $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
+
+ // bit: 20-14; mask: 0x001FC000; diagonal color
+ $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
+
+ // bit: 24-21; mask: 0x01E00000; diagonal style
+ if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
+ $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
+ }
+
+ // bit: 31-26; mask: 0xFC000000 fill pattern
+ if ($fillType = Xls\Style\FillPattern::lookup(((int) 0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
+ $objStyle->getFill()->setFillType($fillType);
+ }
+ // offset: 18; size: 2; pattern and background colour
+ // bit: 6-0; mask: 0x007F; color index for pattern color
+ $objStyle->getFill()->startcolorIndex = (0x007F & self::getUInt2d($recordData, 18)) >> 0;
+
+ // bit: 13-7; mask: 0x3F80; color index for pattern background
+ $objStyle->getFill()->endcolorIndex = (0x3F80 & self::getUInt2d($recordData, 18)) >> 7;
+ } else {
+ // BIFF5
+
+ // offset: 7; size: 1; Text orientation and flags
+ $orientationAndFlags = ord($recordData[7]);
+
+ // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
+ $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
+ switch ($xfOrientation) {
+ case 0:
+ $objStyle->getAlignment()->setTextRotation(0);
+
+ break;
+ case 1:
+ $objStyle->getAlignment()->setTextRotation(Alignment::TEXTROTATION_STACK_PHPSPREADSHEET);
+
+ break;
+ case 2:
+ $objStyle->getAlignment()->setTextRotation(90);
+
+ break;
+ case 3:
+ $objStyle->getAlignment()->setTextRotation(-90);
+
+ break;
+ }
+
+ // offset: 8; size: 4; cell border lines and background area
+ $borderAndBackground = self::getInt4d($recordData, 8);
+
+ // bit: 6-0; mask: 0x0000007F; color index for pattern color
+ $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
+
+ // bit: 13-7; mask: 0x00003F80; color index for pattern background
+ $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
+
+ // bit: 21-16; mask: 0x003F0000; fill pattern
+ $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
+
+ // bit: 24-22; mask: 0x01C00000; bottom line style
+ $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
+
+ // bit: 31-25; mask: 0xFE000000; bottom line color
+ $objStyle->getBorders()->getBottom()->colorIndex = ((int) 0xFE000000 & $borderAndBackground) >> 25;
+
+ // offset: 12; size: 4; cell border lines
+ $borderLines = self::getInt4d($recordData, 12);
+
+ // bit: 2-0; mask: 0x00000007; top line style
+ $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
+
+ // bit: 5-3; mask: 0x00000038; left line style
+ $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
+
+ // bit: 8-6; mask: 0x000001C0; right line style
+ $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
+
+ // bit: 15-9; mask: 0x0000FE00; top line color index
+ $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
+
+ // bit: 22-16; mask: 0x007F0000; left line color index
+ $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
+
+ // bit: 29-23; mask: 0x3F800000; right line color index
+ $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
+ }
+
+ // add cellStyleXf or cellXf and update mapping
+ if ($isCellStyleXf) {
+ // we only read one style XF record which is always the first
+ if ($this->xfIndex == 0) {
+ $this->spreadsheet->addCellStyleXf($objStyle);
+ $this->mapCellStyleXfIndex[$this->xfIndex] = 0;
+ }
+ } else {
+ // we read all cell XF records
+ $this->spreadsheet->addCellXf($objStyle);
+ $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
+ }
+
+ // update XF index for when we read next record
+ ++$this->xfIndex;
+ }
+ }
+
+ private function readXfExt(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; 0x087D = repeated header
+
+ // offset: 2; size: 2
+
+ // offset: 4; size: 8; not used
+
+ // offset: 12; size: 2; record version
+
+ // offset: 14; size: 2; index to XF record which this record modifies
+ $ixfe = self::getUInt2d($recordData, 14);
+
+ // offset: 16; size: 2; not used
+
+ // offset: 18; size: 2; number of extension properties that follow
+ //$cexts = self::getUInt2d($recordData, 18);
+
+ // start reading the actual extension data
+ $offset = 20;
+ while ($offset < $length) {
+ // extension type
+ $extType = self::getUInt2d($recordData, $offset);
+
+ // extension length
+ $cb = self::getUInt2d($recordData, $offset + 2);
+
+ // extension data
+ $extData = substr($recordData, $offset + 4, $cb);
+
+ switch ($extType) {
+ case 4: // fill start color
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
+ $fill->getStartColor()->setRGB($rgb);
+ $fill->startcolorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 5: // fill end color
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
+ $fill->getEndColor()->setRGB($rgb);
+ $fill->endcolorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 7: // border color top
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
+ $top->getColor()->setRGB($rgb);
+ $top->colorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 8: // border color bottom
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
+ $bottom->getColor()->setRGB($rgb);
+ $bottom->colorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 9: // border color left
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
+ $left->getColor()->setRGB($rgb);
+ $left->colorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 10: // border color right
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
+ $right->getColor()->setRGB($rgb);
+ $right->colorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 11: // border color diagonal
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
+ $diagonal->getColor()->setRGB($rgb);
+ $diagonal->colorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ case 13: // font color
+ $xclfType = self::getUInt2d($extData, 0); // color type
+ $xclrValue = substr($extData, 4, 4); // color value (value based on color type)
+
+ if ($xclfType == 2) {
+ $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
+
+ // modify the relevant style property
+ if (isset($this->mapCellXfIndex[$ixfe])) {
+ $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
+ $font->getColor()->setRGB($rgb);
+ $font->colorIndex = null; // normal color index does not apply, discard
+ }
+ }
+
+ break;
+ }
+
+ $offset += $cb;
+ }
+ }
+ }
+
+ /**
+ * Read STYLE record.
+ */
+ private function readStyle(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; index to XF record and flag for built-in style
+ $ixfe = self::getUInt2d($recordData, 0);
+
+ // bit: 11-0; mask 0x0FFF; index to XF record
+ //$xfIndex = (0x0FFF & $ixfe) >> 0;
+
+ // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
+ $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
+
+ if ($isBuiltIn) {
+ // offset: 2; size: 1; identifier for built-in style
+ $builtInId = ord($recordData[2]);
+
+ switch ($builtInId) {
+ case 0x00:
+ // currently, we are not using this for anything
+ break;
+ default:
+ break;
+ }
+ }
+ // user-defined; not supported by PhpSpreadsheet
+ }
+ }
+
+ /**
+ * Read PALETTE record.
+ */
+ private function readPalette(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; number of following colors
+ $nm = self::getUInt2d($recordData, 0);
+
+ // list of RGB colors
+ for ($i = 0; $i < $nm; ++$i) {
+ $rgb = substr($recordData, 2 + 4 * $i, 4);
+ $this->palette[] = self::readRGB($rgb);
+ }
+ }
+ }
+
+ /**
+ * SHEET.
+ *
+ * This record is located in the Workbook Globals
+ * Substream and represents a sheet inside the workbook.
+ * One SHEET record is written for each sheet. It stores the
+ * sheet name and a stream offset to the BOF record of the
+ * respective Sheet Substream within the Workbook Stream.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readSheet(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // offset: 0; size: 4; absolute stream position of the BOF record of the sheet
+ // NOTE: not encrypted
+ $rec_offset = self::getInt4d($this->data, $this->pos + 4);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 4; size: 1; sheet state
+ $sheetState = match (ord($recordData[4])) {
+ 0x00 => Worksheet::SHEETSTATE_VISIBLE,
+ 0x01 => Worksheet::SHEETSTATE_HIDDEN,
+ 0x02 => Worksheet::SHEETSTATE_VERYHIDDEN,
+ default => Worksheet::SHEETSTATE_VISIBLE,
+ };
+
+ // offset: 5; size: 1; sheet type
+ $sheetType = ord($recordData[5]);
+
+ // offset: 6; size: var; sheet name
+ $rec_name = null;
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringShort(substr($recordData, 6));
+ $rec_name = $string['value'];
+ } elseif ($this->version == self::XLS_BIFF7) {
+ $string = $this->readByteStringShort(substr($recordData, 6));
+ $rec_name = $string['value'];
+ }
+
+ $this->sheets[] = [
+ 'name' => $rec_name,
+ 'offset' => $rec_offset,
+ 'sheetState' => $sheetState,
+ 'sheetType' => $sheetType,
+ ];
+ }
+
+ /**
+ * Read EXTERNALBOOK record.
+ */
+ private function readExternalBook(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset within record data
+ $offset = 0;
+
+ // there are 4 types of records
+ if (strlen($recordData) > 4) {
+ // external reference
+ // offset: 0; size: 2; number of sheet names ($nm)
+ $nm = self::getUInt2d($recordData, 0);
+ $offset += 2;
+
+ // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
+ $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
+ $offset += $encodedUrlString['size'];
+
+ // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
+ $externalSheetNames = [];
+ for ($i = 0; $i < $nm; ++$i) {
+ $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
+ $externalSheetNames[] = $externalSheetNameString['value'];
+ $offset += $externalSheetNameString['size'];
+ }
+
+ // store the record data
+ $this->externalBooks[] = [
+ 'type' => 'external',
+ 'encodedUrl' => $encodedUrlString['value'],
+ 'externalSheetNames' => $externalSheetNames,
+ ];
+ } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
+ // internal reference
+ // offset: 0; size: 2; number of sheet in this document
+ // offset: 2; size: 2; 0x01 0x04
+ $this->externalBooks[] = [
+ 'type' => 'internal',
+ ];
+ } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
+ // add-in function
+ // offset: 0; size: 2; 0x0001
+ $this->externalBooks[] = [
+ 'type' => 'addInFunction',
+ ];
+ } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
+ // DDE links, OLE links
+ // offset: 0; size: 2; 0x0000
+ // offset: 2; size: var; encoded source document name
+ $this->externalBooks[] = [
+ 'type' => 'DDEorOLE',
+ ];
+ }
+ }
+
+ /**
+ * Read EXTERNNAME record.
+ */
+ private function readExternName(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // external sheet references provided for named cells
+ if ($this->version == self::XLS_BIFF8) {
+ // offset: 0; size: 2; options
+ //$options = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2;
+
+ // offset: 4; size: 2; not used
+
+ // offset: 6; size: var
+ $nameString = self::readUnicodeStringShort(substr($recordData, 6));
+
+ // offset: var; size: var; formula data
+ $offset = 6 + $nameString['size'];
+ $formula = $this->getFormulaFromStructure(substr($recordData, $offset));
+
+ $this->externalNames[] = [
+ 'name' => $nameString['value'],
+ 'formula' => $formula,
+ ];
+ }
+ }
+
+ /**
+ * Read EXTERNSHEET record.
+ */
+ private function readExternSheet(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // external sheet references provided for named cells
+ if ($this->version == self::XLS_BIFF8) {
+ // offset: 0; size: 2; number of following ref structures
+ $nm = self::getUInt2d($recordData, 0);
+ for ($i = 0; $i < $nm; ++$i) {
+ $this->ref[] = [
+ // offset: 2 + 6 * $i; index to EXTERNALBOOK record
+ 'externalBookIndex' => self::getUInt2d($recordData, 2 + 6 * $i),
+ // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
+ 'firstSheetIndex' => self::getUInt2d($recordData, 4 + 6 * $i),
+ // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
+ 'lastSheetIndex' => self::getUInt2d($recordData, 6 + 6 * $i),
+ ];
+ }
+ }
+ }
+
+ /**
+ * DEFINEDNAME.
+ *
+ * This record is part of a Link Table. It contains the name
+ * and the token array of an internal defined name. Token
+ * arrays of defined names contain tokens with aberrant
+ * token classes.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readDefinedName(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->version == self::XLS_BIFF8) {
+ // retrieves named cells
+
+ // offset: 0; size: 2; option flags
+ $opts = self::getUInt2d($recordData, 0);
+
+ // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
+ $isBuiltInName = (0x0020 & $opts) >> 5;
+
+ // offset: 2; size: 1; keyboard shortcut
+
+ // offset: 3; size: 1; length of the name (character count)
+ $nlen = ord($recordData[3]);
+
+ // offset: 4; size: 2; size of the formula data (it can happen that this is zero)
+ // note: there can also be additional data, this is not included in $flen
+ $flen = self::getUInt2d($recordData, 4);
+
+ // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
+ $scope = self::getUInt2d($recordData, 8);
+
+ // offset: 14; size: var; Name (Unicode string without length field)
+ $string = self::readUnicodeString(substr($recordData, 14), $nlen);
+
+ // offset: var; size: $flen; formula data
+ $offset = 14 + $string['size'];
+ $formulaStructure = pack('v', $flen) . substr($recordData, $offset);
+
+ try {
+ $formula = $this->getFormulaFromStructure($formulaStructure);
+ } catch (PhpSpreadsheetException) {
+ $formula = '';
+ }
+
+ $this->definedname[] = [
+ 'isBuiltInName' => $isBuiltInName,
+ 'name' => $string['value'],
+ 'formula' => $formula,
+ 'scope' => $scope,
+ ];
+ }
+ }
+
+ /**
+ * Read MSODRAWINGGROUP record.
+ */
+ private function readMsoDrawingGroup(): void
+ {
+ //$length = self::getUInt2d($this->data, $this->pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ $this->drawingGroupData .= $recordData;
+ }
+
+ /**
+ * SST - Shared String Table.
+ *
+ * This record contains a list of all strings used anywhere
+ * in the workbook. Each string occurs only once. The
+ * workbook uses indexes into the list to reference the
+ * strings.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readSst(): void
+ {
+ // offset within (spliced) record data
+ $pos = 0;
+
+ // Limit global SST position, further control for bad SST Length in BIFF8 data
+ $limitposSST = 0;
+
+ // get spliced record data
+ $splicedRecordData = $this->getSplicedRecordData();
+
+ $recordData = $splicedRecordData['recordData'];
+ $spliceOffsets = $splicedRecordData['spliceOffsets'];
+
+ // offset: 0; size: 4; total number of strings in the workbook
+ $pos += 4;
+
+ // offset: 4; size: 4; number of following strings ($nm)
+ $nm = self::getInt4d($recordData, 4);
+ $pos += 4;
+
+ // look up limit position
+ foreach ($spliceOffsets as $spliceOffset) {
+ // it can happen that the string is empty, therefore we need
+ // <= and not just <
+ if ($pos <= $spliceOffset) {
+ $limitposSST = $spliceOffset;
+ }
+ }
+
+ // loop through the Unicode strings (16-bit length)
+ for ($i = 0; $i < $nm && $pos < $limitposSST; ++$i) {
+ // number of characters in the Unicode string
+ $numChars = self::getUInt2d($recordData, $pos);
+ $pos += 2;
+
+ // option flags
+ $optionFlags = ord($recordData[$pos]);
+ ++$pos;
+
+ // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
+ $isCompressed = (($optionFlags & 0x01) == 0);
+
+ // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
+ $hasAsian = (($optionFlags & 0x04) != 0);
+
+ // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
+ $hasRichText = (($optionFlags & 0x08) != 0);
+
+ $formattingRuns = 0;
+ if ($hasRichText) {
+ // number of Rich-Text formatting runs
+ $formattingRuns = self::getUInt2d($recordData, $pos);
+ $pos += 2;
+ }
+
+ $extendedRunLength = 0;
+ if ($hasAsian) {
+ // size of Asian phonetic setting
+ $extendedRunLength = self::getInt4d($recordData, $pos);
+ $pos += 4;
+ }
+
+ // expected byte length of character array if not split
+ $len = ($isCompressed) ? $numChars : $numChars * 2;
+
+ // look up limit position - Check it again to be sure that no error occurs when parsing SST structure
+ $limitpos = null;
+ foreach ($spliceOffsets as $spliceOffset) {
+ // it can happen that the string is empty, therefore we need
+ // <= and not just <
+ if ($pos <= $spliceOffset) {
+ $limitpos = $spliceOffset;
+
+ break;
+ }
+ }
+
+ if ($pos + $len <= $limitpos) {
+ // character array is not split between records
+
+ $retstr = substr($recordData, $pos, $len);
+ $pos += $len;
+ } else {
+ // character array is split between records
+
+ // first part of character array
+ $retstr = substr($recordData, $pos, $limitpos - $pos);
+
+ $bytesRead = $limitpos - $pos;
+
+ // remaining characters in Unicode string
+ $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
+
+ $pos = $limitpos;
+
+ // keep reading the characters
+ while ($charsLeft > 0) {
+ // look up next limit position, in case the string span more than one continue record
+ foreach ($spliceOffsets as $spliceOffset) {
+ if ($pos < $spliceOffset) {
+ $limitpos = $spliceOffset;
+
+ break;
+ }
+ }
+
+ // repeated option flags
+ // OpenOffice.org documentation 5.21
+ $option = ord($recordData[$pos]);
+ ++$pos;
+
+ if ($isCompressed && ($option == 0)) {
+ // 1st fragment compressed
+ // this fragment compressed
+ $len = min($charsLeft, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len;
+ $isCompressed = true;
+ } elseif (!$isCompressed && ($option != 0)) {
+ // 1st fragment uncompressed
+ // this fragment uncompressed
+ $len = min($charsLeft * 2, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len / 2;
+ $isCompressed = false;
+ } elseif (!$isCompressed && ($option == 0)) {
+ // 1st fragment uncompressed
+ // this fragment compressed
+ $len = min($charsLeft, $limitpos - $pos);
+ for ($j = 0; $j < $len; ++$j) {
+ $retstr .= $recordData[$pos + $j]
+ . chr(0);
+ }
+ $charsLeft -= $len;
+ $isCompressed = false;
+ } else {
+ // 1st fragment compressed
+ // this fragment uncompressed
+ $newstr = '';
+ $jMax = strlen($retstr);
+ for ($j = 0; $j < $jMax; ++$j) {
+ $newstr .= $retstr[$j] . chr(0);
+ }
+ $retstr = $newstr;
+ $len = min($charsLeft * 2, $limitpos - $pos);
+ $retstr .= substr($recordData, $pos, $len);
+ $charsLeft -= $len / 2;
+ $isCompressed = false;
+ }
+
+ $pos += $len;
+ }
+ }
+
+ // convert to UTF-8
+ $retstr = self::encodeUTF16($retstr, $isCompressed);
+
+ // read additional Rich-Text information, if any
+ $fmtRuns = [];
+ if ($hasRichText) {
+ // list of formatting runs
+ for ($j = 0; $j < $formattingRuns; ++$j) {
+ // first formatted character; zero-based
+ $charPos = self::getUInt2d($recordData, $pos + $j * 4);
+
+ // index to font record
+ $fontIndex = self::getUInt2d($recordData, $pos + 2 + $j * 4);
+
+ $fmtRuns[] = [
+ 'charPos' => $charPos,
+ 'fontIndex' => $fontIndex,
+ ];
+ }
+ $pos += 4 * $formattingRuns;
+ }
+
+ // read additional Asian phonetics information, if any
+ if ($hasAsian) {
+ // For Asian phonetic settings, we skip the extended string data
+ $pos += $extendedRunLength;
+ }
+
+ // store the shared sting
+ $this->sst[] = [
+ 'value' => $retstr,
+ 'fmtRuns' => $fmtRuns,
+ ];
+ }
+
+ // getSplicedRecordData() takes care of moving current position in data stream
+ }
+
+ /**
+ * Read PRINTGRIDLINES record.
+ */
+ private function readPrintGridlines(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+ // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
+ $printGridlines = (bool) self::getUInt2d($recordData, 0);
+ $this->phpSheet->setPrintGridlines($printGridlines);
+ }
+ }
+
+ /**
+ * Read DEFAULTROWHEIGHT record.
+ */
+ private function readDefaultRowHeight(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; option flags
+ // offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
+ $height = self::getUInt2d($recordData, 2);
+ $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
+ }
+
+ /**
+ * Read SHEETPR record.
+ */
+ private function readSheetPr(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2
+
+ // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
+ $isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6;
+ $this->phpSheet->setShowSummaryBelow((bool) $isSummaryBelow);
+
+ // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
+ $isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7;
+ $this->phpSheet->setShowSummaryRight((bool) $isSummaryRight);
+
+ // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
+ // this corresponds to radio button setting in page setup dialog in Excel
+ $this->isFitToPages = (bool) ((0x0100 & self::getUInt2d($recordData, 0)) >> 8);
+ }
+
+ /**
+ * Read HORIZONTALPAGEBREAKS record.
+ */
+ private function readHorizontalPageBreaks(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+ // offset: 0; size: 2; number of the following row index structures
+ $nm = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 6 * $nm; list of $nm row index structures
+ for ($i = 0; $i < $nm; ++$i) {
+ $r = self::getUInt2d($recordData, 2 + 6 * $i);
+ $cf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
+ //$cl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
+
+ // not sure why two column indexes are necessary?
+ $this->phpSheet->setBreak([$cf + 1, $r], Worksheet::BREAK_ROW);
+ }
+ }
+ }
+
+ /**
+ * Read VERTICALPAGEBREAKS record.
+ */
+ private function readVerticalPageBreaks(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+ // offset: 0; size: 2; number of the following column index structures
+ $nm = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 6 * $nm; list of $nm row index structures
+ for ($i = 0; $i < $nm; ++$i) {
+ $c = self::getUInt2d($recordData, 2 + 6 * $i);
+ $rf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
+ //$rl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
+
+ // not sure why two row indexes are necessary?
+ $this->phpSheet->setBreak([$c + 1, ($rf > 0) ? $rf : 1], Worksheet::BREAK_COLUMN);
+ }
+ }
+ }
+
+ /**
+ * Read HEADER record.
+ */
+ private function readHeader(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: var
+ // realized that $recordData can be empty even when record exists
+ if ($recordData) {
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringLong($recordData);
+ } else {
+ $string = $this->readByteStringShort($recordData);
+ }
+
+ $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
+ $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
+ }
+ }
+ }
+
+ /**
+ * Read FOOTER record.
+ */
+ private function readFooter(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: var
+ // realized that $recordData can be empty even when record exists
+ if ($recordData) {
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringLong($recordData);
+ } else {
+ $string = $this->readByteStringShort($recordData);
+ }
+ $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
+ $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
+ }
+ }
+ }
+
+ /**
+ * Read HCENTER record.
+ */
+ private function readHcenter(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
+ $isHorizontalCentered = (bool) self::getUInt2d($recordData, 0);
+
+ $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
+ }
+ }
+
+ /**
+ * Read VCENTER record.
+ */
+ private function readVcenter(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
+ $isVerticalCentered = (bool) self::getUInt2d($recordData, 0);
+
+ $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
+ }
+ }
+
+ /**
+ * Read LEFTMARGIN record.
+ */
+ private function readLeftMargin(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 8
+ $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read RIGHTMARGIN record.
+ */
+ private function readRightMargin(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 8
+ $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read TOPMARGIN record.
+ */
+ private function readTopMargin(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 8
+ $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read BOTTOMMARGIN record.
+ */
+ private function readBottomMargin(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 8
+ $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
+ }
+ }
+
+ /**
+ * Read PAGESETUP record.
+ */
+ private function readPageSetup(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; paper size
+ $paperSize = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; scaling factor
+ $scale = self::getUInt2d($recordData, 2);
+
+ // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
+ $fitToWidth = self::getUInt2d($recordData, 6);
+
+ // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
+ $fitToHeight = self::getUInt2d($recordData, 8);
+
+ // offset: 10; size: 2; option flags
+
+ // bit: 0; mask: 0x0001; 0=down then over, 1=over then down
+ $isOverThenDown = (0x0001 & self::getUInt2d($recordData, 10));
+
+ // bit: 1; mask: 0x0002; 0=landscape, 1=portrait
+ $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1;
+
+ // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
+ // when this bit is set, do not use flags for those properties
+ $isNotInit = (0x0004 & self::getUInt2d($recordData, 10)) >> 2;
+
+ if (!$isNotInit) {
+ $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
+ $this->phpSheet->getPageSetup()->setPageOrder(((bool) $isOverThenDown) ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER);
+ $this->phpSheet->getPageSetup()->setOrientation(((bool) $isPortrait) ? PageSetup::ORIENTATION_PORTRAIT : PageSetup::ORIENTATION_LANDSCAPE);
+
+ $this->phpSheet->getPageSetup()->setScale($scale, false);
+ $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
+ $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
+ $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
+ }
+
+ // offset: 16; size: 8; header margin (IEEE 754 floating-point value)
+ $marginHeader = self::extractNumber(substr($recordData, 16, 8));
+ $this->phpSheet->getPageMargins()->setHeader($marginHeader);
+
+ // offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
+ $marginFooter = self::extractNumber(substr($recordData, 24, 8));
+ $this->phpSheet->getPageMargins()->setFooter($marginFooter);
+ }
+ }
+
+ /**
+ * PROTECT - Sheet protection (BIFF2 through BIFF8)
+ * if this record is omitted, then it also means no sheet protection.
+ */
+ private function readProtect(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit 0, mask 0x01; 1 = sheet is protected
+ $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
+ $this->phpSheet->getProtection()->setSheet((bool) $bool);
+ }
+
+ /**
+ * SCENPROTECT.
+ */
+ private function readScenProtect(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit: 0, mask 0x01; 1 = scenarios are protected
+ $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
+
+ $this->phpSheet->getProtection()->setScenarios((bool) $bool);
+ }
+
+ /**
+ * OBJECTPROTECT.
+ */
+ private function readObjectProtect(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2;
+
+ // bit: 0, mask 0x01; 1 = objects are protected
+ $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
+
+ $this->phpSheet->getProtection()->setObjects((bool) $bool);
+ }
+
+ /**
+ * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
+ */
+ private function readPassword(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; 16-bit hash value of password
+ $password = strtoupper(dechex(self::getUInt2d($recordData, 0))); // the hashed password
+ $this->phpSheet->getProtection()->setPassword($password, true);
+ }
+ }
+
+ /**
+ * Read DEFCOLWIDTH record.
+ */
+ private function readDefColWidth(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; default column width
+ $width = self::getUInt2d($recordData, 0);
+ if ($width != 8) {
+ $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
+ }
+ }
+
+ /**
+ * Read COLINFO record.
+ */
+ private function readColInfo(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; index to first column in range
+ $firstColumnIndex = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to last column in range
+ $lastColumnIndex = self::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
+ $width = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 2; index to XF record for default column formatting
+ $xfIndex = self::getUInt2d($recordData, 6);
+
+ // offset: 8; size: 2; option flags
+ // bit: 0; mask: 0x0001; 1= columns are hidden
+ $isHidden = (0x0001 & self::getUInt2d($recordData, 8)) >> 0;
+
+ // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
+ $level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8;
+
+ // bit: 12; mask: 0x1000; 1 = collapsed
+ $isCollapsed = (bool) ((0x1000 & self::getUInt2d($recordData, 8)) >> 12);
+
+ // offset: 10; size: 2; not used
+
+ for ($i = $firstColumnIndex + 1; $i <= $lastColumnIndex + 1; ++$i) {
+ if ($lastColumnIndex == 255 || $lastColumnIndex == 256) {
+ $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
+
+ break;
+ }
+ $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
+ $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
+ $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
+ $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
+ if (isset($this->mapCellXfIndex[$xfIndex])) {
+ $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+ }
+
+ /**
+ * ROW.
+ *
+ * This record contains the properties of a single row in a
+ * sheet. Rows and cells in a sheet are divided into blocks
+ * of 32 rows.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readRow(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; index of this row
+ $r = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column of the first cell which is described by a cell record
+
+ // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
+
+ // offset: 6; size: 2;
+
+ // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
+ $height = (0x7FFF & self::getUInt2d($recordData, 6)) >> 0;
+
+ // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
+ $useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15;
+
+ if (!$useDefaultHeight) {
+ $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
+ }
+
+ // offset: 8; size: 2; not used
+
+ // offset: 10; size: 2; not used in BIFF5-BIFF8
+
+ // offset: 12; size: 4; option flags and default row formatting
+
+ // bit: 2-0: mask: 0x00000007; outline level of the row
+ $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
+ $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
+
+ // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
+ $isCollapsed = (bool) ((0x00000010 & self::getInt4d($recordData, 12)) >> 4);
+ $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
+
+ // bit: 5; mask: 0x00000020; 1 = row is hidden
+ $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
+ $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
+
+ // bit: 7; mask: 0x00000080; 1 = row has explicit format
+ $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
+
+ // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
+ $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
+
+ if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
+ $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read RK record
+ * This record represents a cell that contains an RK value
+ * (encoded integer or floating-point value). If a
+ * floating-point value cannot be encoded to an RK value,
+ * a NUMBER record will be written. This record replaces the
+ * record INTEGER written in BIFF2.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readRk(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 4; RK value
+ $rknum = self::getInt4d($recordData, 6);
+ $numValue = self::getIEEE754($rknum);
+
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add style information
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell
+ $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read LABELSST record
+ * This record represents a cell that contains a string. It
+ * replaces the LABEL record and RSTRING record used in
+ * BIFF2-BIFF5.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readLabelSst(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+ $cell = null;
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 4; index to SST record
+ $index = self::getInt4d($recordData, 6);
+
+ // add cell
+ if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
+ // then we should treat as rich text
+ $richText = new RichText();
+ $charPos = 0;
+ $sstCount = count($this->sst[$index]['fmtRuns']);
+ for ($i = 0; $i <= $sstCount; ++$i) {
+ if (isset($fmtRuns[$i])) {
+ $text = StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
+ $charPos = $fmtRuns[$i]['charPos'];
+ } else {
+ $text = StringHelper::substring($this->sst[$index]['value'], $charPos, StringHelper::countCharacters($this->sst[$index]['value']));
+ }
+
+ if (StringHelper::countCharacters($text) > 0) {
+ if ($i == 0) { // first text run, no style
+ $richText->createText($text);
+ } else {
+ $textRun = $richText->createTextRun($text);
+ if (isset($fmtRuns[$i - 1])) {
+ if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
+ $fontIndex = $fmtRuns[$i - 1]['fontIndex'];
+ } else {
+ // this has to do with that index 4 is omitted in all BIFF versions for some stra nge reason
+ // check the OpenOffice documentation of the FONT record
+ $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
+ }
+ if (array_key_exists($fontIndex, $this->objFonts) === false) {
+ $fontIndex = count($this->objFonts) - 1;
+ }
+ $textRun->setFont(clone $this->objFonts[$fontIndex]);
+ }
+ }
+ }
+ }
+ if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ $cell->setValueExplicit($richText, DataType::TYPE_STRING);
+ }
+ } else {
+ if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ $cell->setValueExplicit($this->sst[$index]['value'], DataType::TYPE_STRING);
+ }
+ }
+
+ if (!$this->readDataOnly && $cell !== null && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add style information
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MULRK record
+ * This record represents a cell range containing RK value
+ * cells. All cells are located in the same row.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readMulRk(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first column
+ $colFirst = self::getUInt2d($recordData, 2);
+
+ // offset: var; size: 2; index to last column
+ $colLast = self::getUInt2d($recordData, $length - 2);
+ $columns = $colLast - $colFirst + 1;
+
+ // offset within record data
+ $offset = 4;
+
+ for ($i = 1; $i <= $columns; ++$i) {
+ $columnString = Coordinate::stringFromColumnIndex($colFirst + $i);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset: var; size: 2; index to XF record
+ $xfIndex = self::getUInt2d($recordData, $offset);
+
+ // offset: var; size: 4; RK value
+ $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add style
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell value
+ $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
+ }
+
+ $offset += 6;
+ }
+ }
+
+ /**
+ * Read NUMBER record
+ * This record represents a cell that contains a
+ * floating-point value.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readNumber(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size 2; index to column
+ $column = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset 4; size: 2; index to XF record
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ $numValue = self::extractNumber(substr($recordData, 6, 8));
+
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add cell style
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+
+ // add cell value
+ $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read FORMULA record + perhaps a following STRING record if formula result is a string
+ * This record contains the token array and the result of a
+ * formula cell.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readFormula(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; col index
+ $column = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+ // offset: 20: size: variable; formula structure
+ $formulaStructure = substr($recordData, 20);
+
+ // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
+ $options = self::getUInt2d($recordData, 14);
+
+ // bit: 0; mask: 0x0001; 1 = recalculate always
+ // bit: 1; mask: 0x0002; 1 = calculate on open
+ // bit: 2; mask: 0x0008; 1 = part of a shared formula
+ $isPartOfSharedFormula = (bool) (0x0008 & $options);
+
+ // WARNING:
+ // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
+ // the formula data may be ordinary formula data, therefore we need to check
+ // explicitly for the tExp token (0x01)
+ $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
+
+ if ($isPartOfSharedFormula) {
+ // part of shared formula which means there will be a formula with a tExp token and nothing else
+ // get the base cell, grab tExp token
+ $baseRow = self::getUInt2d($formulaStructure, 3);
+ $baseCol = self::getUInt2d($formulaStructure, 5);
+ $this->baseCell = Coordinate::stringFromColumnIndex($baseCol + 1) . ($baseRow + 1);
+ }
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ if ($isPartOfSharedFormula) {
+ // formula is added to this cell after the sheet has been read
+ $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->baseCell;
+ }
+
+ // offset: 16: size: 4; not used
+
+ // offset: 4; size: 2; XF index
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 8; result of the formula
+ if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
+ // String formula. Result follows in appended STRING record
+ $dataType = DataType::TYPE_STRING;
+
+ // read possible SHAREDFMLA record
+ $code = self::getUInt2d($this->data, $this->pos);
+ if ($code == self::XLS_TYPE_SHAREDFMLA) {
+ $this->readSharedFmla();
+ }
+
+ // read STRING record
+ $value = $this->readString();
+ } elseif (
+ (ord($recordData[6]) == 1)
+ && (ord($recordData[12]) == 255)
+ && (ord($recordData[13]) == 255)
+ ) {
+ // Boolean formula. Result is in +2; 0=false, 1=true
+ $dataType = DataType::TYPE_BOOL;
+ $value = (bool) ord($recordData[8]);
+ } elseif (
+ (ord($recordData[6]) == 2)
+ && (ord($recordData[12]) == 255)
+ && (ord($recordData[13]) == 255)
+ ) {
+ // Error formula. Error code is in +2
+ $dataType = DataType::TYPE_ERROR;
+ $value = Xls\ErrorCode::lookup(ord($recordData[8]));
+ } elseif (
+ (ord($recordData[6]) == 3)
+ && (ord($recordData[12]) == 255)
+ && (ord($recordData[13]) == 255)
+ ) {
+ // Formula result is a null string
+ $dataType = DataType::TYPE_NULL;
+ $value = '';
+ } else {
+ // forumla result is a number, first 14 bytes like _NUMBER record
+ $dataType = DataType::TYPE_NUMERIC;
+ $value = self::extractNumber(substr($recordData, 6, 8));
+ }
+
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add cell style
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+
+ // store the formula
+ if (!$isPartOfSharedFormula) {
+ // not part of shared formula
+ // add cell value. If we can read formula, populate with formula, otherwise just used cached value
+ try {
+ if ($this->version != self::XLS_BIFF8) {
+ throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
+ }
+ $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
+ $cell->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
+ } catch (PhpSpreadsheetException) {
+ $cell->setValueExplicit($value, $dataType);
+ }
+ } else {
+ if ($this->version == self::XLS_BIFF8) {
+ // do nothing at this point, formula id added later in the code
+ } else {
+ $cell->setValueExplicit($value, $dataType);
+ }
+ }
+
+ // store the cached calculated value
+ $cell->setCalculatedValue($value, $dataType === DataType::TYPE_NUMERIC);
+ }
+ }
+
+ /**
+ * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
+ * which usually contains relative references.
+ * These will be used to construct the formula in each shared formula part after the sheet is read.
+ */
+ private function readSharedFmla(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
+ //$cellRange = substr($recordData, 0, 6);
+ //$cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
+
+ // offset: 6, size: 1; not used
+
+ // offset: 7, size: 1; number of existing FORMULA records for this shared formula
+ //$no = ord($recordData[7]);
+
+ // offset: 8, size: var; Binary token array of the shared formula
+ $formula = substr($recordData, 8);
+
+ // at this point we only store the shared formula for later use
+ $this->sharedFormulas[$this->baseCell] = $formula;
+ }
+
+ /**
+ * Read a STRING record from current stream position and advance the stream pointer to next record
+ * This record is used for storing result from FORMULA record when it is a string, and
+ * it occurs directly after the FORMULA record.
+ *
+ * @return string The string contents as UTF-8
+ */
+ private function readString()
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringLong($recordData);
+ $value = $string['value'];
+ } else {
+ $string = $this->readByteStringLong($recordData);
+ $value = $string['value'];
+ }
+
+ return $value;
+ }
+
+ /**
+ * Read BOOLERR record
+ * This record represents a Boolean value or error value
+ * cell.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readBoolErr(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; column index
+ $column = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset: 4; size: 2; index to XF record
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 1; the boolean value or error value
+ $boolErr = ord($recordData[6]);
+
+ // offset: 7; size: 1; 0=boolean; 1=error
+ $isError = ord($recordData[7]);
+
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ switch ($isError) {
+ case 0: // boolean
+ $value = (bool) $boolErr;
+
+ // add cell value
+ $cell->setValueExplicit($value, DataType::TYPE_BOOL);
+
+ break;
+ case 1: // error type
+ $value = Xls\ErrorCode::lookup($boolErr);
+
+ // add cell value
+ $cell->setValueExplicit($value, DataType::TYPE_ERROR);
+
+ break;
+ }
+
+ if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add cell style
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MULBLANK record
+ * This record represents a cell range of empty cells. All
+ * cells are located in the same row.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readMulBlank(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first column
+ $fc = self::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2 x nc; list of indexes to XF records
+ // add style information
+ if (!$this->readDataOnly && $this->readEmptyCells) {
+ for ($i = 0; $i < $length / 2 - 3; ++$i) {
+ $columnString = Coordinate::stringFromColumnIndex($fc + $i + 1);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ $xfIndex = self::getUInt2d($recordData, 4 + 2 * $i);
+ if (isset($this->mapCellXfIndex[$xfIndex])) {
+ $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+ }
+
+ // offset: 6; size 2; index to last column (not needed)
+ }
+
+ /**
+ * Read LABEL record
+ * This record represents a cell that contains a string. In
+ * BIFF8 it is usually replaced by the LABELSST record.
+ * Excel still uses this record, if it copies unformatted
+ * text cells to the clipboard.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readLabel(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; index to row
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to column
+ $column = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($column + 1);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset: 4; size: 2; XF index
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ // add cell value
+ // todo: what if string is very long? continue record
+ if ($this->version == self::XLS_BIFF8) {
+ $string = self::readUnicodeStringLong(substr($recordData, 6));
+ $value = $string['value'];
+ } else {
+ $string = $this->readByteStringLong(substr($recordData, 6));
+ $value = $string['value'];
+ }
+ if ($this->readEmptyCells || trim($value) !== '') {
+ $cell = $this->phpSheet->getCell($columnString . ($row + 1));
+ $cell->setValueExplicit($value, DataType::TYPE_STRING);
+
+ if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
+ // add cell style
+ $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Read BLANK record.
+ */
+ private function readBlank(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; row index
+ $row = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; col index
+ $col = self::getUInt2d($recordData, 2);
+ $columnString = Coordinate::stringFromColumnIndex($col + 1);
+
+ // Read cell?
+ if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
+ // offset: 4; size: 2; XF index
+ $xfIndex = self::getUInt2d($recordData, 4);
+
+ // add style information
+ if (!$this->readDataOnly && $this->readEmptyCells && isset($this->mapCellXfIndex[$xfIndex])) {
+ $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
+ }
+ }
+ }
+
+ /**
+ * Read MSODRAWING record.
+ */
+ private function readMsoDrawing(): void
+ {
+ //$length = self::getUInt2d($this->data, $this->pos + 2);
+
+ // get spliced record data
+ $splicedRecordData = $this->getSplicedRecordData();
+ $recordData = $splicedRecordData['recordData'];
+
+ $this->drawingData .= $recordData;
+ }
+
+ /**
+ * Read OBJ record.
+ */
+ private function readObj(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
+ return;
+ }
+
+ // recordData consists of an array of subrecords looking like this:
+ // ft: 2 bytes; ftCmo type (0x15)
+ // cb: 2 bytes; size in bytes of ftCmo data
+ // ot: 2 bytes; Object Type
+ // id: 2 bytes; Object id number
+ // grbit: 2 bytes; Option Flags
+ // data: var; subrecord data
+
+ // for now, we are just interested in the second subrecord containing the object type
+ $ftCmoType = self::getUInt2d($recordData, 0);
+ $cbCmoSize = self::getUInt2d($recordData, 2);
+ $otObjType = self::getUInt2d($recordData, 4);
+ $idObjID = self::getUInt2d($recordData, 6);
+ $grbitOpts = self::getUInt2d($recordData, 6);
+
+ $this->objs[] = [
+ 'ftCmoType' => $ftCmoType,
+ 'cbCmoSize' => $cbCmoSize,
+ 'otObjType' => $otObjType,
+ 'idObjID' => $idObjID,
+ 'grbitOpts' => $grbitOpts,
+ ];
+ $this->textObjRef = $idObjID;
+ }
+
+ /**
+ * Read WINDOW2 record.
+ */
+ private function readWindow2(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; option flags
+ $options = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; index to first visible row
+ //$firstVisibleRow = self::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2; index to first visible colum
+ //$firstVisibleColumn = self::getUInt2d($recordData, 4);
+ $zoomscaleInPageBreakPreview = 0;
+ $zoomscaleInNormalView = 0;
+ if ($this->version === self::XLS_BIFF8) {
+ // offset: 8; size: 2; not used
+ // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
+ // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
+ // offset: 14; size: 4; not used
+ if (!isset($recordData[10])) {
+ $zoomscaleInPageBreakPreview = 0;
+ } else {
+ $zoomscaleInPageBreakPreview = self::getUInt2d($recordData, 10);
+ }
+
+ if ($zoomscaleInPageBreakPreview === 0) {
+ $zoomscaleInPageBreakPreview = 60;
+ }
+
+ if (!isset($recordData[12])) {
+ $zoomscaleInNormalView = 0;
+ } else {
+ $zoomscaleInNormalView = self::getUInt2d($recordData, 12);
+ }
+
+ if ($zoomscaleInNormalView === 0) {
+ $zoomscaleInNormalView = 100;
+ }
+ }
+
+ // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
+ $showGridlines = (bool) ((0x0002 & $options) >> 1);
+ $this->phpSheet->setShowGridlines($showGridlines);
+
+ // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
+ $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
+ $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
+
+ // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
+ $this->frozen = (bool) ((0x0008 & $options) >> 3);
+
+ // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
+ $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
+
+ // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
+ $isActive = (bool) ((0x0400 & $options) >> 10);
+ if ($isActive) {
+ $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
+ $this->activeSheetSet = true;
+ }
+
+ // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
+ $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
+
+ //FIXME: set $firstVisibleRow and $firstVisibleColumn
+
+ if ($this->phpSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_PAGE_LAYOUT) {
+ //NOTE: this setting is inferior to page layout view(Excel2007-)
+ $view = $isPageBreakPreview ? SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : SheetView::SHEETVIEW_NORMAL;
+ $this->phpSheet->getSheetView()->setView($view);
+ if ($this->version === self::XLS_BIFF8) {
+ $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
+ $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
+ $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
+ }
+ }
+ }
+
+ /**
+ * Read PLV Record(Created by Excel2007 or upper).
+ */
+ private function readPageLayoutView(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; rt
+ //->ignore
+ //$rt = self::getUInt2d($recordData, 0);
+ // offset: 2; size: 2; grbitfr
+ //->ignore
+ //$grbitFrt = self::getUInt2d($recordData, 2);
+ // offset: 4; size: 8; reserved
+ //->ignore
+
+ // offset: 12; size 2; zoom scale
+ $wScalePLV = self::getUInt2d($recordData, 12);
+ // offset: 14; size 2; grbit
+ $grbit = self::getUInt2d($recordData, 14);
+
+ // decomprise grbit
+ $fPageLayoutView = $grbit & 0x01;
+ //$fRulerVisible = ($grbit >> 1) & 0x01; //no support
+ //$fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
+
+ if ($fPageLayoutView === 1) {
+ $this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
+ $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
+ }
+ //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
+ }
+
+ /**
+ * Read SCL record.
+ */
+ private function readScl(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // offset: 0; size: 2; numerator of the view magnification
+ $numerator = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; numerator of the view magnification
+ $denumerator = self::getUInt2d($recordData, 2);
+
+ // set the zoom scale (in percent)
+ $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
+ }
+
+ /**
+ * Read PANE record.
+ */
+ private function readPane(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; position of vertical split
+ $px = self::getUInt2d($recordData, 0);
+
+ // offset: 2; size: 2; position of horizontal split
+ $py = self::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2; top most visible row in the bottom pane
+ $rwTop = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 2; first visible left column in the right pane
+ $colLeft = self::getUInt2d($recordData, 6);
+
+ if ($this->frozen) {
+ // frozen panes
+ $cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1);
+ $topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1);
+ $this->phpSheet->freezePane($cell, $topLeftCell);
+ }
+ // unfrozen panes; split windows; not supported by PhpSpreadsheet core
+ }
+ }
+
+ /**
+ * Read SELECTION record. There is one such record for each pane in the sheet.
+ */
+ private function readSelection(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 1; pane identifier
+ //$paneId = ord($recordData[0]);
+
+ // offset: 1; size: 2; index to row of the active cell
+ //$r = self::getUInt2d($recordData, 1);
+
+ // offset: 3; size: 2; index to column of the active cell
+ //$c = self::getUInt2d($recordData, 3);
+
+ // offset: 5; size: 2; index into the following cell range list to the
+ // entry that contains the active cell
+ //$index = self::getUInt2d($recordData, 5);
+
+ // offset: 7; size: var; cell range address list containing all selected cell ranges
+ $data = substr($recordData, 7);
+ $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
+
+ $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
+
+ // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
+ if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
+ $selectedCells = (string) preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
+ }
+
+ // first row '1' + last row '65536' indicates that full column is selected
+ if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
+ $selectedCells = (string) preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
+ }
+
+ // first column 'A' + last column 'IV' indicates that full row is selected
+ if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) {
+ $selectedCells = (string) preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells);
+ }
+
+ $this->phpSheet->setSelectedCells($selectedCells);
+ }
+ }
+
+ private function includeCellRangeFiltered(string $cellRangeAddress): bool
+ {
+ $includeCellRange = true;
+ if ($this->getReadFilter() !== null) {
+ $includeCellRange = false;
+ $rangeBoundaries = Coordinate::getRangeBoundaries($cellRangeAddress);
+ ++$rangeBoundaries[1][0];
+ for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
+ for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
+ if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
+ $includeCellRange = true;
+
+ break 2;
+ }
+ }
+ }
+ }
+
+ return $includeCellRange;
+ }
+
+ /**
+ * MERGEDCELLS.
+ *
+ * This record contains the addresses of merged cell ranges
+ * in the current sheet.
+ *
+ * -- "OpenOffice.org's Documentation of the Microsoft
+ * Excel File Format"
+ */
+ private function readMergedCells(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
+ $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
+ foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
+ if (
+ (str_contains($cellRangeAddress, ':'))
+ && ($this->includeCellRangeFiltered($cellRangeAddress))
+ ) {
+ $this->phpSheet->mergeCells($cellRangeAddress, Worksheet::MERGE_CELL_CONTENT_HIDE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Read HYPERLINK record.
+ */
+ private function readHyperLink(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 8; cell range address of all cells containing this hyperlink
+ try {
+ $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
+ } catch (PhpSpreadsheetException) {
+ return;
+ }
+
+ // offset: 8, size: 16; GUID of StdLink
+
+ // offset: 24, size: 4; unknown value
+
+ // offset: 28, size: 4; option flags
+ // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
+ $isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0;
+
+ // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
+ //$isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
+
+ // bit: 2 (and 4); mask: 0x00000014; 0 = no description
+ $hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2;
+
+ // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
+ $hasText = (0x00000008 & self::getUInt2d($recordData, 28)) >> 3;
+
+ // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
+ $hasFrame = (0x00000080 & self::getUInt2d($recordData, 28)) >> 7;
+
+ // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
+ $isUNC = (0x00000100 & self::getUInt2d($recordData, 28)) >> 8;
+
+ // offset within record data
+ $offset = 32;
+
+ if ($hasDesc) {
+ // offset: 32; size: var; character count of description text
+ $dl = self::getInt4d($recordData, 32);
+ // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
+ //$desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
+ $offset += 4 + 2 * $dl;
+ }
+ if ($hasFrame) {
+ $fl = self::getInt4d($recordData, $offset);
+ $offset += 4 + 2 * $fl;
+ }
+
+ // detect type of hyperlink (there are 4 types)
+ $hyperlinkType = null;
+
+ if ($isUNC) {
+ $hyperlinkType = 'UNC';
+ } elseif (!$isFileLinkOrUrl) {
+ $hyperlinkType = 'workbook';
+ } elseif (ord($recordData[$offset]) == 0x03) {
+ $hyperlinkType = 'local';
+ } elseif (ord($recordData[$offset]) == 0xE0) {
+ $hyperlinkType = 'URL';
+ }
+
+ switch ($hyperlinkType) {
+ case 'URL':
+ // section 5.58.2: Hyperlink containing a URL
+ // e.g. http://example.org/index.php
+
+ // offset: var; size: 16; GUID of URL Moniker
+ $offset += 16;
+ // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
+ $us = self::getInt4d($recordData, $offset);
+ $offset += 4;
+ // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
+ $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
+ $nullOffset = strpos($url, chr(0x00));
+ if ($nullOffset) {
+ $url = substr($url, 0, $nullOffset);
+ }
+ $url .= $hasText ? '#' : '';
+ $offset += $us;
+
+ break;
+ case 'local':
+ // section 5.58.3: Hyperlink to local file
+ // examples:
+ // mydoc.txt
+ // ../../somedoc.xls#Sheet!A1
+
+ // offset: var; size: 16; GUI of File Moniker
+ $offset += 16;
+
+ // offset: var; size: 2; directory up-level count.
+ $upLevelCount = self::getUInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
+ $sl = self::getInt4d($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
+ $shortenedFilePath = substr($recordData, $offset, $sl);
+ $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
+ $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
+
+ $offset += $sl;
+
+ // offset: var; size: 24; unknown sequence
+ $offset += 24;
+
+ // extended file path
+ // offset: var; size: 4; size of the following file link field including string lenth mark
+ $sz = self::getInt4d($recordData, $offset);
+ $offset += 4;
+
+ $extendedFilePath = '';
+ // only present if $sz > 0
+ if ($sz > 0) {
+ // offset: var; size: 4; size of the character array of the extended file path and name
+ $xl = self::getInt4d($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size 2; unknown
+ $offset += 2;
+
+ // offset: var; size $xl; character array of the extended file path and name.
+ $extendedFilePath = substr($recordData, $offset, $xl);
+ $extendedFilePath = self::encodeUTF16($extendedFilePath, false);
+ $offset += $xl;
+ }
+
+ // construct the path
+ $url = str_repeat('..\\', $upLevelCount);
+ $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available
+ $url .= $hasText ? '#' : '';
+
+ break;
+ case 'UNC':
+ // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
+ // todo: implement
+ return;
+ case 'workbook':
+ // section 5.58.5: Hyperlink to the Current Workbook
+ // e.g. Sheet2!B1:C2, stored in text mark field
+ $url = 'sheet://';
+
+ break;
+ default:
+ return;
+ }
+
+ if ($hasText) {
+ // offset: var; size: 4; character count of text mark including trailing zero word
+ $tl = self::getInt4d($recordData, $offset);
+ $offset += 4;
+ // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
+ $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
+ $url .= $text;
+ }
+
+ // apply the hyperlink to all the relevant cells
+ foreach (Coordinate::extractAllCellReferencesInRange($cellRange) as $coordinate) {
+ $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
+ }
+ }
+ }
+
+ /**
+ * Read DATAVALIDATIONS record.
+ */
+ private function readDataValidations(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ //$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+ }
+
+ /**
+ * Read DATAVALIDATION record.
+ */
+ private function readDataValidation(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 4; Options
+ $options = self::getInt4d($recordData, 0);
+
+ // bit: 0-3; mask: 0x0000000F; type
+ $type = (0x0000000F & $options) >> 0;
+ $type = Xls\DataValidationHelper::type($type);
+
+ // bit: 4-6; mask: 0x00000070; error type
+ $errorStyle = (0x00000070 & $options) >> 4;
+ $errorStyle = Xls\DataValidationHelper::errorStyle($errorStyle);
+
+ // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
+ // I have only seen cases where this is 1
+ //$explicitFormula = (0x00000080 & $options) >> 7;
+
+ // bit: 8; mask: 0x00000100; 1= empty cells allowed
+ $allowBlank = (0x00000100 & $options) >> 8;
+
+ // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
+ $suppressDropDown = (0x00000200 & $options) >> 9;
+
+ // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
+ $showInputMessage = (0x00040000 & $options) >> 18;
+
+ // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
+ $showErrorMessage = (0x00080000 & $options) >> 19;
+
+ // bit: 20-23; mask: 0x00F00000; condition operator
+ $operator = (0x00F00000 & $options) >> 20;
+ $operator = Xls\DataValidationHelper::operator($operator);
+
+ if ($type === null || $errorStyle === null || $operator === null) {
+ return;
+ }
+
+ // offset: 4; size: var; title of the prompt box
+ $offset = 4;
+ $string = self::readUnicodeStringLong(substr($recordData, $offset));
+ $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: var; title of the error box
+ $string = self::readUnicodeStringLong(substr($recordData, $offset));
+ $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: var; text of the prompt box
+ $string = self::readUnicodeStringLong(substr($recordData, $offset));
+ $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: var; text of the error box
+ $string = self::readUnicodeStringLong(substr($recordData, $offset));
+ $error = $string['value'] !== chr(0) ? $string['value'] : '';
+ $offset += $string['size'];
+
+ // offset: var; size: 2; size of the formula data for the first condition
+ $sz1 = self::getUInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 2; not used
+ $offset += 2;
+
+ // offset: var; size: $sz1; formula data for first condition (without size field)
+ $formula1 = substr($recordData, $offset, $sz1);
+ $formula1 = pack('v', $sz1) . $formula1; // prepend the length
+
+ try {
+ $formula1 = $this->getFormulaFromStructure($formula1);
+
+ // in list type validity, null characters are used as item separators
+ if ($type == DataValidation::TYPE_LIST) {
+ $formula1 = str_replace(chr(0), ',', $formula1);
+ }
+ } catch (PhpSpreadsheetException $e) {
+ return;
+ }
+ $offset += $sz1;
+
+ // offset: var; size: 2; size of the formula data for the first condition
+ $sz2 = self::getUInt2d($recordData, $offset);
+ $offset += 2;
+
+ // offset: var; size: 2; not used
+ $offset += 2;
+
+ // offset: var; size: $sz2; formula data for second condition (without size field)
+ $formula2 = substr($recordData, $offset, $sz2);
+ $formula2 = pack('v', $sz2) . $formula2; // prepend the length
+
+ try {
+ $formula2 = $this->getFormulaFromStructure($formula2);
+ } catch (PhpSpreadsheetException) {
+ return;
+ }
+ $offset += $sz2;
+
+ // offset: var; size: var; cell range address list with
+ $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
+ $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
+
+ foreach ($cellRangeAddresses as $cellRange) {
+ $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
+ foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $coordinate) {
+ $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
+ $objValidation->setType($type);
+ $objValidation->setErrorStyle($errorStyle);
+ $objValidation->setAllowBlank((bool) $allowBlank);
+ $objValidation->setShowInputMessage((bool) $showInputMessage);
+ $objValidation->setShowErrorMessage((bool) $showErrorMessage);
+ $objValidation->setShowDropDown(!$suppressDropDown);
+ $objValidation->setOperator($operator);
+ $objValidation->setErrorTitle($errorTitle);
+ $objValidation->setError($error);
+ $objValidation->setPromptTitle($promptTitle);
+ $objValidation->setPrompt($prompt);
+ $objValidation->setFormula1($formula1);
+ $objValidation->setFormula2($formula2);
+ }
+ }
+ }
+
+ /**
+ * Read SHEETLAYOUT record. Stores sheet tab color information.
+ */
+ private function readSheetLayout(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if (!$this->readDataOnly) {
+ // offset: 0; size: 2; repeated record identifier 0x0862
+
+ // offset: 2; size: 10; not used
+
+ // offset: 12; size: 4; size of record data
+ // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
+ $sz = self::getInt4d($recordData, 12);
+
+ switch ($sz) {
+ case 0x14:
+ // offset: 16; size: 2; color index for sheet tab
+ $colorIndex = self::getUInt2d($recordData, 16);
+ $color = Xls\Color::map($colorIndex, $this->palette, $this->version);
+ $this->phpSheet->getTabColor()->setRGB($color['rgb']);
+
+ break;
+ case 0x28:
+ // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
+ return;
+ }
+ }
+ }
+
+ /**
+ * Read SHEETPROTECTION record (FEATHEADR).
+ */
+ private function readSheetProtection(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2; repeated record header
+
+ // offset: 2; size: 2; FRT cell reference flag (=0 currently)
+
+ // offset: 4; size: 8; Currently not used and set to 0
+
+ // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
+ $isf = self::getUInt2d($recordData, 12);
+ if ($isf != 2) {
+ return;
+ }
+
+ // offset: 14; size: 1; =1 since this is a feat header
+
+ // offset: 15; size: 4; size of rgbHdrSData
+
+ // rgbHdrSData, assume "Enhanced Protection"
+ // offset: 19; size: 2; option flags
+ $options = self::getUInt2d($recordData, 19);
+
+ // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
+ // Note - do not negate $bool
+ $bool = (0x0001 & $options) >> 0;
+ $this->phpSheet->getProtection()->setObjects((bool) $bool);
+
+ // bit: 1; mask 0x0002; edit scenarios
+ // Note - do not negate $bool
+ $bool = (0x0002 & $options) >> 1;
+ $this->phpSheet->getProtection()->setScenarios((bool) $bool);
+
+ // bit: 2; mask 0x0004; format cells
+ $bool = (0x0004 & $options) >> 2;
+ $this->phpSheet->getProtection()->setFormatCells(!$bool);
+
+ // bit: 3; mask 0x0008; format columns
+ $bool = (0x0008 & $options) >> 3;
+ $this->phpSheet->getProtection()->setFormatColumns(!$bool);
+
+ // bit: 4; mask 0x0010; format rows
+ $bool = (0x0010 & $options) >> 4;
+ $this->phpSheet->getProtection()->setFormatRows(!$bool);
+
+ // bit: 5; mask 0x0020; insert columns
+ $bool = (0x0020 & $options) >> 5;
+ $this->phpSheet->getProtection()->setInsertColumns(!$bool);
+
+ // bit: 6; mask 0x0040; insert rows
+ $bool = (0x0040 & $options) >> 6;
+ $this->phpSheet->getProtection()->setInsertRows(!$bool);
+
+ // bit: 7; mask 0x0080; insert hyperlinks
+ $bool = (0x0080 & $options) >> 7;
+ $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
+
+ // bit: 8; mask 0x0100; delete columns
+ $bool = (0x0100 & $options) >> 8;
+ $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
+
+ // bit: 9; mask 0x0200; delete rows
+ $bool = (0x0200 & $options) >> 9;
+ $this->phpSheet->getProtection()->setDeleteRows(!$bool);
+
+ // bit: 10; mask 0x0400; select locked cells
+ // Note that this is opposite of most of above.
+ $bool = (0x0400 & $options) >> 10;
+ $this->phpSheet->getProtection()->setSelectLockedCells((bool) $bool);
+
+ // bit: 11; mask 0x0800; sort cell range
+ $bool = (0x0800 & $options) >> 11;
+ $this->phpSheet->getProtection()->setSort(!$bool);
+
+ // bit: 12; mask 0x1000; auto filter
+ $bool = (0x1000 & $options) >> 12;
+ $this->phpSheet->getProtection()->setAutoFilter(!$bool);
+
+ // bit: 13; mask 0x2000; pivot tables
+ $bool = (0x2000 & $options) >> 13;
+ $this->phpSheet->getProtection()->setPivotTables(!$bool);
+
+ // bit: 14; mask 0x4000; select unlocked cells
+ // Note that this is opposite of most of above.
+ $bool = (0x4000 & $options) >> 14;
+ $this->phpSheet->getProtection()->setSelectUnlockedCells((bool) $bool);
+
+ // offset: 21; size: 2; not used
+ }
+
+ /**
+ * Read RANGEPROTECTION record
+ * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
+ * where it is referred to as FEAT record.
+ */
+ private function readRangeProtection(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ // local pointer in record data
+ $offset = 0;
+
+ if (!$this->readDataOnly) {
+ $offset += 12;
+
+ // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
+ $isf = self::getUInt2d($recordData, 12);
+ if ($isf != 2) {
+ // we only read FEAT records of type 2
+ return;
+ }
+ $offset += 2;
+
+ $offset += 5;
+
+ // offset: 19; size: 2; count of ref ranges this feature is on
+ $cref = self::getUInt2d($recordData, 19);
+ $offset += 2;
+
+ $offset += 6;
+
+ // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
+ $cellRanges = [];
+ for ($i = 0; $i < $cref; ++$i) {
+ try {
+ $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
+ } catch (PhpSpreadsheetException) {
+ return;
+ }
+ $cellRanges[] = $cellRange;
+ $offset += 8;
+ }
+
+ // offset: var; size: var; variable length of feature specific data
+ //$rgbFeat = substr($recordData, $offset);
+ $offset += 4;
+
+ // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
+ $wPassword = self::getInt4d($recordData, $offset);
+ $offset += 4;
+
+ // Apply range protection to sheet
+ if ($cellRanges) {
+ $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
+ }
+ }
+ }
+
+ /**
+ * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
+ * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
+ * In this case, we must treat the CONTINUE record as a MSODRAWING record.
+ */
+ private function readContinue(): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // check if we are reading drawing data
+ // this is in case a free CONTINUE record occurs in other circumstances we are unaware of
+ if ($this->drawingData == '') {
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ return;
+ }
+
+ // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
+ if ($length < 4) {
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+
+ return;
+ }
+
+ // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
+ // look inside CONTINUE record to see if it looks like a part of an Escher stream
+ // we know that Escher stream may be split at least at
+ // 0xF003 MsofbtSpgrContainer
+ // 0xF004 MsofbtSpContainer
+ // 0xF00D MsofbtClientTextbox
+ $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
+
+ $splitPoint = self::getUInt2d($recordData, 2);
+ if (in_array($splitPoint, $validSplitPoints)) {
+ // get spliced record data (and move pointer to next record)
+ $splicedRecordData = $this->getSplicedRecordData();
+ $this->drawingData .= $splicedRecordData['recordData'];
+
+ return;
+ }
+
+ // move stream pointer to next record
+ $this->pos += 4 + $length;
+ }
+
+ /**
+ * Reads a record from current position in data stream and continues reading data as long as CONTINUE
+ * records are found. Splices the record data pieces and returns the combined string as if record data
+ * is in one piece.
+ * Moves to next current position in data stream to start of next record different from a CONtINUE record.
+ */
+ private function getSplicedRecordData(): array
+ {
+ $data = '';
+ $spliceOffsets = [];
+
+ $i = 0;
+ $spliceOffsets[0] = 0;
+
+ do {
+ ++$i;
+
+ // offset: 0; size: 2; identifier
+ //$identifier = self::getUInt2d($this->data, $this->pos);
+ // offset: 2; size: 2; length
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
+
+ $this->pos += 4 + $length;
+ $nextIdentifier = self::getUInt2d($this->data, $this->pos);
+ } while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
+
+ return [
+ 'recordData' => $data,
+ 'spliceOffsets' => $spliceOffsets,
+ ];
+ }
+
+ /**
+ * Convert formula structure into human readable Excel formula like 'A3+A5*5'.
+ *
+ * @param string $formulaStructure The complete binary data for the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ *
+ * @return string Human readable formula
+ */
+ private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1'): string
+ {
+ // offset: 0; size: 2; size of the following formula data
+ $sz = self::getUInt2d($formulaStructure, 0);
+
+ // offset: 2; size: sz
+ $formulaData = substr($formulaStructure, 2, $sz);
+
+ // offset: 2 + sz; size: variable (optional)
+ if (strlen($formulaStructure) > 2 + $sz) {
+ $additionalData = substr($formulaStructure, 2 + $sz);
+ } else {
+ $additionalData = '';
+ }
+
+ return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
+ }
+
+ /**
+ * Take formula data and additional data for formula and return human readable formula.
+ *
+ * @param string $formulaData The binary data for the formula itself
+ * @param string $additionalData Additional binary data going with the formula
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ *
+ * @return string Human readable formula
+ */
+ private function getFormulaFromData(string $formulaData, string $additionalData = '', string $baseCell = 'A1'): string
+ {
+ // start parsing the formula data
+ $tokens = [];
+
+ while ($formulaData !== '' && $token = $this->getNextToken($formulaData, $baseCell)) {
+ $tokens[] = $token;
+ $formulaData = substr($formulaData, $token['size']);
+ }
+
+ $formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
+
+ return $formulaString;
+ }
+
+ /**
+ * Take array of tokens together with additional data for formula and return human readable formula.
+ *
+ * @param string $additionalData Additional binary data going with the formula
+ *
+ * @return string Human readable formula
+ */
+ private function createFormulaFromTokens(array $tokens, string $additionalData): string
+ {
+ // empty formula?
+ if (empty($tokens)) {
+ return '';
+ }
+
+ $formulaStrings = [];
+ foreach ($tokens as $token) {
+ // initialize spaces
+ $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
+ case 'tConcat': // addition
+ case 'tDiv': // division
+ case 'tEQ': // equality
+ case 'tGE': // greater than or equal
+ case 'tGT': // greater than
+ case 'tIsect': // intersection
+ case 'tLE': // less than or equal
+ case 'tList': // less than or equal
+ case 'tLT': // less than
+ case 'tMul': // multiplication
+ case 'tNE': // multiplication
+ case 'tPower': // power
+ case 'tRange': // range
+ case 'tSub': // subtraction
+ $op2 = array_pop($formulaStrings);
+ $op1 = array_pop($formulaStrings);
+ $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
+ unset($space0, $space1);
+
+ break;
+ case 'tUplus': // unary plus
+ case 'tUminus': // unary minus
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "$space1$space0{$token['data']}$op";
+ unset($space0, $space1);
+
+ break;
+ case 'tPercent': // percent sign
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "$op$space1$space0{$token['data']}";
+ unset($space0, $space1);
+
+ break;
+ case 'tAttrVolatile': // indicates volatile function
+ case 'tAttrIf':
+ case 'tAttrSkip':
+ case 'tAttrChoose':
+ // token is only important for Excel formula evaluator
+ // do nothing
+ break;
+ case 'tAttrSpace': // space / carriage return
+ // space will be used when next token arrives, do not alter formulaString stack
+ switch ($token['data']['spacetype']) {
+ case 'type0':
+ $space0 = str_repeat(' ', $token['data']['spacecount']);
+
+ break;
+ case 'type1':
+ $space1 = str_repeat("\n", $token['data']['spacecount']);
+
+ break;
+ case 'type2':
+ $space2 = str_repeat(' ', $token['data']['spacecount']);
+
+ break;
+ case 'type3':
+ $space3 = str_repeat("\n", $token['data']['spacecount']);
+
+ break;
+ case 'type4':
+ $space4 = str_repeat(' ', $token['data']['spacecount']);
+
+ break;
+ case 'type5':
+ $space5 = str_repeat("\n", $token['data']['spacecount']);
+
+ break;
+ }
+
+ break;
+ case 'tAttrSum': // SUM function with one parameter
+ $op = array_pop($formulaStrings);
+ $formulaStrings[] = "{$space1}{$space0}SUM($op)";
+ unset($space0, $space1);
+
+ break;
+ case 'tFunc': // function with fixed number of arguments
+ case 'tFuncV': // function with variable number of arguments
+ if ($token['data']['function'] != '') {
+ // normal function
+ $ops = []; // array of operators
+ for ($i = 0; $i < $token['data']['args']; ++$i) {
+ $ops[] = array_pop($formulaStrings);
+ }
+ $ops = array_reverse($ops);
+ $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
+ unset($space0, $space1);
+ } else {
+ // add-in function
+ $ops = []; // array of operators
+ for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
+ $ops[] = array_pop($formulaStrings);
+ }
+ $ops = array_reverse($ops);
+ $function = array_pop($formulaStrings);
+ $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
+ unset($space0, $space1);
+ }
+
+ break;
+ case 'tParen': // parenthesis
+ $expression = array_pop($formulaStrings);
+ $formulaStrings[] = "$space3$space2($expression$space5$space4)";
+ unset($space2, $space3, $space4, $space5);
+
+ break;
+ case 'tArray': // array constant
+ $constantArray = self::readBIFF8ConstantArray($additionalData);
+ $formulaStrings[] = $space1 . $space0 . $constantArray['value'];
+ $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
+ unset($space0, $space1);
+
+ break;
+ case 'tMemArea':
+ // bite off chunk of additional data
+ $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
+ $additionalData = substr($additionalData, $cellRangeAddressList['size']);
+ $formulaStrings[] = "$space1$space0{$token['data']}";
+ unset($space0, $space1);
+
+ break;
+ case 'tArea': // cell range address
+ case 'tBool': // boolean
+ case 'tErr': // error code
+ case 'tInt': // integer
+ case 'tMemErr':
+ case 'tMemFunc':
+ case 'tMissArg':
+ case 'tName':
+ case 'tNameX':
+ case 'tNum': // number
+ case 'tRef': // single cell reference
+ case 'tRef3d': // 3d cell reference
+ case 'tArea3d': // 3d cell range reference
+ case 'tRefN':
+ case 'tAreaN':
+ case 'tStr': // string
+ $formulaStrings[] = "$space1$space0{$token['data']}";
+ unset($space0, $space1);
+
+ break;
+ }
+ }
+ $formulaString = $formulaStrings[0];
+
+ return $formulaString;
+ }
+
+ /**
+ * Fetch next token from binary formula data.
+ *
+ * @param string $formulaData Formula data
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ */
+ private function getNextToken(string $formulaData, string $baseCell = 'A1'): array
+ {
+ // offset: 0; size: 1; token id
+ $id = ord($formulaData[0]); // token id
+ $name = false; // initialize token name
+
+ switch ($id) {
+ case 0x03:
+ $name = 'tAdd';
+ $size = 1;
+ $data = '+';
+
+ break;
+ case 0x04:
+ $name = 'tSub';
+ $size = 1;
+ $data = '-';
+
+ break;
+ case 0x05:
+ $name = 'tMul';
+ $size = 1;
+ $data = '*';
+
+ break;
+ case 0x06:
+ $name = 'tDiv';
+ $size = 1;
+ $data = '/';
+
+ break;
+ case 0x07:
+ $name = 'tPower';
+ $size = 1;
+ $data = '^';
+
+ break;
+ case 0x08:
+ $name = 'tConcat';
+ $size = 1;
+ $data = '&';
+
+ break;
+ case 0x09:
+ $name = 'tLT';
+ $size = 1;
+ $data = '<';
+
+ break;
+ case 0x0A:
+ $name = 'tLE';
+ $size = 1;
+ $data = '<=';
+
+ break;
+ case 0x0B:
+ $name = 'tEQ';
+ $size = 1;
+ $data = '=';
+
+ break;
+ case 0x0C:
+ $name = 'tGE';
+ $size = 1;
+ $data = '>=';
+
+ break;
+ case 0x0D:
+ $name = 'tGT';
+ $size = 1;
+ $data = '>';
+
+ break;
+ case 0x0E:
+ $name = 'tNE';
+ $size = 1;
+ $data = '<>';
+
+ break;
+ case 0x0F:
+ $name = 'tIsect';
+ $size = 1;
+ $data = ' ';
+
+ break;
+ case 0x10:
+ $name = 'tList';
+ $size = 1;
+ $data = ',';
+
+ break;
+ case 0x11:
+ $name = 'tRange';
+ $size = 1;
+ $data = ':';
+
+ break;
+ case 0x12:
+ $name = 'tUplus';
+ $size = 1;
+ $data = '+';
+
+ break;
+ case 0x13:
+ $name = 'tUminus';
+ $size = 1;
+ $data = '-';
+
+ break;
+ case 0x14:
+ $name = 'tPercent';
+ $size = 1;
+ $data = '%';
+
+ break;
+ case 0x15: // parenthesis
+ $name = 'tParen';
+ $size = 1;
+ $data = null;
+
+ break;
+ case 0x16: // missing argument
+ $name = 'tMissArg';
+ $size = 1;
+ $data = '';
+
+ break;
+ case 0x17: // string
+ $name = 'tStr';
+ // offset: 1; size: var; Unicode string, 8-bit string length
+ $string = self::readUnicodeStringShort(substr($formulaData, 1));
+ $size = 1 + $string['size'];
+ $data = self::UTF8toExcelDoubleQuoted($string['value']);
+
+ break;
+ case 0x19: // Special attribute
+ // offset: 1; size: 1; attribute type flags:
+ switch (ord($formulaData[1])) {
+ case 0x01:
+ $name = 'tAttrVolatile';
+ $size = 4;
+ $data = null;
+
+ break;
+ case 0x02:
+ $name = 'tAttrIf';
+ $size = 4;
+ $data = null;
+
+ break;
+ case 0x04:
+ $name = 'tAttrChoose';
+ // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
+ $nc = self::getUInt2d($formulaData, 2);
+ // offset: 4; size: 2 * $nc
+ // offset: 4 + 2 * $nc; size: 2
+ $size = 2 * $nc + 6;
+ $data = null;
+
+ break;
+ case 0x08:
+ $name = 'tAttrSkip';
+ $size = 4;
+ $data = null;
+
+ break;
+ case 0x10:
+ $name = 'tAttrSum';
+ $size = 4;
+ $data = null;
+
+ break;
+ case 0x40:
+ case 0x41:
+ $name = 'tAttrSpace';
+ $size = 4;
+ // offset: 2; size: 2; space type and position
+ $spacetype = match (ord($formulaData[2])) {
+ 0x00 => 'type0',
+ 0x01 => 'type1',
+ 0x02 => 'type2',
+ 0x03 => 'type3',
+ 0x04 => 'type4',
+ 0x05 => 'type5',
+ default => throw new Exception('Unrecognized space type in tAttrSpace token'),
+ };
+ // offset: 3; size: 1; number of inserted spaces/carriage returns
+ $spacecount = ord($formulaData[3]);
+
+ $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
+
+ break;
+ default:
+ throw new Exception('Unrecognized attribute flag in tAttr token');
+ }
+
+ break;
+ case 0x1C: // error code
+ // offset: 1; size: 1; error code
+ $name = 'tErr';
+ $size = 2;
+ $data = Xls\ErrorCode::lookup(ord($formulaData[1]));
+
+ break;
+ case 0x1D: // boolean
+ // offset: 1; size: 1; 0 = false, 1 = true;
+ $name = 'tBool';
+ $size = 2;
+ $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
+
+ break;
+ case 0x1E: // integer
+ // offset: 1; size: 2; unsigned 16-bit integer
+ $name = 'tInt';
+ $size = 3;
+ $data = self::getUInt2d($formulaData, 1);
+
+ break;
+ case 0x1F: // number
+ // offset: 1; size: 8;
+ $name = 'tNum';
+ $size = 9;
+ $data = self::extractNumber(substr($formulaData, 1));
+ $data = str_replace(',', '.', (string) $data); // in case non-English locale
+
+ break;
+ case 0x20: // array constant
+ case 0x40:
+ case 0x60:
+ // offset: 1; size: 7; not used
+ $name = 'tArray';
+ $size = 8;
+ $data = null;
+
+ break;
+ case 0x21: // function with fixed number of arguments
+ case 0x41:
+ case 0x61:
+ $name = 'tFunc';
+ $size = 3;
+ // offset: 1; size: 2; index to built-in sheet function
+ switch (self::getUInt2d($formulaData, 1)) {
+ case 2:
+ $function = 'ISNA';
+ $args = 1;
+
+ break;
+ case 3:
+ $function = 'ISERROR';
+ $args = 1;
+
+ break;
+ case 10:
+ $function = 'NA';
+ $args = 0;
+
+ break;
+ case 15:
+ $function = 'SIN';
+ $args = 1;
+
+ break;
+ case 16:
+ $function = 'COS';
+ $args = 1;
+
+ break;
+ case 17:
+ $function = 'TAN';
+ $args = 1;
+
+ break;
+ case 18:
+ $function = 'ATAN';
+ $args = 1;
+
+ break;
+ case 19:
+ $function = 'PI';
+ $args = 0;
+
+ break;
+ case 20:
+ $function = 'SQRT';
+ $args = 1;
+
+ break;
+ case 21:
+ $function = 'EXP';
+ $args = 1;
+
+ break;
+ case 22:
+ $function = 'LN';
+ $args = 1;
+
+ break;
+ case 23:
+ $function = 'LOG10';
+ $args = 1;
+
+ break;
+ case 24:
+ $function = 'ABS';
+ $args = 1;
+
+ break;
+ case 25:
+ $function = 'INT';
+ $args = 1;
+
+ break;
+ case 26:
+ $function = 'SIGN';
+ $args = 1;
+
+ break;
+ case 27:
+ $function = 'ROUND';
+ $args = 2;
+
+ break;
+ case 30:
+ $function = 'REPT';
+ $args = 2;
+
+ break;
+ case 31:
+ $function = 'MID';
+ $args = 3;
+
+ break;
+ case 32:
+ $function = 'LEN';
+ $args = 1;
+
+ break;
+ case 33:
+ $function = 'VALUE';
+ $args = 1;
+
+ break;
+ case 34:
+ $function = 'TRUE';
+ $args = 0;
+
+ break;
+ case 35:
+ $function = 'FALSE';
+ $args = 0;
+
+ break;
+ case 38:
+ $function = 'NOT';
+ $args = 1;
+
+ break;
+ case 39:
+ $function = 'MOD';
+ $args = 2;
+
+ break;
+ case 40:
+ $function = 'DCOUNT';
+ $args = 3;
+
+ break;
+ case 41:
+ $function = 'DSUM';
+ $args = 3;
+
+ break;
+ case 42:
+ $function = 'DAVERAGE';
+ $args = 3;
+
+ break;
+ case 43:
+ $function = 'DMIN';
+ $args = 3;
+
+ break;
+ case 44:
+ $function = 'DMAX';
+ $args = 3;
+
+ break;
+ case 45:
+ $function = 'DSTDEV';
+ $args = 3;
+
+ break;
+ case 48:
+ $function = 'TEXT';
+ $args = 2;
+
+ break;
+ case 61:
+ $function = 'MIRR';
+ $args = 3;
+
+ break;
+ case 63:
+ $function = 'RAND';
+ $args = 0;
+
+ break;
+ case 65:
+ $function = 'DATE';
+ $args = 3;
+
+ break;
+ case 66:
+ $function = 'TIME';
+ $args = 3;
+
+ break;
+ case 67:
+ $function = 'DAY';
+ $args = 1;
+
+ break;
+ case 68:
+ $function = 'MONTH';
+ $args = 1;
+
+ break;
+ case 69:
+ $function = 'YEAR';
+ $args = 1;
+
+ break;
+ case 71:
+ $function = 'HOUR';
+ $args = 1;
+
+ break;
+ case 72:
+ $function = 'MINUTE';
+ $args = 1;
+
+ break;
+ case 73:
+ $function = 'SECOND';
+ $args = 1;
+
+ break;
+ case 74:
+ $function = 'NOW';
+ $args = 0;
+
+ break;
+ case 75:
+ $function = 'AREAS';
+ $args = 1;
+
+ break;
+ case 76:
+ $function = 'ROWS';
+ $args = 1;
+
+ break;
+ case 77:
+ $function = 'COLUMNS';
+ $args = 1;
+
+ break;
+ case 83:
+ $function = 'TRANSPOSE';
+ $args = 1;
+
+ break;
+ case 86:
+ $function = 'TYPE';
+ $args = 1;
+
+ break;
+ case 97:
+ $function = 'ATAN2';
+ $args = 2;
+
+ break;
+ case 98:
+ $function = 'ASIN';
+ $args = 1;
+
+ break;
+ case 99:
+ $function = 'ACOS';
+ $args = 1;
+
+ break;
+ case 105:
+ $function = 'ISREF';
+ $args = 1;
+
+ break;
+ case 111:
+ $function = 'CHAR';
+ $args = 1;
+
+ break;
+ case 112:
+ $function = 'LOWER';
+ $args = 1;
+
+ break;
+ case 113:
+ $function = 'UPPER';
+ $args = 1;
+
+ break;
+ case 114:
+ $function = 'PROPER';
+ $args = 1;
+
+ break;
+ case 117:
+ $function = 'EXACT';
+ $args = 2;
+
+ break;
+ case 118:
+ $function = 'TRIM';
+ $args = 1;
+
+ break;
+ case 119:
+ $function = 'REPLACE';
+ $args = 4;
+
+ break;
+ case 121:
+ $function = 'CODE';
+ $args = 1;
+
+ break;
+ case 126:
+ $function = 'ISERR';
+ $args = 1;
+
+ break;
+ case 127:
+ $function = 'ISTEXT';
+ $args = 1;
+
+ break;
+ case 128:
+ $function = 'ISNUMBER';
+ $args = 1;
+
+ break;
+ case 129:
+ $function = 'ISBLANK';
+ $args = 1;
+
+ break;
+ case 130:
+ $function = 'T';
+ $args = 1;
+
+ break;
+ case 131:
+ $function = 'N';
+ $args = 1;
+
+ break;
+ case 140:
+ $function = 'DATEVALUE';
+ $args = 1;
+
+ break;
+ case 141:
+ $function = 'TIMEVALUE';
+ $args = 1;
+
+ break;
+ case 142:
+ $function = 'SLN';
+ $args = 3;
+
+ break;
+ case 143:
+ $function = 'SYD';
+ $args = 4;
+
+ break;
+ case 162:
+ $function = 'CLEAN';
+ $args = 1;
+
+ break;
+ case 163:
+ $function = 'MDETERM';
+ $args = 1;
+
+ break;
+ case 164:
+ $function = 'MINVERSE';
+ $args = 1;
+
+ break;
+ case 165:
+ $function = 'MMULT';
+ $args = 2;
+
+ break;
+ case 184:
+ $function = 'FACT';
+ $args = 1;
+
+ break;
+ case 189:
+ $function = 'DPRODUCT';
+ $args = 3;
+
+ break;
+ case 190:
+ $function = 'ISNONTEXT';
+ $args = 1;
+
+ break;
+ case 195:
+ $function = 'DSTDEVP';
+ $args = 3;
+
+ break;
+ case 196:
+ $function = 'DVARP';
+ $args = 3;
+
+ break;
+ case 198:
+ $function = 'ISLOGICAL';
+ $args = 1;
+
+ break;
+ case 199:
+ $function = 'DCOUNTA';
+ $args = 3;
+
+ break;
+ case 207:
+ $function = 'REPLACEB';
+ $args = 4;
+
+ break;
+ case 210:
+ $function = 'MIDB';
+ $args = 3;
+
+ break;
+ case 211:
+ $function = 'LENB';
+ $args = 1;
+
+ break;
+ case 212:
+ $function = 'ROUNDUP';
+ $args = 2;
+
+ break;
+ case 213:
+ $function = 'ROUNDDOWN';
+ $args = 2;
+
+ break;
+ case 214:
+ $function = 'ASC';
+ $args = 1;
+
+ break;
+ case 215:
+ $function = 'DBCS';
+ $args = 1;
+
+ break;
+ case 221:
+ $function = 'TODAY';
+ $args = 0;
+
+ break;
+ case 229:
+ $function = 'SINH';
+ $args = 1;
+
+ break;
+ case 230:
+ $function = 'COSH';
+ $args = 1;
+
+ break;
+ case 231:
+ $function = 'TANH';
+ $args = 1;
+
+ break;
+ case 232:
+ $function = 'ASINH';
+ $args = 1;
+
+ break;
+ case 233:
+ $function = 'ACOSH';
+ $args = 1;
+
+ break;
+ case 234:
+ $function = 'ATANH';
+ $args = 1;
+
+ break;
+ case 235:
+ $function = 'DGET';
+ $args = 3;
+
+ break;
+ case 244:
+ $function = 'INFO';
+ $args = 1;
+
+ break;
+ case 252:
+ $function = 'FREQUENCY';
+ $args = 2;
+
+ break;
+ case 261:
+ $function = 'ERROR.TYPE';
+ $args = 1;
+
+ break;
+ case 271:
+ $function = 'GAMMALN';
+ $args = 1;
+
+ break;
+ case 273:
+ $function = 'BINOMDIST';
+ $args = 4;
+
+ break;
+ case 274:
+ $function = 'CHIDIST';
+ $args = 2;
+
+ break;
+ case 275:
+ $function = 'CHIINV';
+ $args = 2;
+
+ break;
+ case 276:
+ $function = 'COMBIN';
+ $args = 2;
+
+ break;
+ case 277:
+ $function = 'CONFIDENCE';
+ $args = 3;
+
+ break;
+ case 278:
+ $function = 'CRITBINOM';
+ $args = 3;
+
+ break;
+ case 279:
+ $function = 'EVEN';
+ $args = 1;
+
+ break;
+ case 280:
+ $function = 'EXPONDIST';
+ $args = 3;
+
+ break;
+ case 281:
+ $function = 'FDIST';
+ $args = 3;
+
+ break;
+ case 282:
+ $function = 'FINV';
+ $args = 3;
+
+ break;
+ case 283:
+ $function = 'FISHER';
+ $args = 1;
+
+ break;
+ case 284:
+ $function = 'FISHERINV';
+ $args = 1;
+
+ break;
+ case 285:
+ $function = 'FLOOR';
+ $args = 2;
+
+ break;
+ case 286:
+ $function = 'GAMMADIST';
+ $args = 4;
+
+ break;
+ case 287:
+ $function = 'GAMMAINV';
+ $args = 3;
+
+ break;
+ case 288:
+ $function = 'CEILING';
+ $args = 2;
+
+ break;
+ case 289:
+ $function = 'HYPGEOMDIST';
+ $args = 4;
+
+ break;
+ case 290:
+ $function = 'LOGNORMDIST';
+ $args = 3;
+
+ break;
+ case 291:
+ $function = 'LOGINV';
+ $args = 3;
+
+ break;
+ case 292:
+ $function = 'NEGBINOMDIST';
+ $args = 3;
+
+ break;
+ case 293:
+ $function = 'NORMDIST';
+ $args = 4;
+
+ break;
+ case 294:
+ $function = 'NORMSDIST';
+ $args = 1;
+
+ break;
+ case 295:
+ $function = 'NORMINV';
+ $args = 3;
+
+ break;
+ case 296:
+ $function = 'NORMSINV';
+ $args = 1;
+
+ break;
+ case 297:
+ $function = 'STANDARDIZE';
+ $args = 3;
+
+ break;
+ case 298:
+ $function = 'ODD';
+ $args = 1;
+
+ break;
+ case 299:
+ $function = 'PERMUT';
+ $args = 2;
+
+ break;
+ case 300:
+ $function = 'POISSON';
+ $args = 3;
+
+ break;
+ case 301:
+ $function = 'TDIST';
+ $args = 3;
+
+ break;
+ case 302:
+ $function = 'WEIBULL';
+ $args = 4;
+
+ break;
+ case 303:
+ $function = 'SUMXMY2';
+ $args = 2;
+
+ break;
+ case 304:
+ $function = 'SUMX2MY2';
+ $args = 2;
+
+ break;
+ case 305:
+ $function = 'SUMX2PY2';
+ $args = 2;
+
+ break;
+ case 306:
+ $function = 'CHITEST';
+ $args = 2;
+
+ break;
+ case 307:
+ $function = 'CORREL';
+ $args = 2;
+
+ break;
+ case 308:
+ $function = 'COVAR';
+ $args = 2;
+
+ break;
+ case 309:
+ $function = 'FORECAST';
+ $args = 3;
+
+ break;
+ case 310:
+ $function = 'FTEST';
+ $args = 2;
+
+ break;
+ case 311:
+ $function = 'INTERCEPT';
+ $args = 2;
+
+ break;
+ case 312:
+ $function = 'PEARSON';
+ $args = 2;
+
+ break;
+ case 313:
+ $function = 'RSQ';
+ $args = 2;
+
+ break;
+ case 314:
+ $function = 'STEYX';
+ $args = 2;
+
+ break;
+ case 315:
+ $function = 'SLOPE';
+ $args = 2;
+
+ break;
+ case 316:
+ $function = 'TTEST';
+ $args = 4;
+
+ break;
+ case 325:
+ $function = 'LARGE';
+ $args = 2;
+
+ break;
+ case 326:
+ $function = 'SMALL';
+ $args = 2;
+
+ break;
+ case 327:
+ $function = 'QUARTILE';
+ $args = 2;
+
+ break;
+ case 328:
+ $function = 'PERCENTILE';
+ $args = 2;
+
+ break;
+ case 331:
+ $function = 'TRIMMEAN';
+ $args = 2;
+
+ break;
+ case 332:
+ $function = 'TINV';
+ $args = 2;
+
+ break;
+ case 337:
+ $function = 'POWER';
+ $args = 2;
+
+ break;
+ case 342:
+ $function = 'RADIANS';
+ $args = 1;
+
+ break;
+ case 343:
+ $function = 'DEGREES';
+ $args = 1;
+
+ break;
+ case 346:
+ $function = 'COUNTIF';
+ $args = 2;
+
+ break;
+ case 347:
+ $function = 'COUNTBLANK';
+ $args = 1;
+
+ break;
+ case 350:
+ $function = 'ISPMT';
+ $args = 4;
+
+ break;
+ case 351:
+ $function = 'DATEDIF';
+ $args = 3;
+
+ break;
+ case 352:
+ $function = 'DATESTRING';
+ $args = 1;
+
+ break;
+ case 353:
+ $function = 'NUMBERSTRING';
+ $args = 2;
+
+ break;
+ case 360:
+ $function = 'PHONETIC';
+ $args = 1;
+
+ break;
+ case 368:
+ $function = 'BAHTTEXT';
+ $args = 1;
+
+ break;
+ default:
+ throw new Exception('Unrecognized function in formula');
+ }
+ $data = ['function' => $function, 'args' => $args];
+
+ break;
+ case 0x22: // function with variable number of arguments
+ case 0x42:
+ case 0x62:
+ $name = 'tFuncV';
+ $size = 4;
+ // offset: 1; size: 1; number of arguments
+ $args = ord($formulaData[1]);
+ // offset: 2: size: 2; index to built-in sheet function
+ $index = self::getUInt2d($formulaData, 2);
+ $function = match ($index) {
+ 0 => 'COUNT',
+ 1 => 'IF',
+ 4 => 'SUM',
+ 5 => 'AVERAGE',
+ 6 => 'MIN',
+ 7 => 'MAX',
+ 8 => 'ROW',
+ 9 => 'COLUMN',
+ 11 => 'NPV',
+ 12 => 'STDEV',
+ 13 => 'DOLLAR',
+ 14 => 'FIXED',
+ 28 => 'LOOKUP',
+ 29 => 'INDEX',
+ 36 => 'AND',
+ 37 => 'OR',
+ 46 => 'VAR',
+ 49 => 'LINEST',
+ 50 => 'TREND',
+ 51 => 'LOGEST',
+ 52 => 'GROWTH',
+ 56 => 'PV',
+ 57 => 'FV',
+ 58 => 'NPER',
+ 59 => 'PMT',
+ 60 => 'RATE',
+ 62 => 'IRR',
+ 64 => 'MATCH',
+ 70 => 'WEEKDAY',
+ 78 => 'OFFSET',
+ 82 => 'SEARCH',
+ 100 => 'CHOOSE',
+ 101 => 'HLOOKUP',
+ 102 => 'VLOOKUP',
+ 109 => 'LOG',
+ 115 => 'LEFT',
+ 116 => 'RIGHT',
+ 120 => 'SUBSTITUTE',
+ 124 => 'FIND',
+ 125 => 'CELL',
+ 144 => 'DDB',
+ 148 => 'INDIRECT',
+ 167 => 'IPMT',
+ 168 => 'PPMT',
+ 169 => 'COUNTA',
+ 183 => 'PRODUCT',
+ 193 => 'STDEVP',
+ 194 => 'VARP',
+ 197 => 'TRUNC',
+ 204 => 'USDOLLAR',
+ 205 => 'FINDB',
+ 206 => 'SEARCHB',
+ 208 => 'LEFTB',
+ 209 => 'RIGHTB',
+ 216 => 'RANK',
+ 219 => 'ADDRESS',
+ 220 => 'DAYS360',
+ 222 => 'VDB',
+ 227 => 'MEDIAN',
+ 228 => 'SUMPRODUCT',
+ 247 => 'DB',
+ 255 => '',
+ 269 => 'AVEDEV',
+ 270 => 'BETADIST',
+ 272 => 'BETAINV',
+ 317 => 'PROB',
+ 318 => 'DEVSQ',
+ 319 => 'GEOMEAN',
+ 320 => 'HARMEAN',
+ 321 => 'SUMSQ',
+ 322 => 'KURT',
+ 323 => 'SKEW',
+ 324 => 'ZTEST',
+ 329 => 'PERCENTRANK',
+ 330 => 'MODE',
+ 336 => 'CONCATENATE',
+ 344 => 'SUBTOTAL',
+ 345 => 'SUMIF',
+ 354 => 'ROMAN',
+ 358 => 'GETPIVOTDATA',
+ 359 => 'HYPERLINK',
+ 361 => 'AVERAGEA',
+ 362 => 'MAXA',
+ 363 => 'MINA',
+ 364 => 'STDEVPA',
+ 365 => 'VARPA',
+ 366 => 'STDEVA',
+ 367 => 'VARA',
+ default => throw new Exception('Unrecognized function in formula'),
+ };
+ $data = ['function' => $function, 'args' => $args];
+
+ break;
+ case 0x23: // index to defined name
+ case 0x43:
+ case 0x63:
+ $name = 'tName';
+ $size = 5;
+ // offset: 1; size: 2; one-based index to definedname record
+ $definedNameIndex = self::getUInt2d($formulaData, 1) - 1;
+ // offset: 2; size: 2; not used
+ $data = $this->definedname[$definedNameIndex]['name'] ?? '';
+
+ break;
+ case 0x24: // single cell reference e.g. A5
+ case 0x44:
+ case 0x64:
+ $name = 'tRef';
+ $size = 5;
+ $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
+
+ break;
+ case 0x25: // cell range reference to cells in the same sheet (2d)
+ case 0x45:
+ case 0x65:
+ $name = 'tArea';
+ $size = 9;
+ $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
+
+ break;
+ case 0x26: // Constant reference sub-expression
+ case 0x46:
+ case 0x66:
+ $name = 'tMemArea';
+ // offset: 1; size: 4; not used
+ // offset: 5; size: 2; size of the following subexpression
+ $subSize = self::getUInt2d($formulaData, 5);
+ $size = 7 + $subSize;
+ $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
+
+ break;
+ case 0x27: // Deleted constant reference sub-expression
+ case 0x47:
+ case 0x67:
+ $name = 'tMemErr';
+ // offset: 1; size: 4; not used
+ // offset: 5; size: 2; size of the following subexpression
+ $subSize = self::getUInt2d($formulaData, 5);
+ $size = 7 + $subSize;
+ $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
+
+ break;
+ case 0x29: // Variable reference sub-expression
+ case 0x49:
+ case 0x69:
+ $name = 'tMemFunc';
+ // offset: 1; size: 2; size of the following sub-expression
+ $subSize = self::getUInt2d($formulaData, 1);
+ $size = 3 + $subSize;
+ $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
+
+ break;
+ case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
+ case 0x4C:
+ case 0x6C:
+ $name = 'tRefN';
+ $size = 5;
+ $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
+
+ break;
+ case 0x2D: // Relative 2d range reference
+ case 0x4D:
+ case 0x6D:
+ $name = 'tAreaN';
+ $size = 9;
+ $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
+
+ break;
+ case 0x39: // External name
+ case 0x59:
+ case 0x79:
+ $name = 'tNameX';
+ $size = 7;
+ // offset: 1; size: 2; index to REF entry in EXTERNSHEET record
+ // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
+ $index = self::getUInt2d($formulaData, 3);
+ // assume index is to EXTERNNAME record
+ $data = $this->externalNames[$index - 1]['name'] ?? '';
+
+ // offset: 5; size: 2; not used
+ break;
+ case 0x3A: // 3d reference to cell
+ case 0x5A:
+ case 0x7A:
+ $name = 'tRef3d';
+ $size = 7;
+
+ try {
+ // offset: 1; size: 2; index to REF entry
+ $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
+ // offset: 3; size: 4; cell address
+ $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
+
+ $data = "$sheetRange!$cellAddress";
+ } catch (PhpSpreadsheetException) {
+ // deleted sheet reference
+ $data = '#REF!';
+ }
+
+ break;
+ case 0x3B: // 3d reference to cell range
+ case 0x5B:
+ case 0x7B:
+ $name = 'tArea3d';
+ $size = 11;
+
+ try {
+ // offset: 1; size: 2; index to REF entry
+ $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
+ // offset: 3; size: 8; cell address
+ $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
+
+ $data = "$sheetRange!$cellRangeAddress";
+ } catch (PhpSpreadsheetException) {
+ // deleted sheet reference
+ $data = '#REF!';
+ }
+
+ break;
+ // Unknown cases // don't know how to deal with
+ default:
+ throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
+ }
+
+ return [
+ 'id' => $id,
+ 'name' => $name,
+ 'size' => $size,
+ 'data' => $data,
+ ];
+ }
+
+ /**
+ * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
+ * section 3.3.4.
+ */
+ private function readBIFF8CellAddress(string $cellAddressStructure): string
+ {
+ // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+ $row = self::getUInt2d($cellAddressStructure, 0) + 1;
+
+ // offset: 2; size: 2; index to column or column offset + relative flags
+ // bit: 7-0; mask 0x00FF; column index
+ $column = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($cellAddressStructure, 2)) + 1);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
+ $column = '$' . $column;
+ }
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
+ $row = '$' . $row;
+ }
+
+ return $column . $row;
+ }
+
+ /**
+ * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+ * to indicate offsets from a base cell
+ * section 3.3.4.
+ *
+ * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
+ */
+ private function readBIFF8CellAddressB(string $cellAddressStructure, $baseCell = 'A1'): string
+ {
+ [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
+ $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
+ $baseRow = (int) $baseRow;
+
+ // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
+ $rowIndex = self::getUInt2d($cellAddressStructure, 0);
+ $row = self::getUInt2d($cellAddressStructure, 0) + 1;
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
+ // offset: 2; size: 2; index to column or column offset + relative flags
+ // bit: 7-0; mask 0x00FF; column index
+ $colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2);
+
+ $column = Coordinate::stringFromColumnIndex($colIndex + 1);
+ $column = '$' . $column;
+ } else {
+ // offset: 2; size: 2; index to column or column offset + relative flags
+ // bit: 7-0; mask 0x00FF; column index
+ $relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
+ $colIndex = $baseCol + $relativeColIndex;
+ $colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256;
+ $colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256;
+ $column = Coordinate::stringFromColumnIndex($colIndex + 1);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
+ $row = '$' . $row;
+ } else {
+ $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
+ $row = $baseRow + $rowIndex;
+ }
+
+ return $column . $row;
+ }
+
+ /**
+ * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
+ * always fixed range
+ * section 2.5.14.
+ */
+ private function readBIFF5CellRangeAddressFixed(string $subData): string
+ {
+ // offset: 0; size: 2; index to first row
+ $fr = self::getUInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row
+ $lr = self::getUInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 1; index to first column
+ $fc = ord($subData[4]);
+
+ // offset: 5; size: 1; index to last column
+ $lc = ord($subData[5]);
+
+ // check values
+ if ($fr > $lr || $fc > $lc) {
+ throw new Exception('Not a cell range address');
+ }
+
+ // column index to letter
+ $fc = Coordinate::stringFromColumnIndex($fc + 1);
+ $lc = Coordinate::stringFromColumnIndex($lc + 1);
+
+ if ($fr == $lr && $fc == $lc) {
+ return "$fc$fr";
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
+ * always fixed range
+ * section 2.5.14.
+ */
+ private function readBIFF8CellRangeAddressFixed(string $subData): string
+ {
+ // offset: 0; size: 2; index to first row
+ $fr = self::getUInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row
+ $lr = self::getUInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 2; index to first column
+ $fc = self::getUInt2d($subData, 4);
+
+ // offset: 6; size: 2; index to last column
+ $lc = self::getUInt2d($subData, 6);
+
+ // check values
+ if ($fr > $lr || $fc > $lc) {
+ throw new Exception('Not a cell range address');
+ }
+
+ // column index to letter
+ $fc = Coordinate::stringFromColumnIndex($fc + 1);
+ $lc = Coordinate::stringFromColumnIndex($lc + 1);
+
+ if ($fr == $lr && $fc == $lc) {
+ return "$fc$fr";
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
+ * there are flags indicating whether column/row index is relative
+ * section 3.3.4.
+ */
+ private function readBIFF8CellRangeAddress(string $subData): string
+ {
+ // todo: if cell range is just a single cell, should this funciton
+ // not just return e.g. 'A1' and not 'A1:A1' ?
+
+ // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
+ $fr = self::getUInt2d($subData, 0) + 1;
+
+ // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
+ $lr = self::getUInt2d($subData, 2) + 1;
+
+ // offset: 4; size: 2; index to first column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $fc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 4)) + 1);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & self::getUInt2d($subData, 4))) {
+ $fc = '$' . $fc;
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & self::getUInt2d($subData, 4))) {
+ $fr = '$' . $fr;
+ }
+
+ // offset: 6; size: 2; index to last column or column offset + relative flags
+
+ // bit: 7-0; mask 0x00FF; column index
+ $lc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 6)) + 1);
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & self::getUInt2d($subData, 6))) {
+ $lc = '$' . $lc;
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & self::getUInt2d($subData, 6))) {
+ $lr = '$' . $lr;
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
+ * to indicate offsets from a base cell
+ * section 3.3.4.
+ *
+ * @param string $baseCell Base cell
+ *
+ * @return string Cell range address
+ */
+ private function readBIFF8CellRangeAddressB(string $subData, string $baseCell = 'A1'): string
+ {
+ [$baseCol, $baseRow] = Coordinate::indexesFromString($baseCell);
+ $baseCol = $baseCol - 1;
+
+ // TODO: if cell range is just a single cell, should this funciton
+ // not just return e.g. 'A1' and not 'A1:A1' ?
+
+ // offset: 0; size: 2; first row
+ $frIndex = self::getUInt2d($subData, 0); // adjust below
+
+ // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
+ $lrIndex = self::getUInt2d($subData, 2); // adjust below
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & self::getUInt2d($subData, 4))) {
+ // absolute column index
+ // offset: 4; size: 2; first column with relative/absolute flags
+ // bit: 7-0; mask 0x00FF; column index
+ $fcIndex = 0x00FF & self::getUInt2d($subData, 4);
+ $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
+ $fc = '$' . $fc;
+ } else {
+ // column offset
+ // offset: 4; size: 2; first column with relative/absolute flags
+ // bit: 7-0; mask 0x00FF; column index
+ $relativeFcIndex = 0x00FF & self::getInt2d($subData, 4);
+ $fcIndex = $baseCol + $relativeFcIndex;
+ $fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256;
+ $fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256;
+ $fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & self::getUInt2d($subData, 4))) {
+ // absolute row index
+ $fr = $frIndex + 1;
+ $fr = '$' . $fr;
+ } else {
+ // row offset
+ $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
+ $fr = $baseRow + $frIndex;
+ }
+
+ // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
+ if (!(0x4000 & self::getUInt2d($subData, 6))) {
+ // absolute column index
+ // offset: 6; size: 2; last column with relative/absolute flags
+ // bit: 7-0; mask 0x00FF; column index
+ $lcIndex = 0x00FF & self::getUInt2d($subData, 6);
+ $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
+ $lc = '$' . $lc;
+ } else {
+ // column offset
+ // offset: 4; size: 2; first column with relative/absolute flags
+ // bit: 7-0; mask 0x00FF; column index
+ $relativeLcIndex = 0x00FF & self::getInt2d($subData, 4);
+ $lcIndex = $baseCol + $relativeLcIndex;
+ $lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256;
+ $lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256;
+ $lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
+ }
+
+ // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
+ if (!(0x8000 & self::getUInt2d($subData, 6))) {
+ // absolute row index
+ $lr = $lrIndex + 1;
+ $lr = '$' . $lr;
+ } else {
+ // row offset
+ $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
+ $lr = $baseRow + $lrIndex;
+ }
+
+ return "$fc$fr:$lc$lr";
+ }
+
+ /**
+ * Read BIFF8 cell range address list
+ * section 2.5.15.
+ */
+ private function readBIFF8CellRangeAddressList(string $subData): array
+ {
+ $cellRangeAddresses = [];
+
+ // offset: 0; size: 2; number of the following cell range addresses
+ $nm = self::getUInt2d($subData, 0);
+
+ $offset = 2;
+ // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
+ for ($i = 0; $i < $nm; ++$i) {
+ $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
+ $offset += 8;
+ }
+
+ return [
+ 'size' => 2 + 8 * $nm,
+ 'cellRangeAddresses' => $cellRangeAddresses,
+ ];
+ }
+
+ /**
+ * Read BIFF5 cell range address list
+ * section 2.5.15.
+ */
+ private function readBIFF5CellRangeAddressList(string $subData): array
+ {
+ $cellRangeAddresses = [];
+
+ // offset: 0; size: 2; number of the following cell range addresses
+ $nm = self::getUInt2d($subData, 0);
+
+ $offset = 2;
+ // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
+ for ($i = 0; $i < $nm; ++$i) {
+ $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
+ $offset += 6;
+ }
+
+ return [
+ 'size' => 2 + 6 * $nm,
+ 'cellRangeAddresses' => $cellRangeAddresses,
+ ];
+ }
+
+ /**
+ * Get a sheet range like Sheet1:Sheet3 from REF index
+ * Note: If there is only one sheet in the range, one gets e.g Sheet1
+ * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
+ * in which case an Exception is thrown.
+ */
+ private function readSheetRangeByRefIndex(int $index): string|false
+ {
+ if (isset($this->ref[$index])) {
+ $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
+
+ switch ($type) {
+ case 'internal':
+ // check if we have a deleted 3d reference
+ if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF || $this->ref[$index]['lastSheetIndex'] == 0xFFFF) {
+ throw new Exception('Deleted sheet reference');
+ }
+
+ // we have normal sheet range (collapsed or uncollapsed)
+ $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
+ $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
+
+ if ($firstSheetName == $lastSheetName) {
+ // collapsed sheet range
+ $sheetRange = $firstSheetName;
+ } else {
+ $sheetRange = "$firstSheetName:$lastSheetName";
+ }
+
+ // escape the single-quotes
+ $sheetRange = str_replace("'", "''", $sheetRange);
+
+ // if there are special characters, we need to enclose the range in single-quotes
+ // todo: check if we have identified the whole set of special characters
+ // it seems that the following characters are not accepted for sheet names
+ // and we may assume that they are not present: []*/:\?
+ if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/u", $sheetRange)) {
+ $sheetRange = "'$sheetRange'";
+ }
+
+ return $sheetRange;
+ default:
+ // TODO: external sheet support
+ throw new Exception('Xls reader only supports internal sheets in formulas');
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * read BIFF8 constant value array from array data
+ * returns e.g. ['value' => '{1,2;3,4}', 'size' => 40]
+ * section 2.5.8.
+ *
+ * @param string $arrayData
+ */
+ private static function readBIFF8ConstantArray($arrayData): array
+ {
+ // offset: 0; size: 1; number of columns decreased by 1
+ $nc = ord($arrayData[0]);
+
+ // offset: 1; size: 2; number of rows decreased by 1
+ $nr = self::getUInt2d($arrayData, 1);
+ $size = 3; // initialize
+ $arrayData = substr($arrayData, 3);
+
+ // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
+ $matrixChunks = [];
+ for ($r = 1; $r <= $nr + 1; ++$r) {
+ $items = [];
+ for ($c = 1; $c <= $nc + 1; ++$c) {
+ $constant = self::readBIFF8Constant($arrayData);
+ $items[] = $constant['value'];
+ $arrayData = substr($arrayData, $constant['size']);
+ $size += $constant['size'];
+ }
+ $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
+ }
+ $matrix = '{' . implode(';', $matrixChunks) . '}';
+
+ return [
+ 'value' => $matrix,
+ 'size' => $size,
+ ];
+ }
+
+ /**
+ * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
+ * section 2.5.7
+ * returns e.g. ['value' => '5', 'size' => 9].
+ *
+ * @param string $valueData
+ */
+ private static function readBIFF8Constant($valueData): array
+ {
+ // offset: 0; size: 1; identifier for type of constant
+ $identifier = ord($valueData[0]);
+
+ switch ($identifier) {
+ case 0x00: // empty constant (what is this?)
+ $value = '';
+ $size = 9;
+
+ break;
+ case 0x01: // number
+ // offset: 1; size: 8; IEEE 754 floating-point value
+ $value = self::extractNumber(substr($valueData, 1, 8));
+ $size = 9;
+
+ break;
+ case 0x02: // string value
+ // offset: 1; size: var; Unicode string, 16-bit string length
+ $string = self::readUnicodeStringLong(substr($valueData, 1));
+ $value = '"' . $string['value'] . '"';
+ $size = 1 + $string['size'];
+
+ break;
+ case 0x04: // boolean
+ // offset: 1; size: 1; 0 = FALSE, 1 = TRUE
+ if (ord($valueData[1])) {
+ $value = 'TRUE';
+ } else {
+ $value = 'FALSE';
+ }
+ $size = 9;
+
+ break;
+ case 0x10: // error code
+ // offset: 1; size: 1; error code
+ $value = Xls\ErrorCode::lookup(ord($valueData[1]));
+ $size = 9;
+
+ break;
+ default:
+ throw new PhpSpreadsheetException('Unsupported BIFF8 constant');
+ }
+
+ return [
+ 'value' => $value,
+ 'size' => $size,
+ ];
+ }
+
+ /**
+ * Extract RGB color
+ * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
+ *
+ * @param string $rgb Encoded RGB value (4 bytes)
+ */
+ private static function readRGB($rgb): array
+ {
+ // offset: 0; size 1; Red component
+ $r = ord($rgb[0]);
+
+ // offset: 1; size: 1; Green component
+ $g = ord($rgb[1]);
+
+ // offset: 2; size: 1; Blue component
+ $b = ord($rgb[2]);
+
+ // HEX notation, e.g. 'FF00FC'
+ $rgb = sprintf('%02X%02X%02X', $r, $g, $b);
+
+ return ['rgb' => $rgb];
+ }
+
+ /**
+ * Read byte string (8-bit string length)
+ * OpenOffice documentation: 2.5.2.
+ */
+ private function readByteStringShort(string $subData): array
+ {
+ // offset: 0; size: 1; length of the string (character count)
+ $ln = ord($subData[0]);
+
+ // offset: 1: size: var; character array (8-bit characters)
+ $value = $this->decodeCodepage(substr($subData, 1, $ln));
+
+ return [
+ 'value' => $value,
+ 'size' => 1 + $ln, // size in bytes of data structure
+ ];
+ }
+
+ /**
+ * Read byte string (16-bit string length)
+ * OpenOffice documentation: 2.5.2.
+ */
+ private function readByteStringLong(string $subData): array
+ {
+ // offset: 0; size: 2; length of the string (character count)
+ $ln = self::getUInt2d($subData, 0);
+
+ // offset: 2: size: var; character array (8-bit characters)
+ $value = $this->decodeCodepage(substr($subData, 2));
+
+ //return $string;
+ return [
+ 'value' => $value,
+ 'size' => 2 + $ln, // size in bytes of data structure
+ ];
+ }
+
+ /**
+ * Extracts an Excel Unicode short string (8-bit string length)
+ * OpenOffice documentation: 2.5.3
+ * function will automatically find out where the Unicode string ends.
+ *
+ * @param string $subData
+ *
+ * @return array
+ */
+ private static function readUnicodeStringShort($subData)
+ {
+ // offset: 0: size: 1; length of the string (character count)
+ $characterCount = ord($subData[0]);
+
+ $string = self::readUnicodeString(substr($subData, 1), $characterCount);
+
+ // add 1 for the string length
+ ++$string['size'];
+
+ return $string;
+ }
+
+ /**
+ * Extracts an Excel Unicode long string (16-bit string length)
+ * OpenOffice documentation: 2.5.3
+ * this function is under construction, needs to support rich text, and Asian phonetic settings.
+ *
+ * @param string $subData
+ *
+ * @return array
+ */
+ private static function readUnicodeStringLong($subData)
+ {
+ // offset: 0: size: 2; length of the string (character count)
+ $characterCount = self::getUInt2d($subData, 0);
+
+ $string = self::readUnicodeString(substr($subData, 2), $characterCount);
+
+ // add 2 for the string length
+ $string['size'] += 2;
+
+ return $string;
+ }
+
+ /**
+ * Read Unicode string with no string length field, but with known character count
+ * this function is under construction, needs to support rich text, and Asian phonetic settings
+ * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
+ *
+ * @param string $subData
+ * @param int $characterCount
+ */
+ private static function readUnicodeString($subData, $characterCount): array
+ {
+ // offset: 0: size: 1; option flags
+ // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
+ $isCompressed = !((0x01 & ord($subData[0])) >> 0);
+
+ // bit: 2; mask: 0x04; Asian phonetic settings
+ //$hasAsian = (0x04) & ord($subData[0]) >> 2;
+
+ // bit: 3; mask: 0x08; Rich-Text settings
+ //$hasRichText = (0x08) & ord($subData[0]) >> 3;
+
+ // offset: 1: size: var; character array
+ // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
+ // needs to be fixed
+ $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
+
+ return [
+ 'value' => $value,
+ 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
+ ];
+ }
+
+ /**
+ * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
+ * Example: hello"world --> "hello""world".
+ *
+ * @param string $value UTF-8 encoded string
+ */
+ private static function UTF8toExcelDoubleQuoted($value): string
+ {
+ return '"' . str_replace('"', '""', $value) . '"';
+ }
+
+ /**
+ * Reads first 8 bytes of a string and return IEEE 754 float.
+ *
+ * @param string $data Binary string that is at least 8 bytes long
+ *
+ * @return float
+ */
+ private static function extractNumber($data): int|float
+ {
+ $rknumhigh = self::getInt4d($data, 4);
+ $rknumlow = self::getInt4d($data, 0);
+ $sign = ($rknumhigh & (int) 0x80000000) >> 31;
+ $exp = (($rknumhigh & 0x7FF00000) >> 20) - 1023;
+ $mantissa = (0x100000 | ($rknumhigh & 0x000FFFFF));
+ $mantissalow1 = ($rknumlow & (int) 0x80000000) >> 31;
+ $mantissalow2 = ($rknumlow & 0x7FFFFFFF);
+ $value = $mantissa / 2 ** (20 - $exp);
+
+ if ($mantissalow1 != 0) {
+ $value += 1 / 2 ** (21 - $exp);
+ }
+
+ $value += $mantissalow2 / 2 ** (52 - $exp);
+ if ($sign) {
+ $value *= -1;
+ }
+
+ return $value;
+ }
+
+ /**
+ * @param int $rknum
+ *
+ * @return float
+ */
+ private static function getIEEE754($rknum): float|int
+ {
+ if (($rknum & 0x02) != 0) {
+ $value = $rknum >> 2;
+ } else {
+ // changes by mmp, info on IEEE754 encoding from
+ // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
+ // The RK format calls for using only the most significant 30 bits
+ // of the 64 bit floating point value. The other 34 bits are assumed
+ // to be 0 so we use the upper 30 bits of $rknum as follows...
+ $sign = ($rknum & (int) 0x80000000) >> 31;
+ $exp = ($rknum & 0x7FF00000) >> 20;
+ $mantissa = (0x100000 | ($rknum & 0x000FFFFC));
+ $value = $mantissa / 2 ** (20 - ($exp - 1023));
+ if ($sign) {
+ $value = -1 * $value;
+ }
+ //end of changes by mmp
+ }
+ if (($rknum & 0x01) != 0) {
+ $value /= 100;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
+ *
+ * @param string $string
+ * @param bool $compressed
+ */
+ private static function encodeUTF16($string, $compressed = false): string
+ {
+ if ($compressed) {
+ $string = self::uncompressByteString($string);
+ }
+
+ return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
+ }
+
+ /**
+ * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
+ *
+ * @param string $string
+ */
+ private static function uncompressByteString($string): string
+ {
+ $uncompressedString = '';
+ $strLen = strlen($string);
+ for ($i = 0; $i < $strLen; ++$i) {
+ $uncompressedString .= $string[$i] . "\0";
+ }
+
+ return $uncompressedString;
+ }
+
+ /**
+ * Convert string to UTF-8. Only used for BIFF5.
+ */
+ private function decodeCodepage(string $string): string
+ {
+ return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
+ }
+
+ /**
+ * Read 16-bit unsigned integer.
+ *
+ * @param string $data
+ * @param int $pos
+ */
+ public static function getUInt2d($data, $pos): int
+ {
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
+ }
+
+ /**
+ * Read 16-bit signed integer.
+ *
+ * @param string $data
+ * @param int $pos
+ *
+ * @return int
+ */
+ public static function getInt2d($data, $pos)
+ {
+ return unpack('s', $data[$pos] . $data[$pos + 1])[1]; // @phpstan-ignore-line
+ }
+
+ /**
+ * Read 32-bit signed integer.
+ *
+ * @param string $data
+ * @param int $pos
+ */
+ public static function getInt4d($data, $pos): int
+ {
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+
+ private function parseRichText(string $is): RichText
+ {
+ $value = new RichText();
+ $value->createText($is);
+
+ return $value;
+ }
+
+ /**
+ * Phpstan 1.4.4 complains that this property is never read.
+ * So, we might be able to get rid of it altogether.
+ * For now, however, this function makes it readable,
+ * which satisfies Phpstan.
+ *
+ * @codeCoverageIgnore
+ */
+ public function getMapCellStyleXfIndex(): array
+ {
+ return $this->mapCellStyleXfIndex;
+ }
+
+ private function readCFHeader(): array
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return [];
+ }
+
+ // offset: 0; size: 2; Rule Count
+// $ruleCount = self::getUInt2d($recordData, 0);
+
+ // offset: var; size: var; cell range address list with
+ $cellRangeAddressList = ($this->version == self::XLS_BIFF8)
+ ? $this->readBIFF8CellRangeAddressList(substr($recordData, 12))
+ : $this->readBIFF5CellRangeAddressList(substr($recordData, 12));
+ $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
+
+ return $cellRangeAddresses;
+ }
+
+ private function readCFRule(array $cellRangeAddresses): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2; Options
+ $cfRule = self::getUInt2d($recordData, 0);
+
+ // bit: 8-15; mask: 0x00FF; type
+ $type = (0x00FF & $cfRule) >> 0;
+ $type = ConditionalFormatting::type($type);
+
+ // bit: 0-7; mask: 0xFF00; type
+ $operator = (0xFF00 & $cfRule) >> 8;
+ $operator = ConditionalFormatting::operator($operator);
+
+ if ($type === null || $operator === null) {
+ return;
+ }
+
+ // offset: 2; size: 2; Size1
+ $size1 = self::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2; Size2
+ $size2 = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 4; Options
+ $options = self::getInt4d($recordData, 6);
+
+ $style = new Style(false, true); // non-supervisor, conditional
+ //$this->getCFStyleOptions($options, $style);
+
+ $hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
+ $hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
+ $hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
+ $hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
+ $hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
+
+ $offset = 12;
+
+ if ($hasFontRecord === true) {
+ $fontStyle = substr($recordData, $offset, 118);
+ $this->getCFFontStyle($fontStyle, $style);
+ $offset += 118;
+ }
+
+ if ($hasAlignmentRecord === true) {
+ //$alignmentStyle = substr($recordData, $offset, 8);
+ //$this->getCFAlignmentStyle($alignmentStyle, $style);
+ $offset += 8;
+ }
+
+ if ($hasBorderRecord === true) {
+ //$borderStyle = substr($recordData, $offset, 8);
+ //$this->getCFBorderStyle($borderStyle, $style);
+ $offset += 8;
+ }
+
+ if ($hasFillRecord === true) {
+ $fillStyle = substr($recordData, $offset, 4);
+ $this->getCFFillStyle($fillStyle, $style);
+ $offset += 4;
+ }
+
+ if ($hasProtectionRecord === true) {
+ //$protectionStyle = substr($recordData, $offset, 4);
+ //$this->getCFProtectionStyle($protectionStyle, $style);
+ $offset += 2;
+ }
+
+ $formula1 = $formula2 = null;
+ if ($size1 > 0) {
+ $formula1 = $this->readCFFormula($recordData, $offset, $size1);
+ if ($formula1 === null) {
+ return;
+ }
+
+ $offset += $size1;
+ }
+
+ if ($size2 > 0) {
+ $formula2 = $this->readCFFormula($recordData, $offset, $size2);
+ if ($formula2 === null) {
+ return;
+ }
+
+ $offset += $size2;
+ }
+
+ $this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style);
+ }
+
+ /*private function getCFStyleOptions(int $options, Style $style): void
+ {
+ }*/
+
+ private function getCFFontStyle(string $options, Style $style): void
+ {
+ $fontSize = self::getInt4d($options, 64);
+ if ($fontSize !== -1) {
+ $style->getFont()->setSize($fontSize / 20); // Convert twips to points
+ }
+
+ $bold = self::getUInt2d($options, 72) === 700; // 400 = normal, 700 = bold
+ $style->getFont()->setBold($bold);
+
+ $color = self::getInt4d($options, 80);
+
+ if ($color !== -1) {
+ $style->getFont()->getColor()->setRGB(Xls\Color::map($color, $this->palette, $this->version)['rgb']);
+ }
+ }
+
+ /*private function getCFAlignmentStyle(string $options, Style $style): void
+ {
+ }*/
+
+ /*private function getCFBorderStyle(string $options, Style $style): void
+ {
+ }*/
+
+ private function getCFFillStyle(string $options, Style $style): void
+ {
+ $fillPattern = self::getUInt2d($options, 0);
+ // bit: 10-15; mask: 0xFC00; type
+ $fillPattern = (0xFC00 & $fillPattern) >> 10;
+ $fillPattern = FillPattern::lookup($fillPattern);
+ $fillPattern = $fillPattern === Fill::FILL_NONE ? Fill::FILL_SOLID : $fillPattern;
+
+ if ($fillPattern !== Fill::FILL_NONE) {
+ $style->getFill()->setFillType($fillPattern);
+
+ $fillColors = self::getUInt2d($options, 2);
+
+ // bit: 0-6; mask: 0x007F; type
+ $color1 = (0x007F & $fillColors) >> 0;
+ $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']);
+
+ // bit: 7-13; mask: 0x3F80; type
+ $color2 = (0x3F80 & $fillColors) >> 7;
+ $style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']);
+ }
+ }
+
+ /*private function getCFProtectionStyle(string $options, Style $style): void
+ {
+ }*/
+
+ private function readCFFormula(string $recordData, int $offset, int $size): float|int|string|null
+ {
+ try {
+ $formula = substr($recordData, $offset, $size);
+ $formula = pack('v', $size) . $formula; // prepend the length
+
+ $formula = $this->getFormulaFromStructure($formula);
+ if (is_numeric($formula)) {
+ return (str_contains($formula, '.')) ? (float) $formula : (int) $formula;
+ }
+
+ return $formula;
+ } catch (PhpSpreadsheetException) {
+ return null;
+ }
+ }
+
+ /**
+ * @param null|float|int|string $formula1
+ * @param null|float|int|string $formula2
+ */
+ private function setCFRules(array $cellRanges, string $type, string $operator, $formula1, $formula2, Style $style): void
+ {
+ foreach ($cellRanges as $cellRange) {
+ $conditional = new Conditional();
+ $conditional->setConditionType($type);
+ $conditional->setOperatorType($operator);
+ if ($formula1 !== null) {
+ $conditional->addCondition($formula1);
+ }
+ if ($formula2 !== null) {
+ $conditional->addCondition($formula2);
+ }
+ $conditional->setStyle($style);
+
+ $conditionalStyles = $this->phpSheet->getStyle($cellRange)->getConditionalStyles();
+ $conditionalStyles[] = $conditional;
+
+ $this->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles);
+ }
+ }
+
+ public function getVersion(): int
+ {
+ return $this->version;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
new file mode 100644
index 0000000..fd5d723
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
@@ -0,0 +1,36 @@
+ 'FF0000']
+ */
+ public static function map($color, array $palette, $version)
+ {
+ if ($color <= 0x07 || $color >= 0x40) {
+ // special built-in color
+ return Color\BuiltIn::lookup($color);
+ } elseif (isset($palette[$color - 8])) {
+ // palette color, color index 0x08 maps to pallete index 0
+ return $palette[$color - 8];
+ }
+
+ // default color table
+ if ($version == Xls::XLS_BIFF8) {
+ return Color\BIFF8::lookup($color);
+ }
+
+ // BIFF5
+ return Color\BIFF5::lookup($color);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
new file mode 100644
index 0000000..5569bbd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
@@ -0,0 +1,75 @@
+ '000000',
+ 0x09 => 'FFFFFF',
+ 0x0A => 'FF0000',
+ 0x0B => '00FF00',
+ 0x0C => '0000FF',
+ 0x0D => 'FFFF00',
+ 0x0E => 'FF00FF',
+ 0x0F => '00FFFF',
+ 0x10 => '800000',
+ 0x11 => '008000',
+ 0x12 => '000080',
+ 0x13 => '808000',
+ 0x14 => '800080',
+ 0x15 => '008080',
+ 0x16 => 'C0C0C0',
+ 0x17 => '808080',
+ 0x18 => '8080FF',
+ 0x19 => '802060',
+ 0x1A => 'FFFFC0',
+ 0x1B => 'A0E0F0',
+ 0x1C => '600080',
+ 0x1D => 'FF8080',
+ 0x1E => '0080C0',
+ 0x1F => 'C0C0FF',
+ 0x20 => '000080',
+ 0x21 => 'FF00FF',
+ 0x22 => 'FFFF00',
+ 0x23 => '00FFFF',
+ 0x24 => '800080',
+ 0x25 => '800000',
+ 0x26 => '008080',
+ 0x27 => '0000FF',
+ 0x28 => '00CFFF',
+ 0x29 => '69FFFF',
+ 0x2A => 'E0FFE0',
+ 0x2B => 'FFFF80',
+ 0x2C => 'A6CAF0',
+ 0x2D => 'DD9CB3',
+ 0x2E => 'B38FEE',
+ 0x2F => 'E3E3E3',
+ 0x30 => '2A6FF9',
+ 0x31 => '3FB8CD',
+ 0x32 => '488436',
+ 0x33 => '958C41',
+ 0x34 => '8E5E42',
+ 0x35 => 'A0627A',
+ 0x36 => '624FAC',
+ 0x37 => '969696',
+ 0x38 => '1D2FBE',
+ 0x39 => '286676',
+ 0x3A => '004500',
+ 0x3B => '453E01',
+ 0x3C => '6A2813',
+ 0x3D => '85396A',
+ 0x3E => '4A3285',
+ 0x3F => '424242',
+ ];
+
+ /**
+ * Map color array from BIFF5 built-in color index.
+ *
+ * @param int $color
+ */
+ public static function lookup($color): array
+ {
+ return ['rgb' => self::BIFF5_COLOR_MAP[$color] ?? '000000'];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
new file mode 100644
index 0000000..baa2c57
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
@@ -0,0 +1,75 @@
+ '000000',
+ 0x09 => 'FFFFFF',
+ 0x0A => 'FF0000',
+ 0x0B => '00FF00',
+ 0x0C => '0000FF',
+ 0x0D => 'FFFF00',
+ 0x0E => 'FF00FF',
+ 0x0F => '00FFFF',
+ 0x10 => '800000',
+ 0x11 => '008000',
+ 0x12 => '000080',
+ 0x13 => '808000',
+ 0x14 => '800080',
+ 0x15 => '008080',
+ 0x16 => 'C0C0C0',
+ 0x17 => '808080',
+ 0x18 => '9999FF',
+ 0x19 => '993366',
+ 0x1A => 'FFFFCC',
+ 0x1B => 'CCFFFF',
+ 0x1C => '660066',
+ 0x1D => 'FF8080',
+ 0x1E => '0066CC',
+ 0x1F => 'CCCCFF',
+ 0x20 => '000080',
+ 0x21 => 'FF00FF',
+ 0x22 => 'FFFF00',
+ 0x23 => '00FFFF',
+ 0x24 => '800080',
+ 0x25 => '800000',
+ 0x26 => '008080',
+ 0x27 => '0000FF',
+ 0x28 => '00CCFF',
+ 0x29 => 'CCFFFF',
+ 0x2A => 'CCFFCC',
+ 0x2B => 'FFFF99',
+ 0x2C => '99CCFF',
+ 0x2D => 'FF99CC',
+ 0x2E => 'CC99FF',
+ 0x2F => 'FFCC99',
+ 0x30 => '3366FF',
+ 0x31 => '33CCCC',
+ 0x32 => '99CC00',
+ 0x33 => 'FFCC00',
+ 0x34 => 'FF9900',
+ 0x35 => 'FF6600',
+ 0x36 => '666699',
+ 0x37 => '969696',
+ 0x38 => '003366',
+ 0x39 => '339966',
+ 0x3A => '003300',
+ 0x3B => '333300',
+ 0x3C => '993300',
+ 0x3D => '993366',
+ 0x3E => '333399',
+ 0x3F => '333333',
+ ];
+
+ /**
+ * Map color array from BIFF8 built-in color index.
+ *
+ * @param int $color
+ */
+ public static function lookup($color): array
+ {
+ return ['rgb' => self::BIFF8_COLOR_MAP[$color] ?? '000000'];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
new file mode 100644
index 0000000..c1d34f0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
@@ -0,0 +1,29 @@
+ '000000',
+ 0x01 => 'FFFFFF',
+ 0x02 => 'FF0000',
+ 0x03 => '00FF00',
+ 0x04 => '0000FF',
+ 0x05 => 'FFFF00',
+ 0x06 => 'FF00FF',
+ 0x07 => '00FFFF',
+ 0x40 => '000000', // system window text color
+ 0x41 => 'FFFFFF', // system window background color
+ ];
+
+ /**
+ * Map built-in color to RGB value.
+ *
+ * @param int $color Indexed color
+ */
+ public static function lookup($color): array
+ {
+ return ['rgb' => self::BUILTIN_COLOR_MAP[$color] ?? '000000'];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
new file mode 100644
index 0000000..8400efb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
@@ -0,0 +1,49 @@
+
+ */
+ private static $types = [
+ 0x01 => Conditional::CONDITION_CELLIS,
+ 0x02 => Conditional::CONDITION_EXPRESSION,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $operators = [
+ 0x00 => Conditional::OPERATOR_NONE,
+ 0x01 => Conditional::OPERATOR_BETWEEN,
+ 0x02 => Conditional::OPERATOR_NOTBETWEEN,
+ 0x03 => Conditional::OPERATOR_EQUAL,
+ 0x04 => Conditional::OPERATOR_NOTEQUAL,
+ 0x05 => Conditional::OPERATOR_GREATERTHAN,
+ 0x06 => Conditional::OPERATOR_LESSTHAN,
+ 0x07 => Conditional::OPERATOR_GREATERTHANOREQUAL,
+ 0x08 => Conditional::OPERATOR_LESSTHANOREQUAL,
+ ];
+
+ public static function type(int $type): ?string
+ {
+ if (isset(self::$types[$type])) {
+ return self::$types[$type];
+ }
+
+ return null;
+ }
+
+ public static function operator(int $operator): ?string
+ {
+ if (isset(self::$operators[$operator])) {
+ return self::$operators[$operator];
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
new file mode 100644
index 0000000..02f844e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
@@ -0,0 +1,72 @@
+
+ */
+ private static $types = [
+ 0x00 => DataValidation::TYPE_NONE,
+ 0x01 => DataValidation::TYPE_WHOLE,
+ 0x02 => DataValidation::TYPE_DECIMAL,
+ 0x03 => DataValidation::TYPE_LIST,
+ 0x04 => DataValidation::TYPE_DATE,
+ 0x05 => DataValidation::TYPE_TIME,
+ 0x06 => DataValidation::TYPE_TEXTLENGTH,
+ 0x07 => DataValidation::TYPE_CUSTOM,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $errorStyles = [
+ 0x00 => DataValidation::STYLE_STOP,
+ 0x01 => DataValidation::STYLE_WARNING,
+ 0x02 => DataValidation::STYLE_INFORMATION,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $operators = [
+ 0x00 => DataValidation::OPERATOR_BETWEEN,
+ 0x01 => DataValidation::OPERATOR_NOTBETWEEN,
+ 0x02 => DataValidation::OPERATOR_EQUAL,
+ 0x03 => DataValidation::OPERATOR_NOTEQUAL,
+ 0x04 => DataValidation::OPERATOR_GREATERTHAN,
+ 0x05 => DataValidation::OPERATOR_LESSTHAN,
+ 0x06 => DataValidation::OPERATOR_GREATERTHANOREQUAL,
+ 0x07 => DataValidation::OPERATOR_LESSTHANOREQUAL,
+ ];
+
+ public static function type(int $type): ?string
+ {
+ if (isset(self::$types[$type])) {
+ return self::$types[$type];
+ }
+
+ return null;
+ }
+
+ public static function errorStyle(int $errorStyle): ?string
+ {
+ if (isset(self::$errorStyles[$errorStyle])) {
+ return self::$errorStyles[$errorStyle];
+ }
+
+ return null;
+ }
+
+ public static function operator(int $operator): ?string
+ {
+ if (isset(self::$operators[$operator])) {
+ return self::$operators[$operator];
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
new file mode 100644
index 0000000..b33163d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
@@ -0,0 +1,26 @@
+ '#NULL!',
+ 0x07 => '#DIV/0!',
+ 0x0F => '#VALUE!',
+ 0x17 => '#REF!',
+ 0x1D => '#NAME?',
+ 0x24 => '#NUM!',
+ 0x2A => '#N/A',
+ ];
+
+ /**
+ * Map error code, e.g. '#N/A'.
+ *
+ * @param int $code
+ */
+ public static function lookup($code): string|bool
+ {
+ return self::ERROR_CODE_MAP[$code] ?? false;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
new file mode 100644
index 0000000..66c6298
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
@@ -0,0 +1,619 @@
+object = $object;
+ }
+
+ private const WHICH_ROUTINE = [
+ self::DGGCONTAINER => 'readDggContainer',
+ self::DGG => 'readDgg',
+ self::BSTORECONTAINER => 'readBstoreContainer',
+ self::BSE => 'readBSE',
+ self::BLIPJPEG => 'readBlipJPEG',
+ self::BLIPPNG => 'readBlipPNG',
+ self::OPT => 'readOPT',
+ self::TERTIARYOPT => 'readTertiaryOPT',
+ self::SPLITMENUCOLORS => 'readSplitMenuColors',
+ self::DGCONTAINER => 'readDgContainer',
+ self::DG => 'readDg',
+ self::SPGRCONTAINER => 'readSpgrContainer',
+ self::SPCONTAINER => 'readSpContainer',
+ self::SPGR => 'readSpgr',
+ self::SP => 'readSp',
+ self::CLIENTTEXTBOX => 'readClientTextbox',
+ self::CLIENTANCHOR => 'readClientAnchor',
+ self::CLIENTDATA => 'readClientData',
+ ];
+
+ /**
+ * Load Escher stream data. May be a partial Escher stream.
+ *
+ * @param string $data
+ *
+ * @return BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
+ */
+ public function load($data)
+ {
+ $this->data = $data;
+
+ // total byte size of Excel data (workbook global substream + sheet substreams)
+ $this->dataSize = strlen($this->data);
+
+ $this->pos = 0;
+
+ // Parse Escher stream
+ while ($this->pos < $this->dataSize) {
+ // offset: 2; size: 2: Record Type
+ $fbt = Xls::getUInt2d($this->data, $this->pos + 2);
+ $routine = self::WHICH_ROUTINE[$fbt] ?? 'readDefault';
+ if (method_exists($this, $routine)) {
+ $this->$routine();
+ }
+ }
+
+ return $this->object;
+ }
+
+ /**
+ * Read a generic record.
+ */
+ private function readDefault(): void
+ {
+ // offset 0; size: 2; recVer and recInstance
+ //$verInstance = Xls::getUInt2d($this->data, $this->pos);
+
+ // offset: 2; size: 2: Record Type
+ //$fbt = Xls::getUInt2d($this->data, $this->pos + 2);
+
+ // bit: 0-3; mask: 0x000F; recVer
+ //$recVer = (0x000F & $verInstance) >> 0;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read DggContainer record (Drawing Group Container).
+ */
+ private function readDggContainer(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // record is a container, read contents
+ $dggContainer = new DggContainer();
+ $this->applyAttribute('setDggContainer', $dggContainer);
+ $reader = new self($dggContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read Dgg record (Drawing Group).
+ */
+ private function readDgg(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read BstoreContainer record (Blip Store Container).
+ */
+ private function readBstoreContainer(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // record is a container, read contents
+ $bstoreContainer = new BstoreContainer();
+ $this->applyAttribute('setBstoreContainer', $bstoreContainer);
+ $reader = new self($bstoreContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read BSE record.
+ */
+ private function readBSE(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // add BSE to BstoreContainer
+ $BSE = new BSE();
+ $this->applyAttribute('addBSE', $BSE);
+
+ $BSE->setBLIPType($recInstance);
+
+ // offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
+ //$btWin32 = ord($recordData[0]);
+
+ // offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
+ //$btMacOS = ord($recordData[1]);
+
+ // offset: 2; size: 16; MD4 digest
+ //$rgbUid = substr($recordData, 2, 16);
+
+ // offset: 18; size: 2; tag
+ //$tag = Xls::getUInt2d($recordData, 18);
+
+ // offset: 20; size: 4; size of BLIP in bytes
+ //$size = Xls::getInt4d($recordData, 20);
+
+ // offset: 24; size: 4; number of references to this BLIP
+ //$cRef = Xls::getInt4d($recordData, 24);
+
+ // offset: 28; size: 4; MSOFO file offset
+ //$foDelay = Xls::getInt4d($recordData, 28);
+
+ // offset: 32; size: 1; unused1
+ //$unused1 = ord($recordData[32]);
+
+ // offset: 33; size: 1; size of nameData in bytes (including null terminator)
+ $cbName = ord($recordData[33]);
+
+ // offset: 34; size: 1; unused2
+ //$unused2 = ord($recordData[34]);
+
+ // offset: 35; size: 1; unused3
+ //$unused3 = ord($recordData[35]);
+
+ // offset: 36; size: $cbName; nameData
+ //$nameData = substr($recordData, 36, $cbName);
+
+ // offset: 36 + $cbName, size: var; the BLIP data
+ $blipData = substr($recordData, 36 + $cbName);
+
+ // record is a container, read contents
+ $reader = new self($BSE);
+ $reader->load($blipData);
+ }
+
+ /**
+ * Read BlipJPEG record. Holds raw JPEG image data.
+ */
+ private function readBlipJPEG(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ $pos = 0;
+
+ // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+ //$rgbUid1 = substr($recordData, 0, 16);
+ $pos += 16;
+
+ // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+ if (in_array($recInstance, [0x046B, 0x06E3])) {
+ //$rgbUid2 = substr($recordData, 16, 16);
+ $pos += 16;
+ }
+
+ // offset: var; size: 1; tag
+ //$tag = ord($recordData[$pos]);
+ ++$pos;
+
+ // offset: var; size: var; the raw image data
+ $data = substr($recordData, $pos);
+
+ $blip = new Blip();
+ $blip->setData($data);
+
+ $this->applyAttribute('setBlip', $blip);
+ }
+
+ /**
+ * Read BlipPNG record. Holds raw PNG image data.
+ */
+ private function readBlipPNG(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ $pos = 0;
+
+ // offset: 0; size: 16; rgbUid1 (MD4 digest of)
+ //$rgbUid1 = substr($recordData, 0, 16);
+ $pos += 16;
+
+ // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
+ if ($recInstance == 0x06E1) {
+ //$rgbUid2 = substr($recordData, 16, 16);
+ $pos += 16;
+ }
+
+ // offset: var; size: 1; tag
+ //$tag = ord($recordData[$pos]);
+ ++$pos;
+
+ // offset: var; size: var; the raw image data
+ $data = substr($recordData, $pos);
+
+ $blip = new Blip();
+ $blip->setData($data);
+
+ $this->applyAttribute('setBlip', $blip);
+ }
+
+ /**
+ * Read OPT record. This record may occur within DggContainer record or SpContainer.
+ */
+ private function readOPT(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ $this->readOfficeArtRGFOPTE($recordData, $recInstance);
+ }
+
+ /**
+ * Read TertiaryOPT record.
+ */
+ private function readTertiaryOPT(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ //$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read SplitMenuColors record.
+ */
+ private function readSplitMenuColors(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read DgContainer record (Drawing Container).
+ */
+ private function readDgContainer(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // record is a container, read contents
+ $dgContainer = new DgContainer();
+ $this->applyAttribute('setDgContainer', $dgContainer);
+ $reader = new self($dgContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read Dg record (Drawing).
+ */
+ private function readDg(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read SpgrContainer record (Shape Group Container).
+ */
+ private function readSpgrContainer(): void
+ {
+ // context is either context DgContainer or SpgrContainer
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // record is a container, read contents
+ $spgrContainer = new SpgrContainer();
+
+ if ($this->object instanceof DgContainer) {
+ // DgContainer
+ $this->object->setSpgrContainer($spgrContainer);
+ } elseif ($this->object instanceof SpgrContainer) {
+ // SpgrContainer
+ $this->object->addChild($spgrContainer);
+ }
+
+ $reader = new self($spgrContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read SpContainer record (Shape Container).
+ */
+ private function readSpContainer(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // add spContainer to spgrContainer
+ $spContainer = new SpContainer();
+ $this->applyAttribute('addChild', $spContainer);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // record is a container, read contents
+ $reader = new self($spContainer);
+ $reader->load($recordData);
+ }
+
+ /**
+ * Read Spgr record (Shape Group).
+ */
+ private function readSpgr(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read Sp record (Shape).
+ */
+ private function readSp(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ //$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read ClientTextbox record.
+ */
+ private function readClientTextbox(): void
+ {
+ // offset: 0; size: 2; recVer and recInstance
+
+ // bit: 4-15; mask: 0xFFF0; recInstance
+ //$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
+
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet.
+ */
+ private function readClientAnchor(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ $recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+
+ // offset: 2; size: 2; upper-left corner column index (0-based)
+ $c1 = Xls::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
+ $startOffsetX = Xls::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 2; upper-left corner row index (0-based)
+ $r1 = Xls::getUInt2d($recordData, 6);
+
+ // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
+ $startOffsetY = Xls::getUInt2d($recordData, 8);
+
+ // offset: 10; size: 2; bottom-right corner column index (0-based)
+ $c2 = Xls::getUInt2d($recordData, 10);
+
+ // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
+ $endOffsetX = Xls::getUInt2d($recordData, 12);
+
+ // offset: 14; size: 2; bottom-right corner row index (0-based)
+ $r2 = Xls::getUInt2d($recordData, 14);
+
+ // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
+ $endOffsetY = Xls::getUInt2d($recordData, 16);
+
+ $this->applyAttribute('setStartCoordinates', Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1));
+ $this->applyAttribute('setStartOffsetX', $startOffsetX);
+ $this->applyAttribute('setStartOffsetY', $startOffsetY);
+ $this->applyAttribute('setEndCoordinates', Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1));
+ $this->applyAttribute('setEndOffsetX', $endOffsetX);
+ $this->applyAttribute('setEndOffsetY', $endOffsetY);
+ }
+
+ private function applyAttribute(string $name, mixed $value): void
+ {
+ if (method_exists($this->object, $name)) {
+ $this->object->$name($value);
+ }
+ }
+
+ /**
+ * Read ClientData record.
+ */
+ private function readClientData(): void
+ {
+ $length = Xls::getInt4d($this->data, $this->pos + 4);
+ //$recordData = substr($this->data, $this->pos + 8, $length);
+
+ // move stream pointer to next record
+ $this->pos += 8 + $length;
+ }
+
+ /**
+ * Read OfficeArtRGFOPTE table of property-value pairs.
+ *
+ * @param string $data Binary data
+ * @param int $n Number of properties
+ */
+ private function readOfficeArtRGFOPTE(string $data, int $n): void
+ {
+ $splicedComplexData = substr($data, 6 * $n);
+
+ // loop through property-value pairs
+ for ($i = 0; $i < $n; ++$i) {
+ // read 6 bytes at a time
+ $fopte = substr($data, 6 * $i, 6);
+
+ // offset: 0; size: 2; opid
+ $opid = Xls::getUInt2d($fopte, 0);
+
+ // bit: 0-13; mask: 0x3FFF; opid.opid
+ $opidOpid = (0x3FFF & $opid) >> 0;
+
+ // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
+ //$opidFBid = (0x4000 & $opid) >> 14;
+
+ // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
+ $opidFComplex = (0x8000 & $opid) >> 15;
+
+ // offset: 2; size: 4; the value for this property
+ $op = Xls::getInt4d($fopte, 2);
+
+ if ($opidFComplex) {
+ $complexData = substr($splicedComplexData, 0, $op);
+ $splicedComplexData = substr($splicedComplexData, $op);
+
+ // we store string value with complex data
+ $value = $complexData;
+ } else {
+ // we store integer value
+ $value = $op;
+ }
+
+ if (method_exists($this->object, 'setOPT')) {
+ $this->object->setOPT($opidOpid, $value);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
new file mode 100644
index 0000000..8f02130
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
@@ -0,0 +1,205 @@
+reset();
+ }
+
+ /**
+ * Reset the MD5 stream context.
+ */
+ public function reset(): void
+ {
+ $this->a = 0x67452301;
+ $this->b = self::signedInt(0xEFCDAB89);
+ $this->c = self::signedInt(0x98BADCFE);
+ $this->d = 0x10325476;
+ }
+
+ /**
+ * Get MD5 stream context.
+ */
+ public function getContext(): string
+ {
+ $s = '';
+ foreach (['a', 'b', 'c', 'd'] as $i) {
+ $v = $this->{$i};
+ $s .= chr($v & 0xFF);
+ $s .= chr(($v >> 8) & 0xFF);
+ $s .= chr(($v >> 16) & 0xFF);
+ $s .= chr(($v >> 24) & 0xFF);
+ }
+
+ return $s;
+ }
+
+ /**
+ * Add data to context.
+ *
+ * @param string $data Data to add
+ */
+ public function add(string $data): void
+ {
+ // @phpstan-ignore-next-line
+ $words = array_values(unpack('V16', $data));
+
+ $A = $this->a;
+ $B = $this->b;
+ $C = $this->c;
+ $D = $this->d;
+
+ $F = [self::class, 'f'];
+ $G = [self::class, 'g'];
+ $H = [self::class, 'h'];
+ $I = [self::class, 'i'];
+
+ // ROUND 1
+ self::step($F, $A, $B, $C, $D, $words[0], 7, 0xD76AA478);
+ self::step($F, $D, $A, $B, $C, $words[1], 12, 0xE8C7B756);
+ self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070DB);
+ self::step($F, $B, $C, $D, $A, $words[3], 22, 0xC1BDCEEE);
+ self::step($F, $A, $B, $C, $D, $words[4], 7, 0xF57C0FAF);
+ self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787C62A);
+ self::step($F, $C, $D, $A, $B, $words[6], 17, 0xA8304613);
+ self::step($F, $B, $C, $D, $A, $words[7], 22, 0xFD469501);
+ self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098D8);
+ self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8B44F7AF);
+ self::step($F, $C, $D, $A, $B, $words[10], 17, 0xFFFF5BB1);
+ self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895CD7BE);
+ self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6B901122);
+ self::step($F, $D, $A, $B, $C, $words[13], 12, 0xFD987193);
+ self::step($F, $C, $D, $A, $B, $words[14], 17, 0xA679438E);
+ self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49B40821);
+
+ // ROUND 2
+ self::step($G, $A, $B, $C, $D, $words[1], 5, 0xF61E2562);
+ self::step($G, $D, $A, $B, $C, $words[6], 9, 0xC040B340);
+ self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265E5A51);
+ self::step($G, $B, $C, $D, $A, $words[0], 20, 0xE9B6C7AA);
+ self::step($G, $A, $B, $C, $D, $words[5], 5, 0xD62F105D);
+ self::step($G, $D, $A, $B, $C, $words[10], 9, 0x02441453);
+ self::step($G, $C, $D, $A, $B, $words[15], 14, 0xD8A1E681);
+ self::step($G, $B, $C, $D, $A, $words[4], 20, 0xE7D3FBC8);
+ self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21E1CDE6);
+ self::step($G, $D, $A, $B, $C, $words[14], 9, 0xC33707D6);
+ self::step($G, $C, $D, $A, $B, $words[3], 14, 0xF4D50D87);
+ self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455A14ED);
+ self::step($G, $A, $B, $C, $D, $words[13], 5, 0xA9E3E905);
+ self::step($G, $D, $A, $B, $C, $words[2], 9, 0xFCEFA3F8);
+ self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676F02D9);
+ self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8D2A4C8A);
+
+ // ROUND 3
+ self::step($H, $A, $B, $C, $D, $words[5], 4, 0xFFFA3942);
+ self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771F681);
+ self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6D9D6122);
+ self::step($H, $B, $C, $D, $A, $words[14], 23, 0xFDE5380C);
+ self::step($H, $A, $B, $C, $D, $words[1], 4, 0xA4BEEA44);
+ self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4BDECFA9);
+ self::step($H, $C, $D, $A, $B, $words[7], 16, 0xF6BB4B60);
+ self::step($H, $B, $C, $D, $A, $words[10], 23, 0xBEBFBC70);
+ self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289B7EC6);
+ self::step($H, $D, $A, $B, $C, $words[0], 11, 0xEAA127FA);
+ self::step($H, $C, $D, $A, $B, $words[3], 16, 0xD4EF3085);
+ self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881D05);
+ self::step($H, $A, $B, $C, $D, $words[9], 4, 0xD9D4D039);
+ self::step($H, $D, $A, $B, $C, $words[12], 11, 0xE6DB99E5);
+ self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1FA27CF8);
+ self::step($H, $B, $C, $D, $A, $words[2], 23, 0xC4AC5665);
+
+ // ROUND 4
+ self::step($I, $A, $B, $C, $D, $words[0], 6, 0xF4292244);
+ self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432AFF97);
+ self::step($I, $C, $D, $A, $B, $words[14], 15, 0xAB9423A7);
+ self::step($I, $B, $C, $D, $A, $words[5], 21, 0xFC93A039);
+ self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655B59C3);
+ self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8F0CCC92);
+ self::step($I, $C, $D, $A, $B, $words[10], 15, 0xFFEFF47D);
+ self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845DD1);
+ self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6FA87E4F);
+ self::step($I, $D, $A, $B, $C, $words[15], 10, 0xFE2CE6E0);
+ self::step($I, $C, $D, $A, $B, $words[6], 15, 0xA3014314);
+ self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4E0811A1);
+ self::step($I, $A, $B, $C, $D, $words[4], 6, 0xF7537E82);
+ self::step($I, $D, $A, $B, $C, $words[11], 10, 0xBD3AF235);
+ self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2AD7D2BB);
+ self::step($I, $B, $C, $D, $A, $words[9], 21, 0xEB86D391);
+
+ $this->a = ($this->a + $A) & self::$allOneBits;
+ $this->b = ($this->b + $B) & self::$allOneBits;
+ $this->c = ($this->c + $C) & self::$allOneBits;
+ $this->d = ($this->d + $D) & self::$allOneBits;
+ }
+
+ private static function f(int $X, int $Y, int $Z): int
+ {
+ return ($X & $Y) | ((~$X) & $Z); // X AND Y OR NOT X AND Z
+ }
+
+ private static function g(int $X, int $Y, int $Z): int
+ {
+ return ($X & $Z) | ($Y & (~$Z)); // X AND Z OR Y AND NOT Z
+ }
+
+ private static function h(int $X, int $Y, int $Z): int
+ {
+ return $X ^ $Y ^ $Z; // X XOR Y XOR Z
+ }
+
+ private static function i(int $X, int $Y, int $Z): int
+ {
+ return $Y ^ ($X | (~$Z)); // Y XOR (X OR NOT Z)
+ }
+
+ /** @param float|int $t may be float on 32-bit system */
+ private static function step(callable $func, int &$A, int $B, int $C, int $D, int $M, int $s, $t): void
+ {
+ $t = self::signedInt($t);
+ $A = (int) ($A + call_user_func($func, $B, $C, $D) + $M + $t) & self::$allOneBits;
+ $A = self::rotate($A, $s);
+ $A = (int) ($B + $A) & self::$allOneBits;
+ }
+
+ /** @param float|int $result may be float on 32-bit system */
+ private static function signedInt($result): int
+ {
+ return is_int($result) ? $result : (int) (PHP_INT_MIN + $result - 1 - PHP_INT_MAX);
+ }
+
+ private static function rotate(int $decimal, int $bits): int
+ {
+ $binary = str_pad(decbin($decimal), 32, '0', STR_PAD_LEFT);
+
+ return self::signedInt(bindec(substr($binary, $bits) . substr($binary, 0, $bits)));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
new file mode 100644
index 0000000..b7c7c90
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
@@ -0,0 +1,63 @@
+i = 0; $this->i < 256; ++$this->i) {
+ $this->s[$this->i] = $this->i;
+ }
+
+ $this->j = 0;
+ for ($this->i = 0; $this->i < 256; ++$this->i) {
+ $this->j = ($this->j + $this->s[$this->i] + ord($key[$this->i % $len])) % 256;
+ $t = $this->s[$this->i];
+ $this->s[$this->i] = $this->s[$this->j];
+ $this->s[$this->j] = $t;
+ }
+ $this->i = $this->j = 0;
+ }
+
+ /**
+ * Symmetric decryption/encryption function.
+ *
+ * @param string $data Data to encrypt/decrypt
+ *
+ * @return string
+ */
+ public function RC4($data)
+ {
+ $len = strlen($data);
+ for ($c = 0; $c < $len; ++$c) {
+ $this->i = ($this->i + 1) % 256;
+ $this->j = ($this->j + $this->s[$this->i]) % 256;
+ $t = $this->s[$this->i];
+ $this->s[$this->i] = $this->s[$this->j];
+ $this->s[$this->j] = $t;
+
+ $t = ($this->s[$this->i] + $this->s[$this->j]) % 256;
+
+ $data[$c] = chr(ord($data[$c]) ^ $this->s[$t]);
+ }
+
+ return $data;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
new file mode 100644
index 0000000..d832eb0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
@@ -0,0 +1,37 @@
+
+ */
+ protected static $borderStyleMap = [
+ 0x00 => StyleBorder::BORDER_NONE,
+ 0x01 => StyleBorder::BORDER_THIN,
+ 0x02 => StyleBorder::BORDER_MEDIUM,
+ 0x03 => StyleBorder::BORDER_DASHED,
+ 0x04 => StyleBorder::BORDER_DOTTED,
+ 0x05 => StyleBorder::BORDER_THICK,
+ 0x06 => StyleBorder::BORDER_DOUBLE,
+ 0x07 => StyleBorder::BORDER_HAIR,
+ 0x08 => StyleBorder::BORDER_MEDIUMDASHED,
+ 0x09 => StyleBorder::BORDER_DASHDOT,
+ 0x0A => StyleBorder::BORDER_MEDIUMDASHDOT,
+ 0x0B => StyleBorder::BORDER_DASHDOTDOT,
+ 0x0C => StyleBorder::BORDER_MEDIUMDASHDOTDOT,
+ 0x0D => StyleBorder::BORDER_SLANTDASHDOT,
+ ];
+
+ public static function lookup(int $index): string
+ {
+ if (isset(self::$borderStyleMap[$index])) {
+ return self::$borderStyleMap[$index];
+ }
+
+ return StyleBorder::BORDER_NONE;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php
new file mode 100644
index 0000000..f03d47f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php
@@ -0,0 +1,50 @@
+
+ */
+ protected static $horizontalAlignmentMap = [
+ 0 => Alignment::HORIZONTAL_GENERAL,
+ 1 => Alignment::HORIZONTAL_LEFT,
+ 2 => Alignment::HORIZONTAL_CENTER,
+ 3 => Alignment::HORIZONTAL_RIGHT,
+ 4 => Alignment::HORIZONTAL_FILL,
+ 5 => Alignment::HORIZONTAL_JUSTIFY,
+ 6 => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+ ];
+
+ /**
+ * @var array
+ */
+ protected static $verticalAlignmentMap = [
+ 0 => Alignment::VERTICAL_TOP,
+ 1 => Alignment::VERTICAL_CENTER,
+ 2 => Alignment::VERTICAL_BOTTOM,
+ 3 => Alignment::VERTICAL_JUSTIFY,
+ ];
+
+ public static function horizontal(Alignment $alignment, int $horizontal): void
+ {
+ if (array_key_exists($horizontal, self::$horizontalAlignmentMap)) {
+ $alignment->setHorizontal(self::$horizontalAlignmentMap[$horizontal]);
+ }
+ }
+
+ public static function vertical(Alignment $alignment, int $vertical): void
+ {
+ if (array_key_exists($vertical, self::$verticalAlignmentMap)) {
+ $alignment->setVertical(self::$verticalAlignmentMap[$vertical]);
+ }
+ }
+
+ public static function wrap(Alignment $alignment, int $wrap): void
+ {
+ $alignment->setWrapText((bool) $wrap);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php
new file mode 100644
index 0000000..e975be4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php
@@ -0,0 +1,39 @@
+setSuperscript(true);
+
+ break;
+ case 0x0002:
+ $font->setSubscript(true);
+
+ break;
+ }
+ }
+
+ /**
+ * @var array
+ */
+ protected static $underlineMap = [
+ 0x01 => Font::UNDERLINE_SINGLE,
+ 0x02 => Font::UNDERLINE_DOUBLE,
+ 0x21 => Font::UNDERLINE_SINGLEACCOUNTING,
+ 0x22 => Font::UNDERLINE_DOUBLEACCOUNTING,
+ ];
+
+ public static function underline(Font $font, int $underline): void
+ {
+ if (array_key_exists($underline, self::$underlineMap)) {
+ $font->setUnderline(self::$underlineMap[$underline]);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
new file mode 100644
index 0000000..32ab5c8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
@@ -0,0 +1,50 @@
+
+ */
+ protected static $fillPatternMap = [
+ 0x00 => Fill::FILL_NONE,
+ 0x01 => Fill::FILL_SOLID,
+ 0x02 => Fill::FILL_PATTERN_MEDIUMGRAY,
+ 0x03 => Fill::FILL_PATTERN_DARKGRAY,
+ 0x04 => Fill::FILL_PATTERN_LIGHTGRAY,
+ 0x05 => Fill::FILL_PATTERN_DARKHORIZONTAL,
+ 0x06 => Fill::FILL_PATTERN_DARKVERTICAL,
+ 0x07 => Fill::FILL_PATTERN_DARKDOWN,
+ 0x08 => Fill::FILL_PATTERN_DARKUP,
+ 0x09 => Fill::FILL_PATTERN_DARKGRID,
+ 0x0A => Fill::FILL_PATTERN_DARKTRELLIS,
+ 0x0B => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
+ 0x0C => Fill::FILL_PATTERN_LIGHTVERTICAL,
+ 0x0D => Fill::FILL_PATTERN_LIGHTDOWN,
+ 0x0E => Fill::FILL_PATTERN_LIGHTUP,
+ 0x0F => Fill::FILL_PATTERN_LIGHTGRID,
+ 0x10 => Fill::FILL_PATTERN_LIGHTTRELLIS,
+ 0x11 => Fill::FILL_PATTERN_GRAY125,
+ 0x12 => Fill::FILL_PATTERN_GRAY0625,
+ ];
+
+ /**
+ * Get fill pattern from index
+ * OpenOffice documentation: 2.5.12.
+ *
+ * @param int $index
+ *
+ * @return string
+ */
+ public static function lookup($index)
+ {
+ if (isset(self::$fillPatternMap[$index])) {
+ return self::$fillPatternMap[$index];
+ }
+
+ return Fill::FILL_NONE;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
new file mode 100644
index 0000000..d9b6014
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -0,0 +1,2351 @@
+referenceHelper = ReferenceHelper::getInstance();
+ $this->securityScanner = XmlScanner::getInstance($this);
+ }
+
+ /**
+ * Can the current IReader read the file?
+ */
+ public function canRead(string $filename): bool
+ {
+ if (!File::testFileNoThrow($filename, self::INITIAL_FILE)) {
+ return false;
+ }
+
+ $result = false;
+ $this->zip = $zip = new ZipArchive();
+
+ if ($zip->open($filename) === true) {
+ [$workbookBasename] = $this->getWorkbookBaseName();
+ $result = !empty($workbookBasename);
+
+ $zip->close();
+ }
+
+ return $result;
+ }
+
+ public static function testSimpleXml(mixed $value): SimpleXMLElement
+ {
+ return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement(' ');
+ }
+
+ public static function getAttributes(?SimpleXMLElement $value, string $ns = ''): SimpleXMLElement
+ {
+ return self::testSimpleXml($value === null ? $value : $value->attributes($ns));
+ }
+
+ // Phpstan thinks, correctly, that xpath can return false.
+ private static function xpathNoFalse(SimpleXmlElement $sxml, string $path): array
+ {
+ return self::falseToArray($sxml->xpath($path));
+ }
+
+ public static function falseToArray(mixed $value): array
+ {
+ return is_array($value) ? $value : [];
+ }
+
+ private function loadZip(string $filename, string $ns = '', bool $replaceUnclosedBr = false): SimpleXMLElement
+ {
+ $contents = $this->getFromZipArchive($this->zip, $filename);
+ if ($replaceUnclosedBr) {
+ $contents = str_replace(' ', ' ', $contents);
+ }
+ $rels = @simplexml_load_string(
+ $this->getSecurityScannerOrThrow()->scan($contents),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions(),
+ $ns
+ );
+
+ return self::testSimpleXml($rels);
+ }
+
+ // This function is just to identify cases where I'm not sure
+ // why empty namespace is required.
+ private function loadZipNonamespace(string $filename, string $ns): SimpleXMLElement
+ {
+ $contents = $this->getFromZipArchive($this->zip, $filename);
+ $rels = simplexml_load_string(
+ $this->getSecurityScannerOrThrow()->scan($contents),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions(),
+ ($ns === '' ? $ns : '')
+ );
+
+ return self::testSimpleXml($rels);
+ }
+
+ private const REL_TO_MAIN = [
+ Namespaces::PURL_OFFICE_DOCUMENT => Namespaces::PURL_MAIN,
+ Namespaces::THUMBNAIL => '',
+ ];
+
+ private const REL_TO_DRAWING = [
+ Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_DRAWING,
+ ];
+
+ private const REL_TO_CHART = [
+ Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_CHART,
+ ];
+
+ /**
+ * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
+ */
+ public function listWorksheetNames(string $filename): array
+ {
+ File::assertFile($filename, self::INITIAL_FILE);
+
+ $worksheetNames = [];
+
+ $this->zip = $zip = new ZipArchive();
+ $zip->open($filename);
+
+ // The files we're looking at here are small enough that simpleXML is more efficient than XMLReader
+ $rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
+ foreach ($rels->Relationship as $relx) {
+ $rel = self::getAttributes($relx);
+ $relType = (string) $rel['Type'];
+ $mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
+ if ($mainNS !== '') {
+ $xmlWorkbook = $this->loadZip((string) $rel['Target'], $mainNS);
+
+ if ($xmlWorkbook->sheets) {
+ foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+ // Check if sheet should be skipped
+ $worksheetNames[] = (string) self::getAttributes($eleSheet)['name'];
+ }
+ }
+ }
+ }
+
+ $zip->close();
+
+ return $worksheetNames;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ File::assertFile($filename, self::INITIAL_FILE);
+
+ $worksheetInfo = [];
+
+ $this->zip = $zip = new ZipArchive();
+ $zip->open($filename);
+
+ $rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
+ foreach ($rels->Relationship as $relx) {
+ $rel = self::getAttributes($relx);
+ $relType = (string) $rel['Type'];
+ $mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
+ if ($mainNS !== '') {
+ $relTarget = (string) $rel['Target'];
+ $dir = dirname($relTarget);
+ $namespace = dirname($relType);
+ $relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', Namespaces::RELATIONSHIPS);
+
+ $worksheets = [];
+ foreach ($relsWorkbook->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ if (
+ ((string) $ele['Type'] === "$namespace/worksheet")
+ || ((string) $ele['Type'] === "$namespace/chartsheet")
+ ) {
+ $worksheets[(string) $ele['Id']] = $ele['Target'];
+ }
+ }
+
+ $xmlWorkbook = $this->loadZip($relTarget, $mainNS);
+ if ($xmlWorkbook->sheets) {
+ $dir = dirname($relTarget);
+
+ /** @var SimpleXMLElement $eleSheet */
+ foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
+ $tmpInfo = [
+ 'worksheetName' => (string) self::getAttributes($eleSheet)['name'],
+ 'lastColumnLetter' => 'A',
+ 'lastColumnIndex' => 0,
+ 'totalRows' => 0,
+ 'totalColumns' => 0,
+ ];
+
+ $fileWorksheet = (string) $worksheets[(string) self::getArrayItem(self::getAttributes($eleSheet, $namespace), 'id')];
+ $fileWorksheetPath = str_starts_with($fileWorksheet, '/') ? substr($fileWorksheet, 1) : "$dir/$fileWorksheet";
+
+ $xml = new XMLReader();
+ $xml->xml(
+ $this->getSecurityScannerOrThrow()->scan(
+ $this->getFromZipArchive($this->zip, $fileWorksheetPath)
+ ),
+ null,
+ Settings::getLibXmlLoaderOptions()
+ );
+ $xml->setParserProperty(2, true);
+
+ $currCells = 0;
+ while ($xml->read()) {
+ if ($xml->localName == 'row' && $xml->nodeType == XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) {
+ $row = $xml->getAttribute('r');
+ $tmpInfo['totalRows'] = $row;
+ $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+ $currCells = 0;
+ } elseif ($xml->localName == 'c' && $xml->nodeType == XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) {
+ $cell = $xml->getAttribute('r');
+ $currCells = $cell ? max($currCells, Coordinate::indexesFromString($cell)[0]) : ($currCells + 1);
+ }
+ }
+ $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
+ $xml->close();
+
+ $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
+ $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+
+ $worksheetInfo[] = $tmpInfo;
+ }
+ }
+ }
+ }
+
+ $zip->close();
+
+ return $worksheetInfo;
+ }
+
+ private static function castToBoolean(SimpleXMLElement $c): bool
+ {
+ $value = isset($c->v) ? (string) $c->v : null;
+ if ($value == '0') {
+ return false;
+ } elseif ($value == '1') {
+ return true;
+ }
+
+ return (bool) $c->v;
+ }
+
+ private static function castToError(?SimpleXMLElement $c): ?string
+ {
+ return isset($c, $c->v) ? (string) $c->v : null;
+ }
+
+ private static function castToString(?SimpleXMLElement $c): ?string
+ {
+ return isset($c, $c->v) ? (string) $c->v : null;
+ }
+
+ private function castToFormula(?SimpleXMLElement $c, string $r, string &$cellDataType, mixed &$value, mixed &$calculatedValue, string $castBaseType, bool $updateSharedCells = true): void
+ {
+ if ($c === null) {
+ return;
+ }
+ $attr = $c->f->attributes();
+ $cellDataType = DataType::TYPE_FORMULA;
+ $value = "={$c->f}";
+ $calculatedValue = self::$castBaseType($c);
+
+ // Shared formula?
+ if (isset($attr['t']) && strtolower((string) $attr['t']) == 'shared') {
+ $instance = (string) $attr['si'];
+
+ if (!isset($this->sharedFormulae[(string) $attr['si']])) {
+ $this->sharedFormulae[$instance] = new SharedFormula($r, $value);
+ } elseif ($updateSharedCells === true) {
+ // It's only worth the overhead of adjusting the shared formula for this cell if we're actually loading
+ // the cell, which may not be the case if we're using a read filter.
+ $master = Coordinate::indexesFromString($this->sharedFormulae[$instance]->master());
+ $current = Coordinate::indexesFromString($r);
+
+ $difference = [0, 0];
+ $difference[0] = $current[0] - $master[0];
+ $difference[1] = $current[1] - $master[1];
+
+ $value = $this->referenceHelper->updateFormulaReferences($this->sharedFormulae[$instance]->formula(), 'A1', $difference[0], $difference[1]);
+ }
+ }
+ }
+
+ private function fileExistsInArchive(ZipArchive $archive, string $fileName = ''): bool
+ {
+ // Root-relative paths
+ if (str_contains($fileName, '//')) {
+ $fileName = substr($fileName, strpos($fileName, '//') + 1);
+ }
+ $fileName = File::realpath($fileName);
+
+ // Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
+ // so we need to load case-insensitively from the zip file
+
+ // Apache POI fixes
+ $contents = $archive->locateName($fileName, ZipArchive::FL_NOCASE);
+ if ($contents === false) {
+ $contents = $archive->locateName(substr($fileName, 1), ZipArchive::FL_NOCASE);
+ }
+
+ return $contents !== false;
+ }
+
+ /**
+ * @param string $fileName
+ *
+ * @return string
+ */
+ private function getFromZipArchive(ZipArchive $archive, $fileName = '')
+ {
+ // Root-relative paths
+ if (str_contains($fileName, '//')) {
+ $fileName = substr($fileName, strpos($fileName, '//') + 1);
+ }
+ // Relative paths generated by dirname($filename) when $filename
+ // has no path (i.e.files in root of the zip archive)
+ $fileName = (string) preg_replace('/^\.\//', '', $fileName);
+ $fileName = File::realpath($fileName);
+
+ // Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
+ // so we need to load case-insensitively from the zip file
+
+ $contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
+
+ // Apache POI fixes
+ if ($contents === false) {
+ $contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
+ }
+
+ // Has the file been saved with Windoze directory separators rather than unix?
+ if ($contents === false) {
+ $contents = $archive->getFromName(str_replace('/', '\\', $fileName), 0, ZipArchive::FL_NOCASE);
+ }
+
+ return ($contents === false) ? '' : $contents;
+ }
+
+ /**
+ * Loads Spreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ File::assertFile($filename, self::INITIAL_FILE);
+
+ // Initialisations
+ $excel = new Spreadsheet();
+ $excel->removeSheetByIndex(0);
+ $addingFirstCellStyleXf = true;
+ $addingFirstCellXf = true;
+
+ $unparsedLoadedData = [];
+
+ $this->zip = $zip = new ZipArchive();
+ $zip->open($filename);
+
+ // Read the theme first, because we need the colour scheme when reading the styles
+ [$workbookBasename, $xmlNamespaceBase] = $this->getWorkbookBaseName();
+ $drawingNS = self::REL_TO_DRAWING[$xmlNamespaceBase] ?? Namespaces::DRAWINGML;
+ $chartNS = self::REL_TO_CHART[$xmlNamespaceBase] ?? Namespaces::CHART;
+ $wbRels = $this->loadZip("xl/_rels/{$workbookBasename}.rels", Namespaces::RELATIONSHIPS);
+ $theme = null;
+ $this->styleReader = new Styles();
+ foreach ($wbRels->Relationship as $relx) {
+ $rel = self::getAttributes($relx);
+ $relTarget = (string) $rel['Target'];
+ if (str_starts_with($relTarget, '/xl/')) {
+ $relTarget = substr($relTarget, 4);
+ }
+ switch ($rel['Type']) {
+ case "$xmlNamespaceBase/theme":
+ if (!$this->fileExistsInArchive($zip, "xl/{$relTarget}")) {
+ break; // issue3770
+ }
+ $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
+ $themeOrderAdditional = count($themeOrderArray);
+
+ $xmlTheme = $this->loadZip("xl/{$relTarget}", $drawingNS);
+ $xmlThemeName = self::getAttributes($xmlTheme);
+ $xmlTheme = $xmlTheme->children($drawingNS);
+ $themeName = (string) $xmlThemeName['name'];
+
+ $colourScheme = self::getAttributes($xmlTheme->themeElements->clrScheme);
+ $colourSchemeName = (string) $colourScheme['name'];
+ $excel->getTheme()->setThemeColorName($colourSchemeName);
+ $colourScheme = $xmlTheme->themeElements->clrScheme->children($drawingNS);
+
+ $themeColours = [];
+ foreach ($colourScheme as $k => $xmlColour) {
+ $themePos = array_search($k, $themeOrderArray);
+ if ($themePos === false) {
+ $themePos = $themeOrderAdditional++;
+ }
+ if (isset($xmlColour->sysClr)) {
+ $xmlColourData = self::getAttributes($xmlColour->sysClr);
+ $themeColours[$themePos] = (string) $xmlColourData['lastClr'];
+ $excel->getTheme()->setThemeColor($k, (string) $xmlColourData['lastClr']);
+ } elseif (isset($xmlColour->srgbClr)) {
+ $xmlColourData = self::getAttributes($xmlColour->srgbClr);
+ $themeColours[$themePos] = (string) $xmlColourData['val'];
+ $excel->getTheme()->setThemeColor($k, (string) $xmlColourData['val']);
+ }
+ }
+ $theme = new Theme($themeName, $colourSchemeName, $themeColours);
+ $this->styleReader->setTheme($theme);
+
+ $fontScheme = self::getAttributes($xmlTheme->themeElements->fontScheme);
+ $fontSchemeName = (string) $fontScheme['name'];
+ $excel->getTheme()->setThemeFontName($fontSchemeName);
+ $majorFonts = [];
+ $minorFonts = [];
+ $fontScheme = $xmlTheme->themeElements->fontScheme->children($drawingNS);
+ $majorLatin = self::getAttributes($fontScheme->majorFont->latin)['typeface'] ?? '';
+ $majorEastAsian = self::getAttributes($fontScheme->majorFont->ea)['typeface'] ?? '';
+ $majorComplexScript = self::getAttributes($fontScheme->majorFont->cs)['typeface'] ?? '';
+ $minorLatin = self::getAttributes($fontScheme->minorFont->latin)['typeface'] ?? '';
+ $minorEastAsian = self::getAttributes($fontScheme->minorFont->ea)['typeface'] ?? '';
+ $minorComplexScript = self::getAttributes($fontScheme->minorFont->cs)['typeface'] ?? '';
+
+ foreach ($fontScheme->majorFont->font as $xmlFont) {
+ $fontAttributes = self::getAttributes($xmlFont);
+ $script = (string) ($fontAttributes['script'] ?? '');
+ if (!empty($script)) {
+ $majorFonts[$script] = (string) ($fontAttributes['typeface'] ?? '');
+ }
+ }
+ foreach ($fontScheme->minorFont->font as $xmlFont) {
+ $fontAttributes = self::getAttributes($xmlFont);
+ $script = (string) ($fontAttributes['script'] ?? '');
+ if (!empty($script)) {
+ $minorFonts[$script] = (string) ($fontAttributes['typeface'] ?? '');
+ }
+ }
+ $excel->getTheme()->setMajorFontValues($majorLatin, $majorEastAsian, $majorComplexScript, $majorFonts);
+ $excel->getTheme()->setMinorFontValues($minorLatin, $minorEastAsian, $minorComplexScript, $minorFonts);
+
+ break;
+ }
+ }
+
+ $rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
+
+ $propertyReader = new PropertyReader($this->getSecurityScannerOrThrow(), $excel->getProperties());
+ $charts = $chartDetails = [];
+ foreach ($rels->Relationship as $relx) {
+ $rel = self::getAttributes($relx);
+ $relTarget = (string) $rel['Target'];
+ // issue 3553
+ if ($relTarget[0] === '/') {
+ $relTarget = substr($relTarget, 1);
+ }
+ $relType = (string) $rel['Type'];
+ $mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
+ switch ($relType) {
+ case Namespaces::CORE_PROPERTIES:
+ $propertyReader->readCoreProperties($this->getFromZipArchive($zip, $relTarget));
+
+ break;
+ case "$xmlNamespaceBase/extended-properties":
+ $propertyReader->readExtendedProperties($this->getFromZipArchive($zip, $relTarget));
+
+ break;
+ case "$xmlNamespaceBase/custom-properties":
+ $propertyReader->readCustomProperties($this->getFromZipArchive($zip, $relTarget));
+
+ break;
+ //Ribbon
+ case Namespaces::EXTENSIBILITY:
+ $customUI = $relTarget;
+ if ($customUI) {
+ $this->readRibbon($excel, $customUI, $zip);
+ }
+
+ break;
+ case "$xmlNamespaceBase/officeDocument":
+ $dir = dirname($relTarget);
+
+ // Do not specify namespace in next stmt - do it in Xpath
+ $relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', Namespaces::RELATIONSHIPS);
+ $relsWorkbook->registerXPathNamespace('rel', Namespaces::RELATIONSHIPS);
+
+ $worksheets = [];
+ $macros = $customUI = null;
+ foreach ($relsWorkbook->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ switch ($ele['Type']) {
+ case Namespaces::WORKSHEET:
+ case Namespaces::PURL_WORKSHEET:
+ $worksheets[(string) $ele['Id']] = $ele['Target'];
+
+ break;
+ case Namespaces::CHARTSHEET:
+ if ($this->includeCharts === true) {
+ $worksheets[(string) $ele['Id']] = $ele['Target'];
+ }
+
+ break;
+ // a vbaProject ? (: some macros)
+ case Namespaces::VBA:
+ $macros = $ele['Target'];
+
+ break;
+ }
+ }
+
+ if ($macros !== null) {
+ $macrosCode = $this->getFromZipArchive($zip, 'xl/vbaProject.bin'); //vbaProject.bin always in 'xl' dir and always named vbaProject.bin
+ if ($macrosCode !== false) {
+ $excel->setMacrosCode($macrosCode);
+ $excel->setHasMacros(true);
+ //short-circuit : not reading vbaProject.bin.rel to get Signature =>allways vbaProjectSignature.bin in 'xl' dir
+ $Certificate = $this->getFromZipArchive($zip, 'xl/vbaProjectSignature.bin');
+ if ($Certificate !== false) {
+ $excel->setMacrosCertificate($Certificate);
+ }
+ }
+ }
+
+ $relType = "rel:Relationship[@Type='"
+ . "$xmlNamespaceBase/styles"
+ . "']";
+ $xpath = self::getArrayItem(self::xpathNoFalse($relsWorkbook, $relType));
+
+ if ($xpath === null) {
+ $xmlStyles = self::testSimpleXml(null);
+ } else {
+ $xmlStyles = $this->loadZip("$dir/$xpath[Target]", $mainNS);
+ }
+
+ $palette = self::extractPalette($xmlStyles);
+ $this->styleReader->setWorkbookPalette($palette);
+ $fills = self::extractStyles($xmlStyles, 'fills', 'fill');
+ $fonts = self::extractStyles($xmlStyles, 'fonts', 'font');
+ $borders = self::extractStyles($xmlStyles, 'borders', 'border');
+ $xfTags = self::extractStyles($xmlStyles, 'cellXfs', 'xf');
+ $cellXfTags = self::extractStyles($xmlStyles, 'cellStyleXfs', 'xf');
+
+ $styles = [];
+ $cellStyles = [];
+ $numFmts = null;
+ if (/*$xmlStyles && */ $xmlStyles->numFmts[0]) {
+ $numFmts = $xmlStyles->numFmts[0];
+ }
+ if (isset($numFmts) && ($numFmts !== null)) {
+ $numFmts->registerXPathNamespace('sml', $mainNS);
+ }
+ $this->styleReader->setNamespace($mainNS);
+ if (!$this->readDataOnly/* && $xmlStyles*/) {
+ foreach ($xfTags as $xfTag) {
+ $xf = self::getAttributes($xfTag);
+ $numFmt = null;
+
+ if ($xf['numFmtId']) {
+ if (isset($numFmts)) {
+ $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+
+ if (isset($tmpNumFmt['formatCode'])) {
+ $numFmt = (string) $tmpNumFmt['formatCode'];
+ }
+ }
+
+ // We shouldn't override any of the built-in MS Excel values (values below id 164)
+ // But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used
+ // So we make allowance for them rather than lose formatting masks
+ if (
+ $numFmt === null
+ && (int) $xf['numFmtId'] < 164
+ && NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== ''
+ ) {
+ $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
+ }
+ }
+ $quotePrefix = (bool) (string) ($xf['quotePrefix'] ?? '');
+
+ $style = (object) [
+ 'numFmt' => $numFmt ?? NumberFormat::FORMAT_GENERAL,
+ 'font' => $fonts[(int) ($xf['fontId'])],
+ 'fill' => $fills[(int) ($xf['fillId'])],
+ 'border' => $borders[(int) ($xf['borderId'])],
+ 'alignment' => $xfTag->alignment,
+ 'protection' => $xfTag->protection,
+ 'quotePrefix' => $quotePrefix,
+ ];
+ $styles[] = $style;
+
+ // add style to cellXf collection
+ $objStyle = new Style();
+ $this->styleReader->readStyle($objStyle, $style);
+ if ($addingFirstCellXf) {
+ $excel->removeCellXfByIndex(0); // remove the default style
+ $addingFirstCellXf = false;
+ }
+ $excel->addCellXf($objStyle);
+ }
+
+ foreach ($cellXfTags as $xfTag) {
+ $xf = self::getAttributes($xfTag);
+ $numFmt = NumberFormat::FORMAT_GENERAL;
+ if ($numFmts && $xf['numFmtId']) {
+ $tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
+ if (isset($tmpNumFmt['formatCode'])) {
+ $numFmt = (string) $tmpNumFmt['formatCode'];
+ } elseif ((int) $xf['numFmtId'] < 165) {
+ $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
+ }
+ }
+
+ $quotePrefix = (bool) (string) ($xf['quotePrefix'] ?? '');
+
+ $cellStyle = (object) [
+ 'numFmt' => $numFmt,
+ 'font' => $fonts[(int) ($xf['fontId'])],
+ 'fill' => $fills[((int) $xf['fillId'])],
+ 'border' => $borders[(int) ($xf['borderId'])],
+ 'alignment' => $xfTag->alignment,
+ 'protection' => $xfTag->protection,
+ 'quotePrefix' => $quotePrefix,
+ ];
+ $cellStyles[] = $cellStyle;
+
+ // add style to cellStyleXf collection
+ $objStyle = new Style();
+ $this->styleReader->readStyle($objStyle, $cellStyle);
+ if ($addingFirstCellStyleXf) {
+ $excel->removeCellStyleXfByIndex(0); // remove the default style
+ $addingFirstCellStyleXf = false;
+ }
+ $excel->addCellStyleXf($objStyle);
+ }
+ }
+ $this->styleReader->setStyleXml($xmlStyles);
+ $this->styleReader->setNamespace($mainNS);
+ $this->styleReader->setStyleBaseData($theme, $styles, $cellStyles);
+ $dxfs = $this->styleReader->dxfs($this->readDataOnly);
+ $styles = $this->styleReader->styles();
+
+ // Read content after setting the styles
+ $sharedStrings = [];
+ $relType = "rel:Relationship[@Type='"
+ //. Namespaces::SHARED_STRINGS
+ . "$xmlNamespaceBase/sharedStrings"
+ . "']";
+ $xpath = self::getArrayItem($relsWorkbook->xpath($relType));
+
+ if ($xpath) {
+ $xmlStrings = $this->loadZip("$dir/$xpath[Target]", $mainNS);
+ if (isset($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);
+ }
+ }
+ }
+ }
+
+ $xmlWorkbook = $this->loadZipNoNamespace($relTarget, $mainNS);
+ $xmlWorkbookNS = $this->loadZip($relTarget, $mainNS);
+
+ // Set base date
+ if ($xmlWorkbookNS->workbookPr) {
+ Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
+ $attrs1904 = self::getAttributes($xmlWorkbookNS->workbookPr);
+ if (isset($attrs1904['date1904'])) {
+ if (self::boolean((string) $attrs1904['date1904'])) {
+ Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
+ }
+ }
+ }
+
+ // Set protection
+ $this->readProtection($excel, $xmlWorkbook);
+
+ $sheetId = 0; // keep track of new sheet id in final workbook
+ $oldSheetId = -1; // keep track of old sheet id in final workbook
+ $countSkippedSheets = 0; // keep track of number of skipped sheets
+ $mapSheetId = []; // mapping of sheet ids from old to new
+
+ $charts = $chartDetails = [];
+
+ if ($xmlWorkbookNS->sheets) {
+ /** @var SimpleXMLElement $eleSheet */
+ foreach ($xmlWorkbookNS->sheets->sheet as $eleSheet) {
+ $eleSheetAttr = self::getAttributes($eleSheet);
+ ++$oldSheetId;
+
+ // Check if sheet should be skipped
+ if (is_array($this->loadSheetsOnly) && !in_array((string) $eleSheetAttr['name'], $this->loadSheetsOnly)) {
+ ++$countSkippedSheets;
+ $mapSheetId[$oldSheetId] = null;
+
+ continue;
+ }
+
+ $sheetReferenceId = (string) self::getArrayItem(self::getAttributes($eleSheet, $xmlNamespaceBase), 'id');
+ if (isset($worksheets[$sheetReferenceId]) === false) {
+ ++$countSkippedSheets;
+ $mapSheetId[$oldSheetId] = null;
+
+ continue;
+ }
+ // Map old sheet id in original workbook to new sheet id.
+ // They will differ if loadSheetsOnly() is being used
+ $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
+
+ // Load sheet
+ $docSheet = $excel->createSheet();
+ // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet
+ // references in formula cells... during the load, all formulae should be correct,
+ // and we're simply bringing the worksheet name in line with the formula, not the
+ // reverse
+ $docSheet->setTitle((string) $eleSheetAttr['name'], false, false);
+
+ $fileWorksheet = (string) $worksheets[$sheetReferenceId];
+ // issue 3665 adds test for /.
+ // This broke XlsxRootZipFilesTest,
+ // but Excel reports an error with that file.
+ // Testing dir for . avoids this problem.
+ // It might be better just to drop the test.
+ if ($fileWorksheet[0] == '/' && $dir !== '.') {
+ $fileWorksheet = substr($fileWorksheet, strlen($dir) + 2);
+ }
+ $xmlSheet = $this->loadZipNoNamespace("$dir/$fileWorksheet", $mainNS);
+ $xmlSheetNS = $this->loadZip("$dir/$fileWorksheet", $mainNS);
+
+ // Shared Formula table is unique to each Worksheet, so we need to reset it here
+ $this->sharedFormulae = [];
+
+ if (isset($eleSheetAttr['state']) && (string) $eleSheetAttr['state'] != '') {
+ $docSheet->setSheetState((string) $eleSheetAttr['state']);
+ }
+ if ($xmlSheetNS) {
+ $xmlSheetMain = $xmlSheetNS->children($mainNS);
+ // Setting Conditional Styles adjusts selected cells, so we need to execute this
+ // before reading the sheet view data to get the actual selected cells
+ if (!$this->readDataOnly && ($xmlSheet->conditionalFormatting)) {
+ (new ConditionalStyles($docSheet, $xmlSheet, $dxfs, $this->styleReader))->load();
+ }
+ if (!$this->readDataOnly && $xmlSheet->extLst) {
+ (new ConditionalStyles($docSheet, $xmlSheet, $dxfs, $this->styleReader))->loadFromExt();
+ }
+ if (isset($xmlSheetMain->sheetViews, $xmlSheetMain->sheetViews->sheetView)) {
+ $sheetViews = new SheetViews($xmlSheetMain->sheetViews->sheetView, $docSheet);
+ $sheetViews->load();
+ }
+
+ $sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheetNS);
+ $sheetViewOptions->load($this->getReadDataOnly(), $this->styleReader);
+
+ (new ColumnAndRowAttributes($docSheet, $xmlSheetNS))
+ ->load($this->getReadFilter(), $this->getReadDataOnly());
+ }
+
+ $holdSelectedCells = $docSheet->getSelectedCells();
+ if ($xmlSheetNS && $xmlSheetNS->sheetData && $xmlSheetNS->sheetData->row) {
+ $cIndex = 1; // Cell Start from 1
+ foreach ($xmlSheetNS->sheetData->row as $row) {
+ $rowIndex = 1;
+ foreach ($row->c as $c) {
+ $cAttr = self::getAttributes($c);
+ $r = (string) $cAttr['r'];
+ if ($r == '') {
+ $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex;
+ }
+ $cellDataType = (string) $cAttr['t'];
+ $originalCellDataTypeNumeric = $cellDataType === '';
+ $value = null;
+ $calculatedValue = null;
+
+ // Read cell?
+ if ($this->getReadFilter() !== null) {
+ $coordinates = Coordinate::coordinateFromString($r);
+
+ if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) {
+ // Normally, just testing for the f attribute should identify this cell as containing a formula
+ // that we need to read, even though it is outside of the filter range, in case it is a shared formula.
+ // But in some cases, this attribute isn't set; so we need to delve a level deeper and look at
+ // whether or not the cell has a child formula element that is shared.
+ if (isset($cAttr->f) || (isset($c->f, $c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')) {
+ $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError', false);
+ }
+ ++$rowIndex;
+
+ continue;
+ }
+ }
+
+ // Read cell!
+ switch ($cellDataType) {
+ case 's':
+ if ((string) $c->v != '') {
+ $value = $sharedStrings[(int) ($c->v)];
+
+ if ($value instanceof RichText) {
+ $value = clone $value;
+ }
+ } else {
+ $value = '';
+ }
+
+ break;
+ case 'b':
+ if (!isset($c->f)) {
+ if (isset($c->v)) {
+ $value = self::castToBoolean($c);
+ } else {
+ $value = null;
+ $cellDataType = DATATYPE::TYPE_NULL;
+ }
+ } else {
+ // Formula
+ $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToBoolean');
+ if (isset($c->f['t'])) {
+ $att = $c->f;
+ $docSheet->getCell($r)->setFormulaAttributes($att);
+ }
+ }
+
+ break;
+ case 'inlineStr':
+ if (isset($c->f)) {
+ $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError');
+ } else {
+ $value = $this->parseRichText($c->is);
+ }
+
+ break;
+ case 'e':
+ if (!isset($c->f)) {
+ $value = self::castToError($c);
+ } else {
+ // Formula
+ $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError');
+ }
+
+ break;
+ default:
+ if (!isset($c->f)) {
+ $value = self::castToString($c);
+ } else {
+ // Formula
+ $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString');
+ if (isset($c->f['t'])) {
+ $attributes = $c->f['t'];
+ $docSheet->getCell($r)->setFormulaAttributes(['t' => (string) $attributes]);
+ }
+ }
+
+ break;
+ }
+
+ // read empty cells or the cells are not empty
+ if ($this->readEmptyCells || ($value !== null && $value !== '')) {
+ // Rich text?
+ if ($value instanceof RichText && $this->readDataOnly) {
+ $value = $value->getPlainText();
+ }
+
+ $cell = $docSheet->getCell($r);
+ // Assign value
+ if ($cellDataType != '') {
+ // it is possible, that datatype is numeric but with an empty string, which result in an error
+ if ($cellDataType === DataType::TYPE_NUMERIC && ($value === '' || $value === null)) {
+ $cellDataType = DataType::TYPE_NULL;
+ }
+ if ($cellDataType !== DataType::TYPE_NULL) {
+ $cell->setValueExplicit($value, $cellDataType);
+ }
+ } else {
+ $cell->setValue($value);
+ }
+ if ($calculatedValue !== null) {
+ $cell->setCalculatedValue($calculatedValue, $originalCellDataTypeNumeric);
+ }
+
+ // Style information?
+ if (!$this->readDataOnly) {
+ $holdSelected = $docSheet->getSelectedCells();
+ $cAttrS = (int) ($cAttr['s'] ?? 0);
+ // no style index means 0, it seems
+ $cAttrS = isset($styles[$cAttrS]) ? $cAttrS : 0;
+ $cell->setXfIndex($cAttrS);
+ // issue 3495
+ if ($cellDataType === DataType::TYPE_FORMULA && $styles[$cAttrS]->quotePrefix === true) {
+ $cell->getStyle()->setQuotePrefix(false);
+ }
+ $docSheet->setSelectedCells($holdSelected);
+ }
+ }
+ ++$rowIndex;
+ }
+ ++$cIndex;
+ }
+ }
+ $docSheet->setSelectedCells($holdSelectedCells);
+ if ($xmlSheetNS && $xmlSheetNS->ignoredErrors) {
+ foreach ($xmlSheetNS->ignoredErrors->ignoredError as $ignoredErrorx) {
+ $ignoredError = self::testSimpleXml($ignoredErrorx);
+ $this->processIgnoredErrors($ignoredError, $docSheet);
+ }
+ }
+
+ if (!$this->readDataOnly && $xmlSheetNS && $xmlSheetNS->sheetProtection) {
+ $protAttr = $xmlSheetNS->sheetProtection->attributes() ?? [];
+ foreach ($protAttr as $key => $value) {
+ $method = 'set' . ucfirst($key);
+ $docSheet->getProtection()->$method(self::boolean((string) $value));
+ }
+ }
+
+ if ($xmlSheet) {
+ $this->readSheetProtection($docSheet, $xmlSheet);
+ }
+
+ if ($this->readDataOnly === false) {
+ $this->readAutoFilter($xmlSheetNS, $docSheet);
+ $this->readBackgroundImage($xmlSheetNS, $docSheet, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels');
+ }
+
+ $this->readTables($xmlSheetNS, $docSheet, $dir, $fileWorksheet, $zip, $mainNS);
+
+ if ($xmlSheetNS && $xmlSheetNS->mergeCells && $xmlSheetNS->mergeCells->mergeCell && !$this->readDataOnly) {
+ foreach ($xmlSheetNS->mergeCells->mergeCell as $mergeCellx) {
+ $mergeCell = $mergeCellx->attributes();
+ $mergeRef = (string) ($mergeCell['ref'] ?? '');
+ if (str_contains($mergeRef, ':')) {
+ $docSheet->mergeCells($mergeRef, Worksheet::MERGE_CELL_CONTENT_HIDE);
+ }
+ }
+ }
+
+ if ($xmlSheet && !$this->readDataOnly) {
+ $unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
+ }
+
+ if ($xmlSheet !== false && isset($xmlSheet->extLst->ext)) {
+ foreach ($xmlSheet->extLst->ext as $extlst) {
+ $extAttrs = $extlst->attributes() ?? [];
+ $extUri = (string) ($extAttrs['uri'] ?? '');
+ if ($extUri !== '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}') {
+ continue;
+ }
+ // Create dataValidations node if does not exists, maybe is better inside the foreach ?
+ if (!$xmlSheet->dataValidations) {
+ $xmlSheet->addChild('dataValidations');
+ }
+
+ foreach ($extlst->children(Namespaces::DATA_VALIDATIONS1)->dataValidations->dataValidation as $item) {
+ $item = self::testSimpleXml($item);
+ $node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation');
+ foreach ($item->attributes() ?? [] as $attr) {
+ $node->addAttribute($attr->getName(), $attr);
+ }
+ $node->addAttribute('sqref', $item->children(Namespaces::DATA_VALIDATIONS2)->sqref);
+ if (isset($item->formula1)) {
+ $childNode = $node->addChild('formula1');
+ if ($childNode !== null) { // null should never happen
+ $childNode[0] = (string) $item->formula1->children(Namespaces::DATA_VALIDATIONS2)->f; // @phpstan-ignore-line
+ }
+ }
+ }
+ }
+ }
+
+ if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
+ (new DataValidations($docSheet, $xmlSheet))->load();
+ }
+
+ // unparsed sheet AlternateContent
+ if ($xmlSheet && !$this->readDataOnly) {
+ $mc = $xmlSheet->children(Namespaces::COMPATIBILITY);
+ if ($mc->AlternateContent) {
+ foreach ($mc->AlternateContent as $alternateContent) {
+ $alternateContent = self::testSimpleXml($alternateContent);
+ $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['AlternateContents'][] = $alternateContent->asXML();
+ }
+ }
+ }
+
+ // Add hyperlinks
+ if (!$this->readDataOnly) {
+ $hyperlinkReader = new Hyperlinks($docSheet);
+ // Locate hyperlink relations
+ $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+ if ($zip->locateName($relationsFileName) !== false) {
+ $relsWorksheet = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS);
+ $hyperlinkReader->readHyperlinks($relsWorksheet);
+ }
+
+ // Loop through hyperlinks
+ if ($xmlSheetNS && $xmlSheetNS->children($mainNS)->hyperlinks) {
+ $hyperlinkReader->setHyperlinks($xmlSheetNS->children($mainNS)->hyperlinks);
+ }
+ }
+
+ // Add comments
+ $comments = [];
+ $vmlComments = [];
+ if (!$this->readDataOnly) {
+ // Locate comment relations
+ $commentRelations = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+ if ($zip->locateName($commentRelations) !== false) {
+ $relsWorksheet = $this->loadZip($commentRelations, Namespaces::RELATIONSHIPS);
+ foreach ($relsWorksheet->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ if ($ele['Type'] == Namespaces::COMMENTS) {
+ $comments[(string) $ele['Id']] = (string) $ele['Target'];
+ }
+ if ($ele['Type'] == Namespaces::VML) {
+ $vmlComments[(string) $ele['Id']] = (string) $ele['Target'];
+ }
+ }
+ }
+
+ // Loop through comments
+ foreach ($comments as $relName => $relPath) {
+ // Load comments file
+ $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
+ // okay to ignore namespace - using xpath
+ $commentsFile = $this->loadZip($relPath, '');
+
+ // Utility variables
+ $authors = [];
+ $commentsFile->registerXpathNamespace('com', $mainNS);
+ $authorPath = self::xpathNoFalse($commentsFile, 'com:authors/com:author');
+ foreach ($authorPath as $author) {
+ $authors[] = (string) $author;
+ }
+
+ // Loop through contents
+ $contentPath = self::xpathNoFalse($commentsFile, 'com:commentList/com:comment');
+ foreach ($contentPath as $comment) {
+ $commentx = $comment->attributes();
+ $commentModel = $docSheet->getComment((string) $commentx['ref']);
+ if (isset($commentx['authorId'])) {
+ $commentModel->setAuthor($authors[(int) $commentx['authorId']]);
+ }
+ $commentModel->setText($this->parseRichText($comment->children($mainNS)->text));
+ }
+ }
+
+ // later we will remove from it real vmlComments
+ $unparsedVmlDrawings = $vmlComments;
+ $vmlDrawingContents = [];
+
+ // Loop through VML comments
+ foreach ($vmlComments as $relName => $relPath) {
+ // Load VML comments file
+ $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
+
+ try {
+ // no namespace okay - processed with Xpath
+ $vmlCommentsFile = $this->loadZip($relPath, '', true);
+ $vmlCommentsFile->registerXPathNamespace('v', Namespaces::URN_VML);
+ } catch (Throwable) {
+ //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
+ continue;
+ }
+
+ // Locate VML drawings image relations
+ $drowingImages = [];
+ $VMLDrawingsRelations = dirname($relPath) . '/_rels/' . basename($relPath) . '.rels';
+ $vmlDrawingContents[$relName] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $relPath));
+ if ($zip->locateName($VMLDrawingsRelations) !== false) {
+ $relsVMLDrawing = $this->loadZip($VMLDrawingsRelations, Namespaces::RELATIONSHIPS);
+ foreach ($relsVMLDrawing->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ if ($ele['Type'] == Namespaces::IMAGE) {
+ $drowingImages[(string) $ele['Id']] = (string) $ele['Target'];
+ }
+ }
+ }
+
+ $shapes = self::xpathNoFalse($vmlCommentsFile, '//v:shape');
+ foreach ($shapes as $shape) {
+ $shape->registerXPathNamespace('v', Namespaces::URN_VML);
+
+ if (isset($shape['style'])) {
+ $style = (string) $shape['style'];
+ $fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
+ $column = null;
+ $row = null;
+ $fillImageRelId = null;
+ $fillImageTitle = '';
+
+ $clientData = $shape->xpath('.//x:ClientData');
+ if (is_array($clientData) && !empty($clientData)) {
+ $clientData = $clientData[0];
+
+ if (isset($clientData['ObjectType']) && (string) $clientData['ObjectType'] == 'Note') {
+ $temp = $clientData->xpath('.//x:Row');
+ if (is_array($temp)) {
+ $row = $temp[0];
+ }
+
+ $temp = $clientData->xpath('.//x:Column');
+ if (is_array($temp)) {
+ $column = $temp[0];
+ }
+ }
+ }
+
+ $fillImageRelNode = $shape->xpath('.//v:fill/@o:relid');
+ if (is_array($fillImageRelNode) && !empty($fillImageRelNode)) {
+ $fillImageRelNode = $fillImageRelNode[0];
+
+ if (isset($fillImageRelNode['relid'])) {
+ $fillImageRelId = (string) $fillImageRelNode['relid'];
+ }
+ }
+
+ $fillImageTitleNode = $shape->xpath('.//v:fill/@o:title');
+ if (is_array($fillImageTitleNode) && !empty($fillImageTitleNode)) {
+ $fillImageTitleNode = $fillImageTitleNode[0];
+
+ if (isset($fillImageTitleNode['title'])) {
+ $fillImageTitle = (string) $fillImageTitleNode['title'];
+ }
+ }
+
+ if (($column !== null) && ($row !== null)) {
+ // Set comment properties
+ $comment = $docSheet->getComment([$column + 1, $row + 1]);
+ $comment->getFillColor()->setRGB($fillColor);
+ if (isset($drowingImages[$fillImageRelId])) {
+ $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+ $objDrawing->setName($fillImageTitle);
+ $imagePath = str_replace(['../', '/xl/'], 'xl/', $drowingImages[$fillImageRelId]);
+ $objDrawing->setPath(
+ 'zip://' . File::realpath($filename) . '#' . $imagePath,
+ true,
+ $zip
+ );
+ $comment->setBackgroundImage($objDrawing);
+ }
+
+ // Parse style
+ $styleArray = explode(';', str_replace(' ', '', $style));
+ foreach ($styleArray as $stylePair) {
+ $stylePair = explode(':', $stylePair);
+
+ if ($stylePair[0] == 'margin-left') {
+ $comment->setMarginLeft($stylePair[1]);
+ }
+ if ($stylePair[0] == 'margin-top') {
+ $comment->setMarginTop($stylePair[1]);
+ }
+ if ($stylePair[0] == 'width') {
+ $comment->setWidth($stylePair[1]);
+ }
+ if ($stylePair[0] == 'height') {
+ $comment->setHeight($stylePair[1]);
+ }
+ if ($stylePair[0] == 'visibility') {
+ $comment->setVisible($stylePair[1] == 'visible');
+ }
+ }
+
+ unset($unparsedVmlDrawings[$relName]);
+ }
+ }
+ }
+ }
+
+ // unparsed vmlDrawing
+ if ($unparsedVmlDrawings) {
+ foreach ($unparsedVmlDrawings as $rId => $relPath) {
+ $rId = substr($rId, 3); // rIdXXX
+ $unparsedVmlDrawing = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['vmlDrawings'];
+ $unparsedVmlDrawing[$rId] = [];
+ $unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath);
+ $unparsedVmlDrawing[$rId]['relFilePath'] = $relPath;
+ $unparsedVmlDrawing[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
+ unset($unparsedVmlDrawing);
+ }
+ }
+
+ // Header/footer images
+ if ($xmlSheetNS && $xmlSheetNS->legacyDrawingHF) {
+ $vmlHfRid = '';
+ $vmlHfRidAttr = $xmlSheetNS->legacyDrawingHF->attributes(Namespaces::SCHEMA_OFFICE_DOCUMENT);
+ if ($vmlHfRidAttr !== null && isset($vmlHfRidAttr['id'])) {
+ $vmlHfRid = (string) $vmlHfRidAttr['id'][0];
+ }
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') !== false) {
+ $relsWorksheet = $this->loadZipNoNamespace(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels', Namespaces::RELATIONSHIPS);
+ $vmlRelationship = '';
+
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ((string) $ele['Type'] == Namespaces::VML && (string) $ele['Id'] === $vmlHfRid) {
+ $vmlRelationship = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
+
+ break;
+ }
+ }
+
+ if ($vmlRelationship != '') {
+ // Fetch linked images
+ $relsVML = $this->loadZipNoNamespace(dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels', Namespaces::RELATIONSHIPS);
+ $drawings = [];
+ if (isset($relsVML->Relationship)) {
+ foreach ($relsVML->Relationship as $ele) {
+ if ($ele['Type'] == Namespaces::IMAGE) {
+ $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']);
+ }
+ }
+ }
+ // Fetch VML document
+ $vmlDrawing = $this->loadZipNoNamespace($vmlRelationship, '');
+ $vmlDrawing->registerXPathNamespace('v', Namespaces::URN_VML);
+
+ $hfImages = [];
+
+ $shapes = self::xpathNoFalse($vmlDrawing, '//v:shape');
+ foreach ($shapes as $idx => $shape) {
+ $shape->registerXPathNamespace('v', Namespaces::URN_VML);
+ $imageData = $shape->xpath('//v:imagedata');
+
+ if (empty($imageData)) {
+ continue;
+ }
+
+ $imageData = $imageData[$idx];
+
+ $imageData = self::getAttributes($imageData, Namespaces::URN_MSOFFICE);
+ $style = self::toCSSArray((string) $shape['style']);
+
+ if (array_key_exists((string) $imageData['relid'], $drawings)) {
+ $shapeId = (string) $shape['id'];
+ $hfImages[$shapeId] = new HeaderFooterDrawing();
+ if (isset($imageData['title'])) {
+ $hfImages[$shapeId]->setName((string) $imageData['title']);
+ }
+
+ $hfImages[$shapeId]->setPath('zip://' . File::realpath($filename) . '#' . $drawings[(string) $imageData['relid']], false);
+ $hfImages[$shapeId]->setResizeProportional(false);
+ $hfImages[$shapeId]->setWidth($style['width']);
+ $hfImages[$shapeId]->setHeight($style['height']);
+ if (isset($style['margin-left'])) {
+ $hfImages[$shapeId]->setOffsetX($style['margin-left']);
+ }
+ $hfImages[$shapeId]->setOffsetY($style['margin-top']);
+ $hfImages[$shapeId]->setResizeProportional(true);
+ }
+ }
+
+ $docSheet->getHeaderFooter()->setImages($hfImages);
+ }
+ }
+ }
+ }
+
+ // TODO: Autoshapes from twoCellAnchors!
+ $drawingFilename = dirname("$dir/$fileWorksheet")
+ . '/_rels/'
+ . basename($fileWorksheet)
+ . '.rels';
+ if (str_starts_with($drawingFilename, 'xl//xl/')) {
+ $drawingFilename = substr($drawingFilename, 4);
+ }
+ if (str_starts_with($drawingFilename, '/xl//xl/')) {
+ $drawingFilename = substr($drawingFilename, 5);
+ }
+ if ($zip->locateName($drawingFilename) !== false) {
+ $relsWorksheet = $this->loadZip($drawingFilename, Namespaces::RELATIONSHIPS);
+ $drawings = [];
+ foreach ($relsWorksheet->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
+ $eleTarget = (string) $ele['Target'];
+ if (str_starts_with($eleTarget, '/xl/')) {
+ $drawings[(string) $ele['Id']] = substr($eleTarget, 1);
+ } else {
+ $drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
+ }
+ }
+ }
+
+ if ($xmlSheetNS->drawing && !$this->readDataOnly) {
+ $unparsedDrawings = [];
+ $fileDrawing = null;
+ foreach ($xmlSheetNS->drawing as $drawing) {
+ $drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
+ $fileDrawing = $drawings[$drawingRelId];
+ $drawingFilename = dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels';
+ $relsDrawing = $this->loadZip($drawingFilename, Namespaces::RELATIONSHIPS);
+
+ $images = [];
+ $hyperlinks = [];
+ if ($relsDrawing && $relsDrawing->Relationship) {
+ foreach ($relsDrawing->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ $eleType = (string) $ele['Type'];
+ if ($eleType === Namespaces::HYPERLINK) {
+ $hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
+ }
+ if ($eleType === "$xmlNamespaceBase/image") {
+ $eleTarget = (string) $ele['Target'];
+ if (str_starts_with($eleTarget, '/xl/')) {
+ $eleTarget = substr($eleTarget, 1);
+ $images[(string) $ele['Id']] = $eleTarget;
+ } else {
+ $images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $eleTarget);
+ }
+ } elseif ($eleType === "$xmlNamespaceBase/chart") {
+ if ($this->includeCharts) {
+ $eleTarget = (string) $ele['Target'];
+ if (str_starts_with($eleTarget, '/xl/')) {
+ $index = substr($eleTarget, 1);
+ } else {
+ $index = self::dirAdd($fileDrawing, $eleTarget);
+ }
+ $charts[$index] = [
+ 'id' => (string) $ele['Id'],
+ 'sheet' => $docSheet->getTitle(),
+ ];
+ }
+ }
+ }
+ }
+
+ $xmlDrawing = $this->loadZipNoNamespace($fileDrawing, '');
+ $xmlDrawingChildren = $xmlDrawing->children(Namespaces::SPREADSHEET_DRAWING);
+
+ if ($xmlDrawingChildren->oneCellAnchor) {
+ foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
+ $oneCellAnchor = self::testSimpleXml($oneCellAnchor);
+ if ($oneCellAnchor->pic->blipFill) {
+ /** @var SimpleXMLElement $blip */
+ $blip = $oneCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
+ /** @var SimpleXMLElement $xfrm */
+ $xfrm = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
+ /** @var SimpleXMLElement $outerShdw */
+ $outerShdw = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
+
+ $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+ $objDrawing->setName((string) self::getArrayItem(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'name'));
+ $objDrawing->setDescription((string) self::getArrayItem(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
+ $embedImageKey = (string) self::getArrayItem(
+ self::getAttributes($blip, $xmlNamespaceBase),
+ 'embed'
+ );
+ if (isset($images[$embedImageKey])) {
+ $objDrawing->setPath(
+ 'zip://' . File::realpath($filename) . '#'
+ . $images[$embedImageKey],
+ false
+ );
+ } else {
+ $linkImageKey = (string) self::getArrayItem(
+ $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
+ 'link'
+ );
+ if (isset($images[$linkImageKey])) {
+ $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
+ $objDrawing->setPath($url);
+ }
+ }
+ $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((int) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1));
+
+ $objDrawing->setOffsetX((int) Drawing::EMUToPixels($oneCellAnchor->from->colOff));
+ $objDrawing->setOffsetY(Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
+ $objDrawing->setResizeProportional(false);
+ $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cx')));
+ $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cy')));
+ if ($xfrm) {
+ $objDrawing->setRotation((int) Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($xfrm), 'rot')));
+ $objDrawing->setFlipVertical((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipV'));
+ $objDrawing->setFlipHorizontal((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipH'));
+ }
+ if ($outerShdw) {
+ $shadow = $objDrawing->getShadow();
+ $shadow->setVisible(true);
+ $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'blurRad')));
+ $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'dist')));
+ $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($outerShdw), 'dir')));
+ $shadow->setAlignment((string) self::getArrayItem(self::getAttributes($outerShdw), 'algn'));
+ $clr = $outerShdw->srgbClr ?? $outerShdw->prstClr;
+ $shadow->getColor()->setRGB(self::getArrayItem(self::getAttributes($clr), 'val'));
+ $shadow->setAlpha(self::getArrayItem(self::getAttributes($clr->alpha), 'val') / 1000);
+ }
+
+ $this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);
+
+ $objDrawing->setWorksheet($docSheet);
+ } elseif ($this->includeCharts && $oneCellAnchor->graphicFrame) {
+ // Exported XLSX from Google Sheets positions charts with a oneCellAnchor
+ $coordinates = Coordinate::stringFromColumnIndex(((int) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1);
+ $offsetX = Drawing::EMUToPixels($oneCellAnchor->from->colOff);
+ $offsetY = Drawing::EMUToPixels($oneCellAnchor->from->rowOff);
+ $width = Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cx'));
+ $height = Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cy'));
+
+ $graphic = $oneCellAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
+ /** @var SimpleXMLElement $chartRef */
+ $chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
+ $thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
+
+ $chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
+ 'fromCoordinate' => $coordinates,
+ 'fromOffsetX' => $offsetX,
+ 'fromOffsetY' => $offsetY,
+ 'width' => $width,
+ 'height' => $height,
+ 'worksheetTitle' => $docSheet->getTitle(),
+ 'oneCellAnchor' => true,
+ ];
+ }
+ }
+ }
+ if ($xmlDrawingChildren->twoCellAnchor) {
+ foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
+ $twoCellAnchor = self::testSimpleXml($twoCellAnchor);
+ if ($twoCellAnchor->pic->blipFill) {
+ $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+ $blip = $twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
+ if (isset($twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->srcRect)) {
+ $objDrawing->setSrcRect($twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->srcRect->attributes());
+ }
+ $xfrm = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
+ $outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
+ $editAs = $twoCellAnchor->attributes();
+ if (isset($editAs, $editAs['editAs'])) {
+ $objDrawing->setEditAs($editAs['editAs']);
+ }
+ $objDrawing->setName((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'name'));
+ $objDrawing->setDescription((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
+ $embedImageKey = (string) self::getArrayItem(
+ self::getAttributes($blip, $xmlNamespaceBase),
+ 'embed'
+ );
+ if (isset($images[$embedImageKey])) {
+ $objDrawing->setPath(
+ 'zip://' . File::realpath($filename) . '#'
+ . $images[$embedImageKey],
+ false
+ );
+ } else {
+ $linkImageKey = (string) self::getArrayItem(
+ $blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
+ 'link'
+ );
+ if (isset($images[$linkImageKey])) {
+ $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
+ $objDrawing->setPath($url);
+ }
+ }
+ $objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1));
+
+ $objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
+ $objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
+
+ $objDrawing->setCoordinates2(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1));
+
+ $objDrawing->setOffsetX2(Drawing::EMUToPixels($twoCellAnchor->to->colOff));
+ $objDrawing->setOffsetY2(Drawing::EMUToPixels($twoCellAnchor->to->rowOff));
+
+ $objDrawing->setResizeProportional(false);
+
+ if ($xfrm) {
+ $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($xfrm->ext), 'cx')));
+ $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($xfrm->ext), 'cy')));
+ $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($xfrm), 'rot')));
+ $objDrawing->setFlipVertical((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipV'));
+ $objDrawing->setFlipHorizontal((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipH'));
+ }
+ if ($outerShdw) {
+ $shadow = $objDrawing->getShadow();
+ $shadow->setVisible(true);
+ $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'blurRad')));
+ $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'dist')));
+ $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($outerShdw), 'dir')));
+ $shadow->setAlignment((string) self::getArrayItem(self::getAttributes($outerShdw), 'algn'));
+ $clr = $outerShdw->srgbClr ?? $outerShdw->prstClr;
+ $shadow->getColor()->setRGB(self::getArrayItem(self::getAttributes($clr), 'val'));
+ $shadow->setAlpha(self::getArrayItem(self::getAttributes($clr->alpha), 'val') / 1000);
+ }
+
+ $this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);
+
+ $objDrawing->setWorksheet($docSheet);
+ } elseif (($this->includeCharts) && ($twoCellAnchor->graphicFrame)) {
+ $fromCoordinate = Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1);
+ $fromOffsetX = Drawing::EMUToPixels($twoCellAnchor->from->colOff);
+ $fromOffsetY = Drawing::EMUToPixels($twoCellAnchor->from->rowOff);
+ $toCoordinate = Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1);
+ $toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
+ $toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
+ $graphic = $twoCellAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
+ /** @var SimpleXMLElement $chartRef */
+ $chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
+ $thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
+
+ $chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
+ 'fromCoordinate' => $fromCoordinate,
+ 'fromOffsetX' => $fromOffsetX,
+ 'fromOffsetY' => $fromOffsetY,
+ 'toCoordinate' => $toCoordinate,
+ 'toOffsetX' => $toOffsetX,
+ 'toOffsetY' => $toOffsetY,
+ 'worksheetTitle' => $docSheet->getTitle(),
+ ];
+ }
+ }
+ }
+ if ($xmlDrawingChildren->absoluteAnchor) {
+ foreach ($xmlDrawingChildren->absoluteAnchor as $absoluteAnchor) {
+ if (($this->includeCharts) && ($absoluteAnchor->graphicFrame)) {
+ $graphic = $absoluteAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
+ /** @var SimpleXMLElement $chartRef */
+ $chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
+ $thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
+ $width = Drawing::EMUToPixels((int) self::getArrayItem(self::getAttributes($absoluteAnchor->ext), 'cx')[0]);
+ $height = Drawing::EMUToPixels((int) self::getArrayItem(self::getAttributes($absoluteAnchor->ext), 'cy')[0]);
+
+ $chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
+ 'fromCoordinate' => 'A1',
+ 'fromOffsetX' => 0,
+ 'fromOffsetY' => 0,
+ 'width' => $width,
+ 'height' => $height,
+ 'worksheetTitle' => $docSheet->getTitle(),
+ ];
+ }
+ }
+ }
+ if (empty($relsDrawing) && $xmlDrawing->count() == 0) {
+ // Save Drawing without rels and children as unparsed
+ $unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
+ }
+ }
+
+ // store original rId of drawing files
+ $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
+ foreach ($relsWorksheet->Relationship as $elex) {
+ $ele = self::getAttributes($elex);
+ if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
+ $drawingRelId = (string) $ele['Id'];
+ $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
+ if (isset($unparsedDrawings[$drawingRelId])) {
+ $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['Drawings'][$drawingRelId] = $unparsedDrawings[$drawingRelId];
+ }
+ }
+ }
+ if ($xmlSheet->legacyDrawing && !$this->readDataOnly) {
+ foreach ($xmlSheet->legacyDrawing as $drawing) {
+ $drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
+ if (isset($vmlDrawingContents[$drawingRelId])) {
+ $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId];
+ }
+ }
+ }
+
+ // unparsed drawing AlternateContent
+ $xmlAltDrawing = $this->loadZip((string) $fileDrawing, Namespaces::COMPATIBILITY);
+
+ if ($xmlAltDrawing->AlternateContent) {
+ foreach ($xmlAltDrawing->AlternateContent as $alternateContent) {
+ $alternateContent = self::testSimpleXml($alternateContent);
+ $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingAlternateContents'][] = $alternateContent->asXML();
+ }
+ }
+ }
+ }
+
+ $this->readFormControlProperties($excel, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
+ $this->readPrinterSettings($excel, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
+
+ // Loop through definedNames
+ if ($xmlWorkbook->definedNames) {
+ foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+ // Extract range
+ $extractedRange = (string) $definedName;
+ if (($spos = strpos($extractedRange, '!')) !== false) {
+ $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
+ } else {
+ $extractedRange = str_replace('$', '', $extractedRange);
+ }
+
+ // Valid range?
+ if ($extractedRange == '') {
+ continue;
+ }
+
+ // Some definedNames are only applicable if we are on the same sheet...
+ if ((string) $definedName['localSheetId'] != '' && (string) $definedName['localSheetId'] == $oldSheetId) {
+ // Switch on type
+ switch ((string) $definedName['name']) {
+ case '_xlnm._FilterDatabase':
+ if ((string) $definedName['hidden'] !== '1') {
+ $extractedRange = explode(',', $extractedRange);
+ foreach ($extractedRange as $range) {
+ $autoFilterRange = $range;
+ if (str_contains($autoFilterRange, ':')) {
+ $docSheet->getAutoFilter()->setRange($autoFilterRange);
+ }
+ }
+ }
+
+ break;
+ case '_xlnm.Print_Titles':
+ // Split $extractedRange
+ $extractedRange = explode(',', $extractedRange);
+
+ // Set print titles
+ foreach ($extractedRange as $range) {
+ $matches = [];
+ $range = str_replace('$', '', $range);
+
+ // check for repeating columns, e g. 'A:A' or 'A:D'
+ if (preg_match('/!?([A-Z]+)\:([A-Z]+)$/', $range, $matches)) {
+ $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$matches[1], $matches[2]]);
+ } elseif (preg_match('/!?(\d+)\:(\d+)$/', $range, $matches)) {
+ // check for repeating rows, e.g. '1:1' or '1:5'
+ $docSheet->getPageSetup()->setRowsToRepeatAtTop([$matches[1], $matches[2]]);
+ }
+ }
+
+ break;
+ case '_xlnm.Print_Area':
+ $rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) ?: [];
+ $newRangeSets = [];
+ foreach ($rangeSets as $rangeSet) {
+ [, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
+ if (empty($rangeSet)) {
+ continue;
+ }
+ if (!str_contains($rangeSet, ':')) {
+ $rangeSet = $rangeSet . ':' . $rangeSet;
+ }
+ $newRangeSets[] = str_replace('$', '', $rangeSet);
+ }
+ if (count($newRangeSets) > 0) {
+ $docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // Next sheet id
+ ++$sheetId;
+ }
+
+ // Loop through definedNames
+ if ($xmlWorkbook->definedNames) {
+ foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
+ // Extract range
+ $extractedRange = (string) $definedName;
+
+ // Valid range?
+ if ($extractedRange == '') {
+ continue;
+ }
+
+ // Some definedNames are only applicable if we are on the same sheet...
+ if ((string) $definedName['localSheetId'] != '') {
+ // Local defined name
+ // Switch on type
+ switch ((string) $definedName['name']) {
+ case '_xlnm._FilterDatabase':
+ case '_xlnm.Print_Titles':
+ case '_xlnm.Print_Area':
+ break;
+ default:
+ if ($mapSheetId[(int) $definedName['localSheetId']] !== null) {
+ $range = Worksheet::extractSheetTitle($extractedRange, true);
+ $scope = $excel->getSheet($mapSheetId[(int) $definedName['localSheetId']]);
+ if (str_contains((string) $definedName, '!')) {
+ $range[0] = str_replace("''", "'", $range[0]);
+ $range[0] = str_replace("'", '', $range[0]);
+ if ($worksheet = $excel->getSheetByName($range[0])) {
+ $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
+ } else {
+ $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true, $scope));
+ }
+ } else {
+ $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true));
+ }
+ }
+
+ break;
+ }
+ } elseif (!isset($definedName['localSheetId'])) {
+ // "Global" definedNames
+ $locatedSheet = null;
+ if (str_contains((string) $definedName, '!')) {
+ // Modify range, and extract the first worksheet reference
+ // Need to split on a comma or a space if not in quotes, and extract the first part.
+ $definedNameValueParts = preg_split("/[ ,](?=([^']*'[^']*')*[^']*$)/miuU", $extractedRange);
+ if (is_array($definedNameValueParts)) {
+ // Extract sheet name
+ [$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true);
+ $extractedSheetName = trim((string) $extractedSheetName, "'");
+
+ // Locate sheet
+ $locatedSheet = $excel->getSheetByName($extractedSheetName);
+ }
+ }
+
+ if ($locatedSheet === null && !DefinedName::testIfFormula($extractedRange)) {
+ $extractedRange = '#REF!';
+ }
+ $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $extractedRange, false));
+ }
+ }
+ }
+ }
+
+ (new WorkbookView($excel))->viewSettings($xmlWorkbook, $mainNS, $mapSheetId, $this->readDataOnly);
+
+ break;
+ }
+ }
+
+ if (!$this->readDataOnly) {
+ $contentTypes = $this->loadZip('[Content_Types].xml');
+
+ // Default content types
+ foreach ($contentTypes->Default as $contentType) {
+ switch ($contentType['ContentType']) {
+ case 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings':
+ $unparsedLoadedData['default_content_types'][(string) $contentType['Extension']] = (string) $contentType['ContentType'];
+
+ break;
+ }
+ }
+
+ // Override content types
+ foreach ($contentTypes->Override as $contentType) {
+ switch ($contentType['ContentType']) {
+ case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml':
+ if ($this->includeCharts) {
+ $chartEntryRef = ltrim((string) $contentType['PartName'], '/');
+ $chartElements = $this->loadZip($chartEntryRef);
+ $chartReader = new Chart($chartNS, $drawingNS);
+ $objChart = $chartReader->readChart($chartElements, basename($chartEntryRef, '.xml'));
+ if (isset($charts[$chartEntryRef])) {
+ $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
+ if (isset($chartDetails[$chartPositionRef]) && $excel->getSheetByName($charts[$chartEntryRef]['sheet']) !== null) {
+ $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
+ $objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
+ // For oneCellAnchor or absoluteAnchor positioned charts,
+ // toCoordinate is not in the data. Does it need to be calculated?
+ if (array_key_exists('toCoordinate', $chartDetails[$chartPositionRef])) {
+ // twoCellAnchor
+ $objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
+ $objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
+ } else {
+ // oneCellAnchor or absoluteAnchor (e.g. Chart sheet)
+ $objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
+ $objChart->setBottomRightPosition('', $chartDetails[$chartPositionRef]['width'], $chartDetails[$chartPositionRef]['height']);
+ if (array_key_exists('oneCellAnchor', $chartDetails[$chartPositionRef])) {
+ $objChart->setOneCellAnchor($chartDetails[$chartPositionRef]['oneCellAnchor']);
+ }
+ }
+ }
+ }
+ }
+
+ break;
+
+ // unparsed
+ case 'application/vnd.ms-excel.controlproperties+xml':
+ $unparsedLoadedData['override_content_types'][(string) $contentType['PartName']] = (string) $contentType['ContentType'];
+
+ break;
+ }
+ }
+ }
+
+ $excel->setUnparsedLoadedData($unparsedLoadedData);
+
+ $zip->close();
+
+ return $excel;
+ }
+
+ private function parseRichText(?SimpleXMLElement $is): RichText
+ {
+ $value = new RichText();
+
+ if (isset($is->t)) {
+ $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
+ } elseif ($is !== null) {
+ if (is_object($is->r)) {
+ /** @var SimpleXMLElement $run */
+ foreach ($is->r as $run) {
+ if (!isset($run->rPr)) {
+ $value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
+ } else {
+ $objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
+ $objFont = $objText->getFont() ?? new StyleFont();
+
+ if (isset($run->rPr->rFont)) {
+ $attr = $run->rPr->rFont->attributes();
+ if (isset($attr['val'])) {
+ $objFont->setName((string) $attr['val']);
+ }
+ }
+ if (isset($run->rPr->sz)) {
+ $attr = $run->rPr->sz->attributes();
+ if (isset($attr['val'])) {
+ $objFont->setSize((float) $attr['val']);
+ }
+ }
+ if (isset($run->rPr->color)) {
+ $objFont->setColor(new Color($this->styleReader->readColor($run->rPr->color)));
+ }
+ if (isset($run->rPr->b)) {
+ $attr = $run->rPr->b->attributes();
+ if (
+ (isset($attr['val']) && self::boolean((string) $attr['val']))
+ || (!isset($attr['val']))
+ ) {
+ $objFont->setBold(true);
+ }
+ }
+ if (isset($run->rPr->i)) {
+ $attr = $run->rPr->i->attributes();
+ if (
+ (isset($attr['val']) && self::boolean((string) $attr['val']))
+ || (!isset($attr['val']))
+ ) {
+ $objFont->setItalic(true);
+ }
+ }
+ if (isset($run->rPr->vertAlign)) {
+ $attr = $run->rPr->vertAlign->attributes();
+ if (isset($attr['val'])) {
+ $vertAlign = strtolower((string) $attr['val']);
+ if ($vertAlign == 'superscript') {
+ $objFont->setSuperscript(true);
+ }
+ if ($vertAlign == 'subscript') {
+ $objFont->setSubscript(true);
+ }
+ }
+ }
+ if (isset($run->rPr->u)) {
+ $attr = $run->rPr->u->attributes();
+ if (!isset($attr['val'])) {
+ $objFont->setUnderline(StyleFont::UNDERLINE_SINGLE);
+ } else {
+ $objFont->setUnderline((string) $attr['val']);
+ }
+ }
+ if (isset($run->rPr->strike)) {
+ $attr = $run->rPr->strike->attributes();
+ if (
+ (isset($attr['val']) && self::boolean((string) $attr['val']))
+ || (!isset($attr['val']))
+ ) {
+ $objFont->setStrikethrough(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $value;
+ }
+
+ private function readRibbon(Spreadsheet $excel, string $customUITarget, ZipArchive $zip): void
+ {
+ $baseDir = dirname($customUITarget);
+ $nameCustomUI = basename($customUITarget);
+ // get the xml file (ribbon)
+ $localRibbon = $this->getFromZipArchive($zip, $customUITarget);
+ $customUIImagesNames = [];
+ $customUIImagesBinaries = [];
+ // something like customUI/_rels/customUI.xml.rels
+ $pathRels = $baseDir . '/_rels/' . $nameCustomUI . '.rels';
+ $dataRels = $this->getFromZipArchive($zip, $pathRels);
+ if ($dataRels) {
+ // exists and not empty if the ribbon have some pictures (other than internal MSO)
+ $UIRels = simplexml_load_string(
+ $this->getSecurityScannerOrThrow()->scan($dataRels),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+ if (false !== $UIRels) {
+ // we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image
+ foreach ($UIRels->Relationship as $ele) {
+ if ((string) $ele['Type'] === Namespaces::SCHEMA_OFFICE_DOCUMENT . '/image') {
+ // an image ?
+ $customUIImagesNames[(string) $ele['Id']] = (string) $ele['Target'];
+ $customUIImagesBinaries[(string) $ele['Target']] = $this->getFromZipArchive($zip, $baseDir . '/' . (string) $ele['Target']);
+ }
+ }
+ }
+ }
+ if ($localRibbon) {
+ $excel->setRibbonXMLData($customUITarget, $localRibbon);
+ if (count($customUIImagesNames) > 0 && count($customUIImagesBinaries) > 0) {
+ $excel->setRibbonBinObjects($customUIImagesNames, $customUIImagesBinaries);
+ } else {
+ $excel->setRibbonBinObjects(null, null);
+ }
+ } else {
+ $excel->setRibbonXMLData(null, null);
+ $excel->setRibbonBinObjects(null, null);
+ }
+ }
+
+ /**
+ * @param null|array|bool|SimpleXMLElement $array
+ * @param int|string $key
+ *
+ * @return mixed
+ */
+ private static function getArrayItem($array, $key = 0)
+ {
+ return ($array === null || is_bool($array)) ? null : ($array[$key] ?? null);
+ }
+
+ /**
+ * @param null|SimpleXMLElement|string $base
+ * @param null|SimpleXMLElement|string $add
+ */
+ private static function dirAdd($base, $add): string
+ {
+ $base = (string) $base;
+ $add = (string) $add;
+
+ return (string) preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
+ }
+
+ private static function toCSSArray(string $style): array
+ {
+ $style = self::stripWhiteSpaceFromStyleString($style);
+
+ $temp = explode(';', $style);
+ $style = [];
+ foreach ($temp as $item) {
+ $item = explode(':', $item);
+
+ if (str_contains($item[1], 'px')) {
+ $item[1] = str_replace('px', '', $item[1]);
+ }
+ if (str_contains($item[1], 'pt')) {
+ $item[1] = str_replace('pt', '', $item[1]);
+ $item[1] = (string) Font::fontSizeToPixels((int) $item[1]);
+ }
+ if (str_contains($item[1], 'in')) {
+ $item[1] = str_replace('in', '', $item[1]);
+ $item[1] = (string) Font::inchSizeToPixels((int) $item[1]);
+ }
+ if (str_contains($item[1], 'cm')) {
+ $item[1] = str_replace('cm', '', $item[1]);
+ $item[1] = (string) Font::centimeterSizeToPixels((int) $item[1]);
+ }
+
+ $style[$item[0]] = $item[1];
+ }
+
+ return $style;
+ }
+
+ public static function stripWhiteSpaceFromStyleString(string $string): string
+ {
+ return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
+ }
+
+ private static function boolean(string $value): bool
+ {
+ if (is_numeric($value)) {
+ return (bool) $value;
+ }
+
+ return $value === 'true' || $value === 'TRUE';
+ }
+
+ private function readHyperLinkDrawing(\PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing, SimpleXMLElement $cellAnchor, array $hyperlinks): void
+ {
+ $hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
+
+ if ($hlinkClick->count() === 0) {
+ return;
+ }
+
+ $hlinkId = (string) self::getAttributes($hlinkClick, Namespaces::SCHEMA_OFFICE_DOCUMENT)['id'];
+ $hyperlink = new Hyperlink(
+ $hyperlinks[$hlinkId],
+ (string) self::getArrayItem(self::getAttributes($cellAnchor->pic->nvPicPr->cNvPr), 'name')
+ );
+ $objDrawing->setHyperlink($hyperlink);
+ }
+
+ private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook): void
+ {
+ if (!$xmlWorkbook->workbookProtection) {
+ return;
+ }
+
+ $excel->getSecurity()->setLockRevision(self::getLockValue($xmlWorkbook->workbookProtection, 'lockRevision'));
+ $excel->getSecurity()->setLockStructure(self::getLockValue($xmlWorkbook->workbookProtection, 'lockStructure'));
+ $excel->getSecurity()->setLockWindows(self::getLockValue($xmlWorkbook->workbookProtection, 'lockWindows'));
+
+ if ($xmlWorkbook->workbookProtection['revisionsPassword']) {
+ $excel->getSecurity()->setRevisionsPassword(
+ (string) $xmlWorkbook->workbookProtection['revisionsPassword'],
+ true
+ );
+ }
+
+ if ($xmlWorkbook->workbookProtection['workbookPassword']) {
+ $excel->getSecurity()->setWorkbookPassword(
+ (string) $xmlWorkbook->workbookProtection['workbookPassword'],
+ true
+ );
+ }
+ }
+
+ private static function getLockValue(SimpleXmlElement $protection, string $key): ?bool
+ {
+ $returnValue = null;
+ $protectKey = $protection[$key];
+ if (!empty($protectKey)) {
+ $protectKey = (string) $protectKey;
+ $returnValue = $protectKey !== 'false' && (bool) $protectKey;
+ }
+
+ return $returnValue;
+ }
+
+ private function readFormControlProperties(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
+ {
+ $zip = $this->zip;
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') === false) {
+ return;
+ }
+
+ $filename = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+ $relsWorksheet = $this->loadZipNoNamespace($filename, Namespaces::RELATIONSHIPS);
+ $ctrlProps = [];
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ((string) $ele['Type'] === Namespaces::SCHEMA_OFFICE_DOCUMENT . '/ctrlProp') {
+ $ctrlProps[(string) $ele['Id']] = $ele;
+ }
+ }
+
+ $unparsedCtrlProps = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['ctrlProps'];
+ foreach ($ctrlProps as $rId => $ctrlProp) {
+ $rId = substr($rId, 3); // rIdXXX
+ $unparsedCtrlProps[$rId] = [];
+ $unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']);
+ $unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target'];
+ $unparsedCtrlProps[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
+ }
+ unset($unparsedCtrlProps);
+ }
+
+ private function readPrinterSettings(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
+ {
+ $zip = $this->zip;
+ if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') === false) {
+ return;
+ }
+
+ $filename = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+ $relsWorksheet = $this->loadZipNoNamespace($filename, Namespaces::RELATIONSHIPS);
+ $sheetPrinterSettings = [];
+ foreach ($relsWorksheet->Relationship as $ele) {
+ if ((string) $ele['Type'] === Namespaces::SCHEMA_OFFICE_DOCUMENT . '/printerSettings') {
+ $sheetPrinterSettings[(string) $ele['Id']] = $ele;
+ }
+ }
+
+ $unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
+ foreach ($sheetPrinterSettings as $rId => $printerSettings) {
+ $rId = substr($rId, 3); // rIdXXX
+ if (!str_ends_with($rId, 'ps')) {
+ $rId = $rId . 'ps'; // rIdXXX, add 'ps' suffix to avoid identical resource identifier collision with unparsed vmlDrawing
+ }
+ $unparsedPrinterSettings[$rId] = [];
+ $target = (string) str_replace('/xl/', '../', (string) $printerSettings['Target']);
+ $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $target);
+ $unparsedPrinterSettings[$rId]['relFilePath'] = $target;
+ $unparsedPrinterSettings[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
+ }
+ unset($unparsedPrinterSettings);
+ }
+
+ private function getWorkbookBaseName(): array
+ {
+ $workbookBasename = '';
+ $xmlNamespaceBase = '';
+
+ // check if it is an OOXML archive
+ $rels = $this->loadZip(self::INITIAL_FILE);
+ foreach ($rels->children(Namespaces::RELATIONSHIPS)->Relationship as $rel) {
+ $rel = self::getAttributes($rel);
+ $type = (string) $rel['Type'];
+ switch ($type) {
+ case Namespaces::OFFICE_DOCUMENT:
+ case Namespaces::PURL_OFFICE_DOCUMENT:
+ $basename = basename((string) $rel['Target']);
+ $xmlNamespaceBase = dirname($type);
+ if (preg_match('/workbook.*\.xml/', $basename)) {
+ $workbookBasename = $basename;
+ }
+
+ break;
+ }
+ }
+
+ return [$workbookBasename, $xmlNamespaceBase];
+ }
+
+ private function readSheetProtection(Worksheet $docSheet, SimpleXMLElement $xmlSheet): void
+ {
+ if ($this->readDataOnly || !$xmlSheet->sheetProtection) {
+ return;
+ }
+
+ $algorithmName = (string) $xmlSheet->sheetProtection['algorithmName'];
+ $protection = $docSheet->getProtection();
+ $protection->setAlgorithm($algorithmName);
+
+ if ($algorithmName) {
+ $protection->setPassword((string) $xmlSheet->sheetProtection['hashValue'], true);
+ $protection->setSalt((string) $xmlSheet->sheetProtection['saltValue']);
+ $protection->setSpinCount((int) $xmlSheet->sheetProtection['spinCount']);
+ } else {
+ $protection->setPassword((string) $xmlSheet->sheetProtection['password'], true);
+ }
+
+ if ($xmlSheet->protectedRanges->protectedRange) {
+ foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
+ $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
+ }
+ }
+ }
+
+ private function readAutoFilter(
+ SimpleXMLElement $xmlSheet,
+ Worksheet $docSheet
+ ): void {
+ if ($xmlSheet && $xmlSheet->autoFilter) {
+ (new AutoFilter($docSheet, $xmlSheet))->load();
+ }
+ }
+
+ private function readBackgroundImage(
+ SimpleXMLElement $xmlSheet,
+ Worksheet $docSheet,
+ string $relsName
+ ): void {
+ if ($xmlSheet && $xmlSheet->picture) {
+ $id = (string) self::getArrayItem(self::getAttributes($xmlSheet->picture, Namespaces::SCHEMA_OFFICE_DOCUMENT), 'id');
+ $rels = $this->loadZip($relsName);
+ foreach ($rels->Relationship as $rel) {
+ $attrs = $rel->attributes() ?? [];
+ $rid = (string) ($attrs['Id'] ?? '');
+ $target = (string) ($attrs['Target'] ?? '');
+ if ($rid === $id && substr($target, 0, 2) === '..') {
+ $target = 'xl' . substr($target, 2);
+ $content = $this->getFromZipArchive($this->zip, $target);
+ $docSheet->setBackgroundImage($content);
+ }
+ }
+ }
+ }
+
+ private function readTables(
+ SimpleXMLElement $xmlSheet,
+ Worksheet $docSheet,
+ string $dir,
+ string $fileWorksheet,
+ ZipArchive $zip,
+ string $namespaceTable
+ ): void {
+ if ($xmlSheet && $xmlSheet->tableParts) {
+ $attributes = $xmlSheet->tableParts->attributes() ?? ['count' => 0];
+ if (((int) $attributes['count']) > 0) {
+ $this->readTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet, $namespaceTable);
+ }
+ }
+ }
+
+ private function readTablesInTablesFile(
+ SimpleXMLElement $xmlSheet,
+ string $dir,
+ string $fileWorksheet,
+ ZipArchive $zip,
+ Worksheet $docSheet,
+ string $namespaceTable
+ ): void {
+ foreach ($xmlSheet->tableParts->tablePart as $tablePart) {
+ $relation = self::getAttributes($tablePart, Namespaces::SCHEMA_OFFICE_DOCUMENT);
+ $tablePartRel = (string) $relation['id'];
+ $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+
+ if ($zip->locateName($relationsFileName) !== false) {
+ $relsTableReferences = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS);
+ foreach ($relsTableReferences->Relationship as $relationship) {
+ $relationshipAttributes = self::getAttributes($relationship, '');
+
+ if ((string) $relationshipAttributes['Id'] === $tablePartRel) {
+ $relationshipFileName = (string) $relationshipAttributes['Target'];
+ $relationshipFilePath = dirname("$dir/$fileWorksheet") . '/' . $relationshipFileName;
+ $relationshipFilePath = File::realpath($relationshipFilePath);
+
+ if ($this->fileExistsInArchive($this->zip, $relationshipFilePath)) {
+ $tableXml = $this->loadZip($relationshipFilePath, $namespaceTable);
+ (new TableReader($docSheet, $tableXml))->load();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static function extractStyles(?SimpleXMLElement $sxml, string $node1, string $node2): array
+ {
+ $array = [];
+ if ($sxml && $sxml->{$node1}->{$node2}) {
+ foreach ($sxml->{$node1}->{$node2} as $node) {
+ $array[] = $node;
+ }
+ }
+
+ return $array;
+ }
+
+ private static function extractPalette(?SimpleXMLElement $sxml): array
+ {
+ $array = [];
+ if ($sxml && $sxml->colors->indexedColors) {
+ foreach ($sxml->colors->indexedColors->rgbColor as $node) {
+ if ($node !== null) {
+ $attr = $node->attributes();
+ if (isset($attr['rgb'])) {
+ $array[] = (string) $attr['rgb'];
+ }
+ }
+ }
+ }
+
+ return $array;
+ }
+
+ private function processIgnoredErrors(SimpleXMLElement $xml, Worksheet $sheet): void
+ {
+ $attributes = self::getAttributes($xml);
+ $sqref = (string) ($attributes['sqref'] ?? '');
+ $numberStoredAsText = (string) ($attributes['numberStoredAsText'] ?? '');
+ $formula = (string) ($attributes['formula'] ?? '');
+ $twoDigitTextYear = (string) ($attributes['twoDigitTextYear'] ?? '');
+ $evalError = (string) ($attributes['evalError'] ?? '');
+ if (!empty($sqref)) {
+ $explodedSqref = explode(' ', $sqref);
+ $pattern1 = '/^([A-Z]{1,3})([0-9]{1,7})(:([A-Z]{1,3})([0-9]{1,7}))?$/';
+ foreach ($explodedSqref as $sqref1) {
+ if (preg_match($pattern1, $sqref1, $matches) === 1) {
+ $firstRow = $matches[2];
+ $firstCol = $matches[1];
+ if (array_key_exists(3, $matches)) {
+ $lastCol = $matches[4];
+ $lastRow = $matches[5];
+ } else {
+ $lastCol = $firstCol;
+ $lastRow = $firstRow;
+ }
+ ++$lastCol;
+ for ($row = $firstRow; $row <= $lastRow; ++$row) {
+ for ($col = $firstCol; $col !== $lastCol; ++$col) {
+ if ($numberStoredAsText === '1') {
+ $sheet->getCell("$col$row")->getIgnoredErrors()->setNumberStoredAsText(true);
+ }
+ if ($formula === '1') {
+ $sheet->getCell("$col$row")->getIgnoredErrors()->setFormula(true);
+ }
+ if ($twoDigitTextYear === '1') {
+ $sheet->getCell("$col$row")->getIgnoredErrors()->setTwoDigitTextYear(true);
+ }
+ if ($evalError === '1') {
+ $sheet->getCell("$col$row")->getIgnoredErrors()->setEvalError(true);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
new file mode 100644
index 0000000..68086cc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
@@ -0,0 +1,167 @@
+parent = $parent;
+ $this->worksheetXml = $worksheetXml;
+ }
+
+ public function load(): void
+ {
+ // Remove all "$" in the auto filter range
+ $attrs = $this->worksheetXml->autoFilter->attributes() ?? [];
+ $autoFilterRange = (string) preg_replace('/\$/', '', $attrs['ref'] ?? '');
+ if (str_contains($autoFilterRange, ':')) {
+ $this->readAutoFilter($autoFilterRange);
+ }
+ }
+
+ private function readAutoFilter(string $autoFilterRange): void
+ {
+ $autoFilter = $this->parent->getAutoFilter();
+ $autoFilter->setRange($autoFilterRange);
+
+ foreach ($this->worksheetXml->autoFilter->filterColumn as $filterColumn) {
+ $attributes = $filterColumn->attributes() ?? [];
+ $column = $autoFilter->getColumnByOffset((int) ($attributes['colId'] ?? 0));
+ // Check for standard filters
+ if ($filterColumn->filters) {
+ $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
+ $filters = Xlsx::testSimpleXml($filterColumn->filters->attributes());
+ if ((isset($filters['blank'])) && ((int) $filters['blank'] == 1)) {
+ // Operator is undefined, but always treated as EQUAL
+ $column->createRule()->setRule('', '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+ }
+ // Standard filters are always an OR join, so no join rule needs to be set
+ // Entries can be either filter elements
+ foreach ($filterColumn->filters->filter as $filterRule) {
+ // Operator is undefined, but always treated as EQUAL
+ $attr2 = $filterRule->attributes() ?? ['val' => ''];
+ $column->createRule()->setRule('', (string) $attr2['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+ }
+
+ // Or Date Group elements
+ $this->readDateRangeAutoFilter($filterColumn->filters, $column);
+ }
+
+ // Check for custom filters
+ $this->readCustomAutoFilter($filterColumn, $column);
+ // Check for dynamic filters
+ $this->readDynamicAutoFilter($filterColumn, $column);
+ // Check for dynamic filters
+ $this->readTopTenAutoFilter($filterColumn, $column);
+ }
+ $autoFilter->setEvaluated(true);
+ }
+
+ private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $column): void
+ {
+ foreach ($filters->dateGroupItem as $dateGroupItemx) {
+ // Operator is undefined, but always treated as EQUAL
+ $dateGroupItem = $dateGroupItemx->attributes();
+ if ($dateGroupItem !== null) {
+ $column->createRule()->setRule(
+ '',
+ [
+ 'year' => (string) $dateGroupItem['year'],
+ 'month' => (string) $dateGroupItem['month'],
+ 'day' => (string) $dateGroupItem['day'],
+ 'hour' => (string) $dateGroupItem['hour'],
+ 'minute' => (string) $dateGroupItem['minute'],
+ 'second' => (string) $dateGroupItem['second'],
+ ],
+ (string) $dateGroupItem['dateTimeGrouping']
+ )->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
+ }
+ }
+ }
+
+ private function readCustomAutoFilter(?SimpleXMLElement $filterColumn, Column $column): void
+ {
+ if (isset($filterColumn, $filterColumn->customFilters)) {
+ $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
+ $customFilters = $filterColumn->customFilters;
+ $attributes = $customFilters->attributes();
+ // Custom filters can an AND or an OR join;
+ // and there should only ever be one or two entries
+ if ((isset($attributes['and'])) && ((string) $attributes['and'] === '1')) {
+ $column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
+ }
+ foreach ($customFilters->customFilter as $filterRule) {
+ $attr2 = $filterRule->attributes() ?? ['operator' => '', 'val' => ''];
+ $column->createRule()->setRule(
+ (string) $attr2['operator'],
+ (string) $attr2['val']
+ )->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
+ }
+ }
+ }
+
+ private function readDynamicAutoFilter(?SimpleXMLElement $filterColumn, Column $column): void
+ {
+ if (isset($filterColumn, $filterColumn->dynamicFilter)) {
+ $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
+ // We should only ever have one dynamic filter
+ foreach ($filterColumn->dynamicFilter as $filterRule) {
+ // Operator is undefined, but always treated as EQUAL
+ $attr2 = $filterRule->attributes() ?? [];
+ $column->createRule()->setRule(
+ '',
+ (string) ($attr2['val'] ?? ''),
+ (string) ($attr2['type'] ?? '')
+ )->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
+ if (isset($attr2['val'])) {
+ $column->setAttribute('val', (string) $attr2['val']);
+ }
+ if (isset($attr2['maxVal'])) {
+ $column->setAttribute('maxVal', (string) $attr2['maxVal']);
+ }
+ }
+ }
+ }
+
+ private function readTopTenAutoFilter(?SimpleXMLElement $filterColumn, Column $column): void
+ {
+ if (isset($filterColumn, $filterColumn->top10)) {
+ $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
+ // We should only ever have one top10 filter
+ foreach ($filterColumn->top10 as $filterRule) {
+ $attr2 = $filterRule->attributes() ?? [];
+ $column->createRule()->setRule(
+ (
+ ((isset($attr2['percent'])) && ((string) $attr2['percent'] === '1'))
+ ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
+ : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
+ ),
+ (string) ($attr2['val'] ?? ''),
+ (
+ ((isset($attr2['top'])) && ((string) $attr2['top'] === '1'))
+ ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
+ : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
+ )
+ )->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
new file mode 100644
index 0000000..beea6bb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
@@ -0,0 +1,21 @@
+cNamespace = $cNamespace;
+ $this->aNamespace = $aNamespace;
+ }
+
+ private static function getAttributeString(SimpleXMLElement $component, string $name): string|null
+ {
+ $attributes = $component->attributes();
+ if (@isset($attributes[$name])) {
+ return (string) $attributes[$name];
+ }
+
+ return null;
+ }
+
+ private static function getAttributeInteger(SimpleXMLElement $component, string $name): int|null
+ {
+ $attributes = $component->attributes();
+ if (@isset($attributes[$name])) {
+ return (int) $attributes[$name];
+ }
+
+ return null;
+ }
+
+ private static function getAttributeBoolean(SimpleXMLElement $component, string $name): bool|null
+ {
+ $attributes = $component->attributes();
+ if (@isset($attributes[$name])) {
+ $value = (string) $attributes[$name];
+
+ return $value === 'true' || $value === '1';
+ }
+
+ return null;
+ }
+
+ private static function getAttributeFloat(SimpleXMLElement $component, string $name): float|null
+ {
+ $attributes = $component->attributes();
+ if (@isset($attributes[$name])) {
+ return (float) $attributes[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * @param string $chartName
+ */
+ public function readChart(SimpleXMLElement $chartElements, $chartName): \PhpOffice\PhpSpreadsheet\Chart\Chart
+ {
+ $chartElementsC = $chartElements->children($this->cNamespace);
+
+ $XaxisLabel = $YaxisLabel = $legend = $title = null;
+ $dispBlanksAs = $plotVisOnly = null;
+ $plotArea = null;
+ $rotX = $rotY = $rAngAx = $perspective = null;
+ $xAxis = new Axis();
+ $yAxis = new Axis();
+ $autoTitleDeleted = null;
+ $chartNoFill = false;
+ $chartBorderLines = null;
+ $chartFillColor = null;
+ $gradientArray = [];
+ $gradientLin = null;
+ $roundedCorners = false;
+ $gapWidth = null;
+ $useUpBars = null;
+ $useDownBars = null;
+ foreach ($chartElementsC as $chartElementKey => $chartElement) {
+ switch ($chartElementKey) {
+ case 'spPr':
+ $children = $chartElementsC->spPr->children($this->aNamespace);
+ if (isset($children->noFill)) {
+ $chartNoFill = true;
+ }
+ if (isset($children->solidFill)) {
+ $chartFillColor = $this->readColor($children->solidFill);
+ }
+ if (isset($children->ln)) {
+ $chartBorderLines = new GridLines();
+ $this->readLineStyle($chartElementsC, $chartBorderLines);
+ }
+
+ break;
+ case 'roundedCorners':
+ /** @var bool */
+ $roundedCorners = self::getAttributeBoolean($chartElementsC->roundedCorners, 'val');
+
+ break;
+ case 'chart':
+ foreach ($chartElement as $chartDetailsKey => $chartDetails) {
+ $chartDetails = Xlsx::testSimpleXml($chartDetails);
+ switch ($chartDetailsKey) {
+ case 'autoTitleDeleted':
+ /** @var bool */
+ $autoTitleDeleted = self::getAttributeBoolean($chartElementsC->chart->autoTitleDeleted, 'val');
+
+ break;
+ case 'view3D':
+ $rotX = self::getAttributeInteger($chartDetails->rotX, 'val');
+ $rotY = self::getAttributeInteger($chartDetails->rotY, 'val');
+ $rAngAx = self::getAttributeInteger($chartDetails->rAngAx, 'val');
+ $perspective = self::getAttributeInteger($chartDetails->perspective, 'val');
+
+ break;
+ case 'plotArea':
+ $plotAreaLayout = $XaxisLabel = $YaxisLabel = null;
+ $plotSeries = $plotAttributes = [];
+ $catAxRead = false;
+ $plotNoFill = false;
+ foreach ($chartDetails as $chartDetailKey => $chartDetail) {
+ $chartDetail = Xlsx::testSimpleXml($chartDetail);
+ switch ($chartDetailKey) {
+ case 'spPr':
+ $possibleNoFill = $chartDetails->spPr->children($this->aNamespace);
+ if (isset($possibleNoFill->noFill)) {
+ $plotNoFill = true;
+ }
+ if (isset($possibleNoFill->gradFill->gsLst)) {
+ foreach ($possibleNoFill->gradFill->gsLst->gs as $gradient) {
+ $gradient = Xlsx::testSimpleXml($gradient);
+ /** @var float */
+ $pos = self::getAttributeFloat($gradient, 'pos');
+ $gradientArray[] = [
+ $pos / ChartProperties::PERCENTAGE_MULTIPLIER,
+ new ChartColor($this->readColor($gradient)),
+ ];
+ }
+ }
+ if (isset($possibleNoFill->gradFill->lin)) {
+ $gradientLin = ChartProperties::XmlToAngle((string) self::getAttributeString($possibleNoFill->gradFill->lin, 'ang'));
+ }
+
+ break;
+ case 'layout':
+ $plotAreaLayout = $this->chartLayoutDetails($chartDetail);
+
+ break;
+ case Axis::AXIS_TYPE_CATEGORY:
+ case Axis::AXIS_TYPE_DATE:
+ $catAxRead = true;
+ if (isset($chartDetail->title)) {
+ $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
+ }
+ $xAxis->setAxisType($chartDetailKey);
+ $this->readEffects($chartDetail, $xAxis);
+ $this->readLineStyle($chartDetail, $xAxis);
+ if (isset($chartDetail->spPr)) {
+ $sppr = $chartDetail->spPr->children($this->aNamespace);
+ if (isset($sppr->solidFill)) {
+ $axisColorArray = $this->readColor($sppr->solidFill);
+ $xAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
+ }
+ if (isset($chartDetail->spPr->ln->noFill)) {
+ $xAxis->setNoFill(true);
+ }
+ }
+ if (isset($chartDetail->majorGridlines)) {
+ $majorGridlines = new GridLines();
+ if (isset($chartDetail->majorGridlines->spPr)) {
+ $this->readEffects($chartDetail->majorGridlines, $majorGridlines);
+ $this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
+ }
+ $xAxis->setMajorGridlines($majorGridlines);
+ }
+ if (isset($chartDetail->minorGridlines)) {
+ $minorGridlines = new GridLines();
+ $minorGridlines->activateObject();
+ if (isset($chartDetail->minorGridlines->spPr)) {
+ $this->readEffects($chartDetail->minorGridlines, $minorGridlines);
+ $this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
+ }
+ $xAxis->setMinorGridlines($minorGridlines);
+ }
+ $this->setAxisProperties($chartDetail, $xAxis);
+
+ break;
+ case Axis::AXIS_TYPE_VALUE:
+ $whichAxis = null;
+ $axPos = null;
+ if (isset($chartDetail->axPos)) {
+ $axPos = self::getAttributeString($chartDetail->axPos, 'val');
+ }
+ if ($catAxRead) {
+ $whichAxis = $yAxis;
+ $yAxis->setAxisType($chartDetailKey);
+ } elseif (!empty($axPos)) {
+ switch ($axPos) {
+ case 't':
+ case 'b':
+ $whichAxis = $xAxis;
+ $xAxis->setAxisType($chartDetailKey);
+
+ break;
+ case 'r':
+ case 'l':
+ $whichAxis = $yAxis;
+ $yAxis->setAxisType($chartDetailKey);
+
+ break;
+ }
+ }
+ if (isset($chartDetail->title)) {
+ $axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
+
+ switch ($axPos) {
+ case 't':
+ case 'b':
+ $XaxisLabel = $axisLabel;
+
+ break;
+ case 'r':
+ case 'l':
+ $YaxisLabel = $axisLabel;
+
+ break;
+ }
+ }
+ $this->readEffects($chartDetail, $whichAxis);
+ $this->readLineStyle($chartDetail, $whichAxis);
+ if ($whichAxis !== null && isset($chartDetail->spPr)) {
+ $sppr = $chartDetail->spPr->children($this->aNamespace);
+ if (isset($sppr->solidFill)) {
+ $axisColorArray = $this->readColor($sppr->solidFill);
+ $whichAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
+ }
+ if (isset($sppr->ln->noFill)) {
+ $whichAxis->setNoFill(true);
+ }
+ }
+ if ($whichAxis !== null && isset($chartDetail->majorGridlines)) {
+ $majorGridlines = new GridLines();
+ if (isset($chartDetail->majorGridlines->spPr)) {
+ $this->readEffects($chartDetail->majorGridlines, $majorGridlines);
+ $this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
+ }
+ $whichAxis->setMajorGridlines($majorGridlines);
+ }
+ if ($whichAxis !== null && isset($chartDetail->minorGridlines)) {
+ $minorGridlines = new GridLines();
+ $minorGridlines->activateObject();
+ if (isset($chartDetail->minorGridlines->spPr)) {
+ $this->readEffects($chartDetail->minorGridlines, $minorGridlines);
+ $this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
+ }
+ $whichAxis->setMinorGridlines($minorGridlines);
+ }
+ $this->setAxisProperties($chartDetail, $whichAxis);
+
+ break;
+ case 'barChart':
+ case 'bar3DChart':
+ $barDirection = self::getAttributeString($chartDetail->barDir, 'val');
+ $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotSer->setPlotDirection("$barDirection");
+ $plotSeries[] = $plotSer;
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'lineChart':
+ case 'line3DChart':
+ $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'areaChart':
+ case 'area3DChart':
+ $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'doughnutChart':
+ case 'pieChart':
+ case 'pie3DChart':
+ $explosion = self::getAttributeString($chartDetail->ser->explosion, 'val');
+ $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotSer->setPlotStyle("$explosion");
+ $plotSeries[] = $plotSer;
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'scatterChart':
+ /** @var string */
+ $scatterStyle = self::getAttributeString($chartDetail->scatterStyle, 'val');
+ $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotSer->setPlotStyle($scatterStyle);
+ $plotSeries[] = $plotSer;
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'bubbleChart':
+ $bubbleScale = self::getAttributeInteger($chartDetail->bubbleScale, 'val');
+ $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotSer->setPlotStyle("$bubbleScale");
+ $plotSeries[] = $plotSer;
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'radarChart':
+ /** @var string */
+ $radarStyle = self::getAttributeString($chartDetail->radarStyle, 'val');
+ $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotSer->setPlotStyle($radarStyle);
+ $plotSeries[] = $plotSer;
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'surfaceChart':
+ case 'surface3DChart':
+ $wireFrame = self::getAttributeBoolean($chartDetail->wireframe, 'val');
+ $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ $plotSer->setPlotStyle("$wireFrame");
+ $plotSeries[] = $plotSer;
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ case 'stockChart':
+ $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
+ if (isset($chartDetail->upDownBars->gapWidth)) {
+ $gapWidth = self::getAttributeInteger($chartDetail->upDownBars->gapWidth, 'val');
+ }
+ if (isset($chartDetail->upDownBars->upBars)) {
+ $useUpBars = true;
+ }
+ if (isset($chartDetail->upDownBars->downBars)) {
+ $useDownBars = true;
+ }
+ $plotAttributes = $this->readChartAttributes($chartDetail);
+
+ break;
+ }
+ }
+ if ($plotAreaLayout == null) {
+ $plotAreaLayout = new Layout();
+ }
+ $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
+ $this->setChartAttributes($plotAreaLayout, $plotAttributes);
+ if ($plotNoFill) {
+ $plotArea->setNoFill(true);
+ }
+ if (!empty($gradientArray)) {
+ $plotArea->setGradientFillProperties($gradientArray, $gradientLin);
+ }
+ if (is_int($gapWidth)) {
+ $plotArea->setGapWidth($gapWidth);
+ }
+ if ($useUpBars === true) {
+ $plotArea->setUseUpBars(true);
+ }
+ if ($useDownBars === true) {
+ $plotArea->setUseDownBars(true);
+ }
+
+ break;
+ case 'plotVisOnly':
+ $plotVisOnly = self::getAttributeString($chartDetails, 'val');
+
+ break;
+ case 'dispBlanksAs':
+ $dispBlanksAs = self::getAttributeString($chartDetails, 'val');
+
+ break;
+ case 'title':
+ $title = $this->chartTitle($chartDetails);
+
+ break;
+ case 'legend':
+ $legendPos = 'r';
+ $legendLayout = null;
+ $legendOverlay = false;
+ $legendBorderLines = null;
+ $legendFillColor = null;
+ $legendText = null;
+ $addLegendText = false;
+ foreach ($chartDetails as $chartDetailKey => $chartDetail) {
+ $chartDetail = Xlsx::testSimpleXml($chartDetail);
+ switch ($chartDetailKey) {
+ case 'legendPos':
+ $legendPos = self::getAttributeString($chartDetail, 'val');
+
+ break;
+ case 'overlay':
+ $legendOverlay = self::getAttributeBoolean($chartDetail, 'val');
+
+ break;
+ case 'layout':
+ $legendLayout = $this->chartLayoutDetails($chartDetail);
+
+ break;
+ case 'spPr':
+ $children = $chartDetails->spPr->children($this->aNamespace);
+ if (isset($children->solidFill)) {
+ $legendFillColor = $this->readColor($children->solidFill);
+ }
+ if (isset($children->ln)) {
+ $legendBorderLines = new GridLines();
+ $this->readLineStyle($chartDetails, $legendBorderLines);
+ }
+
+ break;
+ case 'txPr':
+ $children = $chartDetails->txPr->children($this->aNamespace);
+ $addLegendText = false;
+ $legendText = new AxisText();
+ if (isset($children->p->pPr->defRPr->solidFill)) {
+ $colorArray = $this->readColor($children->p->pPr->defRPr->solidFill);
+ $legendText->getFillColorObject()->setColorPropertiesArray($colorArray);
+ $addLegendText = true;
+ }
+ if (isset($children->p->pPr->defRPr->effectLst)) {
+ $this->readEffects($children->p->pPr->defRPr, $legendText, false);
+ $addLegendText = true;
+ }
+
+ break;
+ }
+ }
+ $legend = new Legend("$legendPos", $legendLayout, (bool) $legendOverlay);
+ if ($legendFillColor !== null) {
+ $legend->getFillColor()->setColorPropertiesArray($legendFillColor);
+ }
+ if ($legendBorderLines !== null) {
+ $legend->setBorderLines($legendBorderLines);
+ }
+ if ($addLegendText) {
+ $legend->setLegendText($legendText);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel, $xAxis, $yAxis);
+ if ($chartNoFill) {
+ $chart->setNoFill(true);
+ }
+ if ($chartFillColor !== null) {
+ $chart->getFillColor()->setColorPropertiesArray($chartFillColor);
+ }
+ if ($chartBorderLines !== null) {
+ $chart->setBorderLines($chartBorderLines);
+ }
+ $chart->setRoundedCorners($roundedCorners);
+ if (is_bool($autoTitleDeleted)) {
+ $chart->setAutoTitleDeleted($autoTitleDeleted);
+ }
+ if (is_int($rotX)) {
+ $chart->setRotX($rotX);
+ }
+ if (is_int($rotY)) {
+ $chart->setRotY($rotY);
+ }
+ if (is_int($rAngAx)) {
+ $chart->setRAngAx($rAngAx);
+ }
+ if (is_int($perspective)) {
+ $chart->setPerspective($perspective);
+ }
+
+ return $chart;
+ }
+
+ private function chartTitle(SimpleXMLElement $titleDetails): Title
+ {
+ $caption = null;
+ $titleLayout = null;
+ $titleOverlay = false;
+ $titleFormula = null;
+ $titleFont = null;
+ foreach ($titleDetails as $titleDetailKey => $chartDetail) {
+ $chartDetail = Xlsx::testSimpleXml($chartDetail);
+ switch ($titleDetailKey) {
+ case 'tx':
+ $caption = [];
+ if (isset($chartDetail->rich)) {
+ $titleDetails = $chartDetail->rich->children($this->aNamespace);
+ foreach ($titleDetails as $titleKey => $titleDetail) {
+ $titleDetail = Xlsx::testSimpleXml($titleDetail);
+ switch ($titleKey) {
+ case 'p':
+ $titleDetailPart = $titleDetail->children($this->aNamespace);
+ $caption[] = $this->parseRichText($titleDetailPart);
+ }
+ }
+ } elseif (isset($chartDetail->strRef->strCache)) {
+ foreach ($chartDetail->strRef->strCache->pt as $pt) {
+ if (isset($pt->v)) {
+ $caption[] = (string) $pt->v;
+ }
+ }
+ if (isset($chartDetail->strRef->f)) {
+ $titleFormula = (string) $chartDetail->strRef->f;
+ }
+ }
+
+ break;
+ case 'overlay':
+ $titleOverlay = self::getAttributeBoolean($chartDetail, 'val');
+
+ break;
+ case 'layout':
+ $titleLayout = $this->chartLayoutDetails($chartDetail);
+
+ break;
+ case 'txPr':
+ if (isset($chartDetail->children($this->aNamespace)->p)) {
+ $titleFont = $this->parseFont($chartDetail->children($this->aNamespace)->p);
+ }
+
+ break;
+ }
+ }
+ $title = new Title($caption, $titleLayout, (bool) $titleOverlay);
+ if (!empty($titleFormula)) {
+ $title->setCellReference($titleFormula);
+ }
+ if ($titleFont !== null) {
+ $title->setFont($titleFont);
+ }
+
+ return $title;
+ }
+
+ private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
+ {
+ if (!isset($chartDetail->manualLayout)) {
+ return null;
+ }
+ $details = $chartDetail->manualLayout->children($this->cNamespace);
+ if ($details === null) {
+ return null;
+ }
+ $layout = [];
+ foreach ($details as $detailKey => $detail) {
+ $detail = Xlsx::testSimpleXml($detail);
+ $layout[$detailKey] = self::getAttributeString($detail, 'val');
+ }
+
+ return new Layout($layout);
+ }
+
+ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType): DataSeries
+ {
+ $multiSeriesType = null;
+ $smoothLine = false;
+ $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = $seriesBubbles = [];
+
+ $seriesDetailSet = $chartDetail->children($this->cNamespace);
+ foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
+ switch ($seriesDetailKey) {
+ case 'grouping':
+ $multiSeriesType = self::getAttributeString($chartDetail->grouping, 'val');
+
+ break;
+ case 'ser':
+ $marker = null;
+ $seriesIndex = '';
+ $fillColor = null;
+ $pointSize = null;
+ $noFill = false;
+ $bubble3D = false;
+ $dptColors = [];
+ $markerFillColor = null;
+ $markerBorderColor = null;
+ $lineStyle = null;
+ $labelLayout = null;
+ $trendLines = [];
+ foreach ($seriesDetails as $seriesKey => $seriesDetail) {
+ $seriesDetail = Xlsx::testSimpleXml($seriesDetail);
+ switch ($seriesKey) {
+ case 'idx':
+ $seriesIndex = self::getAttributeInteger($seriesDetail, 'val');
+
+ break;
+ case 'order':
+ $seriesOrder = self::getAttributeInteger($seriesDetail, 'val');
+ $plotOrder[$seriesIndex] = $seriesOrder;
+
+ break;
+ case 'tx':
+ $seriesLabel[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
+
+ break;
+ case 'spPr':
+ $children = $seriesDetail->children($this->aNamespace);
+ if (isset($children->ln)) {
+ $ln = $children->ln;
+ if (is_countable($ln->noFill) && count($ln->noFill) === 1) {
+ $noFill = true;
+ }
+ $lineStyle = new GridLines();
+ $this->readLineStyle($seriesDetails, $lineStyle);
+ }
+ if (isset($children->effectLst)) {
+ if ($lineStyle === null) {
+ $lineStyle = new GridLines();
+ }
+ $this->readEffects($seriesDetails, $lineStyle);
+ }
+ if (isset($children->solidFill)) {
+ $fillColor = new ChartColor($this->readColor($children->solidFill));
+ }
+
+ break;
+ case 'dPt':
+ $dptIdx = (int) self::getAttributeString($seriesDetail->idx, 'val');
+ if (isset($seriesDetail->spPr)) {
+ $children = $seriesDetail->spPr->children($this->aNamespace);
+ if (isset($children->solidFill)) {
+ $arrayColors = $this->readColor($children->solidFill);
+ $dptColors[$dptIdx] = new ChartColor($arrayColors);
+ }
+ }
+
+ break;
+ case 'trendline':
+ $trendLine = new TrendLine();
+ $this->readLineStyle($seriesDetail, $trendLine);
+ /** @var ?string */
+ $trendLineType = self::getAttributeString($seriesDetail->trendlineType, 'val');
+ /** @var ?bool */
+ $dispRSqr = self::getAttributeBoolean($seriesDetail->dispRSqr, 'val');
+ /** @var ?bool */
+ $dispEq = self::getAttributeBoolean($seriesDetail->dispEq, 'val');
+ /** @var ?int */
+ $order = self::getAttributeInteger($seriesDetail->order, 'val');
+ /** @var ?int */
+ $period = self::getAttributeInteger($seriesDetail->period, 'val');
+ /** @var ?float */
+ $forward = self::getAttributeFloat($seriesDetail->forward, 'val');
+ /** @var ?float */
+ $backward = self::getAttributeFloat($seriesDetail->backward, 'val');
+ /** @var ?float */
+ $intercept = self::getAttributeFloat($seriesDetail->intercept, 'val');
+ /** @var ?string */
+ $name = (string) $seriesDetail->name;
+ $trendLine->setTrendLineProperties(
+ $trendLineType,
+ $order,
+ $period,
+ $dispRSqr,
+ $dispEq,
+ $backward,
+ $forward,
+ $intercept,
+ $name
+ );
+ $trendLines[] = $trendLine;
+
+ break;
+ case 'marker':
+ $marker = self::getAttributeString($seriesDetail->symbol, 'val');
+ $pointSize = self::getAttributeString($seriesDetail->size, 'val');
+ $pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
+ if (isset($seriesDetail->spPr)) {
+ $children = $seriesDetail->spPr->children($this->aNamespace);
+ if (isset($children->solidFill)) {
+ $markerFillColor = $this->readColor($children->solidFill);
+ }
+ if (isset($children->ln->solidFill)) {
+ $markerBorderColor = $this->readColor($children->ln->solidFill);
+ }
+ }
+
+ break;
+ case 'smooth':
+ $smoothLine = self::getAttributeBoolean($seriesDetail, 'val');
+
+ break;
+ case 'cat':
+ $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
+
+ break;
+ case 'val':
+ $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+
+ break;
+ case 'xVal':
+ $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+
+ break;
+ case 'yVal':
+ $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+
+ break;
+ case 'bubbleSize':
+ $seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+
+ break;
+ case 'bubble3D':
+ $bubble3D = self::getAttributeBoolean($seriesDetail, 'val');
+
+ break;
+ case 'dLbls':
+ $labelLayout = new Layout($this->readChartAttributes($seriesDetails));
+
+ break;
+ }
+ }
+ if ($labelLayout) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->setLabelLayout($labelLayout);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->setLabelLayout($labelLayout);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->setLabelLayout($labelLayout);
+ }
+ }
+ if ($noFill) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->setScatterLines(false);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->setScatterLines(false);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->setScatterLines(false);
+ }
+ }
+ if ($lineStyle !== null) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->copyLineStyles($lineStyle);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->copyLineStyles($lineStyle);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->copyLineStyles($lineStyle);
+ }
+ }
+ if ($bubble3D) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->setBubble3D($bubble3D);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->setBubble3D($bubble3D);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->setBubble3D($bubble3D);
+ }
+ }
+ if (!empty($dptColors)) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->setFillColor($dptColors);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->setFillColor($dptColors);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->setFillColor($dptColors);
+ }
+ }
+ if ($markerFillColor !== null) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
+ }
+ }
+ if ($markerBorderColor !== null) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
+ }
+ }
+ if ($smoothLine) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->setSmoothLine(true);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->setSmoothLine(true);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->setSmoothLine(true);
+ }
+ }
+ if (!empty($trendLines)) {
+ if (isset($seriesLabel[$seriesIndex])) {
+ $seriesLabel[$seriesIndex]->setTrendLines($trendLines);
+ }
+ if (isset($seriesCategory[$seriesIndex])) {
+ $seriesCategory[$seriesIndex]->setTrendLines($trendLines);
+ }
+ if (isset($seriesValues[$seriesIndex])) {
+ $seriesValues[$seriesIndex]->setTrendLines($trendLines);
+ }
+ }
+ }
+ }
+ /** @phpstan-ignore-next-line */
+ $series = new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
+ /** @phpstan-ignore-next-line */
+ $series->setPlotBubbleSizes($seriesBubbles);
+
+ return $series;
+ }
+
+ private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?ChartColor $fillColor = null, ?string $pointSize = null): ?DataSeriesValues
+ {
+ if (isset($seriesDetail->strRef)) {
+ $seriesSource = (string) $seriesDetail->strRef->f;
+ $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
+
+ if (isset($seriesDetail->strRef->strCache)) {
+ $seriesData = $this->chartDataSeriesValues($seriesDetail->strRef->strCache->children($this->cNamespace), 's');
+ $seriesValues
+ ->setFormatCode($seriesData['formatCode'])
+ ->setDataValues($seriesData['dataValues']);
+ }
+
+ return $seriesValues;
+ } elseif (isset($seriesDetail->numRef)) {
+ $seriesSource = (string) $seriesDetail->numRef->f;
+ $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
+ if (isset($seriesDetail->numRef->numCache)) {
+ $seriesData = $this->chartDataSeriesValues($seriesDetail->numRef->numCache->children($this->cNamespace));
+ $seriesValues
+ ->setFormatCode($seriesData['formatCode'])
+ ->setDataValues($seriesData['dataValues']);
+ }
+
+ return $seriesValues;
+ } elseif (isset($seriesDetail->multiLvlStrRef)) {
+ $seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
+ $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
+
+ if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
+ $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($this->cNamespace), 's');
+ $seriesValues
+ ->setFormatCode($seriesData['formatCode'])
+ ->setDataValues($seriesData['dataValues']);
+ }
+
+ return $seriesValues;
+ } elseif (isset($seriesDetail->multiLvlNumRef)) {
+ $seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
+ $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
+
+ if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
+ $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($this->cNamespace), 's');
+ $seriesValues
+ ->setFormatCode($seriesData['formatCode'])
+ ->setDataValues($seriesData['dataValues']);
+ }
+
+ return $seriesValues;
+ }
+
+ if (isset($seriesDetail->v)) {
+ return new DataSeriesValues(
+ DataSeriesValues::DATASERIES_TYPE_STRING,
+ null,
+ null,
+ 1,
+ [(string) $seriesDetail->v]
+ );
+ }
+
+ return null;
+ }
+
+ private function chartDataSeriesValues(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
+ {
+ $seriesVal = [];
+ $formatCode = '';
+ $pointCount = 0;
+
+ foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
+ $seriesValue = Xlsx::testSimpleXml($seriesValue);
+ switch ($seriesValueIdx) {
+ case 'ptCount':
+ $pointCount = self::getAttributeInteger($seriesValue, 'val');
+
+ break;
+ case 'formatCode':
+ $formatCode = (string) $seriesValue;
+
+ break;
+ case 'pt':
+ $pointVal = self::getAttributeInteger($seriesValue, 'idx');
+ if ($dataType == 's') {
+ $seriesVal[$pointVal] = (string) $seriesValue->v;
+ } elseif ((string) $seriesValue->v === ExcelError::NA()) {
+ $seriesVal[$pointVal] = null;
+ } else {
+ $seriesVal[$pointVal] = (float) $seriesValue->v;
+ }
+
+ break;
+ }
+ }
+
+ return [
+ 'formatCode' => $formatCode,
+ 'pointCount' => $pointCount,
+ 'dataValues' => $seriesVal,
+ ];
+ }
+
+ private function chartDataSeriesValuesMultiLevel(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
+ {
+ $seriesVal = [];
+ $formatCode = '';
+ $pointCount = 0;
+
+ foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
+ foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
+ $seriesValue = Xlsx::testSimpleXml($seriesValue);
+ switch ($seriesValueIdx) {
+ case 'ptCount':
+ $pointCount = self::getAttributeInteger($seriesValue, 'val');
+
+ break;
+ case 'formatCode':
+ $formatCode = (string) $seriesValue;
+
+ break;
+ case 'pt':
+ $pointVal = self::getAttributeInteger($seriesValue, 'idx');
+ if ($dataType == 's') {
+ $seriesVal[$pointVal][] = (string) $seriesValue->v;
+ } elseif ((string) $seriesValue->v === ExcelError::NA()) {
+ $seriesVal[$pointVal] = null;
+ } else {
+ $seriesVal[$pointVal][] = (float) $seriesValue->v;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return [
+ 'formatCode' => $formatCode,
+ 'pointCount' => $pointCount,
+ 'dataValues' => $seriesVal,
+ ];
+ }
+
+ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText
+ {
+ $value = new RichText();
+ $defaultFontSize = null;
+ $defaultBold = null;
+ $defaultItalic = null;
+ $defaultUnderscore = null;
+ $defaultStrikethrough = null;
+ $defaultBaseline = null;
+ $defaultFontName = null;
+ $defaultLatin = null;
+ $defaultEastAsian = null;
+ $defaultComplexScript = null;
+ $defaultFontColor = null;
+ if (isset($titleDetailPart->pPr->defRPr)) {
+ /** @var ?int */
+ $defaultFontSize = self::getAttributeInteger($titleDetailPart->pPr->defRPr, 'sz');
+ /** @var ?bool */
+ $defaultBold = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'b');
+ /** @var ?bool */
+ $defaultItalic = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'i');
+ /** @var ?string */
+ $defaultUnderscore = self::getAttributeString($titleDetailPart->pPr->defRPr, 'u');
+ /** @var ?string */
+ $defaultStrikethrough = self::getAttributeString($titleDetailPart->pPr->defRPr, 'strike');
+ /** @var ?int */
+ $defaultBaseline = self::getAttributeInteger($titleDetailPart->pPr->defRPr, 'baseline');
+ if (isset($titleDetailPart->defRPr->rFont['val'])) {
+ $defaultFontName = (string) $titleDetailPart->defRPr->rFont['val'];
+ }
+ if (isset($titleDetailPart->pPr->defRPr->latin)) {
+ /** @var ?string */
+ $defaultLatin = self::getAttributeString($titleDetailPart->pPr->defRPr->latin, 'typeface');
+ }
+ if (isset($titleDetailPart->pPr->defRPr->ea)) {
+ /** @var ?string */
+ $defaultEastAsian = self::getAttributeString($titleDetailPart->pPr->defRPr->ea, 'typeface');
+ }
+ if (isset($titleDetailPart->pPr->defRPr->cs)) {
+ /** @var ?string */
+ $defaultComplexScript = self::getAttributeString($titleDetailPart->pPr->defRPr->cs, 'typeface');
+ }
+ if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
+ $defaultFontColor = $this->readColor($titleDetailPart->pPr->defRPr->solidFill);
+ }
+ }
+ foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
+ if (
+ (string) $titleDetailElementKey !== 'r'
+ || !isset($titleDetailElement->t)
+ ) {
+ continue;
+ }
+ $objText = $value->createTextRun((string) $titleDetailElement->t);
+ if ($objText->getFont() === null) {
+ // @codeCoverageIgnoreStart
+ continue;
+ // @codeCoverageIgnoreEnd
+ }
+ $fontSize = null;
+ $bold = null;
+ $italic = null;
+ $underscore = null;
+ $strikethrough = null;
+ $baseline = null;
+ $fontName = null;
+ $latinName = null;
+ $eastAsian = null;
+ $complexScript = null;
+ $fontColor = null;
+ $underlineColor = null;
+ if (isset($titleDetailElement->rPr)) {
+ // not used now, not sure it ever was, grandfathering
+ if (isset($titleDetailElement->rPr->rFont['val'])) {
+ // @codeCoverageIgnoreStart
+ $fontName = (string) $titleDetailElement->rPr->rFont['val'];
+ // @codeCoverageIgnoreEnd
+ }
+ if (isset($titleDetailElement->rPr->latin)) {
+ /** @var ?string */
+ $latinName = self::getAttributeString($titleDetailElement->rPr->latin, 'typeface');
+ }
+ if (isset($titleDetailElement->rPr->ea)) {
+ /** @var ?string */
+ $eastAsian = self::getAttributeString($titleDetailElement->rPr->ea, 'typeface');
+ }
+ if (isset($titleDetailElement->rPr->cs)) {
+ /** @var ?string */
+ $complexScript = self::getAttributeString($titleDetailElement->rPr->cs, 'typeface');
+ }
+ /** @var ?int */
+ $fontSize = self::getAttributeInteger($titleDetailElement->rPr, 'sz');
+
+ // not used now, not sure it ever was, grandfathering
+ if (isset($titleDetailElement->rPr->solidFill)) {
+ $fontColor = $this->readColor($titleDetailElement->rPr->solidFill);
+ }
+
+ /** @var ?bool */
+ $bold = self::getAttributeBoolean($titleDetailElement->rPr, 'b');
+
+ /** @var ?bool */
+ $italic = self::getAttributeBoolean($titleDetailElement->rPr, 'i');
+
+ /** @var ?int */
+ $baseline = self::getAttributeInteger($titleDetailElement->rPr, 'baseline');
+
+ /** @var ?string */
+ $underscore = self::getAttributeString($titleDetailElement->rPr, 'u');
+ if (isset($titleDetailElement->rPr->uFill->solidFill)) {
+ $underlineColor = $this->readColor($titleDetailElement->rPr->uFill->solidFill);
+ }
+
+ /** @var ?string */
+ $strikethrough = self::getAttributeString($titleDetailElement->rPr, 'strike');
+ }
+
+ $fontFound = false;
+ $latinName = $latinName ?? $defaultLatin;
+ if ($latinName !== null) {
+ $objText->getFont()->setLatin($latinName);
+ $fontFound = true;
+ }
+ $eastAsian = $eastAsian ?? $defaultEastAsian;
+ if ($eastAsian !== null) {
+ $objText->getFont()->setEastAsian($eastAsian);
+ $fontFound = true;
+ }
+ $complexScript = $complexScript ?? $defaultComplexScript;
+ if ($complexScript !== null) {
+ $objText->getFont()->setComplexScript($complexScript);
+ $fontFound = true;
+ }
+ $fontName = $fontName ?? $defaultFontName;
+ if ($fontName !== null) {
+ // @codeCoverageIgnoreStart
+ $objText->getFont()->setName($fontName);
+ $fontFound = true;
+ // @codeCoverageIgnoreEnd
+ }
+
+ $fontSize = $fontSize ?? $defaultFontSize;
+ if (is_int($fontSize)) {
+ $objText->getFont()->setSize(floor($fontSize / 100));
+ $fontFound = true;
+ } else {
+ $objText->getFont()->setSize(null, true);
+ }
+
+ $fontColor = $fontColor ?? $defaultFontColor;
+ if (!empty($fontColor)) {
+ $objText->getFont()->setChartColor($fontColor);
+ $fontFound = true;
+ }
+
+ $bold = $bold ?? $defaultBold;
+ if ($bold !== null) {
+ $objText->getFont()->setBold($bold);
+ $fontFound = true;
+ }
+
+ $italic = $italic ?? $defaultItalic;
+ if ($italic !== null) {
+ $objText->getFont()->setItalic($italic);
+ $fontFound = true;
+ }
+
+ $baseline = $baseline ?? $defaultBaseline;
+ if ($baseline !== null) {
+ $objText->getFont()->setBaseLine($baseline);
+ if ($baseline > 0) {
+ $objText->getFont()->setSuperscript(true);
+ } elseif ($baseline < 0) {
+ $objText->getFont()->setSubscript(true);
+ }
+ $fontFound = true;
+ }
+
+ $underscore = $underscore ?? $defaultUnderscore;
+ if ($underscore !== null) {
+ if ($underscore == 'sng') {
+ $objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
+ } elseif ($underscore == 'dbl') {
+ $objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
+ } elseif ($underscore !== '') {
+ $objText->getFont()->setUnderline($underscore);
+ } else {
+ $objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
+ }
+ $fontFound = true;
+ if ($underlineColor) {
+ $objText->getFont()->setUnderlineColor($underlineColor);
+ }
+ }
+
+ $strikethrough = $strikethrough ?? $defaultStrikethrough;
+ if ($strikethrough !== null) {
+ $objText->getFont()->setStrikeType($strikethrough);
+ if ($strikethrough == 'noStrike') {
+ $objText->getFont()->setStrikethrough(false);
+ } else {
+ $objText->getFont()->setStrikethrough(true);
+ }
+ $fontFound = true;
+ }
+ if ($fontFound === false) {
+ $objText->setFont(null);
+ }
+ }
+
+ return $value;
+ }
+
+ private function parseFont(SimpleXMLElement $titleDetailPart): ?Font
+ {
+ if (!isset($titleDetailPart->pPr->defRPr)) {
+ return null;
+ }
+ $fontArray = [];
+ $fontArray['size'] = self::getAttributeInteger($titleDetailPart->pPr->defRPr, 'sz');
+ $fontArray['bold'] = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'b');
+ $fontArray['italic'] = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'i');
+ $fontArray['underscore'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'u');
+ $fontArray['strikethrough'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'strike');
+ $fontArray['cap'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'cap');
+
+ if (isset($titleDetailPart->pPr->defRPr->latin)) {
+ $fontArray['latin'] = self::getAttributeString($titleDetailPart->pPr->defRPr->latin, 'typeface');
+ }
+ if (isset($titleDetailPart->pPr->defRPr->ea)) {
+ $fontArray['eastAsian'] = self::getAttributeString($titleDetailPart->pPr->defRPr->ea, 'typeface');
+ }
+ if (isset($titleDetailPart->pPr->defRPr->cs)) {
+ $fontArray['complexScript'] = self::getAttributeString($titleDetailPart->pPr->defRPr->cs, 'typeface');
+ }
+ if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
+ $fontArray['chartColor'] = new ChartColor($this->readColor($titleDetailPart->pPr->defRPr->solidFill));
+ }
+ $font = new Font();
+ $font->setSize(null, true);
+ $font->applyFromArray($fontArray);
+
+ return $font;
+ }
+
+ /**
+ * @param ?SimpleXMLElement $chartDetail
+ */
+ private function readChartAttributes($chartDetail): array
+ {
+ $plotAttributes = [];
+ if (isset($chartDetail->dLbls)) {
+ if (isset($chartDetail->dLbls->dLblPos)) {
+ $plotAttributes['dLblPos'] = self::getAttributeString($chartDetail->dLbls->dLblPos, 'val');
+ }
+ if (isset($chartDetail->dLbls->numFmt)) {
+ $plotAttributes['numFmtCode'] = self::getAttributeString($chartDetail->dLbls->numFmt, 'formatCode');
+ $plotAttributes['numFmtLinked'] = self::getAttributeBoolean($chartDetail->dLbls->numFmt, 'sourceLinked');
+ }
+ if (isset($chartDetail->dLbls->showLegendKey)) {
+ $plotAttributes['showLegendKey'] = self::getAttributeString($chartDetail->dLbls->showLegendKey, 'val');
+ }
+ if (isset($chartDetail->dLbls->showVal)) {
+ $plotAttributes['showVal'] = self::getAttributeString($chartDetail->dLbls->showVal, 'val');
+ }
+ if (isset($chartDetail->dLbls->showCatName)) {
+ $plotAttributes['showCatName'] = self::getAttributeString($chartDetail->dLbls->showCatName, 'val');
+ }
+ if (isset($chartDetail->dLbls->showSerName)) {
+ $plotAttributes['showSerName'] = self::getAttributeString($chartDetail->dLbls->showSerName, 'val');
+ }
+ if (isset($chartDetail->dLbls->showPercent)) {
+ $plotAttributes['showPercent'] = self::getAttributeString($chartDetail->dLbls->showPercent, 'val');
+ }
+ if (isset($chartDetail->dLbls->showBubbleSize)) {
+ $plotAttributes['showBubbleSize'] = self::getAttributeString($chartDetail->dLbls->showBubbleSize, 'val');
+ }
+ if (isset($chartDetail->dLbls->showLeaderLines)) {
+ $plotAttributes['showLeaderLines'] = self::getAttributeString($chartDetail->dLbls->showLeaderLines, 'val');
+ }
+ if (isset($chartDetail->dLbls->spPr)) {
+ $sppr = $chartDetail->dLbls->spPr->children($this->aNamespace);
+ if (isset($sppr->solidFill)) {
+ $plotAttributes['labelFillColor'] = new ChartColor($this->readColor($sppr->solidFill));
+ }
+ if (isset($sppr->ln->solidFill)) {
+ $plotAttributes['labelBorderColor'] = new ChartColor($this->readColor($sppr->ln->solidFill));
+ }
+ }
+ if (isset($chartDetail->dLbls->txPr)) {
+ $txpr = $chartDetail->dLbls->txPr->children($this->aNamespace);
+ if (isset($txpr->p)) {
+ $plotAttributes['labelFont'] = $this->parseFont($txpr->p);
+ if (isset($txpr->p->pPr->defRPr->effectLst)) {
+ $labelEffects = new GridLines();
+ $this->readEffects($txpr->p->pPr->defRPr, $labelEffects, false);
+ $plotAttributes['labelEffects'] = $labelEffects;
+ }
+ }
+ }
+ }
+
+ return $plotAttributes;
+ }
+
+ private function setChartAttributes(Layout $plotArea, array $plotAttributes): void
+ {
+ foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
+ switch ($plotAttributeKey) {
+ case 'showLegendKey':
+ $plotArea->setShowLegendKey($plotAttributeValue);
+
+ break;
+ case 'showVal':
+ $plotArea->setShowVal($plotAttributeValue);
+
+ break;
+ case 'showCatName':
+ $plotArea->setShowCatName($plotAttributeValue);
+
+ break;
+ case 'showSerName':
+ $plotArea->setShowSerName($plotAttributeValue);
+
+ break;
+ case 'showPercent':
+ $plotArea->setShowPercent($plotAttributeValue);
+
+ break;
+ case 'showBubbleSize':
+ $plotArea->setShowBubbleSize($plotAttributeValue);
+
+ break;
+ case 'showLeaderLines':
+ $plotArea->setShowLeaderLines($plotAttributeValue);
+
+ break;
+ }
+ }
+ }
+
+ private function readEffects(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject, bool $getSppr = true): void
+ {
+ if (!isset($chartObject)) {
+ return;
+ }
+ if ($getSppr) {
+ if (!isset($chartDetail->spPr)) {
+ return;
+ }
+ $sppr = $chartDetail->spPr->children($this->aNamespace);
+ } else {
+ $sppr = $chartDetail;
+ }
+ if (isset($sppr->effectLst->glow)) {
+ $axisGlowSize = (float) self::getAttributeInteger($sppr->effectLst->glow, 'rad') / ChartProperties::POINTS_WIDTH_MULTIPLIER;
+ if ($axisGlowSize != 0.0) {
+ $colorArray = $this->readColor($sppr->effectLst->glow);
+ $chartObject->setGlowProperties($axisGlowSize, $colorArray['value'], $colorArray['alpha'], $colorArray['type']);
+ }
+ }
+
+ if (isset($sppr->effectLst->softEdge)) {
+ /** @var string */
+ $softEdgeSize = self::getAttributeString($sppr->effectLst->softEdge, 'rad');
+ if (is_numeric($softEdgeSize)) {
+ $chartObject->setSoftEdges((float) ChartProperties::xmlToPoints($softEdgeSize));
+ }
+ }
+
+ $type = '';
+ foreach (self::SHADOW_TYPES as $shadowType) {
+ if (isset($sppr->effectLst->$shadowType)) {
+ $type = $shadowType;
+
+ break;
+ }
+ }
+ if ($type !== '') {
+ /** @var string */
+ $blur = self::getAttributeString($sppr->effectLst->$type, 'blurRad');
+ $blur = is_numeric($blur) ? ChartProperties::xmlToPoints($blur) : null;
+ /** @var string */
+ $dist = self::getAttributeString($sppr->effectLst->$type, 'dist');
+ $dist = is_numeric($dist) ? ChartProperties::xmlToPoints($dist) : null;
+ /** @var string */
+ $direction = self::getAttributeString($sppr->effectLst->$type, 'dir');
+ $direction = is_numeric($direction) ? ChartProperties::xmlToAngle($direction) : null;
+ $algn = self::getAttributeString($sppr->effectLst->$type, 'algn');
+ $rot = self::getAttributeString($sppr->effectLst->$type, 'rotWithShape');
+ $size = [];
+ foreach (['sx', 'sy'] as $sizeType) {
+ $sizeValue = self::getAttributeString($sppr->effectLst->$type, $sizeType);
+ if (is_numeric($sizeValue)) {
+ $size[$sizeType] = ChartProperties::xmlToTenthOfPercent((string) $sizeValue);
+ } else {
+ $size[$sizeType] = null;
+ }
+ }
+ foreach (['kx', 'ky'] as $sizeType) {
+ $sizeValue = self::getAttributeString($sppr->effectLst->$type, $sizeType);
+ if (is_numeric($sizeValue)) {
+ $size[$sizeType] = ChartProperties::xmlToAngle((string) $sizeValue);
+ } else {
+ $size[$sizeType] = null;
+ }
+ }
+ $colorArray = $this->readColor($sppr->effectLst->$type);
+ $chartObject
+ ->setShadowProperty('effect', $type)
+ ->setShadowProperty('blur', $blur)
+ ->setShadowProperty('direction', $direction)
+ ->setShadowProperty('distance', $dist)
+ ->setShadowProperty('algn', $algn)
+ ->setShadowProperty('rotWithShape', $rot)
+ ->setShadowProperty('size', $size)
+ ->setShadowProperty('color', $colorArray);
+ }
+ }
+
+ private const SHADOW_TYPES = [
+ 'outerShdw',
+ 'innerShdw',
+ ];
+
+ private function readColor(SimpleXMLElement $colorXml): array
+ {
+ $result = [
+ 'type' => null,
+ 'value' => null,
+ 'alpha' => null,
+ 'brightness' => null,
+ ];
+ foreach (ChartColor::EXCEL_COLOR_TYPES as $type) {
+ if (isset($colorXml->$type)) {
+ $result['type'] = $type;
+ $result['value'] = self::getAttributeString($colorXml->$type, 'val');
+ if (isset($colorXml->$type->alpha)) {
+ /** @var string */
+ $alpha = self::getAttributeString($colorXml->$type->alpha, 'val');
+ if (is_numeric($alpha)) {
+ $result['alpha'] = ChartColor::alphaFromXml($alpha);
+ }
+ }
+ if (isset($colorXml->$type->lumMod)) {
+ /** @var string */
+ $brightness = self::getAttributeString($colorXml->$type->lumMod, 'val');
+ if (is_numeric($brightness)) {
+ $result['brightness'] = ChartColor::alphaFromXml($brightness);
+ }
+ }
+
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+ private function readLineStyle(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject): void
+ {
+ if (!isset($chartObject, $chartDetail->spPr)) {
+ return;
+ }
+ $sppr = $chartDetail->spPr->children($this->aNamespace);
+
+ if (!isset($sppr->ln)) {
+ return;
+ }
+ $lineWidth = null;
+ /** @var string */
+ $lineWidthTemp = self::getAttributeString($sppr->ln, 'w');
+ if (is_numeric($lineWidthTemp)) {
+ $lineWidth = ChartProperties::xmlToPoints($lineWidthTemp);
+ }
+ /** @var string */
+ $compoundType = self::getAttributeString($sppr->ln, 'cmpd');
+ /** @var string */
+ $dashType = self::getAttributeString($sppr->ln->prstDash, 'val');
+ /** @var string */
+ $capType = self::getAttributeString($sppr->ln, 'cap');
+ if (isset($sppr->ln->miter)) {
+ $joinType = ChartProperties::LINE_STYLE_JOIN_MITER;
+ } elseif (isset($sppr->ln->bevel)) {
+ $joinType = ChartProperties::LINE_STYLE_JOIN_BEVEL;
+ } else {
+ $joinType = '';
+ }
+ $headArrowSize = '';
+ $endArrowSize = '';
+ /** @var string */
+ $headArrowType = self::getAttributeString($sppr->ln->headEnd, 'type');
+ /** @var string */
+ $headArrowWidth = self::getAttributeString($sppr->ln->headEnd, 'w');
+ /** @var string */
+ $headArrowLength = self::getAttributeString($sppr->ln->headEnd, 'len');
+ /** @var string */
+ $endArrowType = self::getAttributeString($sppr->ln->tailEnd, 'type');
+ /** @var string */
+ $endArrowWidth = self::getAttributeString($sppr->ln->tailEnd, 'w');
+ /** @var string */
+ $endArrowLength = self::getAttributeString($sppr->ln->tailEnd, 'len');
+ $chartObject->setLineStyleProperties(
+ $lineWidth,
+ $compoundType,
+ $dashType,
+ $capType,
+ $joinType,
+ $headArrowType,
+ $headArrowSize,
+ $endArrowType,
+ $endArrowSize,
+ $headArrowWidth,
+ $headArrowLength,
+ $endArrowWidth,
+ $endArrowLength
+ );
+ $colorArray = $this->readColor($sppr->ln->solidFill);
+ $chartObject->getLineColor()->setColorPropertiesArray($colorArray);
+ }
+
+ private function setAxisProperties(SimpleXMLElement $chartDetail, ?Axis $whichAxis): void
+ {
+ if (!isset($whichAxis)) {
+ return;
+ }
+ if (isset($chartDetail->delete)) {
+ $whichAxis->setAxisOption('hidden', (string) self::getAttributeString($chartDetail->delete, 'val'));
+ }
+ if (isset($chartDetail->numFmt)) {
+ $whichAxis->setAxisNumberProperties(
+ (string) self::getAttributeString($chartDetail->numFmt, 'formatCode'),
+ null,
+ (int) self::getAttributeInteger($chartDetail->numFmt, 'sourceLinked')
+ );
+ }
+ if (isset($chartDetail->crossBetween)) {
+ $whichAxis->setCrossBetween((string) self::getAttributeString($chartDetail->crossBetween, 'val'));
+ }
+ if (isset($chartDetail->dispUnits, $chartDetail->dispUnits->builtInUnit)) {
+ $whichAxis->setAxisOption('dispUnitsBuiltIn', (string) self::getAttributeString($chartDetail->dispUnits->builtInUnit, 'val'));
+ if (isset($chartDetail->dispUnits->dispUnitsLbl)) {
+ $whichAxis->setDispUnitsTitle(new Title());
+ // TODO parse title elements
+ }
+ }
+ if (isset($chartDetail->majorTickMark)) {
+ $whichAxis->setAxisOption('major_tick_mark', (string) self::getAttributeString($chartDetail->majorTickMark, 'val'));
+ }
+ if (isset($chartDetail->minorTickMark)) {
+ $whichAxis->setAxisOption('minor_tick_mark', (string) self::getAttributeString($chartDetail->minorTickMark, 'val'));
+ }
+ if (isset($chartDetail->tickLblPos)) {
+ $whichAxis->setAxisOption('axis_labels', (string) self::getAttributeString($chartDetail->tickLblPos, 'val'));
+ }
+ if (isset($chartDetail->crosses)) {
+ $whichAxis->setAxisOption('horizontal_crosses', (string) self::getAttributeString($chartDetail->crosses, 'val'));
+ }
+ if (isset($chartDetail->crossesAt)) {
+ $whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttributeString($chartDetail->crossesAt, 'val'));
+ }
+ if (isset($chartDetail->scaling->logBase)) {
+ $whichAxis->setAxisOption('logBase', (string) self::getAttributeString($chartDetail->scaling->logBase, 'val'));
+ }
+ if (isset($chartDetail->scaling->orientation)) {
+ $whichAxis->setAxisOption('orientation', (string) self::getAttributeString($chartDetail->scaling->orientation, 'val'));
+ }
+ if (isset($chartDetail->scaling->max)) {
+ $whichAxis->setAxisOption('maximum', (string) self::getAttributeString($chartDetail->scaling->max, 'val'));
+ }
+ if (isset($chartDetail->scaling->min)) {
+ $whichAxis->setAxisOption('minimum', (string) self::getAttributeString($chartDetail->scaling->min, 'val'));
+ }
+ if (isset($chartDetail->scaling->min)) {
+ $whichAxis->setAxisOption('minimum', (string) self::getAttributeString($chartDetail->scaling->min, 'val'));
+ }
+ if (isset($chartDetail->majorUnit)) {
+ $whichAxis->setAxisOption('major_unit', (string) self::getAttributeString($chartDetail->majorUnit, 'val'));
+ }
+ if (isset($chartDetail->minorUnit)) {
+ $whichAxis->setAxisOption('minor_unit', (string) self::getAttributeString($chartDetail->minorUnit, 'val'));
+ }
+ if (isset($chartDetail->baseTimeUnit)) {
+ $whichAxis->setAxisOption('baseTimeUnit', (string) self::getAttributeString($chartDetail->baseTimeUnit, 'val'));
+ }
+ if (isset($chartDetail->majorTimeUnit)) {
+ $whichAxis->setAxisOption('majorTimeUnit', (string) self::getAttributeString($chartDetail->majorTimeUnit, 'val'));
+ }
+ if (isset($chartDetail->minorTimeUnit)) {
+ $whichAxis->setAxisOption('minorTimeUnit', (string) self::getAttributeString($chartDetail->minorTimeUnit, 'val'));
+ }
+ if (isset($chartDetail->txPr)) {
+ $children = $chartDetail->txPr->children($this->aNamespace);
+ $addAxisText = false;
+ $axisText = new AxisText();
+ if (isset($children->bodyPr)) {
+ /** @var string */
+ $textRotation = self::getAttributeString($children->bodyPr, 'rot');
+ if (is_numeric($textRotation)) {
+ $axisText->setRotation((int) ChartProperties::xmlToAngle($textRotation));
+ $addAxisText = true;
+ }
+ }
+ if (isset($children->p->pPr->defRPr)) {
+ $font = $this->parseFont($children->p);
+ if ($font !== null) {
+ $axisText->setFont($font);
+ $addAxisText = true;
+ }
+ }
+ if (isset($children->p->pPr->defRPr->effectLst)) {
+ $this->readEffects($children->p->pPr->defRPr, $axisText, false);
+ $addAxisText = true;
+ }
+ if ($addAxisText) {
+ $whichAxis->setAxisText($axisText);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
new file mode 100644
index 0000000..41eab03
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
@@ -0,0 +1,219 @@
+worksheet = $workSheet;
+ $this->worksheetXml = $worksheetXml;
+ }
+
+ /**
+ * Set Worksheet column attributes by attributes array passed.
+ *
+ * @param string $columnAddress A, B, ... DX, ...
+ * @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
+ * 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
+ */
+ private function setColumnAttributes(string $columnAddress, array $columnAttributes): void
+ {
+ if (isset($columnAttributes['xfIndex'])) {
+ $this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
+ }
+ if (isset($columnAttributes['visible'])) {
+ $this->worksheet->getColumnDimension($columnAddress)->setVisible($columnAttributes['visible']);
+ }
+ if (isset($columnAttributes['collapsed'])) {
+ $this->worksheet->getColumnDimension($columnAddress)->setCollapsed($columnAttributes['collapsed']);
+ }
+ if (isset($columnAttributes['outlineLevel'])) {
+ $this->worksheet->getColumnDimension($columnAddress)->setOutlineLevel($columnAttributes['outlineLevel']);
+ }
+ if (isset($columnAttributes['width'])) {
+ $this->worksheet->getColumnDimension($columnAddress)->setWidth($columnAttributes['width']);
+ }
+ }
+
+ /**
+ * Set Worksheet row attributes by attributes array passed.
+ *
+ * @param int $rowNumber 1, 2, 3, ... 99, ...
+ * @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
+ * 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
+ */
+ private function setRowAttributes(int $rowNumber, array $rowAttributes): void
+ {
+ if (isset($rowAttributes['xfIndex'])) {
+ $this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
+ }
+ if (isset($rowAttributes['visible'])) {
+ $this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
+ }
+ if (isset($rowAttributes['collapsed'])) {
+ $this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
+ }
+ if (isset($rowAttributes['outlineLevel'])) {
+ $this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
+ }
+ if (isset($rowAttributes['rowHeight'])) {
+ $this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
+ }
+ }
+
+ public function load(?IReadFilter $readFilter = null, bool $readDataOnly = false): void
+ {
+ if ($this->worksheetXml === null) {
+ return;
+ }
+
+ $columnsAttributes = [];
+ $rowsAttributes = [];
+ if (isset($this->worksheetXml->cols)) {
+ $columnsAttributes = $this->readColumnAttributes($this->worksheetXml->cols, $readDataOnly);
+ }
+
+ if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
+ $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
+ }
+
+ if ($readFilter !== null && $readFilter::class === DefaultReadFilter::class) {
+ $readFilter = null;
+ }
+
+ // set columns/rows attributes
+ $columnsAttributesAreSet = [];
+ foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
+ if (
+ $readFilter === null
+ || !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)
+ ) {
+ if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
+ $this->setColumnAttributes($columnCoordinate, $columnAttributes);
+ $columnsAttributesAreSet[$columnCoordinate] = true;
+ }
+ }
+ }
+
+ $rowsAttributesAreSet = [];
+ foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
+ if (
+ $readFilter === null
+ || !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)
+ ) {
+ if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
+ $this->setRowAttributes($rowCoordinate, $rowAttributes);
+ $rowsAttributesAreSet[$rowCoordinate] = true;
+ }
+ }
+ }
+ }
+
+ private function isFilteredColumn(IReadFilter $readFilter, string $columnCoordinate, array $rowsAttributes): bool
+ {
+ foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
+ if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function readColumnAttributes(SimpleXMLElement $worksheetCols, bool $readDataOnly): array
+ {
+ $columnAttributes = [];
+
+ foreach ($worksheetCols->col as $columnx) {
+ $column = $columnx->attributes();
+ if ($column !== null) {
+ $startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
+ $endColumn = Coordinate::stringFromColumnIndex((int) $column['max']);
+ ++$endColumn;
+ for ($columnAddress = $startColumn; $columnAddress !== $endColumn; ++$columnAddress) {
+ $columnAttributes[$columnAddress] = $this->readColumnRangeAttributes($column, $readDataOnly);
+
+ if ((int) ($column['max']) == 16384) {
+ break;
+ }
+ }
+ }
+ }
+
+ return $columnAttributes;
+ }
+
+ private function readColumnRangeAttributes(?SimpleXMLElement $column, bool $readDataOnly): array
+ {
+ $columnAttributes = [];
+ if ($column !== null) {
+ if (isset($column['style']) && !$readDataOnly) {
+ $columnAttributes['xfIndex'] = (int) $column['style'];
+ }
+ if (isset($column['hidden']) && self::boolean($column['hidden'])) {
+ $columnAttributes['visible'] = false;
+ }
+ if (isset($column['collapsed']) && self::boolean($column['collapsed'])) {
+ $columnAttributes['collapsed'] = true;
+ }
+ if (isset($column['outlineLevel']) && ((int) $column['outlineLevel']) > 0) {
+ $columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
+ }
+ if (isset($column['width'])) {
+ $columnAttributes['width'] = (float) $column['width'];
+ }
+ }
+
+ return $columnAttributes;
+ }
+
+ private function isFilteredRow(IReadFilter $readFilter, int $rowCoordinate, array $columnsAttributes): bool
+ {
+ foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
+ if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDataOnly): array
+ {
+ $rowAttributes = [];
+
+ foreach ($worksheetRow as $rowx) {
+ $row = $rowx->attributes();
+ if ($row !== null) {
+ if (isset($row['ht']) && !$readDataOnly) {
+ $rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
+ }
+ if (isset($row['hidden']) && self::boolean($row['hidden'])) {
+ $rowAttributes[(int) $row['r']]['visible'] = false;
+ }
+ if (isset($row['collapsed']) && self::boolean($row['collapsed'])) {
+ $rowAttributes[(int) $row['r']]['collapsed'] = true;
+ }
+ if (isset($row['outlineLevel']) && (int) $row['outlineLevel'] > 0) {
+ $rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
+ }
+ if (isset($row['s']) && !$readDataOnly) {
+ $rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
+ }
+ }
+ }
+
+ return $rowAttributes;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
new file mode 100644
index 0000000..280c75e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
@@ -0,0 +1,340 @@
+worksheet = $workSheet;
+ $this->worksheetXml = $worksheetXml;
+ $this->dxfs = $dxfs;
+ $this->styleReader = $styleReader;
+ }
+
+ public function load(): void
+ {
+ $selectedCells = $this->worksheet->getSelectedCells();
+
+ $this->setConditionalStyles(
+ $this->worksheet,
+ $this->readConditionalStyles($this->worksheetXml),
+ $this->worksheetXml->extLst
+ );
+
+ $this->worksheet->setSelectedCells($selectedCells);
+ }
+
+ public function loadFromExt(): void
+ {
+ $selectedCells = $this->worksheet->getSelectedCells();
+
+ $this->ns = $this->worksheetXml->getNamespaces(true);
+ $this->setConditionalsFromExt(
+ $this->readConditionalsFromExt($this->worksheetXml->extLst)
+ );
+
+ $this->worksheet->setSelectedCells($selectedCells);
+ }
+
+ private function setConditionalsFromExt(array $conditionals): void
+ {
+ foreach ($conditionals as $conditionalRange => $cfRules) {
+ ksort($cfRules);
+ // Priority is used as the key for sorting; but may not start at 0,
+ // so we use array_values to reset the index after sorting.
+ $this->worksheet->getStyle($conditionalRange)
+ ->setConditionalStyles(array_values($cfRules));
+ }
+ }
+
+ private function readConditionalsFromExt(SimpleXMLElement $extLst): array
+ {
+ $conditionals = [];
+ if (!isset($extLst->ext)) {
+ return $conditionals;
+ }
+
+ foreach ($extLst->ext as $extlstcond) {
+ $extAttrs = $extlstcond->attributes() ?? [];
+ $extUri = (string) ($extAttrs['uri'] ?? '');
+ if ($extUri !== '{78C0D931-6437-407d-A8EE-F0AAD7539E65}') {
+ continue;
+ }
+ $conditionalFormattingRuleXml = $extlstcond->children($this->ns['x14']);
+ if (!$conditionalFormattingRuleXml->conditionalFormattings) {
+ return [];
+ }
+
+ foreach ($conditionalFormattingRuleXml->children($this->ns['x14']) as $extFormattingXml) {
+ $extFormattingRangeXml = $extFormattingXml->children($this->ns['xm']);
+ if (!$extFormattingRangeXml->sqref) {
+ continue;
+ }
+
+ $sqref = (string) $extFormattingRangeXml->sqref;
+ $extCfRuleXml = $extFormattingXml->cfRule;
+
+ $attributes = $extCfRuleXml->attributes();
+ if (!$attributes) {
+ continue;
+ }
+ $conditionType = (string) $attributes->type;
+ if (
+ !Conditional::isValidConditionType($conditionType)
+ || $conditionType === Conditional::CONDITION_DATABAR
+ ) {
+ continue;
+ }
+
+ $priority = (int) $attributes->priority;
+
+ $conditional = $this->readConditionalRuleFromExt($extCfRuleXml, $attributes);
+ $cfStyle = $this->readStyleFromExt($extCfRuleXml);
+ $conditional->setStyle($cfStyle);
+ $conditionals[$sqref][$priority] = $conditional;
+ }
+ }
+
+ return $conditionals;
+ }
+
+ private function readConditionalRuleFromExt(SimpleXMLElement $cfRuleXml, SimpleXMLElement $attributes): Conditional
+ {
+ $conditionType = (string) $attributes->type;
+ $operatorType = (string) $attributes->operator;
+
+ $operands = [];
+ foreach ($cfRuleXml->children($this->ns['xm']) as $cfRuleOperandsXml) {
+ $operands[] = (string) $cfRuleOperandsXml;
+ }
+
+ $conditional = new Conditional();
+ $conditional->setConditionType($conditionType);
+ $conditional->setOperatorType($operatorType);
+ if (
+ $conditionType === Conditional::CONDITION_CONTAINSTEXT
+ || $conditionType === Conditional::CONDITION_NOTCONTAINSTEXT
+ || $conditionType === Conditional::CONDITION_BEGINSWITH
+ || $conditionType === Conditional::CONDITION_ENDSWITH
+ || $conditionType === Conditional::CONDITION_TIMEPERIOD
+ ) {
+ $conditional->setText(array_pop($operands) ?? '');
+ }
+ $conditional->setConditions($operands);
+
+ return $conditional;
+ }
+
+ private function readStyleFromExt(SimpleXMLElement $extCfRuleXml): Style
+ {
+ $cfStyle = new Style(false, true);
+ if ($extCfRuleXml->dxf) {
+ $styleXML = $extCfRuleXml->dxf->children();
+
+ if ($styleXML->borders) {
+ $this->styleReader->readBorderStyle($cfStyle->getBorders(), $styleXML->borders);
+ }
+ if ($styleXML->fill) {
+ $this->styleReader->readFillStyle($cfStyle->getFill(), $styleXML->fill);
+ }
+ }
+
+ return $cfStyle;
+ }
+
+ private function readConditionalStyles(SimpleXMLElement $xmlSheet): array
+ {
+ $conditionals = [];
+ foreach ($xmlSheet->conditionalFormatting as $conditional) {
+ foreach ($conditional->cfRule as $cfRule) {
+ if (Conditional::isValidConditionType((string) $cfRule['type']) && (!isset($cfRule['dxfId']) || isset($this->dxfs[(int) ($cfRule['dxfId'])]))) {
+ $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
+ } elseif ((string) $cfRule['type'] == Conditional::CONDITION_DATABAR) {
+ $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
+ }
+ }
+ }
+
+ return $conditionals;
+ }
+
+ private function setConditionalStyles(Worksheet $worksheet, array $conditionals, SimpleXMLElement $xmlExtLst): void
+ {
+ foreach ($conditionals as $cellRangeReference => $cfRules) {
+ ksort($cfRules);
+ $conditionalStyles = $this->readStyleRules($cfRules, $xmlExtLst);
+
+ // Extract all cell references in $cellRangeReference
+ $cellBlocks = explode(' ', str_replace('$', '', strtoupper($cellRangeReference)));
+ foreach ($cellBlocks as $cellBlock) {
+ $worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
+ }
+ }
+ }
+
+ private function readStyleRules(array $cfRules, SimpleXMLElement $extLst): array
+ {
+ $conditionalFormattingRuleExtensions = ConditionalFormattingRuleExtension::parseExtLstXml($extLst);
+ $conditionalStyles = [];
+
+ /** @var SimpleXMLElement $cfRule */
+ foreach ($cfRules as $cfRule) {
+ $objConditional = new Conditional();
+ $objConditional->setConditionType((string) $cfRule['type']);
+ $objConditional->setOperatorType((string) $cfRule['operator']);
+ $objConditional->setNoFormatSet(!isset($cfRule['dxfId']));
+
+ if ((string) $cfRule['text'] != '') {
+ $objConditional->setText((string) $cfRule['text']);
+ } elseif ((string) $cfRule['timePeriod'] != '') {
+ $objConditional->setText((string) $cfRule['timePeriod']);
+ }
+
+ if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
+ $objConditional->setStopIfTrue(true);
+ }
+
+ if (count($cfRule->formula) >= 1) {
+ foreach ($cfRule->formula as $formulax) {
+ $formula = (string) $formulax;
+ if ($formula === 'TRUE') {
+ $objConditional->addCondition(true);
+ } elseif ($formula === 'FALSE') {
+ $objConditional->addCondition(false);
+ } else {
+ $objConditional->addCondition($formula);
+ }
+ }
+ } else {
+ $objConditional->addCondition('');
+ }
+
+ if (isset($cfRule->dataBar)) {
+ $objConditional->setDataBar(
+ $this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions)
+ );
+ } elseif (isset($cfRule->colorScale)) {
+ $objConditional->setColorScale(
+ $this->readColorScale($cfRule)
+ );
+ } elseif (isset($cfRule['dxfId'])) {
+ $objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
+ }
+
+ $conditionalStyles[] = $objConditional;
+ }
+
+ return $conditionalStyles;
+ }
+
+ /**
+ * @param SimpleXMLElement|stdClass $cfRule
+ */
+ private function readDataBarOfConditionalRule($cfRule, array $conditionalFormattingRuleExtensions): ConditionalDataBar
+ {
+ $dataBar = new ConditionalDataBar();
+ //dataBar attribute
+ if (isset($cfRule->dataBar['showValue'])) {
+ $dataBar->setShowValue((bool) $cfRule->dataBar['showValue']);
+ }
+
+ //dataBar children
+ //conditionalFormatValueObjects
+ $cfvoXml = $cfRule->dataBar->cfvo;
+ $cfvoIndex = 0;
+ foreach ((count($cfvoXml) > 1 ? $cfvoXml : [$cfvoXml]) as $cfvo) {
+ if ($cfvoIndex === 0) {
+ $dataBar->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
+ }
+ if ($cfvoIndex === 1) {
+ $dataBar->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
+ }
+ ++$cfvoIndex;
+ }
+
+ //color
+ if (isset($cfRule->dataBar->color)) {
+ $dataBar->setColor($this->styleReader->readColor($cfRule->dataBar->color));
+ }
+ //extLst
+ $this->readDataBarExtLstOfConditionalRule($dataBar, $cfRule, $conditionalFormattingRuleExtensions);
+
+ return $dataBar;
+ }
+
+ private function readColorScale(simpleXMLElement|stdClass $cfRule): ConditionalColorScale
+ {
+ $colorScale = new ConditionalColorScale();
+ $types = [];
+ foreach ($cfRule->colorScale->cfvo as $cfvoXml) {
+ $attr = $cfvoXml->attributes() ?? [];
+ $type = (string) ($attr['type'] ?? '');
+ $types[] = $type;
+ $val = $attr['val'] ?? null;
+ if ($type === 'min') {
+ $colorScale->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject($type, $val));
+ } elseif ($type === 'percentile') {
+ $colorScale->setMidpointConditionalFormatValueObject(new ConditionalFormatValueObject($type, $val));
+ } elseif ($type === 'max') {
+ $colorScale->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject($type, $val));
+ }
+ }
+ $idx = 0;
+ foreach ($cfRule->colorScale->color as $color) {
+ $type = $types[$idx];
+ $rgb = $this->styleReader->readColor($color);
+ if ($type === 'min') {
+ $colorScale->setMinimumColor(new Color($rgb));
+ } elseif ($type === 'percentile') {
+ $colorScale->setMidpointColor(new Color($rgb));
+ } elseif ($type === 'max') {
+ $colorScale->setMaximumColor(new Color($rgb));
+ }
+ ++$idx;
+ }
+
+ return $colorScale;
+ }
+
+ /**
+ * @param SimpleXMLElement|stdClass $cfRule
+ */
+ private function readDataBarExtLstOfConditionalRule(ConditionalDataBar $dataBar, $cfRule, array $conditionalFormattingRuleExtensions): void
+ {
+ if (isset($cfRule->extLst)) {
+ $ns = $cfRule->extLst->getNamespaces(true);
+ foreach ((count($cfRule->extLst) > 0 ? $cfRule->extLst->ext : [$cfRule->extLst->ext]) as $ext) {
+ $extId = (string) $ext->children($ns['x14'])->id;
+ if (isset($conditionalFormattingRuleExtensions[$extId]) && (string) $ext['uri'] === '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}') {
+ $dataBar->setConditionalFormattingRuleExt($conditionalFormattingRuleExtensions[$extId]);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
new file mode 100644
index 0000000..d494dc9
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
@@ -0,0 +1,65 @@
+worksheet = $workSheet;
+ $this->worksheetXml = $worksheetXml;
+ }
+
+ public function load(): void
+ {
+ foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
+ // Uppercase coordinate
+ $range = strtoupper((string) $dataValidation['sqref']);
+ $rangeSet = explode(' ', $range);
+ foreach ($rangeSet as $range) {
+ if (preg_match('/^[A-Z]{1,3}\\d{1,7}/', $range, $matches) === 1) {
+ // Ensure left/top row of range exists, thereby
+ // adjusting high row/column.
+ $this->worksheet->getCell($matches[0]);
+ }
+ }
+ }
+ foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
+ // Uppercase coordinate
+ $range = strtoupper((string) $dataValidation['sqref']);
+ $rangeSet = explode(' ', $range);
+ foreach ($rangeSet as $range) {
+ $stRange = $this->worksheet->shrinkRangeToFit($range);
+
+ // Extract all cell references in $range
+ foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
+ // Create validation
+ $docValidation = $this->worksheet->getCell($reference)->getDataValidation();
+ $docValidation->setType((string) $dataValidation['type']);
+ $docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
+ $docValidation->setOperator((string) $dataValidation['operator']);
+ $docValidation->setAllowBlank(filter_var($dataValidation['allowBlank'], FILTER_VALIDATE_BOOLEAN));
+ // showDropDown is inverted (works as hideDropDown if true)
+ $docValidation->setShowDropDown(!filter_var($dataValidation['showDropDown'], FILTER_VALIDATE_BOOLEAN));
+ $docValidation->setShowInputMessage(filter_var($dataValidation['showInputMessage'], FILTER_VALIDATE_BOOLEAN));
+ $docValidation->setShowErrorMessage(filter_var($dataValidation['showErrorMessage'], FILTER_VALIDATE_BOOLEAN));
+ $docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
+ $docValidation->setError((string) $dataValidation['error']);
+ $docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
+ $docValidation->setPrompt((string) $dataValidation['prompt']);
+ $docValidation->setFormula1((string) $dataValidation->formula1);
+ $docValidation->setFormula2((string) $dataValidation->formula2);
+ $docValidation->setSqref($range);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
new file mode 100644
index 0000000..e047f23
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
@@ -0,0 +1,65 @@
+worksheet = $workSheet;
+ }
+
+ public function readHyperlinks(SimpleXMLElement $relsWorksheet): void
+ {
+ foreach ($relsWorksheet->children(Namespaces::RELATIONSHIPS)->Relationship as $elementx) {
+ $element = Xlsx::getAttributes($elementx);
+ if ($element->Type == Namespaces::HYPERLINK) {
+ $this->hyperlinks[(string) $element->Id] = (string) $element->Target;
+ }
+ }
+ }
+
+ public function setHyperlinks(SimpleXMLElement $worksheetXml): void
+ {
+ foreach ($worksheetXml->children(Namespaces::MAIN)->hyperlink as $hyperlink) {
+ if ($hyperlink !== null) {
+ $this->setHyperlink($hyperlink, $this->worksheet);
+ }
+ }
+ }
+
+ private function setHyperlink(SimpleXMLElement $hyperlink, Worksheet $worksheet): void
+ {
+ // Link url
+ $linkRel = Xlsx::getAttributes($hyperlink, Namespaces::SCHEMA_OFFICE_DOCUMENT);
+
+ $attributes = Xlsx::getAttributes($hyperlink);
+ foreach (Coordinate::extractAllCellReferencesInRange($attributes->ref) as $cellReference) {
+ $cell = $worksheet->getCell($cellReference);
+ if (isset($linkRel['id'])) {
+ $hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']] ?? null;
+ if (isset($attributes['location'])) {
+ $hyperlinkUrl .= '#' . (string) $attributes['location'];
+ }
+ $cell->getHyperlink()->setUrl($hyperlinkUrl);
+ } elseif (isset($attributes['location'])) {
+ $cell->getHyperlink()->setUrl('sheet://' . (string) $attributes['location']);
+ }
+
+ // Tooltip
+ if (isset($attributes['tooltip'])) {
+ $cell->getHyperlink()->setTooltip((string) $attributes['tooltip']);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php
new file mode 100644
index 0000000..fa3e57e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php
@@ -0,0 +1,118 @@
+worksheet = $workSheet;
+ $this->worksheetXml = $worksheetXml;
+ }
+
+ public function load(array $unparsedLoadedData): array
+ {
+ $worksheetXml = $this->worksheetXml;
+ if ($worksheetXml === null) {
+ return $unparsedLoadedData;
+ }
+
+ $this->margins($worksheetXml, $this->worksheet);
+ $unparsedLoadedData = $this->pageSetup($worksheetXml, $this->worksheet, $unparsedLoadedData);
+ $this->headerFooter($worksheetXml, $this->worksheet);
+ $this->pageBreaks($worksheetXml, $this->worksheet);
+
+ return $unparsedLoadedData;
+ }
+
+ private function margins(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+ {
+ if ($xmlSheet->pageMargins) {
+ $docPageMargins = $worksheet->getPageMargins();
+ $docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
+ $docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
+ $docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
+ $docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
+ $docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
+ $docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
+ }
+ }
+
+ private function pageSetup(SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData): array
+ {
+ if ($xmlSheet->pageSetup) {
+ $docPageSetup = $worksheet->getPageSetup();
+
+ if (isset($xmlSheet->pageSetup['orientation'])) {
+ $docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
+ }
+ if (isset($xmlSheet->pageSetup['paperSize'])) {
+ $docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
+ }
+ if (isset($xmlSheet->pageSetup['scale'])) {
+ $docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
+ }
+ if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
+ $docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
+ }
+ if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
+ $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
+ }
+ if (
+ isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber'])
+ && self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])
+ ) {
+ $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
+ }
+ if (isset($xmlSheet->pageSetup['pageOrder'])) {
+ $docPageSetup->setPageOrder((string) $xmlSheet->pageSetup['pageOrder']);
+ }
+
+ $relAttributes = $xmlSheet->pageSetup->attributes(Namespaces::SCHEMA_OFFICE_DOCUMENT);
+ if (isset($relAttributes['id'])) {
+ $relid = (string) $relAttributes['id'];
+ if (!str_ends_with($relid, 'ps')) {
+ $relid .= 'ps';
+ }
+ $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = $relid;
+ }
+ }
+
+ return $unparsedLoadedData;
+ }
+
+ private function headerFooter(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+ {
+ if ($xmlSheet->headerFooter) {
+ $docHeaderFooter = $worksheet->getHeaderFooter();
+
+ if (
+ isset($xmlSheet->headerFooter['differentOddEven'])
+ && self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])
+ ) {
+ $docHeaderFooter->setDifferentOddEven(true);
+ } else {
+ $docHeaderFooter->setDifferentOddEven(false);
+ }
+ if (
+ isset($xmlSheet->headerFooter['differentFirst'])
+ && self::boolean((string) $xmlSheet->headerFooter['differentFirst'])
+ ) {
+ $docHeaderFooter->setDifferentFirst(true);
+ } else {
+ $docHeaderFooter->setDifferentFirst(false);
+ }
+ if (
+ isset($xmlSheet->headerFooter['scaleWithDoc'])
+ && !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])
+ ) {
+ $docHeaderFooter->setScaleWithDocument(false);
+ } else {
+ $docHeaderFooter->setScaleWithDocument(true);
+ }
+ if (
+ isset($xmlSheet->headerFooter['alignWithMargins'])
+ && !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])
+ ) {
+ $docHeaderFooter->setAlignWithMargins(false);
+ } else {
+ $docHeaderFooter->setAlignWithMargins(true);
+ }
+
+ $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
+ $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
+ $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
+ $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
+ $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
+ $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
+ }
+ }
+
+ private function pageBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+ {
+ if ($xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk) {
+ $this->rowBreaks($xmlSheet, $worksheet);
+ }
+ if ($xmlSheet->colBreaks && $xmlSheet->colBreaks->brk) {
+ $this->columnBreaks($xmlSheet, $worksheet);
+ }
+ }
+
+ private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+ {
+ foreach ($xmlSheet->rowBreaks->brk as $brk) {
+ $rowBreakMax = isset($brk['max']) ? ((int) $brk['max']) : -1;
+ if ($brk['man']) {
+ $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW, $rowBreakMax);
+ }
+ }
+ }
+
+ private function columnBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
+ {
+ foreach ($xmlSheet->colBreaks->brk as $brk) {
+ if ($brk['man']) {
+ $worksheet->setBreak(
+ Coordinate::stringFromColumnIndex(((int) $brk['id']) + 1) . '1',
+ Worksheet::BREAK_COLUMN
+ );
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
new file mode 100644
index 0000000..d59fa28
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
@@ -0,0 +1,106 @@
+securityScanner = $securityScanner;
+ $this->docProps = $docProps;
+ }
+
+ private static function nullOrSimple(mixed $obj): ?SimpleXMLElement
+ {
+ return ($obj instanceof SimpleXMLElement) ? $obj : null;
+ }
+
+ private function extractPropertyData(string $propertyData): ?SimpleXMLElement
+ {
+ // okay to omit namespace because everything will be processed by xpath
+ $obj = simplexml_load_string(
+ $this->securityScanner->scan($propertyData),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+
+ return self::nullOrSimple($obj);
+ }
+
+ public function readCoreProperties(string $propertyData): void
+ {
+ $xmlCore = $this->extractPropertyData($propertyData);
+
+ if (is_object($xmlCore)) {
+ $xmlCore->registerXPathNamespace('dc', Namespaces::DC_ELEMENTS);
+ $xmlCore->registerXPathNamespace('dcterms', Namespaces::DC_TERMS);
+ $xmlCore->registerXPathNamespace('cp', Namespaces::CORE_PROPERTIES2);
+
+ $this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
+ $this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
+ $this->docProps->setCreated((string) self::getArrayItem($xmlCore->xpath('dcterms:created'))); //! respect xsi:type
+ $this->docProps->setModified((string) self::getArrayItem($xmlCore->xpath('dcterms:modified'))); //! respect xsi:type
+ $this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
+ $this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
+ $this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
+ $this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
+ $this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
+ }
+ }
+
+ public function readExtendedProperties(string $propertyData): void
+ {
+ $xmlCore = $this->extractPropertyData($propertyData);
+
+ if (is_object($xmlCore)) {
+ if (isset($xmlCore->Company)) {
+ $this->docProps->setCompany((string) $xmlCore->Company);
+ }
+ if (isset($xmlCore->Manager)) {
+ $this->docProps->setManager((string) $xmlCore->Manager);
+ }
+ if (isset($xmlCore->HyperlinkBase)) {
+ $this->docProps->setHyperlinkBase((string) $xmlCore->HyperlinkBase);
+ }
+ }
+ }
+
+ public function readCustomProperties(string $propertyData): void
+ {
+ $xmlCore = $this->extractPropertyData($propertyData);
+
+ if (is_object($xmlCore)) {
+ foreach ($xmlCore as $xmlProperty) {
+ /** @var SimpleXMLElement $xmlProperty */
+ $cellDataOfficeAttributes = $xmlProperty->attributes();
+ if (isset($cellDataOfficeAttributes['name'])) {
+ $propertyName = (string) $cellDataOfficeAttributes['name'];
+ $cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+ $attributeType = $cellDataOfficeChildren->getName();
+ $attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
+ $attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType);
+ $attributeType = DocumentProperties::convertPropertyType($attributeType);
+ $this->docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param null|array|false $array
+ */
+ private static function getArrayItem($array, mixed $key = 0): ?SimpleXMLElement
+ {
+ return is_array($array) ? ($array[$key] ?? null) : null;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php
new file mode 100644
index 0000000..fb7a393
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php
@@ -0,0 +1,26 @@
+master = $master;
+ $this->formula = $formula;
+ }
+
+ public function master(): string
+ {
+ return $this->master;
+ }
+
+ public function formula(): string
+ {
+ return $this->formula;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
new file mode 100644
index 0000000..136b92f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
@@ -0,0 +1,138 @@
+worksheet = $workSheet;
+ $this->worksheetXml = $worksheetXml;
+ }
+
+ public function load(bool $readDataOnly, Styles $styleReader): void
+ {
+ if ($this->worksheetXml === null) {
+ return;
+ }
+
+ if (isset($this->worksheetXml->sheetPr)) {
+ $sheetPr = $this->worksheetXml->sheetPr;
+ $this->tabColor($sheetPr, $styleReader);
+ $this->codeName($sheetPr);
+ $this->outlines($sheetPr);
+ $this->pageSetup($sheetPr);
+ }
+
+ if (isset($this->worksheetXml->sheetFormatPr)) {
+ $this->sheetFormat($this->worksheetXml->sheetFormatPr);
+ }
+
+ if (!$readDataOnly && isset($this->worksheetXml->printOptions)) {
+ $this->printOptions($this->worksheetXml->printOptions);
+ }
+ }
+
+ private function tabColor(SimpleXMLElement $sheetPr, Styles $styleReader): void
+ {
+ if (isset($sheetPr->tabColor)) {
+ $this->worksheet->getTabColor()->setARGB($styleReader->readColor($sheetPr->tabColor));
+ }
+ }
+
+ private function codeName(SimpleXMLElement $sheetPrx): void
+ {
+ $sheetPr = $sheetPrx->attributes() ?? [];
+ if (isset($sheetPr['codeName'])) {
+ $this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
+ }
+ }
+
+ private function outlines(SimpleXMLElement $sheetPr): void
+ {
+ if (isset($sheetPr->outlinePr)) {
+ $attr = $sheetPr->outlinePr->attributes() ?? [];
+ if (
+ isset($attr['summaryRight'])
+ && !self::boolean((string) $attr['summaryRight'])
+ ) {
+ $this->worksheet->setShowSummaryRight(false);
+ } else {
+ $this->worksheet->setShowSummaryRight(true);
+ }
+
+ if (
+ isset($attr['summaryBelow'])
+ && !self::boolean((string) $attr['summaryBelow'])
+ ) {
+ $this->worksheet->setShowSummaryBelow(false);
+ } else {
+ $this->worksheet->setShowSummaryBelow(true);
+ }
+ }
+ }
+
+ private function pageSetup(SimpleXMLElement $sheetPr): void
+ {
+ if (isset($sheetPr->pageSetUpPr)) {
+ $attr = $sheetPr->pageSetUpPr->attributes() ?? [];
+ if (
+ isset($attr['fitToPage'])
+ && !self::boolean((string) $attr['fitToPage'])
+ ) {
+ $this->worksheet->getPageSetup()->setFitToPage(false);
+ } else {
+ $this->worksheet->getPageSetup()->setFitToPage(true);
+ }
+ }
+ }
+
+ private function sheetFormat(SimpleXMLElement $sheetFormatPrx): void
+ {
+ $sheetFormatPr = $sheetFormatPrx->attributes() ?? [];
+ if (
+ isset($sheetFormatPr['customHeight'])
+ && self::boolean((string) $sheetFormatPr['customHeight'])
+ && isset($sheetFormatPr['defaultRowHeight'])
+ ) {
+ $this->worksheet->getDefaultRowDimension()
+ ->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
+ }
+
+ if (isset($sheetFormatPr['defaultColWidth'])) {
+ $this->worksheet->getDefaultColumnDimension()
+ ->setWidth((float) $sheetFormatPr['defaultColWidth']);
+ }
+
+ if (
+ isset($sheetFormatPr['zeroHeight'])
+ && ((string) $sheetFormatPr['zeroHeight'] === '1')
+ ) {
+ $this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
+ }
+ }
+
+ private function printOptions(SimpleXMLElement $printOptionsx): void
+ {
+ $printOptions = $printOptionsx->attributes() ?? [];
+ if (isset($printOptions['gridLinesSet']) && self::boolean((string) $printOptions['gridLinesSet'])) {
+ $this->worksheet->setShowGridlines(true);
+ }
+ if (isset($printOptions['gridLines']) && self::boolean((string) $printOptions['gridLines'])) {
+ $this->worksheet->setPrintGridlines(true);
+ }
+ if (isset($printOptions['horizontalCentered']) && self::boolean((string) $printOptions['horizontalCentered'])) {
+ $this->worksheet->getPageSetup()->setHorizontalCentered(true);
+ }
+ if (isset($printOptions['verticalCentered']) && self::boolean((string) $printOptions['verticalCentered'])) {
+ $this->worksheet->getPageSetup()->setVerticalCentered(true);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
new file mode 100644
index 0000000..98e74e3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
@@ -0,0 +1,185 @@
+sheetViewXml = $sheetViewXml;
+ $this->sheetViewAttributes = Xlsx::testSimpleXml($sheetViewXml->attributes());
+ $this->worksheet = $workSheet;
+ }
+
+ public function load(): void
+ {
+ $this->topLeft();
+ $this->zoomScale();
+ $this->view();
+ $this->gridLines();
+ $this->headers();
+ $this->direction();
+ $this->showZeros();
+
+ $usesPanes = false;
+ if (isset($this->sheetViewXml->pane)) {
+ $this->pane();
+ $usesPanes = true;
+ }
+ if (isset($this->sheetViewXml->selection)) {
+ foreach ($this->sheetViewXml->selection as $selection) {
+ $this->selection($selection, $usesPanes);
+ }
+ }
+ }
+
+ private function zoomScale(): void
+ {
+ if (isset($this->sheetViewAttributes->zoomScale)) {
+ $zoomScale = (int) ($this->sheetViewAttributes->zoomScale);
+ if ($zoomScale <= 0) {
+ // setZoomScale will throw an Exception if the scale is less than or equals 0
+ // that is OK when manually creating documents, but we should be able to read all documents
+ $zoomScale = 100;
+ }
+
+ $this->worksheet->getSheetView()->setZoomScale($zoomScale);
+ }
+
+ if (isset($this->sheetViewAttributes->zoomScaleNormal)) {
+ $zoomScaleNormal = (int) ($this->sheetViewAttributes->zoomScaleNormal);
+ if ($zoomScaleNormal <= 0) {
+ // setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
+ // that is OK when manually creating documents, but we should be able to read all documents
+ $zoomScaleNormal = 100;
+ }
+
+ $this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
+ }
+ }
+
+ private function view(): void
+ {
+ if (isset($this->sheetViewAttributes->view)) {
+ $this->worksheet->getSheetView()->setView((string) $this->sheetViewAttributes->view);
+ }
+ }
+
+ private function topLeft(): void
+ {
+ if (isset($this->sheetViewAttributes->topLeftCell)) {
+ $this->worksheet->setTopLeftCell($this->sheetViewAttributes->topLeftCell);
+ }
+ }
+
+ private function gridLines(): void
+ {
+ if (isset($this->sheetViewAttributes->showGridLines)) {
+ $this->worksheet->setShowGridLines(
+ self::boolean((string) $this->sheetViewAttributes->showGridLines)
+ );
+ }
+ }
+
+ private function headers(): void
+ {
+ if (isset($this->sheetViewAttributes->showRowColHeaders)) {
+ $this->worksheet->setShowRowColHeaders(
+ self::boolean((string) $this->sheetViewAttributes->showRowColHeaders)
+ );
+ }
+ }
+
+ private function direction(): void
+ {
+ if (isset($this->sheetViewAttributes->rightToLeft)) {
+ $this->worksheet->setRightToLeft(
+ self::boolean((string) $this->sheetViewAttributes->rightToLeft)
+ );
+ }
+ }
+
+ private function showZeros(): void
+ {
+ if (isset($this->sheetViewAttributes->showZeros)) {
+ $this->worksheet->getSheetView()->setShowZeros(
+ self::boolean((string) $this->sheetViewAttributes->showZeros)
+ );
+ }
+ }
+
+ private function pane(): void
+ {
+ $xSplit = 0;
+ $ySplit = 0;
+ $topLeftCell = null;
+ $paneAttributes = $this->sheetViewXml->pane->attributes();
+
+ if (isset($paneAttributes->xSplit)) {
+ $xSplit = (int) ($paneAttributes->xSplit);
+ $this->worksheet->setXSplit($xSplit);
+ }
+
+ if (isset($paneAttributes->ySplit)) {
+ $ySplit = (int) ($paneAttributes->ySplit);
+ $this->worksheet->setYSplit($ySplit);
+ }
+ $paneState = isset($paneAttributes->state) ? ((string) $paneAttributes->state) : '';
+ $this->worksheet->setPaneState($paneState);
+ if (isset($paneAttributes->topLeftCell)) {
+ $topLeftCell = (string) $paneAttributes->topLeftCell;
+ $this->worksheet->setPaneTopLeftCell($topLeftCell);
+ if ($paneState === Worksheet::PANE_FROZEN) {
+ $this->worksheet->setTopLeftCell($topLeftCell);
+ }
+ }
+ $activePane = isset($paneAttributes->activePane) ? ((string) $paneAttributes->activePane) : 'topLeft';
+ $this->worksheet->setActivePane($activePane);
+ $this->activePane = $activePane;
+ if ($paneState === Worksheet::PANE_FROZEN || $paneState === Worksheet::PANE_FROZENSPLIT) {
+ $this->worksheet->freezePane(
+ Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
+ $topLeftCell,
+ $paneState === Worksheet::PANE_FROZENSPLIT
+ );
+ }
+ }
+
+ private function selection(?SimpleXMLElement $selection, bool $usesPanes): void
+ {
+ $attributes = ($selection === null) ? null : $selection->attributes();
+ if ($attributes !== null) {
+ $position = (string) $attributes->pane;
+ if ($usesPanes && $position === '') {
+ $position = 'topLeft';
+ }
+ $activeCell = (string) $attributes->activeCell;
+ $sqref = (string) $attributes->sqref;
+ $sqref = explode(' ', $sqref);
+ $sqref = $sqref[0];
+ if ($position === '') {
+ $this->worksheet->setSelectedCells($sqref);
+ } else {
+ $pane = new Pane($position, $sqref, $activeCell);
+ $this->worksheet->setPane($position, $pane);
+ if ($position === $this->activePane && $sqref !== '') {
+ $this->worksheet->setSelectedCells($sqref);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
new file mode 100644
index 0000000..46ecd3e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
@@ -0,0 +1,445 @@
+namespace = $namespace;
+ }
+
+ public function setWorkbookPalette(array $palette): void
+ {
+ $this->workbookPalette = $palette;
+ }
+
+ private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
+ {
+ $attr = $value->attributes('');
+ if ($attr === null || count($attr) === 0) {
+ $attr = $value->attributes($this->namespace);
+ }
+
+ return Xlsx::testSimpleXml($attr);
+ }
+
+ public function setStyleXml(SimpleXmlElement $styleXml): void
+ {
+ $this->styleXml = $styleXml;
+ }
+
+ public function setTheme(Theme $theme): void
+ {
+ $this->theme = $theme;
+ }
+
+ public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
+ {
+ $this->theme = $theme;
+ $this->styles = $styles;
+ $this->cellStyles = $cellStyles;
+ }
+
+ public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
+ {
+ if (isset($fontStyleXml->name)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->name);
+ if (isset($attr['val'])) {
+ $fontStyle->setName((string) $attr['val']);
+ }
+ }
+ if (isset($fontStyleXml->sz)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->sz);
+ if (isset($attr['val'])) {
+ $fontStyle->setSize((float) $attr['val']);
+ }
+ }
+ if (isset($fontStyleXml->b)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->b);
+ $fontStyle->setBold(!isset($attr['val']) || self::boolean((string) $attr['val']));
+ }
+ if (isset($fontStyleXml->i)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->i);
+ $fontStyle->setItalic(!isset($attr['val']) || self::boolean((string) $attr['val']));
+ }
+ if (isset($fontStyleXml->strike)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->strike);
+ $fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
+ }
+ $fontStyle->getColor()->setARGB($this->readColor($fontStyleXml->color));
+
+ if (isset($fontStyleXml->u)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->u);
+ if (!isset($attr['val'])) {
+ $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
+ } else {
+ $fontStyle->setUnderline((string) $attr['val']);
+ }
+ }
+ if (isset($fontStyleXml->vertAlign)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->vertAlign);
+ if (isset($attr['val'])) {
+ $verticalAlign = strtolower((string) $attr['val']);
+ if ($verticalAlign === 'superscript') {
+ $fontStyle->setSuperscript(true);
+ } elseif ($verticalAlign === 'subscript') {
+ $fontStyle->setSubscript(true);
+ }
+ }
+ }
+ if (isset($fontStyleXml->scheme)) {
+ $attr = $this->getStyleAttributes($fontStyleXml->scheme);
+ $fontStyle->setScheme((string) $attr['val']);
+ }
+ }
+
+ private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
+ {
+ if ((string) $numfmtStyleXml['formatCode'] !== '') {
+ $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmtStyleXml['formatCode']));
+
+ return;
+ }
+ $numfmt = $this->getStyleAttributes($numfmtStyleXml);
+ if (isset($numfmt['formatCode'])) {
+ $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmt['formatCode']));
+ }
+ }
+
+ public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
+ {
+ if ($fillStyleXml->gradientFill) {
+ /** @var SimpleXMLElement $gradientFill */
+ $gradientFill = $fillStyleXml->gradientFill[0];
+ $attr = $this->getStyleAttributes($gradientFill);
+ if (!empty($attr['type'])) {
+ $fillStyle->setFillType((string) $attr['type']);
+ }
+ $fillStyle->setRotation((float) ($attr['degree']));
+ $gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
+ $fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
+ $fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
+ } elseif ($fillStyleXml->patternFill) {
+ $defaultFillStyle = Fill::FILL_NONE;
+ if ($fillStyleXml->patternFill->fgColor) {
+ $fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
+ $defaultFillStyle = Fill::FILL_SOLID;
+ }
+ if ($fillStyleXml->patternFill->bgColor) {
+ $fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
+ $defaultFillStyle = Fill::FILL_SOLID;
+ }
+
+ $type = '';
+ if ((string) $fillStyleXml->patternFill['patternType'] !== '') {
+ $type = (string) $fillStyleXml->patternFill['patternType'];
+ } else {
+ $attr = $this->getStyleAttributes($fillStyleXml->patternFill);
+ $type = (string) $attr['patternType'];
+ }
+ $patternType = ($type === '') ? $defaultFillStyle : $type;
+
+ $fillStyle->setFillType($patternType);
+ }
+ }
+
+ public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
+ {
+ $diagonalUp = $this->getAttribute($borderStyleXml, 'diagonalUp');
+ $diagonalUp = self::boolean($diagonalUp);
+ $diagonalDown = $this->getAttribute($borderStyleXml, 'diagonalDown');
+ $diagonalDown = self::boolean($diagonalDown);
+ if ($diagonalUp === false) {
+ if ($diagonalDown === false) {
+ $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
+ } else {
+ $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+ }
+ } elseif ($diagonalDown === false) {
+ $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
+ } else {
+ $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
+ }
+
+ if (isset($borderStyleXml->left)) {
+ $this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
+ }
+ if (isset($borderStyleXml->right)) {
+ $this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
+ }
+ if (isset($borderStyleXml->top)) {
+ $this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
+ }
+ if (isset($borderStyleXml->bottom)) {
+ $this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
+ }
+ if (isset($borderStyleXml->diagonal)) {
+ $this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
+ }
+ }
+
+ private function getAttribute(SimpleXMLElement $xml, string $attribute): string
+ {
+ $style = '';
+ if ((string) $xml[$attribute] !== '') {
+ $style = (string) $xml[$attribute];
+ } else {
+ $attr = $this->getStyleAttributes($xml);
+ if (isset($attr[$attribute])) {
+ $style = (string) $attr[$attribute];
+ }
+ }
+
+ return $style;
+ }
+
+ private function readBorder(Border $border, SimpleXMLElement $borderXml): void
+ {
+ $style = $this->getAttribute($borderXml, 'style');
+ if ($style !== '') {
+ $border->setBorderStyle((string) $style);
+ } else {
+ $border->setBorderStyle(Border::BORDER_NONE);
+ }
+ if (isset($borderXml->color)) {
+ $border->getColor()->setARGB($this->readColor($borderXml->color));
+ }
+ }
+
+ public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
+ {
+ $horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
+ if ($horizontal !== '') {
+ $alignment->setHorizontal($horizontal);
+ }
+ $vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
+ if ($vertical !== '') {
+ $alignment->setVertical($vertical);
+ }
+
+ $textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
+ if ($textRotation > 90) {
+ $textRotation = 90 - $textRotation;
+ }
+ $alignment->setTextRotation($textRotation);
+
+ $wrapText = $this->getAttribute($alignmentXml, 'wrapText');
+ $alignment->setWrapText(self::boolean((string) $wrapText));
+ $shrinkToFit = $this->getAttribute($alignmentXml, 'shrinkToFit');
+ $alignment->setShrinkToFit(self::boolean((string) $shrinkToFit));
+ $indent = (int) $this->getAttribute($alignmentXml, 'indent');
+ $alignment->setIndent(max($indent, 0));
+ $readingOrder = (int) $this->getAttribute($alignmentXml, 'readingOrder');
+ $alignment->setReadOrder(max($readingOrder, 0));
+ }
+
+ private static function formatGeneral(string $formatString): string
+ {
+ if ($formatString === 'GENERAL') {
+ $formatString = NumberFormat::FORMAT_GENERAL;
+ }
+
+ return $formatString;
+ }
+
+ /**
+ * Read style.
+ *
+ * @param SimpleXMLElement|stdClass $style
+ */
+ public function readStyle(Style $docStyle, $style): void
+ {
+ if ($style instanceof SimpleXMLElement) {
+ $this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
+ } else {
+ $docStyle->getNumberFormat()->setFormatCode(self::formatGeneral((string) $style->numFmt));
+ }
+
+ if (isset($style->font)) {
+ $this->readFontStyle($docStyle->getFont(), $style->font);
+ }
+
+ if (isset($style->fill)) {
+ $this->readFillStyle($docStyle->getFill(), $style->fill);
+ }
+
+ if (isset($style->border)) {
+ $this->readBorderStyle($docStyle->getBorders(), $style->border);
+ }
+
+ if (isset($style->alignment)) {
+ $this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
+ }
+
+ // protection
+ if (isset($style->protection)) {
+ $this->readProtectionLocked($docStyle, $style->protection);
+ $this->readProtectionHidden($docStyle, $style->protection);
+ }
+
+ // top-level style settings
+ if (isset($style->quotePrefix)) {
+ $docStyle->setQuotePrefix((bool) $style->quotePrefix);
+ }
+ }
+
+ /**
+ * Read protection locked attribute.
+ */
+ public function readProtectionLocked(Style $docStyle, SimpleXMLElement $style): void
+ {
+ $locked = '';
+ if ((string) $style['locked'] !== '') {
+ $locked = (string) $style['locked'];
+ } else {
+ $attr = $this->getStyleAttributes($style);
+ if (isset($attr['locked'])) {
+ $locked = (string) $attr['locked'];
+ }
+ }
+ if ($locked !== '') {
+ if (self::boolean($locked)) {
+ $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
+ } else {
+ $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
+ }
+ }
+ }
+
+ /**
+ * Read protection hidden attribute.
+ */
+ public function readProtectionHidden(Style $docStyle, SimpleXMLElement $style): void
+ {
+ $hidden = '';
+ if ((string) $style['hidden'] !== '') {
+ $hidden = (string) $style['hidden'];
+ } else {
+ $attr = $this->getStyleAttributes($style);
+ if (isset($attr['hidden'])) {
+ $hidden = (string) $attr['hidden'];
+ }
+ }
+ if ($hidden !== '') {
+ if (self::boolean((string) $hidden)) {
+ $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
+ } else {
+ $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
+ }
+ }
+ }
+
+ public function readColor(SimpleXMLElement $color, bool $background = false): string
+ {
+ $attr = $this->getStyleAttributes($color);
+ if (isset($attr['rgb'])) {
+ return (string) $attr['rgb'];
+ }
+ if (isset($attr['indexed'])) {
+ $indexedColor = (int) $attr['indexed'];
+ if ($indexedColor >= count($this->workbookPalette)) {
+ return Color::indexedColor($indexedColor - 7, $background)->getARGB() ?? '';
+ }
+
+ return Color::indexedColor($indexedColor, $background, $this->workbookPalette)->getARGB() ?? '';
+ }
+ if (isset($attr['theme'])) {
+ if ($this->theme !== null) {
+ $returnColour = $this->theme->getColourByIndex((int) $attr['theme']);
+ if (isset($attr['tint'])) {
+ $tintAdjust = (float) $attr['tint'];
+ $returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
+ }
+
+ return 'FF' . $returnColour;
+ }
+ }
+
+ return ($background) ? 'FFFFFFFF' : 'FF000000';
+ }
+
+ public function dxfs(bool $readDataOnly = false): array
+ {
+ $dxfs = [];
+ if (!$readDataOnly && $this->styleXml) {
+ // Conditional Styles
+ if ($this->styleXml->dxfs) {
+ foreach ($this->styleXml->dxfs->dxf as $dxf) {
+ $style = new Style(false, true);
+ $this->readStyle($style, $dxf);
+ $dxfs[] = $style;
+ }
+ }
+ // Cell Styles
+ if ($this->styleXml->cellStyles) {
+ foreach ($this->styleXml->cellStyles->cellStyle as $cellStylex) {
+ $cellStyle = Xlsx::getAttributes($cellStylex);
+ if ((int) ($cellStyle['builtinId']) == 0) {
+ if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
+ // Set default style
+ $style = new Style();
+ $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
+
+ // normal style, currently not using it for anything
+ }
+ }
+ }
+ }
+ }
+
+ return $dxfs;
+ }
+
+ public function styles(): array
+ {
+ return $this->styles;
+ }
+
+ /**
+ * Get array item.
+ *
+ * @param mixed $array (usually array, in theory can be false)
+ *
+ * @return stdClass
+ */
+ private static function getArrayItem(mixed $array, int $key = 0)
+ {
+ return is_array($array) ? ($array[$key] ?? null) : null;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php
new file mode 100644
index 0000000..a63c817
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php
@@ -0,0 +1,116 @@
+worksheet = $workSheet;
+ $this->tableXml = $tableXml;
+ }
+
+ /**
+ * Loads Table into the Worksheet.
+ */
+ public function load(): void
+ {
+ $this->tableAttributes = $this->tableXml->attributes() ?? [];
+ // Remove all "$" in the table range
+ $tableRange = (string) preg_replace('/\$/', '', $this->tableAttributes['ref'] ?? '');
+ if (str_contains($tableRange, ':')) {
+ $this->readTable($tableRange);
+ }
+ }
+
+ /**
+ * Read Table from xml.
+ */
+ private function readTable(string $tableRange): void
+ {
+ $table = new Table($tableRange);
+ $table->setName((string) ($this->tableAttributes['displayName'] ?? ''));
+ $table->setShowHeaderRow(((string) ($this->tableAttributes['headerRowCount'] ?? '')) !== '0');
+ $table->setShowTotalsRow(((string) ($this->tableAttributes['totalsRowCount'] ?? '')) === '1');
+
+ $this->readTableAutoFilter($table, $this->tableXml->autoFilter);
+ $this->readTableColumns($table, $this->tableXml->tableColumns);
+ $this->readTableStyle($table, $this->tableXml->tableStyleInfo);
+
+ (new AutoFilter($table, $this->tableXml))->load();
+ $this->worksheet->addTable($table);
+ }
+
+ /**
+ * Reads TableAutoFilter from xml.
+ */
+ private function readTableAutoFilter(Table $table, SimpleXMLElement $autoFilterXml): void
+ {
+ if ($autoFilterXml->filterColumn === null) {
+ $table->setAllowFilter(false);
+
+ return;
+ }
+
+ foreach ($autoFilterXml->filterColumn as $filterColumn) {
+ $attributes = $filterColumn->attributes() ?? ['colId' => 0, 'hiddenButton' => 0];
+ $column = $table->getColumnByOffset((int) $attributes['colId']);
+ $column->setShowFilterButton(((string) $attributes['hiddenButton']) !== '1');
+ }
+ }
+
+ /**
+ * Reads TableColumns from xml.
+ */
+ private function readTableColumns(Table $table, SimpleXMLElement $tableColumnsXml): void
+ {
+ $offset = 0;
+ foreach ($tableColumnsXml->tableColumn as $tableColumn) {
+ $attributes = $tableColumn->attributes() ?? ['totalsRowLabel' => 0, 'totalsRowFunction' => 0];
+ $column = $table->getColumnByOffset($offset++);
+
+ if ($table->getShowTotalsRow()) {
+ if ($attributes['totalsRowLabel']) {
+ $column->setTotalsRowLabel((string) $attributes['totalsRowLabel']);
+ }
+
+ if ($attributes['totalsRowFunction']) {
+ $column->setTotalsRowFunction((string) $attributes['totalsRowFunction']);
+ }
+ }
+
+ if ($tableColumn->calculatedColumnFormula) {
+ $column->setColumnFormula((string) $tableColumn->calculatedColumnFormula);
+ }
+ }
+ }
+
+ /**
+ * Reads TableStyle from xml.
+ */
+ private function readTableStyle(Table $table, SimpleXMLElement $tableStyleInfoXml): void
+ {
+ $tableStyle = new TableStyle();
+ $attributes = $tableStyleInfoXml->attributes();
+ if ($attributes !== null) {
+ $tableStyle->setTheme((string) $attributes['name']);
+ $tableStyle->setShowRowStripes((string) $attributes['showRowStripes'] === '1');
+ $tableStyle->setShowColumnStripes((string) $attributes['showColumnStripes'] === '1');
+ $tableStyle->setShowFirstColumn((string) $attributes['showFirstColumn'] === '1');
+ $tableStyle->setShowLastColumn((string) $attributes['showLastColumn'] === '1');
+ }
+ $table->setStyle($tableStyle);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
new file mode 100644
index 0000000..706c4d1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
@@ -0,0 +1,78 @@
+themeName = $themeName;
+ $this->colourSchemeName = $colourSchemeName;
+ $this->colourMap = $colourMap;
+ }
+
+ /**
+ * Not called by Reader, never accessible any other time.
+ *
+ * @return string
+ *
+ * @codeCoverageIgnore
+ */
+ public function getThemeName()
+ {
+ return $this->themeName;
+ }
+
+ /**
+ * Not called by Reader, never accessible any other time.
+ *
+ * @return string
+ *
+ * @codeCoverageIgnore
+ */
+ public function getColourSchemeName()
+ {
+ return $this->colourSchemeName;
+ }
+
+ /**
+ * Get colour Map Value by Position.
+ *
+ * @param int $index
+ *
+ * @return null|string
+ */
+ public function getColourByIndex($index)
+ {
+ return $this->colourMap[$index] ?? null;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php
new file mode 100644
index 0000000..b3060ef
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php
@@ -0,0 +1,141 @@
+spreadsheet = $spreadsheet;
+ }
+
+ public function viewSettings(SimpleXMLElement $xmlWorkbook, mixed $mainNS, array $mapSheetId, bool $readDataOnly): void
+ {
+ // Default active sheet index to the first loaded worksheet from the file
+ $this->spreadsheet->setActiveSheetIndex(0);
+
+ $workbookView = $xmlWorkbook->children($mainNS)->bookViews->workbookView;
+ if ($readDataOnly !== true && !empty($workbookView)) {
+ $workbookViewAttributes = self::testSimpleXml(self::getAttributes($workbookView));
+ // active sheet index
+ $activeTab = (int) $workbookViewAttributes->activeTab; // refers to old sheet index
+ // keep active sheet index if sheet is still loaded, else first sheet is set as the active worksheet
+ if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
+ $this->spreadsheet->setActiveSheetIndex($mapSheetId[$activeTab]);
+ }
+
+ $this->horizontalScroll($workbookViewAttributes);
+ $this->verticalScroll($workbookViewAttributes);
+ $this->sheetTabs($workbookViewAttributes);
+ $this->minimized($workbookViewAttributes);
+ $this->autoFilterDateGrouping($workbookViewAttributes);
+ $this->firstSheet($workbookViewAttributes);
+ $this->visibility($workbookViewAttributes);
+ $this->tabRatio($workbookViewAttributes);
+ }
+ }
+
+ public static function testSimpleXml(mixed $value): SimpleXMLElement
+ {
+ return ($value instanceof SimpleXMLElement)
+ ? $value
+ : new SimpleXMLElement(' ');
+ }
+
+ public static function getAttributes(?SimpleXMLElement $value, string $ns = ''): SimpleXMLElement
+ {
+ return self::testSimpleXml($value === null ? $value : $value->attributes($ns));
+ }
+
+ /**
+ * Convert an 'xsd:boolean' XML value to a PHP boolean value.
+ * A valid 'xsd:boolean' XML value can be one of the following
+ * four values: 'true', 'false', '1', '0'. It is case sensitive.
+ *
+ * Note that just doing '(bool) $xsdBoolean' is not safe,
+ * since '(bool) "false"' returns true.
+ *
+ * @see https://www.w3.org/TR/xmlschema11-2/#boolean
+ *
+ * @param string $xsdBoolean An XML string value of type 'xsd:boolean'
+ *
+ * @return bool Boolean value
+ */
+ private function castXsdBooleanToBool(string $xsdBoolean): bool
+ {
+ if ($xsdBoolean === 'false') {
+ return false;
+ }
+
+ return (bool) $xsdBoolean;
+ }
+
+ private function horizontalScroll(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->showHorizontalScroll)) {
+ $showHorizontalScroll = (string) $workbookViewAttributes->showHorizontalScroll;
+ $this->spreadsheet->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll));
+ }
+ }
+
+ private function verticalScroll(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->showVerticalScroll)) {
+ $showVerticalScroll = (string) $workbookViewAttributes->showVerticalScroll;
+ $this->spreadsheet->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll));
+ }
+ }
+
+ private function sheetTabs(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->showSheetTabs)) {
+ $showSheetTabs = (string) $workbookViewAttributes->showSheetTabs;
+ $this->spreadsheet->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs));
+ }
+ }
+
+ private function minimized(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->minimized)) {
+ $minimized = (string) $workbookViewAttributes->minimized;
+ $this->spreadsheet->setMinimized($this->castXsdBooleanToBool($minimized));
+ }
+ }
+
+ private function autoFilterDateGrouping(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->autoFilterDateGrouping)) {
+ $autoFilterDateGrouping = (string) $workbookViewAttributes->autoFilterDateGrouping;
+ $this->spreadsheet->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping));
+ }
+ }
+
+ private function firstSheet(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->firstSheet)) {
+ $firstSheet = (string) $workbookViewAttributes->firstSheet;
+ $this->spreadsheet->setFirstSheetIndex((int) $firstSheet);
+ }
+ }
+
+ private function visibility(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->visibility)) {
+ $visibility = (string) $workbookViewAttributes->visibility;
+ $this->spreadsheet->setVisibility($visibility);
+ }
+ }
+
+ private function tabRatio(SimpleXMLElement $workbookViewAttributes): void
+ {
+ if (isset($workbookViewAttributes->tabRatio)) {
+ $tabRatio = (string) $workbookViewAttributes->tabRatio;
+ $this->spreadsheet->setTabRatio((int) $tabRatio);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
new file mode 100644
index 0000000..09baf33
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
@@ -0,0 +1,656 @@
+securityScanner = XmlScanner::getInstance($this);
+ }
+
+ /** @var string */
+ private $fileContents = '';
+
+ public static function xmlMappings(): array
+ {
+ return array_merge(
+ Style\Fill::FILL_MAPPINGS,
+ Style\Border::BORDER_MAPPINGS
+ );
+ }
+
+ /**
+ * Can the current IReader read the file?
+ */
+ public function canRead(string $filename): bool
+ {
+ // Office xmlns:o="urn:schemas-microsoft-com:office:office"
+ // Excel xmlns:x="urn:schemas-microsoft-com:office:excel"
+ // XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
+ // Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
+ // XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
+ // XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
+ // MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset"
+ // Rowset xmlns:z="#RowsetSchema"
+ //
+
+ $signature = [
+ '/m', $data, $matches)) {
+ $charSet = strtoupper($matches[1]);
+ if (preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet) === 1) {
+ $data = StringHelper::convertEncoding($data, 'UTF-8', $charSet);
+ $data = (string) preg_replace('/()/um', '$1' . 'UTF-8' . '$2', $data, 1);
+ }
+ }
+ $this->fileContents = $data;
+
+ return $valid;
+ }
+
+ /**
+ * Check if the file is a valid SimpleXML.
+ *
+ * @return false|SimpleXMLElement
+ */
+ public function trySimpleXMLLoadString(string $filename): SimpleXMLElement|bool
+ {
+ try {
+ $xml = simplexml_load_string(
+ $this->getSecurityScannerOrThrow()->scan($this->fileContents ?: file_get_contents($filename)),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+ } catch (\Exception $e) {
+ throw new Exception('Cannot load invalid XML file: ' . $filename, 0, $e);
+ }
+ $this->fileContents = '';
+
+ return $xml;
+ }
+
+ /**
+ * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
+ */
+ public function listWorksheetNames(string $filename): array
+ {
+ File::assertFile($filename);
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+ }
+
+ $worksheetNames = [];
+
+ $xml = $this->trySimpleXMLLoadString($filename);
+ if ($xml === false) {
+ throw new Exception("Problem reading {$filename}");
+ }
+
+ $xml_ss = $xml->children(self::NAMESPACES_SS);
+ foreach ($xml_ss->Worksheet as $worksheet) {
+ $worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
+ $worksheetNames[] = (string) $worksheet_ss['Name'];
+ }
+
+ return $worksheetNames;
+ }
+
+ /**
+ * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+ */
+ public function listWorksheetInfo(string $filename): array
+ {
+ File::assertFile($filename);
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+ }
+
+ $worksheetInfo = [];
+
+ $xml = $this->trySimpleXMLLoadString($filename);
+ if ($xml === false) {
+ throw new Exception("Problem reading {$filename}");
+ }
+
+ $worksheetID = 1;
+ $xml_ss = $xml->children(self::NAMESPACES_SS);
+ foreach ($xml_ss->Worksheet as $worksheet) {
+ $worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
+
+ $tmpInfo = [];
+ $tmpInfo['worksheetName'] = '';
+ $tmpInfo['lastColumnLetter'] = 'A';
+ $tmpInfo['lastColumnIndex'] = 0;
+ $tmpInfo['totalRows'] = 0;
+ $tmpInfo['totalColumns'] = 0;
+
+ $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
+ if (isset($worksheet_ss['Name'])) {
+ $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
+ }
+
+ if (isset($worksheet->Table->Row)) {
+ $rowIndex = 0;
+
+ foreach ($worksheet->Table->Row as $rowData) {
+ $columnIndex = 0;
+ $rowHasData = false;
+
+ foreach ($rowData->Cell as $cell) {
+ if (isset($cell->Data)) {
+ $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
+ $rowHasData = true;
+ }
+
+ ++$columnIndex;
+ }
+
+ ++$rowIndex;
+
+ if ($rowHasData) {
+ $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
+ }
+ }
+ }
+
+ $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
+ $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
+
+ $worksheetInfo[] = $tmpInfo;
+ ++$worksheetID;
+ }
+
+ return $worksheetInfo;
+ }
+
+ /**
+ * Loads Spreadsheet from string.
+ */
+ public function loadSpreadsheetFromString(string $contents): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+ $spreadsheet->removeSheetByIndex(0);
+
+ // Load into this instance
+ return $this->loadIntoExisting($contents, $spreadsheet, true);
+ }
+
+ /**
+ * Loads Spreadsheet from file.
+ */
+ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
+ {
+ // Create new Spreadsheet
+ $spreadsheet = new Spreadsheet();
+ $spreadsheet->removeSheetByIndex(0);
+
+ // Load into this instance
+ return $this->loadIntoExisting($filename, $spreadsheet);
+ }
+
+ /**
+ * Loads from file or contents into Spreadsheet instance.
+ *
+ * @param string $filename file name if useContents is false else file contents
+ */
+ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet, bool $useContents = false): Spreadsheet
+ {
+ if ($useContents) {
+ $this->fileContents = $filename;
+ } else {
+ File::assertFile($filename);
+ if (!$this->canRead($filename)) {
+ throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+ }
+ }
+
+ $xml = $this->trySimpleXMLLoadString($filename);
+ if ($xml === false) {
+ throw new Exception("Problem reading {$filename}");
+ }
+
+ $namespaces = $xml->getNamespaces(true);
+
+ (new Properties($spreadsheet))->readProperties($xml, $namespaces);
+
+ $this->styles = (new Style())->parseStyles($xml, $namespaces);
+ if (isset($this->styles['Default'])) {
+ $spreadsheet->getCellXfCollection()[0]->applyFromArray($this->styles['Default']);
+ }
+
+ $worksheetID = 0;
+ $xml_ss = $xml->children(self::NAMESPACES_SS);
+
+ /** @var null|SimpleXMLElement $worksheetx */
+ foreach ($xml_ss->Worksheet as $worksheetx) {
+ $worksheet = $worksheetx ?? new SimpleXMLElement(' ');
+ $worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
+
+ if (
+ isset($this->loadSheetsOnly, $worksheet_ss['Name'])
+ && (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))
+ ) {
+ continue;
+ }
+
+ // Create new Worksheet
+ $spreadsheet->createSheet();
+ $spreadsheet->setActiveSheetIndex($worksheetID);
+ $worksheetName = '';
+ if (isset($worksheet_ss['Name'])) {
+ $worksheetName = (string) $worksheet_ss['Name'];
+ // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
+ // formula cells... during the load, all formulae should be correct, and we're simply bringing
+ // the worksheet name in line with the formula, not the reverse
+ $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
+ }
+ if (isset($worksheet_ss['Protected'])) {
+ $protection = (string) $worksheet_ss['Protected'] === '1';
+ $spreadsheet->getActiveSheet()->getProtection()->setSheet($protection);
+ }
+
+ // locally scoped defined names
+ if (isset($worksheet->Names[0])) {
+ foreach ($worksheet->Names[0] as $definedName) {
+ $definedName_ss = self::getAttributes($definedName, self::NAMESPACES_SS);
+ $name = (string) $definedName_ss['Name'];
+ $definedValue = (string) $definedName_ss['RefersTo'];
+ $convertedValue = AddressHelper::convertFormulaToA1($definedValue);
+ if ($convertedValue[0] === '=') {
+ $convertedValue = substr($convertedValue, 1);
+ }
+ $spreadsheet->addDefinedName(DefinedName::createInstance($name, $spreadsheet->getActiveSheet(), $convertedValue, true));
+ }
+ }
+
+ $columnID = 'A';
+ if (isset($worksheet->Table->Column)) {
+ foreach ($worksheet->Table->Column as $columnData) {
+ $columnData_ss = self::getAttributes($columnData, self::NAMESPACES_SS);
+ $colspan = 0;
+ if (isset($columnData_ss['Span'])) {
+ $spanAttr = (string) $columnData_ss['Span'];
+ if (is_numeric($spanAttr)) {
+ $colspan = max(0, (int) $spanAttr);
+ }
+ }
+ if (isset($columnData_ss['Index'])) {
+ $columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']);
+ }
+ $columnWidth = null;
+ if (isset($columnData_ss['Width'])) {
+ $columnWidth = $columnData_ss['Width'];
+ }
+ $columnVisible = null;
+ if (isset($columnData_ss['Hidden'])) {
+ $columnVisible = ((string) $columnData_ss['Hidden']) !== '1';
+ }
+ while ($colspan >= 0) {
+ if (isset($columnWidth)) {
+ $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
+ }
+ if (isset($columnVisible)) {
+ $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setVisible($columnVisible);
+ }
+ ++$columnID;
+ --$colspan;
+ }
+ }
+ }
+
+ $rowID = 1;
+ if (isset($worksheet->Table->Row)) {
+ $additionalMergedCells = 0;
+ foreach ($worksheet->Table->Row as $rowData) {
+ $rowHasData = false;
+ $row_ss = self::getAttributes($rowData, self::NAMESPACES_SS);
+ if (isset($row_ss['Index'])) {
+ $rowID = (int) $row_ss['Index'];
+ }
+ if (isset($row_ss['Hidden'])) {
+ $rowVisible = ((string) $row_ss['Hidden']) !== '1';
+ $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setVisible($rowVisible);
+ }
+
+ $columnID = 'A';
+ foreach ($rowData->Cell as $cell) {
+ $cell_ss = self::getAttributes($cell, self::NAMESPACES_SS);
+ if (isset($cell_ss['Index'])) {
+ $columnID = Coordinate::stringFromColumnIndex((int) $cell_ss['Index']);
+ }
+ $cellRange = $columnID . $rowID;
+
+ if ($this->getReadFilter() !== null) {
+ if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
+ ++$columnID;
+
+ continue;
+ }
+ }
+
+ if (isset($cell_ss['HRef'])) {
+ $spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl((string) $cell_ss['HRef']);
+ }
+
+ if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
+ $columnTo = $columnID;
+ if (isset($cell_ss['MergeAcross'])) {
+ $additionalMergedCells += (int) $cell_ss['MergeAcross'];
+ $columnTo = Coordinate::stringFromColumnIndex((int) (Coordinate::columnIndexFromString($columnID) + $cell_ss['MergeAcross']));
+ }
+ $rowTo = $rowID;
+ if (isset($cell_ss['MergeDown'])) {
+ $rowTo = $rowTo + $cell_ss['MergeDown'];
+ }
+ $cellRange .= ':' . $columnTo . $rowTo;
+ $spreadsheet->getActiveSheet()->mergeCells($cellRange, Worksheet::MERGE_CELL_CONTENT_HIDE);
+ }
+
+ $hasCalculatedValue = false;
+ $cellDataFormula = '';
+ if (isset($cell_ss['Formula'])) {
+ $cellDataFormula = $cell_ss['Formula'];
+ $hasCalculatedValue = true;
+ }
+ if (isset($cell->Data)) {
+ $cellData = $cell->Data;
+ $cellValue = (string) $cellData;
+ $type = DataType::TYPE_NULL;
+ $cellData_ss = self::getAttributes($cellData, self::NAMESPACES_SS);
+ if (isset($cellData_ss['Type'])) {
+ $cellDataType = $cellData_ss['Type'];
+ switch ($cellDataType) {
+ /*
+ const TYPE_STRING = 's';
+ const TYPE_FORMULA = 'f';
+ const TYPE_NUMERIC = 'n';
+ const TYPE_BOOL = 'b';
+ const TYPE_NULL = 'null';
+ const TYPE_INLINE = 'inlineStr';
+ const TYPE_ERROR = 'e';
+ */
+ case 'String':
+ $type = DataType::TYPE_STRING;
+
+ break;
+ case 'Number':
+ $type = DataType::TYPE_NUMERIC;
+ $cellValue = (float) $cellValue;
+ if (floor($cellValue) == $cellValue) {
+ $cellValue = (int) $cellValue;
+ }
+
+ break;
+ case 'Boolean':
+ $type = DataType::TYPE_BOOL;
+ $cellValue = ($cellValue != 0);
+
+ break;
+ case 'DateTime':
+ $type = DataType::TYPE_NUMERIC;
+ $dateTime = new DateTime($cellValue, new DateTimeZone('UTC'));
+ $cellValue = Date::PHPToExcel($dateTime);
+
+ break;
+ case 'Error':
+ $type = DataType::TYPE_ERROR;
+ $hasCalculatedValue = false;
+
+ break;
+ }
+ }
+
+ $originalType = $type;
+ if ($hasCalculatedValue) {
+ $type = DataType::TYPE_FORMULA;
+ $columnNumber = Coordinate::columnIndexFromString($columnID);
+ $cellDataFormula = AddressHelper::convertFormulaToA1($cellDataFormula, $rowID, $columnNumber);
+ }
+
+ $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
+ if ($hasCalculatedValue) {
+ $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue, $originalType === DataType::TYPE_NUMERIC);
+ }
+ $rowHasData = true;
+ }
+
+ if (isset($cell->Comment)) {
+ $this->parseCellComment($cell->Comment, $spreadsheet, $columnID, $rowID);
+ }
+
+ if (isset($cell_ss['StyleID'])) {
+ $style = (string) $cell_ss['StyleID'];
+ if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
+ //if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) {
+ // $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null);
+ //}
+ $spreadsheet->getActiveSheet()->getStyle($cellRange)
+ ->applyFromArray($this->styles[$style]);
+ }
+ }
+ ++$columnID;
+ while ($additionalMergedCells > 0) {
+ ++$columnID;
+ --$additionalMergedCells;
+ }
+ }
+
+ if ($rowHasData) {
+ if (isset($row_ss['Height'])) {
+ $rowHeight = $row_ss['Height'];
+ $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setRowHeight((float) $rowHeight);
+ }
+ }
+
+ ++$rowID;
+ }
+ }
+
+ $dataValidations = new Xml\DataValidations();
+ $dataValidations->loadDataValidations($worksheet, $spreadsheet);
+ $xmlX = $worksheet->children(Namespaces::URN_EXCEL);
+ if (isset($xmlX->WorksheetOptions)) {
+ if (isset($xmlX->WorksheetOptions->FreezePanes)) {
+ $freezeRow = $freezeColumn = 1;
+ if (isset($xmlX->WorksheetOptions->SplitHorizontal)) {
+ $freezeRow = (int) $xmlX->WorksheetOptions->SplitHorizontal + 1;
+ }
+ if (isset($xmlX->WorksheetOptions->SplitVertical)) {
+ $freezeColumn = (int) $xmlX->WorksheetOptions->SplitVertical + 1;
+ }
+ $leftTopRow = (string) $xmlX->WorksheetOptions->TopRowBottomPane;
+ $leftTopColumn = (string) $xmlX->WorksheetOptions->LeftColumnRightPane;
+ if (is_numeric($leftTopRow) && is_numeric($leftTopColumn)) {
+ $leftTopCoordinate = Coordinate::stringFromColumnIndex((int) $leftTopColumn + 1) . (string) ($leftTopRow + 1);
+ $spreadsheet->getActiveSheet()->freezePane(Coordinate::stringFromColumnIndex($freezeColumn) . (string) $freezeRow, $leftTopCoordinate, !isset($xmlX->WorksheetOptions->FrozenNoSplit));
+ } else {
+ $spreadsheet->getActiveSheet()->freezePane(Coordinate::stringFromColumnIndex($freezeColumn) . (string) $freezeRow, null, !isset($xmlX->WorksheetOptions->FrozenNoSplit));
+ }
+ } elseif (isset($xmlX->WorksheetOptions->SplitVertical) || isset($xmlX->WorksheetOptions->SplitHorizontal)) {
+ if (isset($xmlX->WorksheetOptions->SplitHorizontal)) {
+ $ySplit = (int) $xmlX->WorksheetOptions->SplitHorizontal;
+ $spreadsheet->getActiveSheet()->setYSplit($ySplit);
+ }
+ if (isset($xmlX->WorksheetOptions->SplitVertical)) {
+ $xSplit = (int) $xmlX->WorksheetOptions->SplitVertical;
+ $spreadsheet->getActiveSheet()->setXSplit($xSplit);
+ }
+ if (isset($xmlX->WorksheetOptions->LeftColumnVisible) || isset($xmlX->WorksheetOptions->TopRowVisible)) {
+ $leftTopColumn = $leftTopRow = 1;
+ if (isset($xmlX->WorksheetOptions->LeftColumnVisible)) {
+ $leftTopColumn = 1 + (int) $xmlX->WorksheetOptions->LeftColumnVisible;
+ }
+ if (isset($xmlX->WorksheetOptions->TopRowVisible)) {
+ $leftTopRow = 1 + (int) $xmlX->WorksheetOptions->TopRowVisible;
+ }
+ $leftTopCoordinate = Coordinate::stringFromColumnIndex($leftTopColumn) . "$leftTopRow";
+ $spreadsheet->getActiveSheet()->setTopLeftCell($leftTopCoordinate);
+ }
+
+ $leftTopColumn = $leftTopRow = 1;
+ if (isset($xmlX->WorksheetOptions->LeftColumnRightPane)) {
+ $leftTopColumn = 1 + (int) $xmlX->WorksheetOptions->LeftColumnRightPane;
+ }
+ if (isset($xmlX->WorksheetOptions->TopRowBottomPane)) {
+ $leftTopRow = 1 + (int) $xmlX->WorksheetOptions->TopRowBottomPane;
+ }
+ $leftTopCoordinate = Coordinate::stringFromColumnIndex($leftTopColumn) . "$leftTopRow";
+ $spreadsheet->getActiveSheet()->setPaneTopLeftCell($leftTopCoordinate);
+ }
+ (new PageSettings($xmlX))->loadPageSettings($spreadsheet);
+ if (isset($xmlX->WorksheetOptions->TopRowVisible, $xmlX->WorksheetOptions->LeftColumnVisible)) {
+ $leftTopRow = (string) $xmlX->WorksheetOptions->TopRowVisible;
+ $leftTopColumn = (string) $xmlX->WorksheetOptions->LeftColumnVisible;
+ if (is_numeric($leftTopRow) && is_numeric($leftTopColumn)) {
+ $leftTopCoordinate = Coordinate::stringFromColumnIndex((int) $leftTopColumn + 1) . (string) ($leftTopRow + 1);
+ $spreadsheet->getActiveSheet()->setTopLeftCell($leftTopCoordinate);
+ }
+ }
+ $rangeCalculated = false;
+ if (isset($xmlX->WorksheetOptions->Panes->Pane->RangeSelection)) {
+ if (1 === preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $xmlX->WorksheetOptions->Panes->Pane->RangeSelection, $selectionMatches)) {
+ $selectedCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
+ . $selectionMatches[1]
+ . ':'
+ . Coordinate::stringFromColumnIndex((int) $selectionMatches[4])
+ . $selectionMatches[3];
+ $spreadsheet->getActiveSheet()->setSelectedCells($selectedCell);
+ $rangeCalculated = true;
+ }
+ }
+ if (!$rangeCalculated) {
+ if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveRow)) {
+ $activeRow = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveRow;
+ } else {
+ $activeRow = 0;
+ }
+ if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveCol)) {
+ $activeColumn = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveCol;
+ } else {
+ $activeColumn = 0;
+ }
+ if (is_numeric($activeRow) && is_numeric($activeColumn)) {
+ $selectedCell = Coordinate::stringFromColumnIndex((int) $activeColumn + 1) . (string) ($activeRow + 1);
+ $spreadsheet->getActiveSheet()->setSelectedCells($selectedCell);
+ }
+ }
+ }
+ ++$worksheetID;
+ }
+
+ // Globally scoped defined names
+ $activeSheetIndex = 0;
+ if (isset($xml->ExcelWorkbook->ActiveSheet)) {
+ $activeSheetIndex = (int) (string) $xml->ExcelWorkbook->ActiveSheet;
+ }
+ $activeWorksheet = $spreadsheet->setActiveSheetIndex($activeSheetIndex);
+ if (isset($xml->Names[0])) {
+ foreach ($xml->Names[0] as $definedName) {
+ $definedName_ss = self::getAttributes($definedName, self::NAMESPACES_SS);
+ $name = (string) $definedName_ss['Name'];
+ $definedValue = (string) $definedName_ss['RefersTo'];
+ $convertedValue = AddressHelper::convertFormulaToA1($definedValue);
+ if ($convertedValue[0] === '=') {
+ $convertedValue = substr($convertedValue, 1);
+ }
+ $spreadsheet->addDefinedName(DefinedName::createInstance($name, $activeWorksheet, $convertedValue));
+ }
+ }
+
+ // Return
+ return $spreadsheet;
+ }
+
+ protected function parseCellComment(
+ SimpleXMLElement $comment,
+ Spreadsheet $spreadsheet,
+ string $columnID,
+ int $rowID
+ ): void {
+ $commentAttributes = $comment->attributes(self::NAMESPACES_SS);
+ $author = 'unknown';
+ if (isset($commentAttributes->Author)) {
+ $author = (string) $commentAttributes->Author;
+ }
+
+ $node = $comment->Data->asXML();
+ $annotation = strip_tags((string) $node);
+ $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)
+ ->setAuthor($author)
+ ->setText($this->parseRichText($annotation));
+ }
+
+ protected function parseRichText(string $annotation): RichText
+ {
+ $value = new RichText();
+
+ $value->createText($annotation);
+
+ return $value;
+ }
+
+ private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
+ {
+ return ($simple === null)
+ ? new SimpleXMLElement(' ')
+ : ($simple->attributes($node) ?? new SimpleXMLElement(' '));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php
new file mode 100644
index 0000000..31748cb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php
@@ -0,0 +1,177 @@
+ DataValidation::OPERATOR_BETWEEN,
+ 'equal' => DataValidation::OPERATOR_EQUAL,
+ 'greater' => DataValidation::OPERATOR_GREATERTHAN,
+ 'greaterorequal' => DataValidation::OPERATOR_GREATERTHANOREQUAL,
+ 'less' => DataValidation::OPERATOR_LESSTHAN,
+ 'lessorequal' => DataValidation::OPERATOR_LESSTHANOREQUAL,
+ 'notbetween' => DataValidation::OPERATOR_NOTBETWEEN,
+ 'notequal' => DataValidation::OPERATOR_NOTEQUAL,
+ ];
+
+ private const TYPE_MAPPINGS = [
+ 'textlength' => DataValidation::TYPE_TEXTLENGTH,
+ ];
+
+ private int $thisRow = 0;
+
+ private int $thisColumn = 0;
+
+ private function replaceR1C1(array $matches): string
+ {
+ return AddressHelper::convertToA1($matches[0], $this->thisRow, $this->thisColumn, false);
+ }
+
+ public function loadDataValidations(SimpleXMLElement $worksheet, Spreadsheet $spreadsheet): void
+ {
+ $xmlX = $worksheet->children(Namespaces::URN_EXCEL);
+ $sheet = $spreadsheet->getActiveSheet();
+ /** @var callable */
+ $pregCallback = [$this, 'replaceR1C1'];
+ foreach ($xmlX->DataValidation as $dataValidation) {
+ $cells = [];
+ $validation = new DataValidation();
+
+ // set defaults
+ $validation->setShowDropDown(true);
+ $validation->setShowInputMessage(true);
+ $validation->setShowErrorMessage(true);
+ $validation->setShowDropDown(true);
+ $this->thisRow = 1;
+ $this->thisColumn = 1;
+
+ foreach ($dataValidation as $tagName => $tagValue) {
+ $tagValue = (string) $tagValue;
+ $tagValueLower = strtolower($tagValue);
+ switch ($tagName) {
+ case 'Range':
+ foreach (explode(',', $tagValue) as $range) {
+ $cell = '';
+ if (preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $range, $selectionMatches) === 1) {
+ // range
+ $firstCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
+ . $selectionMatches[1];
+ $cell = $firstCell
+ . ':'
+ . Coordinate::stringFromColumnIndex((int) $selectionMatches[4])
+ . $selectionMatches[3];
+ $this->thisRow = (int) $selectionMatches[1];
+ $this->thisColumn = (int) $selectionMatches[2];
+ $sheet->getCell($firstCell);
+ } elseif (preg_match('/^R(\d+)C(\d+)$/', (string) $range, $selectionMatches) === 1) {
+ // cell
+ $cell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
+ . $selectionMatches[1];
+ $sheet->getCell($cell);
+ $this->thisRow = (int) $selectionMatches[1];
+ $this->thisColumn = (int) $selectionMatches[2];
+ } elseif (preg_match('/^C(\d+)$/', (string) $range, $selectionMatches) === 1) {
+ // column
+ $firstCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[1])
+ . '1';
+ $cell = $firstCell
+ . ':'
+ . Coordinate::stringFromColumnIndex((int) $selectionMatches[1])
+ . ((string) AddressRange::MAX_ROW);
+ $this->thisColumn = (int) $selectionMatches[1];
+ $sheet->getCell($firstCell);
+ } elseif (preg_match('/^R(\d+)$/', (string) $range, $selectionMatches)) {
+ // row
+ $firstCell = 'A'
+ . $selectionMatches[1];
+ $cell = $firstCell
+ . ':'
+ . AddressRange::MAX_COLUMN
+ . $selectionMatches[1];
+ $this->thisRow = (int) $selectionMatches[1];
+ $sheet->getCell($firstCell);
+ }
+
+ $validation->setSqref($cell);
+ $stRange = $sheet->shrinkRangeToFit($cell);
+ $cells = array_merge($cells, Coordinate::extractAllCellReferencesInRange($stRange));
+ }
+
+ break;
+ case 'Type':
+ $validation->setType(self::TYPE_MAPPINGS[$tagValueLower] ?? $tagValueLower);
+
+ break;
+ case 'Qualifier':
+ $validation->setOperator(self::OPERATOR_MAPPINGS[$tagValueLower] ?? $tagValueLower);
+
+ break;
+ case 'InputTitle':
+ $validation->setPromptTitle($tagValue);
+
+ break;
+ case 'InputMessage':
+ $validation->setPrompt($tagValue);
+
+ break;
+ case 'InputHide':
+ $validation->setShowInputMessage(false);
+
+ break;
+ case 'ErrorStyle':
+ $validation->setErrorStyle($tagValueLower);
+
+ break;
+ case 'ErrorTitle':
+ $validation->setErrorTitle($tagValue);
+
+ break;
+ case 'ErrorMessage':
+ $validation->setError($tagValue);
+
+ break;
+ case 'ErrorHide':
+ $validation->setShowErrorMessage(false);
+
+ break;
+ case 'ComboHide':
+ $validation->setShowDropDown(false);
+
+ break;
+ case 'UseBlank':
+ $validation->setAllowBlank(true);
+
+ break;
+ case 'CellRangeList':
+ // FIXME missing FIXME
+
+ break;
+ case 'Min':
+ case 'Value':
+ $tagValue = (string) preg_replace_callback(AddressHelper::R1C1_COORDINATE_REGEX, $pregCallback, $tagValue);
+ $validation->setFormula1($tagValue);
+
+ break;
+ case 'Max':
+ $tagValue = (string) preg_replace_callback(AddressHelper::R1C1_COORDINATE_REGEX, $pregCallback, $tagValue);
+ $validation->setFormula2($tagValue);
+
+ break;
+ }
+ }
+
+ foreach ($cells as $cell) {
+ $sheet->getCell($cell)->setDataValidation(clone $validation);
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php
new file mode 100644
index 0000000..8f9d464
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php
@@ -0,0 +1,130 @@
+pageSetup($xmlX, $this->getPrintDefaults());
+ $this->printSettings = $this->printSetup($xmlX, $printSettings);
+ }
+
+ public function loadPageSettings(Spreadsheet $spreadsheet): void
+ {
+ $spreadsheet->getActiveSheet()->getPageSetup()
+ ->setPaperSize($this->printSettings->paperSize)
+ ->setOrientation($this->printSettings->orientation)
+ ->setScale($this->printSettings->scale)
+ ->setVerticalCentered($this->printSettings->verticalCentered)
+ ->setHorizontalCentered($this->printSettings->horizontalCentered)
+ ->setPageOrder($this->printSettings->printOrder);
+ $spreadsheet->getActiveSheet()->getPageMargins()
+ ->setTop($this->printSettings->topMargin)
+ ->setHeader($this->printSettings->headerMargin)
+ ->setLeft($this->printSettings->leftMargin)
+ ->setRight($this->printSettings->rightMargin)
+ ->setBottom($this->printSettings->bottomMargin)
+ ->setFooter($this->printSettings->footerMargin);
+ }
+
+ private function getPrintDefaults(): stdClass
+ {
+ return (object) [
+ 'paperSize' => 9,
+ 'orientation' => PageSetup::ORIENTATION_DEFAULT,
+ 'scale' => 100,
+ 'horizontalCentered' => false,
+ 'verticalCentered' => false,
+ 'printOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER,
+ 'topMargin' => 0.75,
+ 'headerMargin' => 0.3,
+ 'leftMargin' => 0.7,
+ 'rightMargin' => 0.7,
+ 'bottomMargin' => 0.75,
+ 'footerMargin' => 0.3,
+ ];
+ }
+
+ private function pageSetup(SimpleXMLElement $xmlX, stdClass $printDefaults): stdClass
+ {
+ if (isset($xmlX->WorksheetOptions->PageSetup)) {
+ foreach ($xmlX->WorksheetOptions->PageSetup as $pageSetupData) {
+ foreach ($pageSetupData as $pageSetupKey => $pageSetupValue) {
+ $pageSetupAttributes = $pageSetupValue->attributes(Namespaces::URN_EXCEL);
+ if ($pageSetupAttributes !== null) {
+ switch ($pageSetupKey) {
+ case 'Layout':
+ $this->setLayout($printDefaults, $pageSetupAttributes);
+
+ break;
+ case 'Header':
+ $printDefaults->headerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
+
+ break;
+ case 'Footer':
+ $printDefaults->footerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
+
+ break;
+ case 'PageMargins':
+ $this->setMargins($printDefaults, $pageSetupAttributes);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return $printDefaults;
+ }
+
+ private function printSetup(SimpleXMLElement $xmlX, stdClass $printDefaults): stdClass
+ {
+ if (isset($xmlX->WorksheetOptions->Print)) {
+ foreach ($xmlX->WorksheetOptions->Print as $printData) {
+ foreach ($printData as $printKey => $printValue) {
+ switch ($printKey) {
+ case 'LeftToRight':
+ $printDefaults->printOrder = PageSetup::PAGEORDER_OVER_THEN_DOWN;
+
+ break;
+ case 'PaperSizeIndex':
+ $printDefaults->paperSize = (int) $printValue ?: 9;
+
+ break;
+ case 'Scale':
+ $printDefaults->scale = (int) $printValue ?: 100;
+
+ break;
+ }
+ }
+ }
+ }
+
+ return $printDefaults;
+ }
+
+ private function setLayout(stdClass $printDefaults, SimpleXMLElement $pageSetupAttributes): void
+ {
+ $printDefaults->orientation = (string) strtolower($pageSetupAttributes->Orientation ?? '') ?: PageSetup::ORIENTATION_PORTRAIT;
+ $printDefaults->horizontalCentered = (bool) $pageSetupAttributes->CenterHorizontal ?: false;
+ $printDefaults->verticalCentered = (bool) $pageSetupAttributes->CenterVertical ?: false;
+ }
+
+ private function setMargins(stdClass $printDefaults, SimpleXMLElement $pageSetupAttributes): void
+ {
+ $printDefaults->leftMargin = (float) $pageSetupAttributes->Left ?: 1.0;
+ $printDefaults->rightMargin = (float) $pageSetupAttributes->Right ?: 1.0;
+ $printDefaults->topMargin = (float) $pageSetupAttributes->Top ?: 1.0;
+ $printDefaults->bottomMargin = (float) $pageSetupAttributes->Bottom ?: 1.0;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
new file mode 100644
index 0000000..17e1121
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
@@ -0,0 +1,155 @@
+spreadsheet = $spreadsheet;
+ }
+
+ public function readProperties(SimpleXMLElement $xml, array $namespaces): void
+ {
+ $this->readStandardProperties($xml);
+ $this->readCustomProperties($xml, $namespaces);
+ }
+
+ protected function readStandardProperties(SimpleXMLElement $xml): void
+ {
+ if (isset($xml->DocumentProperties[0])) {
+ $docProps = $this->spreadsheet->getProperties();
+
+ foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
+ $propertyValue = (string) $propertyValue;
+
+ $this->processStandardProperty($docProps, $propertyName, $propertyValue);
+ }
+ }
+ }
+
+ protected function readCustomProperties(SimpleXMLElement $xml, array $namespaces): void
+ {
+ if (isset($xml->CustomDocumentProperties) && is_iterable($xml->CustomDocumentProperties[0])) {
+ $docProps = $this->spreadsheet->getProperties();
+
+ foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
+ $propertyAttributes = self::getAttributes($propertyValue, $namespaces['dt']);
+ $propertyName = (string) preg_replace_callback('/_x([0-9a-f]{4})_/i', [$this, 'hex2str'], $propertyName);
+
+ $this->processCustomProperty($docProps, $propertyName, $propertyValue, $propertyAttributes);
+ }
+ }
+ }
+
+ protected function processStandardProperty(
+ DocumentProperties $docProps,
+ string $propertyName,
+ string $stringValue
+ ): void {
+ switch ($propertyName) {
+ case 'Title':
+ $docProps->setTitle($stringValue);
+
+ break;
+ case 'Subject':
+ $docProps->setSubject($stringValue);
+
+ break;
+ case 'Author':
+ $docProps->setCreator($stringValue);
+
+ break;
+ case 'Created':
+ $docProps->setCreated($stringValue);
+
+ break;
+ case 'LastAuthor':
+ $docProps->setLastModifiedBy($stringValue);
+
+ break;
+ case 'LastSaved':
+ $docProps->setModified($stringValue);
+
+ break;
+ case 'Company':
+ $docProps->setCompany($stringValue);
+
+ break;
+ case 'Category':
+ $docProps->setCategory($stringValue);
+
+ break;
+ case 'Manager':
+ $docProps->setManager($stringValue);
+
+ break;
+ case 'HyperlinkBase':
+ $docProps->setHyperlinkBase($stringValue);
+
+ break;
+ case 'Keywords':
+ $docProps->setKeywords($stringValue);
+
+ break;
+ case 'Description':
+ $docProps->setDescription($stringValue);
+
+ break;
+ }
+ }
+
+ protected function processCustomProperty(
+ DocumentProperties $docProps,
+ string $propertyName,
+ ?SimpleXMLElement $propertyValue,
+ SimpleXMLElement $propertyAttributes
+ ): void {
+ switch ((string) $propertyAttributes) {
+ case 'boolean':
+ $propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
+ $propertyValue = (bool) (string) $propertyValue;
+
+ break;
+ case 'integer':
+ $propertyType = DocumentProperties::PROPERTY_TYPE_INTEGER;
+ $propertyValue = (int) $propertyValue;
+
+ break;
+ case 'float':
+ $propertyType = DocumentProperties::PROPERTY_TYPE_FLOAT;
+ $propertyValue = (float) $propertyValue;
+
+ break;
+ case 'dateTime.tz':
+ case 'dateTime.iso8601tz':
+ $propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
+ $propertyValue = trim((string) $propertyValue);
+
+ break;
+ default:
+ $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
+ $propertyValue = trim((string) $propertyValue);
+
+ break;
+ }
+
+ $docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
+ }
+
+ protected function hex2str(array $hex): string
+ {
+ return mb_chr((int) hexdec($hex[1]), 'UTF-8');
+ }
+
+ private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
+ {
+ return ($simple === null) ? new SimpleXMLElement(' ') : ($simple->attributes($node) ?? new SimpleXMLElement(' '));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
new file mode 100644
index 0000000..698acf6
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
@@ -0,0 +1,107 @@
+Styles) || !is_iterable($xml->Styles[0])) {
+ return [];
+ }
+
+ $alignmentStyleParser = new Style\Alignment();
+ $borderStyleParser = new Style\Border();
+ $fontStyleParser = new Style\Font();
+ $fillStyleParser = new Style\Fill();
+ $numberFormatStyleParser = new Style\NumberFormat();
+
+ foreach ($xml->Styles[0] as $style) {
+ $style_ss = self::getAttributes($style, $namespaces['ss']);
+ $styleID = (string) $style_ss['ID'];
+ $this->styles[$styleID] = $this->styles['Default'] ?? [];
+
+ $alignment = $border = $font = $fill = $numberFormat = $protection = [];
+
+ foreach ($style as $styleType => $styleDatax) {
+ $styleData = self::getSxml($styleDatax);
+ $styleAttributes = $styleData->attributes($namespaces['ss']);
+
+ switch ($styleType) {
+ case 'Alignment':
+ if ($styleAttributes) {
+ $alignment = $alignmentStyleParser->parseStyle($styleAttributes);
+ }
+
+ break;
+ case 'Borders':
+ $border = $borderStyleParser->parseStyle($styleData, $namespaces);
+
+ break;
+ case 'Font':
+ if ($styleAttributes) {
+ $font = $fontStyleParser->parseStyle($styleAttributes);
+ }
+
+ break;
+ case 'Interior':
+ if ($styleAttributes) {
+ $fill = $fillStyleParser->parseStyle($styleAttributes);
+ }
+
+ break;
+ case 'NumberFormat':
+ if ($styleAttributes) {
+ $numberFormat = $numberFormatStyleParser->parseStyle($styleAttributes);
+ }
+
+ break;
+ case 'Protection':
+ $locked = $hidden = null;
+ $styleAttributesP = $styleData->attributes($namespaces['x']);
+ if (isset($styleAttributes['Protected'])) {
+ $locked = ((bool) (string) $styleAttributes['Protected']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED;
+ }
+ if (isset($styleAttributesP['HideFormula'])) {
+ $hidden = ((bool) (string) $styleAttributesP['HideFormula']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED;
+ }
+ if ($locked !== null || $hidden !== null) {
+ $protection['protection'] = [];
+ if ($locked !== null) {
+ $protection['protection']['locked'] = $locked;
+ }
+ if ($hidden !== null) {
+ $protection['protection']['hidden'] = $hidden;
+ }
+ }
+
+ break;
+ }
+ }
+
+ $this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat, $protection);
+ }
+
+ return $this->styles;
+ }
+
+ private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
+ {
+ return ($simple === null) ? new SimpleXMLElement(' ') : ($simple->attributes($node) ?? new SimpleXMLElement(' '));
+ }
+
+ private static function getSxml(?SimpleXMLElement $simple): SimpleXMLElement
+ {
+ return ($simple !== null) ? $simple : new SimpleXMLElement(' ');
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php
new file mode 100644
index 0000000..d136354
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php
@@ -0,0 +1,58 @@
+ $styleAttributeValue) {
+ $styleAttributeValue = (string) $styleAttributeValue;
+ switch ($styleAttributeKey) {
+ case 'Vertical':
+ if (self::identifyFixedStyleValue(self::VERTICAL_ALIGNMENT_STYLES, $styleAttributeValue)) {
+ $style['alignment']['vertical'] = $styleAttributeValue;
+ }
+
+ break;
+ case 'Horizontal':
+ if (self::identifyFixedStyleValue(self::HORIZONTAL_ALIGNMENT_STYLES, $styleAttributeValue)) {
+ $style['alignment']['horizontal'] = $styleAttributeValue;
+ }
+
+ break;
+ case 'WrapText':
+ $style['alignment']['wrapText'] = true;
+
+ break;
+ case 'Rotate':
+ $style['alignment']['textRotation'] = $styleAttributeValue;
+
+ break;
+ }
+ }
+
+ return $style;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php
new file mode 100644
index 0000000..d88f8cd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php
@@ -0,0 +1,98 @@
+ [
+ '1continuous' => BorderStyle::BORDER_THIN,
+ '1dash' => BorderStyle::BORDER_DASHED,
+ '1dashdot' => BorderStyle::BORDER_DASHDOT,
+ '1dashdotdot' => BorderStyle::BORDER_DASHDOTDOT,
+ '1dot' => BorderStyle::BORDER_DOTTED,
+ '1double' => BorderStyle::BORDER_DOUBLE,
+ '2continuous' => BorderStyle::BORDER_MEDIUM,
+ '2dash' => BorderStyle::BORDER_MEDIUMDASHED,
+ '2dashdot' => BorderStyle::BORDER_MEDIUMDASHDOT,
+ '2dashdotdot' => BorderStyle::BORDER_MEDIUMDASHDOTDOT,
+ '2dot' => BorderStyle::BORDER_DOTTED,
+ '2double' => BorderStyle::BORDER_DOUBLE,
+ '3continuous' => BorderStyle::BORDER_THICK,
+ '3dash' => BorderStyle::BORDER_MEDIUMDASHED,
+ '3dashdot' => BorderStyle::BORDER_MEDIUMDASHDOT,
+ '3dashdotdot' => BorderStyle::BORDER_MEDIUMDASHDOTDOT,
+ '3dot' => BorderStyle::BORDER_DOTTED,
+ '3double' => BorderStyle::BORDER_DOUBLE,
+ ],
+ ];
+
+ public function parseStyle(SimpleXMLElement $styleData, array $namespaces): array
+ {
+ $style = [];
+
+ $diagonalDirection = '';
+ $borderPosition = '';
+ foreach ($styleData->Border as $borderStyle) {
+ $borderAttributes = self::getAttributes($borderStyle, $namespaces['ss']);
+ $thisBorder = [];
+ $styleType = (string) $borderAttributes->Weight;
+ $styleType .= strtolower((string) $borderAttributes->LineStyle);
+ $thisBorder['borderStyle'] = self::BORDER_MAPPINGS['borderStyle'][$styleType] ?? BorderStyle::BORDER_NONE;
+
+ foreach ($borderAttributes as $borderStyleKey => $borderStyleValuex) {
+ $borderStyleValue = (string) $borderStyleValuex;
+ switch ($borderStyleKey) {
+ case 'Position':
+ [$borderPosition, $diagonalDirection]
+ = $this->parsePosition($borderStyleValue, $diagonalDirection);
+
+ break;
+ case 'Color':
+ $borderColour = substr($borderStyleValue, 1);
+ $thisBorder['color']['rgb'] = $borderColour;
+
+ break;
+ }
+ }
+
+ if ($borderPosition) {
+ $style['borders'][$borderPosition] = $thisBorder;
+ } elseif ($diagonalDirection) {
+ $style['borders']['diagonalDirection'] = $diagonalDirection;
+ $style['borders']['diagonal'] = $thisBorder;
+ }
+ }
+
+ return $style;
+ }
+
+ protected function parsePosition(string $borderStyleValue, string $diagonalDirection): array
+ {
+ $borderStyleValue = strtolower($borderStyleValue);
+
+ if (in_array($borderStyleValue, self::BORDER_POSITIONS)) {
+ $borderPosition = $borderStyleValue;
+ } elseif ($borderStyleValue === 'diagonalleft') {
+ $diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_DOWN;
+ } elseif ($borderStyleValue === 'diagonalright') {
+ $diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_UP;
+ }
+
+ return [$borderPosition ?? null, $diagonalDirection];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php
new file mode 100644
index 0000000..9a61215
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php
@@ -0,0 +1,63 @@
+ [
+ 'solid' => FillStyles::FILL_SOLID,
+ 'gray75' => FillStyles::FILL_PATTERN_DARKGRAY,
+ 'gray50' => FillStyles::FILL_PATTERN_MEDIUMGRAY,
+ 'gray25' => FillStyles::FILL_PATTERN_LIGHTGRAY,
+ 'gray125' => FillStyles::FILL_PATTERN_GRAY125,
+ 'gray0625' => FillStyles::FILL_PATTERN_GRAY0625,
+ 'horzstripe' => FillStyles::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
+ 'vertstripe' => FillStyles::FILL_PATTERN_DARKVERTICAL, // vertical stripe
+ 'reversediagstripe' => FillStyles::FILL_PATTERN_DARKUP, // reverse diagonal stripe
+ 'diagstripe' => FillStyles::FILL_PATTERN_DARKDOWN, // diagonal stripe
+ 'diagcross' => FillStyles::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
+ 'thickdiagcross' => FillStyles::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
+ 'thinhorzstripe' => FillStyles::FILL_PATTERN_LIGHTHORIZONTAL,
+ 'thinvertstripe' => FillStyles::FILL_PATTERN_LIGHTVERTICAL,
+ 'thinreversediagstripe' => FillStyles::FILL_PATTERN_LIGHTUP,
+ 'thindiagstripe' => FillStyles::FILL_PATTERN_LIGHTDOWN,
+ 'thinhorzcross' => FillStyles::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
+ 'thindiagcross' => FillStyles::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
+ ],
+ ];
+
+ public function parseStyle(SimpleXMLElement $styleAttributes): array
+ {
+ $style = [];
+
+ foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValuex) {
+ $styleAttributeValue = (string) $styleAttributeValuex;
+ switch ($styleAttributeKey) {
+ case 'Color':
+ $style['fill']['endColor']['rgb'] = substr($styleAttributeValue, 1);
+ $style['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1);
+
+ break;
+ case 'PatternColor':
+ $style['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1);
+
+ break;
+ case 'Pattern':
+ $lcStyleAttributeValue = strtolower((string) $styleAttributeValue);
+ $style['fill']['fillType']
+ = self::FILL_MAPPINGS['fillType'][$lcStyleAttributeValue] ?? FillStyles::FILL_NONE;
+
+ break;
+ }
+ }
+
+ return $style;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php
new file mode 100644
index 0000000..5f82488
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php
@@ -0,0 +1,79 @@
+ $styleAttributeValue) {
+ $styleAttributeValue = (string) $styleAttributeValue;
+ switch ($styleAttributeKey) {
+ case 'FontName':
+ $style['font']['name'] = $styleAttributeValue;
+
+ break;
+ case 'Size':
+ $style['font']['size'] = $styleAttributeValue;
+
+ break;
+ case 'Color':
+ $style['font']['color']['rgb'] = substr($styleAttributeValue, 1);
+
+ break;
+ case 'Bold':
+ $style['font']['bold'] = $styleAttributeValue === '1';
+
+ break;
+ case 'Italic':
+ $style['font']['italic'] = $styleAttributeValue === '1';
+
+ break;
+ case 'Underline':
+ $style = $this->parseUnderline($style, $styleAttributeValue);
+
+ break;
+ case 'VerticalAlign':
+ $style = $this->parseVerticalAlign($style, $styleAttributeValue);
+
+ break;
+ }
+ }
+
+ return $style;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php
new file mode 100644
index 0000000..a31aa9e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php
@@ -0,0 +1,33 @@
+ $styleAttributeValue) {
+ $styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue);
+
+ switch ($styleAttributeValue) {
+ case 'Short Date':
+ $styleAttributeValue = 'dd/mm/yyyy';
+
+ break;
+ }
+
+ if ($styleAttributeValue > '') {
+ $style['numberFormat']['formatCode'] = $styleAttributeValue;
+ }
+ }
+
+ return $style;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
new file mode 100644
index 0000000..8103a71
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
@@ -0,0 +1,30 @@
+') : ($simple->attributes($node) ?? new SimpleXMLElement(' '));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
new file mode 100644
index 0000000..5ec7967
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
@@ -0,0 +1,1207 @@
+getBreaks();
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aBreaks, [self::class, 'cellReverseSort'])
+ : uksort($aBreaks, [self::class, 'cellSort']);
+
+ foreach ($aBreaks as $cellAddress => $value) {
+ if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
+ // If we're deleting, then clear any defined breaks that are within the range
+ // of rows/columns that we're deleting
+ $worksheet->setBreak($cellAddress, Worksheet::BREAK_NONE);
+ } else {
+ // Otherwise update any affected breaks by inserting a new break at the appropriate point
+ // and removing the old affected break
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($cellAddress !== $newReference) {
+ $worksheet->setBreak($newReference, $value)
+ ->setBreak($cellAddress, Worksheet::BREAK_NONE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update cell comments when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ */
+ protected function adjustComments(Worksheet $worksheet): void
+ {
+ $aComments = $worksheet->getComments();
+ $aNewComments = []; // the new array of all comments
+
+ foreach ($aComments as $cellAddress => &$value) {
+ // Any comments inside a deleted range will be ignored
+ if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === false) {
+ // Otherwise build a new array of comments indexed by the adjusted cell reference
+ $newReference = $this->updateCellReference($cellAddress);
+ $aNewComments[$newReference] = $value;
+ }
+ }
+ // Replace the comments array with the new set of comments
+ $worksheet->setComments($aNewComments);
+ }
+
+ /**
+ * Update hyperlinks when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function adjustHyperlinks(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
+ {
+ $aHyperlinkCollection = $worksheet->getHyperlinkCollection();
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aHyperlinkCollection, [self::class, 'cellReverseSort'])
+ : uksort($aHyperlinkCollection, [self::class, 'cellSort']);
+
+ foreach ($aHyperlinkCollection as $cellAddress => $value) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
+ $worksheet->setHyperlink($cellAddress, null);
+ } elseif ($cellAddress !== $newReference) {
+ $worksheet->setHyperlink($newReference, $value);
+ $worksheet->setHyperlink($cellAddress, null);
+ }
+ }
+ }
+
+ /**
+ * Update conditional formatting styles when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function adjustConditionalFormatting(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
+ {
+ $aStyles = $worksheet->getConditionalStylesCollection();
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aStyles, [self::class, 'cellReverseSort'])
+ : uksort($aStyles, [self::class, 'cellSort']);
+
+ foreach ($aStyles as $cellAddress => $cfRules) {
+ $worksheet->removeConditionalStyles($cellAddress);
+ $newReference = $this->updateCellReference($cellAddress);
+
+ foreach ($cfRules as &$cfRule) {
+ /** @var Conditional $cfRule */
+ $conditions = $cfRule->getConditions();
+ foreach ($conditions as &$condition) {
+ if (is_string($condition)) {
+ $condition = $this->updateFormulaReferences(
+ $condition,
+ $this->cellReferenceHelper->beforeCellAddress(),
+ $numberOfColumns,
+ $numberOfRows,
+ $worksheet->getTitle(),
+ true
+ );
+ }
+ }
+ $cfRule->setConditions($conditions);
+ }
+ $worksheet->setConditionalStyles($newReference, $cfRules);
+ }
+ }
+
+ /**
+ * Update data validations when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function adjustDataValidations(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
+ {
+ $aDataValidationCollection = $worksheet->getDataValidationCollection();
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aDataValidationCollection, [self::class, 'cellReverseSort'])
+ : uksort($aDataValidationCollection, [self::class, 'cellSort']);
+
+ foreach ($aDataValidationCollection as $cellAddress => $dataValidation) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($cellAddress !== $newReference) {
+ $dataValidation->setSqref($newReference);
+ $worksheet->setDataValidation($newReference, $dataValidation);
+ $worksheet->setDataValidation($cellAddress, null);
+ }
+ }
+ }
+
+ /**
+ * Update merged cells when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ */
+ protected function adjustMergeCells(Worksheet $worksheet): void
+ {
+ $aMergeCells = $worksheet->getMergeCells();
+ $aNewMergeCells = []; // the new array of all merge cells
+ foreach ($aMergeCells as $cellAddress => &$value) {
+ $newReference = $this->updateCellReference($cellAddress);
+ $aNewMergeCells[$newReference] = $newReference;
+ }
+ $worksheet->setMergeCells($aNewMergeCells); // replace the merge cells array
+ }
+
+ /**
+ * Update protected cells when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function adjustProtectedCells(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
+ {
+ $aProtectedCells = $worksheet->getProtectedCells();
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aProtectedCells, [self::class, 'cellReverseSort'])
+ : uksort($aProtectedCells, [self::class, 'cellSort']);
+ foreach ($aProtectedCells as $cellAddress => $value) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($cellAddress !== $newReference) {
+ $worksheet->protectCells($newReference, $value, true);
+ $worksheet->unprotectCells($cellAddress);
+ }
+ }
+ }
+
+ /**
+ * Update column dimensions when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ */
+ protected function adjustColumnDimensions(Worksheet $worksheet): void
+ {
+ $aColumnDimensions = array_reverse($worksheet->getColumnDimensions(), true);
+ if (!empty($aColumnDimensions)) {
+ foreach ($aColumnDimensions as $objColumnDimension) {
+ $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1');
+ [$newReference] = Coordinate::coordinateFromString($newReference);
+ if ($objColumnDimension->getColumnIndex() !== $newReference) {
+ $objColumnDimension->setColumnIndex($newReference);
+ }
+ }
+
+ $worksheet->refreshColumnDimensions();
+ }
+ }
+
+ /**
+ * Update row dimensions when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ * @param int $beforeRow Number of the row we're inserting/deleting before
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function adjustRowDimensions(Worksheet $worksheet, $beforeRow, $numberOfRows): void
+ {
+ $aRowDimensions = array_reverse($worksheet->getRowDimensions(), true);
+ if (!empty($aRowDimensions)) {
+ foreach ($aRowDimensions as $objRowDimension) {
+ $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex());
+ [, $newReference] = Coordinate::coordinateFromString($newReference);
+ $newRoweference = (int) $newReference;
+ if ($objRowDimension->getRowIndex() !== $newRoweference) {
+ $objRowDimension->setRowIndex($newRoweference);
+ }
+ }
+
+ $worksheet->refreshRowDimensions();
+
+ $copyDimension = $worksheet->getRowDimension($beforeRow - 1);
+ for ($i = $beforeRow; $i <= $beforeRow - 1 + $numberOfRows; ++$i) {
+ $newDimension = $worksheet->getRowDimension($i);
+ $newDimension->setRowHeight($copyDimension->getRowHeight());
+ $newDimension->setVisible($copyDimension->getVisible());
+ $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
+ $newDimension->setCollapsed($copyDimension->getCollapsed());
+ }
+ }
+ }
+
+ /**
+ * Insert a new column or row, updating all possible related data.
+ *
+ * @param string $beforeCellAddress Insert before this cell address (e.g. 'A1')
+ * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ * @param Worksheet $worksheet The worksheet that we're editing
+ */
+ public function insertNewBefore(
+ string $beforeCellAddress,
+ int $numberOfColumns,
+ int $numberOfRows,
+ Worksheet $worksheet
+ ): void {
+ $remove = ($numberOfColumns < 0 || $numberOfRows < 0);
+
+ if (
+ $this->cellReferenceHelper === null
+ || $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
+ ) {
+ $this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
+ }
+
+ // Get coordinate of $beforeCellAddress
+ [$beforeColumn, $beforeRow] = Coordinate::indexesFromString($beforeCellAddress);
+
+ // Clear cells if we are removing columns or rows
+ $highestColumn = $worksheet->getHighestColumn();
+ $highestRow = $worksheet->getHighestRow();
+
+ // 1. Clear column strips if we are removing columns
+ if ($numberOfColumns < 0 && $beforeColumn - 2 + $numberOfColumns > 0) {
+ $this->clearColumnStrips($highestRow, $beforeColumn, $numberOfColumns, $worksheet);
+ }
+
+ // 2. Clear row strips if we are removing rows
+ if ($numberOfRows < 0 && $beforeRow - 1 + $numberOfRows > 0) {
+ $this->clearRowStrips($highestColumn, $beforeColumn, $beforeRow, $numberOfRows, $worksheet);
+ }
+
+ // Find missing coordinates. This is important when inserting column before the last column
+ $cellCollection = $worksheet->getCellCollection();
+ $missingCoordinates = array_filter(
+ array_map(fn ($row): string => "{$highestColumn}{$row}", range(1, $highestRow)),
+ fn ($coordinate): bool => $cellCollection->has($coordinate) === false
+ );
+
+ // Create missing cells with null values
+ if (!empty($missingCoordinates)) {
+ foreach ($missingCoordinates as $coordinate) {
+ $worksheet->createNewCell($coordinate);
+ }
+ }
+
+ $allCoordinates = $worksheet->getCoordinates();
+ if ($remove) {
+ // It's faster to reverse and pop than to use unshift, especially with large cell collections
+ $allCoordinates = array_reverse($allCoordinates);
+ }
+
+ // Loop through cells, bottom-up, and change cell coordinate
+ while ($coordinate = array_pop($allCoordinates)) {
+ $cell = $worksheet->getCell($coordinate);
+ $cellIndex = Coordinate::columnIndexFromString($cell->getColumn());
+
+ if ($cellIndex - 1 + $numberOfColumns < 0) {
+ continue;
+ }
+
+ // New coordinate
+ $newCoordinate = Coordinate::stringFromColumnIndex($cellIndex + $numberOfColumns) . ($cell->getRow() + $numberOfRows);
+
+ // Should the cell be updated? Move value and cellXf index from one cell to another.
+ if (($cellIndex >= $beforeColumn) && ($cell->getRow() >= $beforeRow)) {
+ // Update cell styles
+ $worksheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex());
+
+ // Insert this cell at its new location
+ if ($cell->getDataType() === DataType::TYPE_FORMULA) {
+ // Formula should be adjusted
+ $worksheet->getCell($newCoordinate)
+ ->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true));
+ } else {
+ // Cell value should not be adjusted
+ $worksheet->getCell($newCoordinate)->setValueExplicit($cell->getValue(), $cell->getDataType());
+ }
+
+ // Clear the original cell
+ $worksheet->getCellCollection()->delete($coordinate);
+ } else {
+ /* We don't need to update styles for rows/columns before our insertion position,
+ but we do still need to adjust any formulae in those cells */
+ if ($cell->getDataType() === DataType::TYPE_FORMULA) {
+ // Formula should be adjusted
+ $cell->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true));
+ }
+ }
+ }
+
+ // Duplicate styles for the newly inserted cells
+ $highestColumn = $worksheet->getHighestColumn();
+ $highestRow = $worksheet->getHighestRow();
+
+ if ($numberOfColumns > 0 && $beforeColumn - 2 > 0) {
+ $this->duplicateStylesByColumn($worksheet, $beforeColumn, $beforeRow, $highestRow, $numberOfColumns);
+ }
+
+ if ($numberOfRows > 0 && $beforeRow - 1 > 0) {
+ $this->duplicateStylesByRow($worksheet, $beforeColumn, $beforeRow, $highestColumn, $numberOfRows);
+ }
+
+ // Update worksheet: column dimensions
+ $this->adjustColumnDimensions($worksheet);
+
+ // Update worksheet: row dimensions
+ $this->adjustRowDimensions($worksheet, $beforeRow, $numberOfRows);
+
+ // Update worksheet: page breaks
+ $this->adjustPageBreaks($worksheet, $numberOfColumns, $numberOfRows);
+
+ // Update worksheet: comments
+ $this->adjustComments($worksheet);
+
+ // Update worksheet: hyperlinks
+ $this->adjustHyperlinks($worksheet, $numberOfColumns, $numberOfRows);
+
+ // Update worksheet: conditional formatting styles
+ $this->adjustConditionalFormatting($worksheet, $numberOfColumns, $numberOfRows);
+
+ // Update worksheet: data validations
+ $this->adjustDataValidations($worksheet, $numberOfColumns, $numberOfRows);
+
+ // Update worksheet: merge cells
+ $this->adjustMergeCells($worksheet);
+
+ // Update worksheet: protected cells
+ $this->adjustProtectedCells($worksheet, $numberOfColumns, $numberOfRows);
+
+ // Update worksheet: autofilter
+ $this->adjustAutoFilter($worksheet, $beforeCellAddress, $numberOfColumns);
+
+ // Update worksheet: table
+ $this->adjustTable($worksheet, $beforeCellAddress, $numberOfColumns);
+
+ // Update worksheet: freeze pane
+ if ($worksheet->getFreezePane()) {
+ $splitCell = $worksheet->getFreezePane();
+ $topLeftCell = $worksheet->getTopLeftCell() ?? '';
+
+ $splitCell = $this->updateCellReference($splitCell);
+ $topLeftCell = $this->updateCellReference($topLeftCell);
+
+ $worksheet->freezePane($splitCell, $topLeftCell);
+ }
+
+ // Page setup
+ if ($worksheet->getPageSetup()->isPrintAreaSet()) {
+ $worksheet->getPageSetup()->setPrintArea(
+ $this->updateCellReference($worksheet->getPageSetup()->getPrintArea())
+ );
+ }
+
+ // Update worksheet: drawings
+ $aDrawings = $worksheet->getDrawingCollection();
+ foreach ($aDrawings as $objDrawing) {
+ $newReference = $this->updateCellReference($objDrawing->getCoordinates());
+ if ($objDrawing->getCoordinates() != $newReference) {
+ $objDrawing->setCoordinates($newReference);
+ }
+ if ($objDrawing->getCoordinates2() !== '') {
+ $newReference = $this->updateCellReference($objDrawing->getCoordinates2());
+ if ($objDrawing->getCoordinates2() != $newReference) {
+ $objDrawing->setCoordinates2($newReference);
+ }
+ }
+ }
+
+ // Update workbook: define names
+ if (count($worksheet->getParentOrThrow()->getDefinedNames()) > 0) {
+ $this->updateDefinedNames($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ }
+
+ // Garbage collect
+ $worksheet->garbageCollect();
+ }
+
+ /**
+ * Update references within formulas.
+ *
+ * @param string $formula Formula to update
+ * @param string $beforeCellAddress Insert before this one
+ * @param int $numberOfColumns Number of columns to insert
+ * @param int $numberOfRows Number of rows to insert
+ * @param string $worksheetName Worksheet name/title
+ *
+ * @return string Updated formula
+ */
+ public function updateFormulaReferences(
+ $formula = '',
+ $beforeCellAddress = 'A1',
+ $numberOfColumns = 0,
+ $numberOfRows = 0,
+ $worksheetName = '',
+ bool $includeAbsoluteReferences = false,
+ bool $onlyAbsoluteReferences = false
+ ): string {
+ if (
+ $this->cellReferenceHelper === null
+ || $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
+ ) {
+ $this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
+ }
+
+ // Update cell references in the formula
+ $formulaBlocks = explode('"', $formula);
+ $i = false;
+ foreach ($formulaBlocks as &$formulaBlock) {
+ // Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
+ $i = $i === false;
+ if ($i) {
+ $adjustCount = 0;
+ $newCellTokens = $cellTokens = [];
+ // Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
+ $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+ if ($matchCount > 0) {
+ foreach ($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+ $fromString .= $match[3] . ':' . $match[4];
+ $modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences), 2);
+ $modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences), 2);
+
+ if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
+ if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
+ $toString = ($match[2] > '') ? $match[2] . '!' : '';
+ $toString .= $modified3 . ':' . $modified4;
+ // 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 = 100000;
+ $row = 10000000 + (int) trim($match[3], '$');
+ $cellIndex = "{$column}{$row}";
+
+ $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach ($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+ $fromString .= $match[3] . ':' . $match[4];
+ $modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences), 0, -2);
+ $modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences), 0, -2);
+
+ if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
+ if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
+ $toString = ($match[2] > '') ? $match[2] . '!' : '';
+ $toString .= $modified3 . ':' . $modified4;
+ // 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($match[3], '$')) + 100000;
+ $row = 10000000;
+ $cellIndex = "{$column}{$row}";
+
+ $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach ($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+ $fromString .= $match[3] . ':' . $match[4];
+ $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences);
+ $modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences);
+
+ if ($match[3] . $match[4] !== $modified3 . $modified4) {
+ if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
+ $toString = ($match[2] > '') ? $match[2] . '!' : '';
+ $toString .= $modified3 . ':' . $modified4;
+ [$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 = (int) trim($row, '$') + 10000000;
+ $cellIndex = "{$column}{$row}";
+
+ $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+ $cellTokens[$cellIndex] = '/(? 0) {
+ foreach ($matches as $match) {
+ $fromString = ($match[2] > '') ? $match[2] . '!' : '';
+ $fromString .= $match[3];
+
+ $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences);
+ if ($match[3] !== $modified3) {
+ if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
+ $toString = ($match[2] > '') ? $match[2] . '!' : '';
+ $toString .= $modified3;
+ [$column, $row] = Coordinate::coordinateFromString($match[3]);
+ $columnAdditionalIndex = $column[0] === '$' ? 1 : 0;
+ $rowAdditionalIndex = $row[0] === '$' ? 1 : 0;
+ // 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 = (int) trim($row, '$') + 10000000;
+ $cellIndex = $row . $rowAdditionalIndex . $column . $columnAdditionalIndex;
+
+ $newCellTokens[$cellIndex] = preg_quote($toString, '/');
+ $cellTokens[$cellIndex] = '/(? 0) {
+ if ($numberOfColumns > 0 || $numberOfRows > 0) {
+ krsort($cellTokens);
+ krsort($newCellTokens);
+ } else {
+ ksort($cellTokens);
+ ksort($newCellTokens);
+ } // Update cell references in the formula
+ $formulaBlock = str_replace('\\', '', (string) preg_replace($cellTokens, $newCellTokens, $formulaBlock));
+ }
+ }
+ }
+ unset($formulaBlock);
+
+ // Then rebuild the formula string
+ return implode('"', $formulaBlocks);
+ }
+
+ /**
+ * Update all cell references within a formula, irrespective of worksheet.
+ */
+ public function updateFormulaReferencesAnyWorksheet(string $formula = '', int $numberOfColumns = 0, int $numberOfRows = 0): string
+ {
+ $formula = $this->updateCellReferencesAllWorksheets($formula, $numberOfColumns, $numberOfRows);
+
+ if ($numberOfColumns !== 0) {
+ $formula = $this->updateColumnRangesAllWorksheets($formula, $numberOfColumns);
+ }
+
+ if ($numberOfRows !== 0) {
+ $formula = $this->updateRowRangesAllWorksheets($formula, $numberOfRows);
+ }
+
+ return $formula;
+ }
+
+ private function updateCellReferencesAllWorksheets(string $formula, int $numberOfColumns, int $numberOfRows): string
+ {
+ $splitCount = preg_match_all(
+ '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui',
+ $formula,
+ $splitRanges,
+ PREG_OFFSET_CAPTURE
+ );
+
+ $columnLengths = array_map('strlen', array_column($splitRanges[6], 0));
+ $rowLengths = array_map('strlen', array_column($splitRanges[7], 0));
+ $columnOffsets = array_column($splitRanges[6], 1);
+ $rowOffsets = array_column($splitRanges[7], 1);
+
+ $columns = $splitRanges[6];
+ $rows = $splitRanges[7];
+
+ while ($splitCount > 0) {
+ --$splitCount;
+ $columnLength = $columnLengths[$splitCount];
+ $rowLength = $rowLengths[$splitCount];
+ $columnOffset = $columnOffsets[$splitCount];
+ $rowOffset = $rowOffsets[$splitCount];
+ $column = $columns[$splitCount][0];
+ $row = $rows[$splitCount][0];
+
+ if (!empty($column) && $column[0] !== '$') {
+ $column = ((Coordinate::columnIndexFromString($column) + $numberOfColumns) % AddressRange::MAX_COLUMN_INT) ?: AddressRange::MAX_COLUMN_INT;
+ $column = Coordinate::stringFromColumnIndex($column);
+ $rowOffset -= ($columnLength - strlen($column));
+ $formula = substr($formula, 0, $columnOffset) . $column . substr($formula, $columnOffset + $columnLength);
+ }
+ if (!empty($row) && $row[0] !== '$') {
+ $row = (((int) $row + $numberOfRows) % AddressRange::MAX_ROW) ?: AddressRange::MAX_ROW;
+ $formula = substr($formula, 0, $rowOffset) . $row . substr($formula, $rowOffset + $rowLength);
+ }
+ }
+
+ return $formula;
+ }
+
+ private function updateColumnRangesAllWorksheets(string $formula, int $numberOfColumns): string
+ {
+ $splitCount = preg_match_all(
+ '/' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '/mui',
+ $formula,
+ $splitRanges,
+ PREG_OFFSET_CAPTURE
+ );
+
+ $fromColumnLengths = array_map('strlen', array_column($splitRanges[1], 0));
+ $fromColumnOffsets = array_column($splitRanges[1], 1);
+ $toColumnLengths = array_map('strlen', array_column($splitRanges[2], 0));
+ $toColumnOffsets = array_column($splitRanges[2], 1);
+
+ $fromColumns = $splitRanges[1];
+ $toColumns = $splitRanges[2];
+
+ while ($splitCount > 0) {
+ --$splitCount;
+ $fromColumnLength = $fromColumnLengths[$splitCount];
+ $toColumnLength = $toColumnLengths[$splitCount];
+ $fromColumnOffset = $fromColumnOffsets[$splitCount];
+ $toColumnOffset = $toColumnOffsets[$splitCount];
+ $fromColumn = $fromColumns[$splitCount][0];
+ $toColumn = $toColumns[$splitCount][0];
+
+ if (!empty($fromColumn) && $fromColumn[0] !== '$') {
+ $fromColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($fromColumn) + $numberOfColumns);
+ $formula = substr($formula, 0, $fromColumnOffset) . $fromColumn . substr($formula, $fromColumnOffset + $fromColumnLength);
+ }
+ if (!empty($toColumn) && $toColumn[0] !== '$') {
+ $toColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($toColumn) + $numberOfColumns);
+ $formula = substr($formula, 0, $toColumnOffset) . $toColumn . substr($formula, $toColumnOffset + $toColumnLength);
+ }
+ }
+
+ return $formula;
+ }
+
+ private function updateRowRangesAllWorksheets(string $formula, int $numberOfRows): string
+ {
+ $splitCount = preg_match_all(
+ '/' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '/mui',
+ $formula,
+ $splitRanges,
+ PREG_OFFSET_CAPTURE
+ );
+
+ $fromRowLengths = array_map('strlen', array_column($splitRanges[1], 0));
+ $fromRowOffsets = array_column($splitRanges[1], 1);
+ $toRowLengths = array_map('strlen', array_column($splitRanges[2], 0));
+ $toRowOffsets = array_column($splitRanges[2], 1);
+
+ $fromRows = $splitRanges[1];
+ $toRows = $splitRanges[2];
+
+ while ($splitCount > 0) {
+ --$splitCount;
+ $fromRowLength = $fromRowLengths[$splitCount];
+ $toRowLength = $toRowLengths[$splitCount];
+ $fromRowOffset = $fromRowOffsets[$splitCount];
+ $toRowOffset = $toRowOffsets[$splitCount];
+ $fromRow = $fromRows[$splitCount][0];
+ $toRow = $toRows[$splitCount][0];
+
+ if (!empty($fromRow) && $fromRow[0] !== '$') {
+ $fromRow = (int) $fromRow + $numberOfRows;
+ $formula = substr($formula, 0, $fromRowOffset) . $fromRow . substr($formula, $fromRowOffset + $fromRowLength);
+ }
+ if (!empty($toRow) && $toRow[0] !== '$') {
+ $toRow = (int) $toRow + $numberOfRows;
+ $formula = substr($formula, 0, $toRowOffset) . $toRow . substr($formula, $toRowOffset + $toRowLength);
+ }
+ }
+
+ return $formula;
+ }
+
+ /**
+ * Update cell reference.
+ *
+ * @param string $cellReference Cell address or range of addresses
+ *
+ * @return string Updated cell range
+ */
+ private function updateCellReference($cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false)
+ {
+ // Is it in another worksheet? Will not have to update anything.
+ if (str_contains($cellReference, '!')) {
+ return $cellReference;
+ }
+ // Is it a range or a single cell?
+ if (!Coordinate::coordinateIsRange($cellReference)) {
+ // Single cell
+ return $this->cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences, $onlyAbsoluteReferences);
+ }
+
+ // Range
+ return $this->updateCellRange($cellReference, $includeAbsoluteReferences, $onlyAbsoluteReferences);
+ }
+
+ /**
+ * Update named formulae (i.e. containing worksheet references / named ranges).
+ *
+ * @param Spreadsheet $spreadsheet Object to update
+ * @param string $oldName Old name (name to replace)
+ * @param string $newName New name
+ */
+ public function updateNamedFormulae(Spreadsheet $spreadsheet, string $oldName = '', string $newName = ''): void
+ {
+ if ($oldName == '') {
+ return;
+ }
+
+ foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
+ foreach ($sheet->getCoordinates(false) as $coordinate) {
+ $cell = $sheet->getCell($coordinate);
+ if ($cell->getDataType() === DataType::TYPE_FORMULA) {
+ $formula = $cell->getValue();
+ if (str_contains($formula, $oldName)) {
+ $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
+ $formula = str_replace($oldName . '!', $newName . '!', $formula);
+ $cell->setValueExplicit($formula, DataType::TYPE_FORMULA);
+ }
+ }
+ }
+ }
+ }
+
+ private function updateDefinedNames(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
+ {
+ foreach ($worksheet->getParentOrThrow()->getDefinedNames() as $definedName) {
+ if ($definedName->isFormula() === false) {
+ $this->updateNamedRange($definedName, $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ } else {
+ $this->updateNamedFormula($definedName, $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ }
+ }
+ }
+
+ private function updateNamedRange(DefinedName $definedName, Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
+ {
+ $cellAddress = $definedName->getValue();
+ $asFormula = ($cellAddress[0] === '=');
+ if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) {
+ /**
+ * If we delete the entire range that is referenced by a Named Range, MS Excel sets the value to #REF!
+ * PhpSpreadsheet still only does a basic adjustment, so the Named Range will still reference Cells.
+ * Note that this applies only when deleting columns/rows; subsequent insertion won't fix the #REF!
+ * TODO Can we work out a method to identify Named Ranges that cease to be valid, so that we can replace
+ * them with a #REF!
+ */
+ if ($asFormula === true) {
+ $formula = $this->updateFormulaReferences($cellAddress, $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true, true);
+ $definedName->setValue($formula);
+ } else {
+ $definedName->setValue($this->updateCellReference(ltrim($cellAddress, '='), true));
+ }
+ }
+ }
+
+ private function updateNamedFormula(DefinedName $definedName, Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
+ {
+ if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) {
+ /**
+ * If we delete the entire range that is referenced by a Named Formula, MS Excel sets the value to #REF!
+ * PhpSpreadsheet still only does a basic adjustment, so the Named Formula will still reference Cells.
+ * Note that this applies only when deleting columns/rows; subsequent insertion won't fix the #REF!
+ * TODO Can we work out a method to identify Named Ranges that cease to be valid, so that we can replace
+ * them with a #REF!
+ */
+ $formula = $definedName->getValue();
+ $formula = $this->updateFormulaReferences($formula, $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true);
+ $definedName->setValue($formula);
+ }
+ }
+
+ /**
+ * Update cell range.
+ *
+ * @param string $cellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
+ *
+ * @return string Updated cell range
+ */
+ private function updateCellRange(string $cellRange = 'A1:A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
+ {
+ if (!Coordinate::coordinateIsRange($cellRange)) {
+ throw new Exception('Only cell ranges may be passed to this method.');
+ }
+
+ // Update range
+ $range = Coordinate::splitRange($cellRange);
+ $ic = count($range);
+ for ($i = 0; $i < $ic; ++$i) {
+ $jc = count($range[$i]);
+ for ($j = 0; $j < $jc; ++$j) {
+ if (ctype_alpha($range[$i][$j])) {
+ $range[$i][$j] = Coordinate::coordinateFromString(
+ $this->cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences, $onlyAbsoluteReferences)
+ )[0];
+ } elseif (ctype_digit($range[$i][$j])) {
+ $range[$i][$j] = Coordinate::coordinateFromString(
+ $this->cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences)
+ )[1];
+ } else {
+ $range[$i][$j] = $this->cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences);
+ }
+ }
+ }
+
+ // Recreate range string
+ return Coordinate::buildRange($range);
+ }
+
+ private function clearColumnStrips(int $highestRow, int $beforeColumn, int $numberOfColumns, Worksheet $worksheet): void
+ {
+ $startColumnId = Coordinate::stringFromColumnIndex($beforeColumn + $numberOfColumns);
+ $endColumnId = Coordinate::stringFromColumnIndex($beforeColumn);
+
+ for ($row = 1; $row <= $highestRow - 1; ++$row) {
+ for ($column = $startColumnId; $column !== $endColumnId; ++$column) {
+ $coordinate = $column . $row;
+ $this->clearStripCell($worksheet, $coordinate);
+ }
+ }
+ }
+
+ private function clearRowStrips(string $highestColumn, int $beforeColumn, int $beforeRow, int $numberOfRows, Worksheet $worksheet): void
+ {
+ $startColumnId = Coordinate::stringFromColumnIndex($beforeColumn);
+ ++$highestColumn;
+
+ for ($column = $startColumnId; $column !== $highestColumn; ++$column) {
+ for ($row = $beforeRow + $numberOfRows; $row <= $beforeRow - 1; ++$row) {
+ $coordinate = $column . $row;
+ $this->clearStripCell($worksheet, $coordinate);
+ }
+ }
+ }
+
+ private function clearStripCell(Worksheet $worksheet, string $coordinate): void
+ {
+ $worksheet->removeConditionalStyles($coordinate);
+ $worksheet->setHyperlink($coordinate);
+ $worksheet->setDataValidation($coordinate);
+ $worksheet->removeComment($coordinate);
+
+ if ($worksheet->cellExists($coordinate)) {
+ $worksheet->getCell($coordinate)->setValueExplicit(null, DataType::TYPE_NULL);
+ $worksheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
+
+ private function adjustAutoFilter(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns): void
+ {
+ $autoFilter = $worksheet->getAutoFilter();
+ $autoFilterRange = $autoFilter->getRange();
+ if (!empty($autoFilterRange)) {
+ if ($numberOfColumns !== 0) {
+ $autoFilterColumns = $autoFilter->getColumns();
+ if (count($autoFilterColumns) > 0) {
+ $column = '';
+ $row = 0;
+ sscanf($beforeCellAddress, '%[A-Z]%d', $column, $row);
+ $columnIndex = Coordinate::columnIndexFromString((string) $column);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($autoFilterRange);
+ if ($columnIndex <= $rangeEnd[0]) {
+ if ($numberOfColumns < 0) {
+ $this->adjustAutoFilterDeleteRules($columnIndex, $numberOfColumns, $autoFilterColumns, $autoFilter);
+ }
+ $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
+
+ // Shuffle columns in autofilter range
+ if ($numberOfColumns > 0) {
+ $this->adjustAutoFilterInsert($startCol, $numberOfColumns, $rangeEnd[0], $autoFilter);
+ } else {
+ $this->adjustAutoFilterDelete($startCol, $numberOfColumns, $rangeEnd[0], $autoFilter);
+ }
+ }
+ }
+ }
+
+ $worksheet->setAutoFilter(
+ $this->updateCellReference($autoFilterRange)
+ );
+ }
+ }
+
+ private function adjustAutoFilterDeleteRules(int $columnIndex, int $numberOfColumns, array $autoFilterColumns, AutoFilter $autoFilter): void
+ {
+ // If we're actually deleting any columns that fall within the autofilter range,
+ // then we delete any rules for those columns
+ $deleteColumn = $columnIndex + $numberOfColumns - 1;
+ $deleteCount = abs($numberOfColumns);
+
+ for ($i = 1; $i <= $deleteCount; ++$i) {
+ $columnName = Coordinate::stringFromColumnIndex($deleteColumn + 1);
+ if (isset($autoFilterColumns[$columnName])) {
+ $autoFilter->clearColumn($columnName);
+ }
+ ++$deleteColumn;
+ }
+ }
+
+ private function adjustAutoFilterInsert(int $startCol, int $numberOfColumns, int $rangeEnd, AutoFilter $autoFilter): void
+ {
+ $startColRef = $startCol;
+ $endColRef = $rangeEnd;
+ $toColRef = $rangeEnd + $numberOfColumns;
+
+ do {
+ $autoFilter->shiftColumn(Coordinate::stringFromColumnIndex($endColRef), Coordinate::stringFromColumnIndex($toColRef));
+ --$endColRef;
+ --$toColRef;
+ } while ($startColRef <= $endColRef);
+ }
+
+ private function adjustAutoFilterDelete(int $startCol, int $numberOfColumns, int $rangeEnd, AutoFilter $autoFilter): void
+ {
+ // For delete, we shuffle from beginning to end to avoid overwriting
+ $startColID = Coordinate::stringFromColumnIndex($startCol);
+ $toColID = Coordinate::stringFromColumnIndex($startCol + $numberOfColumns);
+ $endColID = Coordinate::stringFromColumnIndex($rangeEnd + 1);
+
+ do {
+ $autoFilter->shiftColumn($startColID, $toColID);
+ ++$startColID;
+ ++$toColID;
+ } while ($startColID !== $endColID);
+ }
+
+ private function adjustTable(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns): void
+ {
+ $tableCollection = $worksheet->getTableCollection();
+
+ foreach ($tableCollection as $table) {
+ $tableRange = $table->getRange();
+ if (!empty($tableRange)) {
+ if ($numberOfColumns !== 0) {
+ $tableColumns = $table->getColumns();
+ if (count($tableColumns) > 0) {
+ $column = '';
+ $row = 0;
+ sscanf($beforeCellAddress, '%[A-Z]%d', $column, $row);
+ $columnIndex = Coordinate::columnIndexFromString((string) $column);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($tableRange);
+ if ($columnIndex <= $rangeEnd[0]) {
+ if ($numberOfColumns < 0) {
+ $this->adjustTableDeleteRules($columnIndex, $numberOfColumns, $tableColumns, $table);
+ }
+ $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
+
+ // Shuffle columns in table range
+ if ($numberOfColumns > 0) {
+ $this->adjustTableInsert($startCol, $numberOfColumns, $rangeEnd[0], $table);
+ } else {
+ $this->adjustTableDelete($startCol, $numberOfColumns, $rangeEnd[0], $table);
+ }
+ }
+ }
+ }
+
+ $table->setRange($this->updateCellReference($tableRange));
+ }
+ }
+ }
+
+ private function adjustTableDeleteRules(int $columnIndex, int $numberOfColumns, array $tableColumns, Table $table): void
+ {
+ // If we're actually deleting any columns that fall within the table range,
+ // then we delete any rules for those columns
+ $deleteColumn = $columnIndex + $numberOfColumns - 1;
+ $deleteCount = abs($numberOfColumns);
+
+ for ($i = 1; $i <= $deleteCount; ++$i) {
+ $columnName = Coordinate::stringFromColumnIndex($deleteColumn + 1);
+ if (isset($tableColumns[$columnName])) {
+ $table->clearColumn($columnName);
+ }
+ ++$deleteColumn;
+ }
+ }
+
+ private function adjustTableInsert(int $startCol, int $numberOfColumns, int $rangeEnd, Table $table): void
+ {
+ $startColRef = $startCol;
+ $endColRef = $rangeEnd;
+ $toColRef = $rangeEnd + $numberOfColumns;
+
+ do {
+ $table->shiftColumn(Coordinate::stringFromColumnIndex($endColRef), Coordinate::stringFromColumnIndex($toColRef));
+ --$endColRef;
+ --$toColRef;
+ } while ($startColRef <= $endColRef);
+ }
+
+ private function adjustTableDelete(int $startCol, int $numberOfColumns, int $rangeEnd, Table $table): void
+ {
+ // For delete, we shuffle from beginning to end to avoid overwriting
+ $startColID = Coordinate::stringFromColumnIndex($startCol);
+ $toColID = Coordinate::stringFromColumnIndex($startCol + $numberOfColumns);
+ $endColID = Coordinate::stringFromColumnIndex($rangeEnd + 1);
+
+ do {
+ $table->shiftColumn($startColID, $toColID);
+ ++$startColID;
+ ++$toColID;
+ } while ($startColID !== $endColID);
+ }
+
+ private function duplicateStylesByColumn(Worksheet $worksheet, int $beforeColumn, int $beforeRow, int $highestRow, int $numberOfColumns): void
+ {
+ $beforeColumnName = Coordinate::stringFromColumnIndex($beforeColumn - 1);
+ for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
+ // Style
+ $coordinate = $beforeColumnName . $i;
+ if ($worksheet->cellExists($coordinate)) {
+ $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
+ for ($j = $beforeColumn; $j <= $beforeColumn - 1 + $numberOfColumns; ++$j) {
+ $worksheet->getCell([$j, $i])->setXfIndex($xfIndex);
+ }
+ }
+ }
+ }
+
+ private function duplicateStylesByRow(Worksheet $worksheet, int $beforeColumn, int $beforeRow, string $highestColumn, int $numberOfRows): void
+ {
+ $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
+ for ($i = $beforeColumn; $i <= $highestColumnIndex; ++$i) {
+ // Style
+ $coordinate = Coordinate::stringFromColumnIndex($i) . ($beforeRow - 1);
+ if ($worksheet->cellExists($coordinate)) {
+ $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
+ for ($j = $beforeRow; $j <= $beforeRow - 1 + $numberOfRows; ++$j) {
+ $worksheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+ }
+ }
+ }
+ }
+
+ /**
+ * __clone implementation. Cloning should not be allowed in a Singleton!
+ */
+ final public function __clone()
+ {
+ throw new Exception('Cloning a Singleton is not allowed!');
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
new file mode 100644
index 0000000..284a365
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
@@ -0,0 +1,38 @@
+richTextElements = [];
+
+ // Rich-Text string attached to cell?
+ if ($cell !== null) {
+ // Add cell text and style
+ if ($cell->getValue() != '') {
+ $objRun = new Run($cell->getValue());
+ $objRun->setFont(clone $cell->getWorksheet()->getStyle($cell->getCoordinate())->getFont());
+ $this->addText($objRun);
+ }
+
+ // Set parent value
+ $cell->setValueExplicit($this, DataType::TYPE_STRING);
+ }
+ }
+
+ /**
+ * Add text.
+ *
+ * @param ITextElement $text Rich text element
+ *
+ * @return $this
+ */
+ public function addText(ITextElement $text): static
+ {
+ $this->richTextElements[] = $text;
+
+ return $this;
+ }
+
+ /**
+ * Create text.
+ *
+ * @param string $text Text
+ */
+ public function createText($text): TextElement
+ {
+ $objText = new TextElement($text);
+ $this->addText($objText);
+
+ return $objText;
+ }
+
+ /**
+ * Create text run.
+ *
+ * @param string $text Text
+ */
+ public function createTextRun($text): Run
+ {
+ $objText = new Run($text);
+ $this->addText($objText);
+
+ return $objText;
+ }
+
+ /**
+ * Get plain text.
+ */
+ public function getPlainText(): string
+ {
+ // Return value
+ $returnValue = '';
+
+ // Loop through all ITextElements
+ foreach ($this->richTextElements as $text) {
+ $returnValue .= $text->getText();
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Convert to string.
+ */
+ public function __toString(): string
+ {
+ return $this->getPlainText();
+ }
+
+ /**
+ * Get Rich Text elements.
+ *
+ * @return ITextElement[]
+ */
+ public function getRichTextElements()
+ {
+ return $this->richTextElements;
+ }
+
+ /**
+ * Set Rich Text elements.
+ *
+ * @param ITextElement[] $textElements Array of elements
+ *
+ * @return $this
+ */
+ public function setRichTextElements(array $textElements): static
+ {
+ $this->richTextElements = $textElements;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ $hashElements = '';
+ foreach ($this->richTextElements as $element) {
+ $hashElements .= $element->getHashCode();
+ }
+
+ return md5(
+ $hashElements
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ $newValue = is_object($value) ? (clone $value) : $value;
+ if (is_array($value)) {
+ $newValue = [];
+ foreach ($value as $key2 => $value2) {
+ $newValue[$key2] = is_object($value2) ? (clone $value2) : $value2;
+ }
+ }
+ $this->$key = $newValue;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
new file mode 100644
index 0000000..05d9057
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
@@ -0,0 +1,75 @@
+font = new Font();
+ }
+
+ /**
+ * Get font.
+ *
+ * @return null|Font
+ */
+ public function getFont()
+ {
+ return $this->font;
+ }
+
+ public function getFontOrThrow(): Font
+ {
+ if ($this->font === null) {
+ throw new SpreadsheetException('unexpected null font');
+ }
+
+ return $this->font;
+ }
+
+ /**
+ * Set font.
+ *
+ * @param Font $font Font
+ *
+ * @return $this
+ */
+ public function setFont(?Font $font = null): static
+ {
+ $this->font = $font;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ return md5(
+ $this->getText()
+ . (($this->font === null) ? '' : $this->font->getHashCode())
+ . __CLASS__
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
new file mode 100644
index 0000000..b8049dd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
@@ -0,0 +1,73 @@
+text = $text;
+ }
+
+ /**
+ * Get text.
+ *
+ * @return string Text
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * Set text.
+ *
+ * @param string $text Text
+ *
+ * @return $this
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+
+ return $this;
+ }
+
+ /**
+ * Get font. For this class, the return value is always null.
+ *
+ * @return null|Font
+ */
+ public function getFont()
+ {
+ return null;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ $this->text
+ . __CLASS__
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
new file mode 100644
index 0000000..f7daa45
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
@@ -0,0 +1,226 @@
+setLocale($locale);
+ }
+
+ public static function getLocale(): string
+ {
+ return Calculation::getInstance()->getLocale();
+ }
+
+ /**
+ * Identify to PhpSpreadsheet the external library to use for rendering charts.
+ *
+ * @param string $rendererClassName Class name of the chart renderer
+ * eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
+ */
+ public static function setChartRenderer(string $rendererClassName): void
+ {
+ if (!is_a($rendererClassName, IRenderer::class, true)) {
+ throw new Exception('Chart renderer must implement ' . IRenderer::class);
+ }
+
+ self::$chartRenderer = $rendererClassName;
+ }
+
+ public static function unsetChartRenderer(): void
+ {
+ self::$chartRenderer = null;
+ }
+
+ /**
+ * Return the Chart Rendering Library that PhpSpreadsheet is currently configured to use.
+ *
+ * @return null|string Class name of the chart renderer
+ * eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
+ */
+ public static function getChartRenderer(): ?string
+ {
+ return self::$chartRenderer;
+ }
+
+ public static function htmlEntityFlags(): int
+ {
+ return ENT_COMPAT;
+ }
+
+ /**
+ * Set default options for libxml loader.
+ *
+ * @param ?int $options Default options for libxml loader
+ */
+ public static function setLibXmlLoaderOptions($options): int
+ {
+ if ($options === null) {
+ $options = defined('LIBXML_DTDLOAD') ? (LIBXML_DTDLOAD | LIBXML_DTDATTR) : 0;
+ }
+ self::$libXmlLoaderOptions = $options;
+
+ return $options;
+ }
+
+ /**
+ * Get default options for libxml loader.
+ * Defaults to LIBXML_DTDLOAD | LIBXML_DTDATTR when not set explicitly.
+ *
+ * @return int Default options for libxml loader
+ */
+ public static function getLibXmlLoaderOptions(): int
+ {
+ if (self::$libXmlLoaderOptions === null) {
+ return self::setLibXmlLoaderOptions(null);
+ }
+
+ return self::$libXmlLoaderOptions;
+ }
+
+ /**
+ * Deprecated, has no effect.
+ *
+ * @param bool $state
+ *
+ * @deprecated will be removed without replacement as it is no longer necessary on PHP 7.3.0+
+ *
+ * @codeCoverageIgnore
+ */
+ public static function setLibXmlDisableEntityLoader($state): void
+ {
+ // noop
+ }
+
+ /**
+ * Deprecated, has no effect.
+ *
+ * @return bool $state
+ *
+ * @deprecated will be removed without replacement as it is no longer necessary on PHP 7.3.0+
+ *
+ * @codeCoverageIgnore
+ */
+ public static function getLibXmlDisableEntityLoader(): bool
+ {
+ return true;
+ }
+
+ /**
+ * Sets the implementation of cache that should be used for cell collection.
+ */
+ public static function setCache(?CacheInterface $cache): void
+ {
+ self::$cache = $cache;
+ }
+
+ /**
+ * Gets the implementation of cache that is being used for cell collection.
+ */
+ public static function getCache(): CacheInterface
+ {
+ if (!self::$cache) {
+ self::$cache = self::useSimpleCacheVersion3() ? new Memory\SimpleCache3() : new Memory\SimpleCache1();
+ }
+
+ return self::$cache;
+ }
+
+ public static function useSimpleCacheVersion3(): bool
+ {
+ return (new ReflectionClass(CacheInterface::class))->getMethod('get')->getReturnType() !== null;
+ }
+
+ /**
+ * Set the HTTP client implementation to be used for network request.
+ */
+ public static function setHttpClient(ClientInterface $httpClient, RequestFactoryInterface $requestFactory): void
+ {
+ self::$httpClient = $httpClient;
+ self::$requestFactory = $requestFactory;
+ }
+
+ /**
+ * Unset the HTTP client configuration.
+ */
+ public static function unsetHttpClient(): void
+ {
+ self::$httpClient = null;
+ self::$requestFactory = null;
+ }
+
+ /**
+ * Get the HTTP client implementation to be used for network request.
+ */
+ public static function getHttpClient(): ClientInterface
+ {
+ if (!self::$httpClient || !self::$requestFactory) {
+ throw new Exception('HTTP client must be configured via Settings::setHttpClient() to be able to use WEBSERVICE function.');
+ }
+
+ return self::$httpClient;
+ }
+
+ /**
+ * Get the HTTP request factory.
+ */
+ public static function getRequestFactory(): RequestFactoryInterface
+ {
+ if (!self::$httpClient || !self::$requestFactory) {
+ throw new Exception('HTTP client must be configured via Settings::setHttpClient() to be able to use WEBSERVICE function.');
+ }
+
+ return self::$requestFactory;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
new file mode 100644
index 0000000..8718a61
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
@@ -0,0 +1,114 @@
+ 'CP1252', // CodePage is not always correctly set when the xls file was saved by Apple's Numbers program
+ 367 => 'ASCII', // ASCII
+ 437 => 'CP437', // OEM US
+ //720 => 'notsupported', // OEM Arabic
+ 737 => 'CP737', // OEM Greek
+ 775 => 'CP775', // OEM Baltic
+ 850 => 'CP850', // OEM Latin I
+ 852 => 'CP852', // OEM Latin II (Central European)
+ 855 => 'CP855', // OEM Cyrillic
+ 857 => 'CP857', // OEM Turkish
+ 858 => 'CP858', // OEM Multilingual Latin I with Euro
+ 860 => 'CP860', // OEM Portugese
+ 861 => 'CP861', // OEM Icelandic
+ 862 => 'CP862', // OEM Hebrew
+ 863 => 'CP863', // OEM Canadian (French)
+ 864 => 'CP864', // OEM Arabic
+ 865 => 'CP865', // OEM Nordic
+ 866 => 'CP866', // OEM Cyrillic (Russian)
+ 869 => 'CP869', // OEM Greek (Modern)
+ 874 => 'CP874', // ANSI Thai
+ 932 => 'CP932', // ANSI Japanese Shift-JIS
+ 936 => 'CP936', // ANSI Chinese Simplified GBK
+ 949 => 'CP949', // ANSI Korean (Wansung)
+ 950 => 'CP950', // ANSI Chinese Traditional BIG5
+ 1200 => 'UTF-16LE', // UTF-16 (BIFF8)
+ 1250 => 'CP1250', // ANSI Latin II (Central European)
+ 1251 => 'CP1251', // ANSI Cyrillic
+ 1252 => 'CP1252', // ANSI Latin I (BIFF4-BIFF7)
+ 1253 => 'CP1253', // ANSI Greek
+ 1254 => 'CP1254', // ANSI Turkish
+ 1255 => 'CP1255', // ANSI Hebrew
+ 1256 => 'CP1256', // ANSI Arabic
+ 1257 => 'CP1257', // ANSI Baltic
+ 1258 => 'CP1258', // ANSI Vietnamese
+ 1361 => 'CP1361', // ANSI Korean (Johab)
+ 10000 => 'MAC', // Apple Roman
+ 10001 => 'CP932', // Macintosh Japanese
+ 10002 => 'CP950', // Macintosh Chinese Traditional
+ 10003 => 'CP1361', // Macintosh Korean
+ 10004 => 'MACARABIC', // Apple Arabic
+ 10005 => 'MACHEBREW', // Apple Hebrew
+ 10006 => 'MACGREEK', // Macintosh Greek
+ 10007 => 'MACCYRILLIC', // Macintosh Cyrillic
+ 10008 => 'CP936', // Macintosh - Simplified Chinese (GB 2312)
+ 10010 => 'MACROMANIA', // Macintosh Romania
+ 10017 => 'MACUKRAINE', // Macintosh Ukraine
+ 10021 => 'MACTHAI', // Macintosh Thai
+ 10029 => ['MACCENTRALEUROPE', 'MAC-CENTRALEUROPE'], // Macintosh Central Europe
+ 10079 => 'MACICELAND', // Macintosh Icelandic
+ 10081 => 'MACTURKISH', // Macintosh Turkish
+ 10082 => 'MACCROATIAN', // Macintosh Croatian
+ 21010 => 'UTF-16LE', // UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE
+ 32768 => 'MAC', // Apple Roman
+ //32769 => 'unsupported', // ANSI Latin I (BIFF2-BIFF3)
+ 65000 => 'UTF-7', // Unicode (UTF-7)
+ 65001 => 'UTF-8', // Unicode (UTF-8)
+ 99999 => ['unsupported'], // Unicode (UTF-8)
+ ];
+
+ public static function validate(string $codePage): bool
+ {
+ return in_array($codePage, self::$pageArray, true);
+ }
+
+ /**
+ * Convert Microsoft Code Page Identifier to Code Page Name which iconv
+ * and mbstring understands.
+ *
+ * @param int $codePage Microsoft Code Page Indentifier
+ *
+ * @return string Code Page Name
+ */
+ public static function numberToName(int $codePage): string
+ {
+ if (array_key_exists($codePage, self::$pageArray)) {
+ $value = self::$pageArray[$codePage];
+ if (is_array($value)) {
+ foreach ($value as $encoding) {
+ if (@iconv('UTF-8', $encoding, ' ') !== false) {
+ self::$pageArray[$codePage] = $encoding;
+
+ return $encoding;
+ }
+ }
+
+ throw new PhpSpreadsheetException("Code page $codePage not implemented on this system.");
+ } else {
+ return $value;
+ }
+ }
+ if ($codePage == 720 || $codePage == 32769) {
+ throw new PhpSpreadsheetException("Code page $codePage not supported."); // OEM Arabic
+ }
+
+ throw new PhpSpreadsheetException('Unknown codepage: ' . $codePage);
+ }
+
+ public static function getEncodings(): array
+ {
+ return self::$pageArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
new file mode 100644
index 0000000..db92998
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
@@ -0,0 +1,562 @@
+ 'January',
+ 'Feb' => 'February',
+ 'Mar' => 'March',
+ 'Apr' => 'April',
+ 'May' => 'May',
+ 'Jun' => 'June',
+ 'Jul' => 'July',
+ 'Aug' => 'August',
+ 'Sep' => 'September',
+ 'Oct' => 'October',
+ 'Nov' => 'November',
+ 'Dec' => 'December',
+ ];
+
+ /**
+ * @var string[]
+ */
+ public static $numberSuffixes = [
+ 'st',
+ 'nd',
+ 'rd',
+ 'th',
+ ];
+
+ /**
+ * Base calendar year to use for calculations
+ * Value is either CALENDAR_WINDOWS_1900 (1900) or CALENDAR_MAC_1904 (1904).
+ *
+ * @var int
+ */
+ protected static $excelCalendar = self::CALENDAR_WINDOWS_1900;
+
+ /**
+ * Default timezone to use for DateTime objects.
+ *
+ * @var null|DateTimeZone
+ */
+ protected static $defaultTimeZone;
+
+ /**
+ * Set the Excel calendar (Windows 1900 or Mac 1904).
+ *
+ * @param int $baseYear Excel base date (1900 or 1904)
+ *
+ * @return bool Success or failure
+ */
+ public static function setExcelCalendar($baseYear): bool
+ {
+ if (
+ ($baseYear == self::CALENDAR_WINDOWS_1900)
+ || ($baseYear == self::CALENDAR_MAC_1904)
+ ) {
+ self::$excelCalendar = $baseYear;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the Excel calendar (Windows 1900 or Mac 1904).
+ *
+ * @return int Excel base date (1900 or 1904)
+ */
+ public static function getExcelCalendar()
+ {
+ return self::$excelCalendar;
+ }
+
+ /**
+ * Set the Default timezone to use for dates.
+ *
+ * @param null|DateTimeZone|string $timeZone The timezone to set for all Excel datetimestamp to PHP DateTime Object conversions
+ *
+ * @return bool Success or failure
+ */
+ public static function setDefaultTimezone($timeZone): bool
+ {
+ try {
+ $timeZone = self::validateTimeZone($timeZone);
+ self::$defaultTimeZone = $timeZone;
+ $retval = true;
+ } catch (PhpSpreadsheetException) {
+ $retval = false;
+ }
+
+ return $retval;
+ }
+
+ /**
+ * Return the Default timezone, or UTC if default not set.
+ */
+ public static function getDefaultTimezone(): DateTimeZone
+ {
+ return self::$defaultTimeZone ?? new DateTimeZone('UTC');
+ }
+
+ /**
+ * Return the Default timezone, or local timezone if default is not set.
+ */
+ public static function getDefaultOrLocalTimezone(): DateTimeZone
+ {
+ return self::$defaultTimeZone ?? new DateTimeZone(date_default_timezone_get());
+ }
+
+ /**
+ * Return the Default timezone even if null.
+ */
+ public static function getDefaultTimezoneOrNull(): ?DateTimeZone
+ {
+ return self::$defaultTimeZone;
+ }
+
+ /**
+ * Validate a timezone.
+ *
+ * @param null|DateTimeZone|string $timeZone The timezone to validate, either as a timezone string or object
+ *
+ * @return ?DateTimeZone The timezone as a timezone object
+ */
+ private static function validateTimeZone($timeZone): ?DateTimeZone
+ {
+ if ($timeZone instanceof DateTimeZone || $timeZone === null) {
+ return $timeZone;
+ }
+ if (in_array($timeZone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC))) {
+ return new DateTimeZone($timeZone);
+ }
+
+ throw new PhpSpreadsheetException('Invalid timezone');
+ }
+
+ /**
+ * @param mixed $value Converts a date/time in ISO-8601 standard format date string to an Excel
+ * serialized timestamp.
+ * See https://en.wikipedia.org/wiki/ISO_8601 for details of the ISO-8601 standard format.
+ *
+ * @return float|int
+ */
+ public static function convertIsoDate(mixed $value)
+ {
+ if (!is_string($value)) {
+ throw new Exception('Non-string value supplied for Iso Date conversion');
+ }
+
+ $date = new DateTime($value);
+ $dateErrors = DateTime::getLastErrors();
+
+ if (is_array($dateErrors) && ($dateErrors['warning_count'] > 0 || $dateErrors['error_count'] > 0)) {
+ throw new Exception("Invalid string $value supplied for datatype Date");
+ }
+
+ $newValue = SharedDate::PHPToExcel($date);
+ if ($newValue === false) {
+ throw new Exception("Invalid string $value supplied for datatype Date");
+ }
+
+ if (preg_match('/^\\s*\\d?\\d:\\d\\d(:\\d\\d([.]\\d+)?)?\\s*(am|pm)?\\s*$/i', $value) == 1) {
+ $newValue = fmod($newValue, 1.0);
+ }
+
+ return $newValue;
+ }
+
+ /**
+ * Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
+ *
+ * @param float|int $excelTimestamp MS Excel serialized date/time value
+ * @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
+ * if you don't want to treat it as a UTC value
+ * Use the default (UTC) unless you absolutely need a conversion
+ *
+ * @return DateTime PHP date/time object
+ */
+ public static function excelToDateTimeObject($excelTimestamp, $timeZone = null)
+ {
+ $timeZone = ($timeZone === null) ? self::getDefaultTimezone() : self::validateTimeZone($timeZone);
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
+ if ($excelTimestamp < 1 && self::$excelCalendar === self::CALENDAR_WINDOWS_1900) {
+ // Unix timestamp base date
+ $baseDate = new DateTime('1970-01-01', $timeZone);
+ } else {
+ // MS Excel calendar base dates
+ if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
+ // Allow adjustment for 1900 Leap Year in MS Excel
+ $baseDate = ($excelTimestamp < 60) ? new DateTime('1899-12-31', $timeZone) : new DateTime('1899-12-30', $timeZone);
+ } else {
+ $baseDate = new DateTime('1904-01-01', $timeZone);
+ }
+ }
+ } else {
+ $baseDate = new DateTime('1899-12-30', $timeZone);
+ }
+
+ $days = floor($excelTimestamp);
+ $partDay = $excelTimestamp - $days;
+ $hms = 86400 * $partDay;
+ $microseconds = (int) round(fmod($hms, 1) * 1000000);
+ $hms = (int) floor($hms);
+ $hours = intdiv($hms, 3600);
+ $hms -= $hours * 3600;
+ $minutes = intdiv($hms, 60);
+ $seconds = $hms % 60;
+
+ if ($days >= 0) {
+ $days = '+' . $days;
+ }
+ $interval = $days . ' days';
+
+ return $baseDate->modify($interval)
+ ->setTime((int) $hours, (int) $minutes, (int) $seconds, (int) $microseconds);
+ }
+
+ /**
+ * Convert a MS serialized datetime value from Excel to a unix timestamp.
+ * The use of Unix timestamps, and therefore this function, is discouraged.
+ * They are not Y2038-safe on a 32-bit system, and have no timezone info.
+ *
+ * @param float|int $excelTimestamp MS Excel serialized date/time value
+ * @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
+ * if you don't want to treat it as a UTC value
+ * Use the default (UTC) unless you absolutely need a conversion
+ *
+ * @return int Unix timetamp for this date/time
+ */
+ public static function excelToTimestamp($excelTimestamp, $timeZone = null): int
+ {
+ $dto = self::excelToDateTimeObject($excelTimestamp, $timeZone);
+ self::roundMicroseconds($dto);
+
+ return (int) $dto->format('U');
+ }
+
+ /**
+ * Convert a date from PHP to an MS Excel serialized date/time value.
+ *
+ * @param mixed $dateValue PHP DateTime object or a string - Unix timestamp is also permitted, but discouraged;
+ * not Y2038-safe on a 32-bit system, and no timezone info
+ *
+ * @return false|float Excel date/time value
+ * or boolean FALSE on failure
+ */
+ public static function PHPToExcel(mixed $dateValue)
+ {
+ if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) {
+ return self::dateTimeToExcel($dateValue);
+ } elseif (is_numeric($dateValue)) {
+ return self::timestampToExcel($dateValue);
+ } elseif (is_string($dateValue)) {
+ return self::stringToExcel($dateValue);
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a PHP DateTime object to an MS Excel serialized date/time value.
+ *
+ * @param DateTimeInterface $dateValue PHP DateTime object
+ *
+ * @return float MS Excel serialized date/time value
+ */
+ public static function dateTimeToExcel(DateTimeInterface $dateValue): float
+ {
+ $seconds = (float) sprintf('%d.%06d', $dateValue->format('s'), $dateValue->format('u'));
+
+ return self::formattedPHPToExcel(
+ (int) $dateValue->format('Y'),
+ (int) $dateValue->format('m'),
+ (int) $dateValue->format('d'),
+ (int) $dateValue->format('H'),
+ (int) $dateValue->format('i'),
+ $seconds
+ );
+ }
+
+ /**
+ * Convert a Unix timestamp to an MS Excel serialized date/time value.
+ * The use of Unix timestamps, and therefore this function, is discouraged.
+ * They are not Y2038-safe on a 32-bit system, and have no timezone info.
+ *
+ * @param float|int|string $unixTimestamp Unix Timestamp
+ *
+ * @return false|float MS Excel serialized date/time value
+ */
+ public static function timestampToExcel($unixTimestamp): bool|float
+ {
+ if (!is_numeric($unixTimestamp)) {
+ return false;
+ }
+
+ return self::dateTimeToExcel(new DateTime('@' . $unixTimestamp));
+ }
+
+ /**
+ * formattedPHPToExcel.
+ *
+ * @param int $year
+ * @param int $month
+ * @param int $day
+ * @param int $hours
+ * @param int $minutes
+ * @param float|int $seconds
+ *
+ * @return float Excel date/time value
+ */
+ public static function formattedPHPToExcel($year, $month, $day, $hours = 0, $minutes = 0, $seconds = 0): float
+ {
+ if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
+ //
+ // Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
+ // This affects every date following 28th February 1900
+ //
+ $excel1900isLeapYear = true;
+ if (($year == 1900) && ($month <= 2)) {
+ $excel1900isLeapYear = false;
+ }
+ $myexcelBaseDate = 2415020;
+ } else {
+ $myexcelBaseDate = 2416481;
+ $excel1900isLeapYear = false;
+ }
+
+ // Julian base date Adjustment
+ if ($month > 2) {
+ $month -= 3;
+ } else {
+ $month += 9;
+ --$year;
+ }
+
+ // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
+ $century = (int) substr((string) $year, 0, 2);
+ $decade = (int) substr((string) $year, 2, 2);
+ $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myexcelBaseDate + $excel1900isLeapYear;
+
+ $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
+
+ return (float) $excelDate + $excelTime;
+ }
+
+ /**
+ * Is a given cell a date/time?
+ */
+ public static function isDateTime(Cell $cell, mixed $value = null, bool $dateWithoutTimeOkay = true): bool
+ {
+ $result = false;
+ $worksheet = $cell->getWorksheetOrNull();
+ $spreadsheet = ($worksheet === null) ? null : $worksheet->getParent();
+ if ($worksheet !== null && $spreadsheet !== null) {
+ $index = $spreadsheet->getActiveSheetIndex();
+ $selected = $worksheet->getSelectedCells();
+
+ try {
+ $result = is_numeric($value ?? $cell->getCalculatedValue())
+ && self::isDateTimeFormat(
+ $worksheet->getStyle(
+ $cell->getCoordinate()
+ )->getNumberFormat(),
+ $dateWithoutTimeOkay
+ );
+ } catch (Exception) {
+ // Result is already false, so no need to actually do anything here
+ }
+ $worksheet->setSelectedCells($selected);
+ $spreadsheet->setActiveSheetIndex($index);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Is a given NumberFormat code a date/time format code?
+ */
+ public static function isDateTimeFormat(NumberFormat $excelFormatCode, bool $dateWithoutTimeOkay = true): bool
+ {
+ return self::isDateTimeFormatCode((string) $excelFormatCode->getFormatCode(), $dateWithoutTimeOkay);
+ }
+
+ private const POSSIBLE_DATETIME_FORMAT_CHARACTERS = 'eymdHs';
+ private const POSSIBLE_TIME_FORMAT_CHARACTERS = 'Hs'; // note - no 'm' due to ambiguity
+
+ /**
+ * Is a given number format code a date/time?
+ *
+ * @param string $excelFormatCode
+ */
+ public static function isDateTimeFormatCode($excelFormatCode, bool $dateWithoutTimeOkay = true): bool
+ {
+ if (strtolower($excelFormatCode) === strtolower(NumberFormat::FORMAT_GENERAL)) {
+ // "General" contains an epoch letter 'e', so we trap for it explicitly here (case-insensitive check)
+ return false;
+ }
+ if (preg_match('/[0#]E[+-]0/i', $excelFormatCode)) {
+ // Scientific format
+ return false;
+ }
+
+ // Switch on formatcode
+ if (in_array($excelFormatCode, NumberFormat::DATE_TIME_OR_DATETIME_ARRAY, true)) {
+ return $dateWithoutTimeOkay || in_array($excelFormatCode, NumberFormat::TIME_OR_DATETIME_ARRAY);
+ }
+
+ // Typically number, currency or accounting (or occasionally fraction) formats
+ if ((str_starts_with($excelFormatCode, '_')) || (str_starts_with($excelFormatCode, '0 '))) {
+ return false;
+ }
+ // Some "special formats" provided in German Excel versions were detected as date time value,
+ // so filter them out here - "\C\H\-00000" (Switzerland) and "\D-00000" (Germany).
+ if (str_contains($excelFormatCode, '-00000')) {
+ return false;
+ }
+ $possibleFormatCharacters = $dateWithoutTimeOkay ? self::POSSIBLE_DATETIME_FORMAT_CHARACTERS : self::POSSIBLE_TIME_FORMAT_CHARACTERS;
+ // Try checking for any of the date formatting characters that don't appear within square braces
+ if (preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $excelFormatCode)) {
+ // We might also have a format mask containing quoted strings...
+ // we don't want to test for any of our characters within the quoted blocks
+ if (str_contains($excelFormatCode, '"')) {
+ $segMatcher = false;
+ foreach (explode('"', $excelFormatCode) as $subVal) {
+ // Only test in alternate array entries (the non-quoted blocks)
+ $segMatcher = $segMatcher === false;
+ if (
+ $segMatcher
+ && (preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $subVal))
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ // No date...
+ return false;
+ }
+
+ /**
+ * Convert a date/time string to Excel time.
+ *
+ * @param string $dateValue Examples: '2009-12-31', '2009-12-31 15:59', '2009-12-31 15:59:10'
+ *
+ * @return false|float Excel date/time serial value
+ */
+ public static function stringToExcel($dateValue): bool|float
+ {
+ if (strlen($dateValue) < 2) {
+ return false;
+ }
+ if (!preg_match('/^(\d{1,4}[ \.\/\-][A-Z]{3,9}([ \.\/\-]\d{1,4})?|[A-Z]{3,9}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?|\d{1,4}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?)( \d{1,2}:\d{1,2}(:\d{1,2})?)?$/iu', $dateValue)) {
+ return false;
+ }
+
+ $dateValueNew = DateTimeExcel\DateValue::fromString($dateValue);
+
+ if (!is_float($dateValueNew)) {
+ return false;
+ }
+
+ if (str_contains($dateValue, ':')) {
+ $timeValue = DateTimeExcel\TimeValue::fromString($dateValue);
+ if (!is_float($timeValue)) {
+ return false;
+ }
+ $dateValueNew += $timeValue;
+ }
+
+ return $dateValueNew;
+ }
+
+ /**
+ * Converts a month name (either a long or a short name) to a month number.
+ *
+ * @param string $monthName Month name or abbreviation
+ *
+ * @return int|string Month number (1 - 12), or the original string argument if it isn't a valid month name
+ */
+ public static function monthStringToNumber($monthName)
+ {
+ $monthIndex = 1;
+ foreach (self::$monthNames as $shortMonthName => $longMonthName) {
+ if (($monthName === $longMonthName) || ($monthName === $shortMonthName)) {
+ return $monthIndex;
+ }
+ ++$monthIndex;
+ }
+
+ return $monthName;
+ }
+
+ /**
+ * Strips an ordinal from a numeric value.
+ *
+ * @param string $day Day number with an ordinal
+ *
+ * @return int|string The integer value with any ordinal stripped, or the original string argument if it isn't a valid numeric
+ */
+ public static function dayStringToNumber($day)
+ {
+ $strippedDayValue = (str_replace(self::$numberSuffixes, '', $day));
+ if (is_numeric($strippedDayValue)) {
+ return (int) $strippedDayValue;
+ }
+
+ return $day;
+ }
+
+ public static function dateTimeFromTimestamp(string $date, ?DateTimeZone $timeZone = null): DateTime
+ {
+ $dtobj = DateTime::createFromFormat('U', $date) ?: new DateTime();
+ $dtobj->setTimeZone($timeZone ?? self::getDefaultOrLocalTimezone());
+
+ return $dtobj;
+ }
+
+ public static function formattedDateTimeFromTimestamp(string $date, string $format, ?DateTimeZone $timeZone = null): string
+ {
+ $dtobj = self::dateTimeFromTimestamp($date, $timeZone);
+
+ return $dtobj->format($format);
+ }
+
+ public static function roundMicroseconds(DateTime $dti): void
+ {
+ $microseconds = (int) $dti->format('u');
+ if ($microseconds >= 500000) {
+ $dti->modify('+1 second');
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
new file mode 100644
index 0000000..f7ce022
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
@@ -0,0 +1,175 @@
+getName();
+ $size = $defaultFont->getSize();
+
+ if (isset(Font::$defaultColumnWidths[$name][$size])) {
+ // Exact width can be determined
+ return $pixelValue * Font::$defaultColumnWidths[$name][$size]['width']
+ / Font::$defaultColumnWidths[$name][$size]['px'];
+ }
+
+ // We don't have data for this particular font and size, use approximation by
+ // extrapolating from Calibri 11
+ return $pixelValue * 11 * Font::$defaultColumnWidths['Calibri'][11]['width']
+ / Font::$defaultColumnWidths['Calibri'][11]['px'] / $size;
+ }
+
+ /**
+ * Convert column width from (intrinsic) Excel units to pixels.
+ *
+ * @param float $cellWidth Value in cell dimension
+ * @param \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont Default font of the workbook
+ *
+ * @return int Value in pixels
+ */
+ public static function cellDimensionToPixels($cellWidth, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont): int
+ {
+ // Font name and size
+ $name = $defaultFont->getName();
+ $size = $defaultFont->getSize();
+
+ if (isset(Font::$defaultColumnWidths[$name][$size])) {
+ // Exact width can be determined
+ $colWidth = $cellWidth * Font::$defaultColumnWidths[$name][$size]['px']
+ / Font::$defaultColumnWidths[$name][$size]['width'];
+ } else {
+ // We don't have data for this particular font and size, use approximation by
+ // extrapolating from Calibri 11
+ $colWidth = $cellWidth * $size * Font::$defaultColumnWidths['Calibri'][11]['px']
+ / Font::$defaultColumnWidths['Calibri'][11]['width'] / 11;
+ }
+
+ // Round pixels to closest integer
+ $colWidth = (int) round($colWidth);
+
+ return $colWidth;
+ }
+
+ /**
+ * Convert pixels to points.
+ *
+ * @param int $pixelValue Value in pixels
+ *
+ * @return float Value in points
+ */
+ public static function pixelsToPoints($pixelValue): float
+ {
+ return $pixelValue * 0.75;
+ }
+
+ /**
+ * Convert points to pixels.
+ *
+ * @param float|int $pointValue Value in points
+ *
+ * @return int Value in pixels
+ */
+ public static function pointsToPixels($pointValue): int
+ {
+ if ($pointValue != 0) {
+ return (int) ceil($pointValue / 0.75);
+ }
+
+ return 0;
+ }
+
+ /**
+ * Convert degrees to angle.
+ *
+ * @param int $degrees Degrees
+ *
+ * @return int Angle
+ */
+ public static function degreesToAngle($degrees): int
+ {
+ return (int) round($degrees * 60000);
+ }
+
+ /**
+ * Convert angle to degrees.
+ *
+ * @param int|SimpleXMLElement $angle Angle
+ *
+ * @return int Degrees
+ */
+ public static function angleToDegrees($angle): int
+ {
+ $angle = (int) $angle;
+ if ($angle != 0) {
+ return (int) round($angle / 60000);
+ }
+
+ return 0;
+ }
+
+ /**
+ * Create a new image from file. By alexander at alexauto dot nl.
+ *
+ * @see http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
+ *
+ * @param string $bmpFilename Path to Windows DIB (BMP) image
+ *
+ * @deprecated 1.26 use Php function imagecreatefrombmp instead
+ *
+ * @codeCoverageIgnore
+ */
+ public static function imagecreatefrombmp($bmpFilename): GdImage
+ {
+ $retVal = @imagecreatefrombmp($bmpFilename);
+ if ($retVal === false) {
+ throw new ReaderException("Unable to create image from $bmpFilename");
+ }
+
+ return $retVal;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
new file mode 100644
index 0000000..466e7e8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
@@ -0,0 +1,64 @@
+dggContainer;
+ }
+
+ /**
+ * Set Drawing Group Container.
+ *
+ * @param Escher\DggContainer $dggContainer
+ *
+ * @return Escher\DggContainer
+ */
+ public function setDggContainer($dggContainer)
+ {
+ return $this->dggContainer = $dggContainer;
+ }
+
+ /**
+ * Get Drawing Container.
+ *
+ * @return ?Escher\DgContainer
+ */
+ public function getDgContainer()
+ {
+ return $this->dgContainer;
+ }
+
+ /**
+ * Set Drawing Container.
+ *
+ * @param Escher\DgContainer $dgContainer
+ *
+ * @return Escher\DgContainer
+ */
+ public function setDgContainer($dgContainer)
+ {
+ return $this->dgContainer = $dgContainer;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
new file mode 100644
index 0000000..9cf5d43
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
@@ -0,0 +1,61 @@
+dgId;
+ }
+
+ public function setDgId(int $value): void
+ {
+ $this->dgId = $value;
+ }
+
+ public function getLastSpId(): ?int
+ {
+ return $this->lastSpId;
+ }
+
+ public function setLastSpId(int $value): void
+ {
+ $this->lastSpId = $value;
+ }
+
+ public function getSpgrContainer(): ?DgContainer\SpgrContainer
+ {
+ return $this->spgrContainer;
+ }
+
+ public function getSpgrContainerOrThrow(): DgContainer\SpgrContainer
+ {
+ if ($this->spgrContainer !== null) {
+ return $this->spgrContainer;
+ }
+
+ throw new SpreadsheetException('spgrContainer is unexpectedly null');
+ }
+
+ /** @param DgContainer\SpgrContainer $spgrContainer */
+ public function setSpgrContainer($spgrContainer): DgContainer\SpgrContainer
+ {
+ return $this->spgrContainer = $spgrContainer;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
new file mode 100644
index 0000000..01cbecc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
@@ -0,0 +1,71 @@
+parent = $parent;
+ }
+
+ /**
+ * Get the parent Shape Group Container if any.
+ */
+ public function getParent(): ?self
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Add a child. This will be either spgrContainer or spContainer.
+ */
+ public function addChild(mixed $child): void
+ {
+ $this->children[] = $child;
+ $child->setParent($this);
+ }
+
+ /**
+ * Get collection of Shape Containers.
+ */
+ public function getChildren(): array
+ {
+ return $this->children;
+ }
+
+ /**
+ * Recursively get all spContainers within this spgrContainer.
+ *
+ * @return SpgrContainer\SpContainer[]
+ */
+ public function getAllSpContainers(): array
+ {
+ $allSpContainers = [];
+
+ foreach ($this->children as $child) {
+ if ($child instanceof self) {
+ $allSpContainers = array_merge($allSpContainers, $child->getAllSpContainers());
+ } else {
+ $allSpContainers[] = $child;
+ }
+ }
+
+ return $allSpContainers;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
new file mode 100644
index 0000000..7f010c5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
@@ -0,0 +1,368 @@
+parent = $parent;
+ }
+
+ /**
+ * Get the parent Shape Group Container.
+ *
+ * @return SpgrContainer
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Set whether this is a group shape.
+ *
+ * @param bool $value
+ */
+ public function setSpgr($value): void
+ {
+ $this->spgr = $value;
+ }
+
+ /**
+ * Get whether this is a group shape.
+ *
+ * @return bool
+ */
+ public function getSpgr()
+ {
+ return $this->spgr;
+ }
+
+ /**
+ * Set the shape type.
+ *
+ * @param int $value
+ */
+ public function setSpType($value): void
+ {
+ $this->spType = $value;
+ }
+
+ /**
+ * Get the shape type.
+ *
+ * @return int
+ */
+ public function getSpType()
+ {
+ return $this->spType;
+ }
+
+ /**
+ * Set the shape flag.
+ *
+ * @param int $value
+ */
+ public function setSpFlag($value): void
+ {
+ $this->spFlag = $value;
+ }
+
+ /**
+ * Get the shape flag.
+ *
+ * @return int
+ */
+ public function getSpFlag()
+ {
+ return $this->spFlag;
+ }
+
+ /**
+ * Set the shape index.
+ *
+ * @param int $value
+ */
+ public function setSpId($value): void
+ {
+ $this->spId = $value;
+ }
+
+ /**
+ * Get the shape index.
+ *
+ * @return int
+ */
+ public function getSpId()
+ {
+ return $this->spId;
+ }
+
+ /**
+ * Set an option for the Shape Group Container.
+ *
+ * @param int $property The number specifies the option
+ */
+ public function setOPT($property, mixed $value): void
+ {
+ $this->OPT[$property] = $value;
+ }
+
+ /**
+ * Get an option for the Shape Group Container.
+ *
+ * @param int $property The number specifies the option
+ *
+ * @return mixed
+ */
+ public function getOPT($property)
+ {
+ if (isset($this->OPT[$property])) {
+ return $this->OPT[$property];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the collection of options.
+ *
+ * @return array
+ */
+ public function getOPTCollection()
+ {
+ return $this->OPT;
+ }
+
+ /**
+ * Set cell coordinates of upper-left corner of shape.
+ *
+ * @param string $value eg: 'A1'
+ */
+ public function setStartCoordinates($value): void
+ {
+ $this->startCoordinates = $value;
+ }
+
+ /**
+ * Get cell coordinates of upper-left corner of shape.
+ *
+ * @return string
+ */
+ public function getStartCoordinates()
+ {
+ return $this->startCoordinates;
+ }
+
+ /**
+ * Set offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
+ *
+ * @param int $startOffsetX
+ */
+ public function setStartOffsetX($startOffsetX): void
+ {
+ $this->startOffsetX = $startOffsetX;
+ }
+
+ /**
+ * Get offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
+ *
+ * @return int
+ */
+ public function getStartOffsetX()
+ {
+ return $this->startOffsetX;
+ }
+
+ /**
+ * Set offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
+ *
+ * @param int $startOffsetY
+ */
+ public function setStartOffsetY($startOffsetY): void
+ {
+ $this->startOffsetY = $startOffsetY;
+ }
+
+ /**
+ * Get offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
+ *
+ * @return int
+ */
+ public function getStartOffsetY()
+ {
+ return $this->startOffsetY;
+ }
+
+ /**
+ * Set cell coordinates of bottom-right corner of shape.
+ *
+ * @param string $value eg: 'A1'
+ */
+ public function setEndCoordinates($value): void
+ {
+ $this->endCoordinates = $value;
+ }
+
+ /**
+ * Get cell coordinates of bottom-right corner of shape.
+ *
+ * @return string
+ */
+ public function getEndCoordinates()
+ {
+ return $this->endCoordinates;
+ }
+
+ /**
+ * Set offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
+ *
+ * @param int $endOffsetX
+ */
+ public function setEndOffsetX($endOffsetX): void
+ {
+ $this->endOffsetX = $endOffsetX;
+ }
+
+ /**
+ * Get offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
+ *
+ * @return int
+ */
+ public function getEndOffsetX()
+ {
+ return $this->endOffsetX;
+ }
+
+ /**
+ * Set offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
+ *
+ * @param int $endOffsetY
+ */
+ public function setEndOffsetY($endOffsetY): void
+ {
+ $this->endOffsetY = $endOffsetY;
+ }
+
+ /**
+ * Get offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
+ *
+ * @return int
+ */
+ public function getEndOffsetY()
+ {
+ return $this->endOffsetY;
+ }
+
+ /**
+ * Get the nesting level of this spContainer. This is the number of spgrContainers between this spContainer and
+ * the dgContainer. A value of 1 = immediately within first spgrContainer
+ * Higher nesting level occurs if and only if spContainer is part of a shape group.
+ *
+ * @return int Nesting level
+ */
+ public function getNestingLevel(): int
+ {
+ $nestingLevel = 0;
+
+ $parent = $this->getParent();
+ while ($parent instanceof SpgrContainer) {
+ ++$nestingLevel;
+ $parent = $parent->getParent();
+ }
+
+ return $nestingLevel;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
new file mode 100644
index 0000000..3a40110
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
@@ -0,0 +1,174 @@
+spIdMax;
+ }
+
+ /**
+ * Set maximum shape index of all shapes in all drawings (plus one).
+ *
+ * @param int $value
+ */
+ public function setSpIdMax($value): void
+ {
+ $this->spIdMax = $value;
+ }
+
+ /**
+ * Get total number of drawings saved.
+ *
+ * @return int
+ */
+ public function getCDgSaved()
+ {
+ return $this->cDgSaved;
+ }
+
+ /**
+ * Set total number of drawings saved.
+ *
+ * @param int $value
+ */
+ public function setCDgSaved($value): void
+ {
+ $this->cDgSaved = $value;
+ }
+
+ /**
+ * Get total number of shapes saved (including group shapes).
+ *
+ * @return int
+ */
+ public function getCSpSaved()
+ {
+ return $this->cSpSaved;
+ }
+
+ /**
+ * Set total number of shapes saved (including group shapes).
+ *
+ * @param int $value
+ */
+ public function setCSpSaved($value): void
+ {
+ $this->cSpSaved = $value;
+ }
+
+ /**
+ * Get BLIP Store Container.
+ *
+ * @return ?DggContainer\BstoreContainer
+ */
+ public function getBstoreContainer()
+ {
+ return $this->bstoreContainer;
+ }
+
+ /**
+ * Set BLIP Store Container.
+ *
+ * @param DggContainer\BstoreContainer $bstoreContainer
+ */
+ public function setBstoreContainer($bstoreContainer): void
+ {
+ $this->bstoreContainer = $bstoreContainer;
+ }
+
+ /**
+ * Set an option for the drawing group.
+ *
+ * @param int $property The number specifies the option
+ */
+ public function setOPT($property, mixed $value): void
+ {
+ $this->OPT[$property] = $value;
+ }
+
+ /**
+ * Get an option for the drawing group.
+ *
+ * @param int $property The number specifies the option
+ *
+ * @return mixed
+ */
+ public function getOPT($property)
+ {
+ if (isset($this->OPT[$property])) {
+ return $this->OPT[$property];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get identifier clusters.
+ *
+ * @return array
+ */
+ public function getIDCLs()
+ {
+ return $this->IDCLs;
+ }
+
+ /**
+ * Set identifier clusters. [ => , ...].
+ *
+ * @param array $IDCLs
+ */
+ public function setIDCLs($IDCLs): void
+ {
+ $this->IDCLs = $IDCLs;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
new file mode 100644
index 0000000..7203b66
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
@@ -0,0 +1,32 @@
+BSECollection[] = $BSE;
+ $BSE->setParent($this);
+ }
+
+ /**
+ * Get the collection of BLIP Store Entries.
+ *
+ * @return BstoreContainer\BSE[]
+ */
+ public function getBSECollection()
+ {
+ return $this->BSECollection;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
new file mode 100644
index 0000000..328ac6b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
@@ -0,0 +1,88 @@
+parent = $parent;
+ }
+
+ /**
+ * Get the BLIP.
+ *
+ * @return ?BSE\Blip
+ */
+ public function getBlip()
+ {
+ return $this->blip;
+ }
+
+ /**
+ * Set the BLIP.
+ */
+ public function setBlip(BSE\Blip $blip): void
+ {
+ $this->blip = $blip;
+ $blip->setParent($this);
+ }
+
+ /**
+ * Get the BLIP type.
+ *
+ * @return int
+ */
+ public function getBlipType()
+ {
+ return $this->blipType;
+ }
+
+ /**
+ * Set the BLIP type.
+ *
+ * @param int $blipType
+ */
+ public function setBlipType($blipType): void
+ {
+ $this->blipType = $blipType;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
new file mode 100644
index 0000000..ea036af
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
@@ -0,0 +1,56 @@
+data;
+ }
+
+ /**
+ * Set the raw image data.
+ *
+ * @param string $data
+ */
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * Set parent BSE.
+ */
+ public function setParent(BSE $parent): void
+ {
+ $this->parent = $parent;
+ }
+
+ /**
+ * Get parent BSE.
+ */
+ public function getParent(): BSE
+ {
+ return $this->parent;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
new file mode 100644
index 0000000..e5e470e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
@@ -0,0 +1,197 @@
+open($zipFile);
+ if ($res === true) {
+ $returnValue = ($zip->getFromName($archiveFile) !== false);
+ $zip->close();
+
+ return $returnValue;
+ }
+ }
+
+ return false;
+ }
+
+ return file_exists($filename);
+ }
+
+ /**
+ * Returns canonicalized absolute pathname, also for ZIP archives.
+ */
+ public static function realpath(string $filename): string
+ {
+ // Returnvalue
+ $returnValue = '';
+
+ // Try using realpath()
+ if (file_exists($filename)) {
+ $returnValue = realpath($filename) ?: '';
+ }
+
+ // Found something?
+ if ($returnValue === '') {
+ $pathArray = explode('/', $filename);
+ while (in_array('..', $pathArray) && $pathArray[0] != '..') {
+ $iMax = count($pathArray);
+ for ($i = 1; $i < $iMax; ++$i) {
+ if ($pathArray[$i] == '..') {
+ array_splice($pathArray, $i - 1, 2);
+
+ break;
+ }
+ }
+ }
+ $returnValue = implode('/', $pathArray);
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+ /**
+ * Get the systems temporary directory.
+ */
+ public static function sysGetTempDir(): string
+ {
+ $path = sys_get_temp_dir();
+ if (self::$useUploadTempDirectory) {
+ // use upload-directory when defined to allow running on environments having very restricted
+ // open_basedir configs
+ if (ini_get('upload_tmp_dir') !== false) {
+ if ($temp = ini_get('upload_tmp_dir')) {
+ if (file_exists($temp)) {
+ $path = $temp;
+ }
+ }
+ }
+ }
+
+ return realpath($path) ?: '';
+ }
+
+ public static function temporaryFilename(): string
+ {
+ $filename = tempnam(self::sysGetTempDir(), 'phpspreadsheet');
+ if ($filename === false) {
+ throw new Exception('Could not create temporary file');
+ }
+
+ return $filename;
+ }
+
+ /**
+ * Assert that given path is an existing file and is readable, otherwise throw exception.
+ */
+ public static function assertFile(string $filename, string $zipMember = ''): void
+ {
+ if (!is_file($filename)) {
+ throw new ReaderException('File "' . $filename . '" does not exist.');
+ }
+
+ if (!is_readable($filename)) {
+ throw new ReaderException('Could not open "' . $filename . '" for reading.');
+ }
+
+ if ($zipMember !== '') {
+ $zipfile = "zip://$filename#$zipMember";
+ if (!self::fileExists($zipfile)) {
+ // Has the file been saved with Windoze directory separators rather than unix?
+ $zipfile = "zip://$filename#" . str_replace('/', '\\', $zipMember);
+ if (!self::fileExists($zipfile)) {
+ throw new ReaderException("Could not find zip member $zipfile");
+ }
+ }
+ }
+ }
+
+ /**
+ * Same as assertFile, except return true/false and don't throw Exception.
+ */
+ public static function testFileNoThrow(string $filename, ?string $zipMember = null): bool
+ {
+ if (!is_file($filename)) {
+ return false;
+ }
+ if (!is_readable($filename)) {
+ return false;
+ }
+ if ($zipMember === null) {
+ return true;
+ }
+ // validate zip, but don't check specific member
+ if ($zipMember === '') {
+ return self::validateZipFirst4($filename);
+ }
+
+ $zipfile = "zip://$filename#$zipMember";
+ if (self::fileExists($zipfile)) {
+ return true;
+ }
+
+ // Has the file been saved with Windoze directory separators rather than unix?
+ $zipfile = "zip://$filename#" . str_replace('/', '\\', $zipMember);
+
+ return self::fileExists($zipfile);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
new file mode 100644
index 0000000..7beeb63
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
@@ -0,0 +1,745 @@
+ [
+ 'x' => self::ARIAL,
+ 'xb' => self::ARIAL_BOLD,
+ 'xi' => self::ARIAL_ITALIC,
+ 'xbi' => self::ARIAL_BOLD_ITALIC,
+ ],
+ 'Calibri' => [
+ 'x' => self::CALIBRI,
+ 'xb' => self::CALIBRI_BOLD,
+ 'xi' => self::CALIBRI_ITALIC,
+ 'xbi' => self::CALIBRI_BOLD_ITALIC,
+ ],
+ 'Comic Sans MS' => [
+ 'x' => self::COMIC_SANS_MS,
+ 'xb' => self::COMIC_SANS_MS_BOLD,
+ 'xi' => self::COMIC_SANS_MS,
+ 'xbi' => self::COMIC_SANS_MS_BOLD,
+ ],
+ 'Courier New' => [
+ 'x' => self::COURIER_NEW,
+ 'xb' => self::COURIER_NEW_BOLD,
+ 'xi' => self::COURIER_NEW_ITALIC,
+ 'xbi' => self::COURIER_NEW_BOLD_ITALIC,
+ ],
+ 'Georgia' => [
+ 'x' => self::GEORGIA,
+ 'xb' => self::GEORGIA_BOLD,
+ 'xi' => self::GEORGIA_ITALIC,
+ 'xbi' => self::GEORGIA_BOLD_ITALIC,
+ ],
+ 'Impact' => [
+ 'x' => self::IMPACT,
+ 'xb' => self::IMPACT,
+ 'xi' => self::IMPACT,
+ 'xbi' => self::IMPACT,
+ ],
+ 'Liberation Sans' => [
+ 'x' => self::LIBERATION_SANS,
+ 'xb' => self::LIBERATION_SANS_BOLD,
+ 'xi' => self::LIBERATION_SANS_ITALIC,
+ 'xbi' => self::LIBERATION_SANS_BOLD_ITALIC,
+ ],
+ 'Lucida Console' => [
+ 'x' => self::LUCIDA_CONSOLE,
+ 'xb' => self::LUCIDA_CONSOLE,
+ 'xi' => self::LUCIDA_CONSOLE,
+ 'xbi' => self::LUCIDA_CONSOLE,
+ ],
+ 'Lucida Sans Unicode' => [
+ 'x' => self::LUCIDA_SANS_UNICODE,
+ 'xb' => self::LUCIDA_SANS_UNICODE,
+ 'xi' => self::LUCIDA_SANS_UNICODE,
+ 'xbi' => self::LUCIDA_SANS_UNICODE,
+ ],
+ 'Microsoft Sans Serif' => [
+ 'x' => self::MICROSOFT_SANS_SERIF,
+ 'xb' => self::MICROSOFT_SANS_SERIF,
+ 'xi' => self::MICROSOFT_SANS_SERIF,
+ 'xbi' => self::MICROSOFT_SANS_SERIF,
+ ],
+ 'Palatino Linotype' => [
+ 'x' => self::PALATINO_LINOTYPE,
+ 'xb' => self::PALATINO_LINOTYPE_BOLD,
+ 'xi' => self::PALATINO_LINOTYPE_ITALIC,
+ 'xbi' => self::PALATINO_LINOTYPE_BOLD_ITALIC,
+ ],
+ 'Symbol' => [
+ 'x' => self::SYMBOL,
+ 'xb' => self::SYMBOL,
+ 'xi' => self::SYMBOL,
+ 'xbi' => self::SYMBOL,
+ ],
+ 'Tahoma' => [
+ 'x' => self::TAHOMA,
+ 'xb' => self::TAHOMA_BOLD,
+ 'xi' => self::TAHOMA,
+ 'xbi' => self::TAHOMA_BOLD,
+ ],
+ 'Times New Roman' => [
+ 'x' => self::TIMES_NEW_ROMAN,
+ 'xb' => self::TIMES_NEW_ROMAN_BOLD,
+ 'xi' => self::TIMES_NEW_ROMAN_ITALIC,
+ 'xbi' => self::TIMES_NEW_ROMAN_BOLD_ITALIC,
+ ],
+ 'Trebuchet MS' => [
+ 'x' => self::TREBUCHET_MS,
+ 'xb' => self::TREBUCHET_MS_BOLD,
+ 'xi' => self::TREBUCHET_MS_ITALIC,
+ 'xbi' => self::TREBUCHET_MS_BOLD_ITALIC,
+ ],
+ 'Verdana' => [
+ 'x' => self::VERDANA,
+ 'xb' => self::VERDANA_BOLD,
+ 'xi' => self::VERDANA_ITALIC,
+ 'xbi' => self::VERDANA_BOLD_ITALIC,
+ ],
+ ];
+
+ /**
+ * Array that can be used to supplement FONT_FILE_NAMES for calculating exact width.
+ *
+ * @var array>
+ */
+ private static $extraFontArray = [];
+
+ /** @param array> $extraFontArray */
+ public static function setExtraFontArray(array $extraFontArray): void
+ {
+ self::$extraFontArray = $extraFontArray;
+ }
+
+ /** @return array> */
+ public static function getExtraFontArray(): array
+ {
+ return self::$extraFontArray;
+ }
+
+ /**
+ * AutoSize method.
+ *
+ * @var string
+ */
+ private static $autoSizeMethod = self::AUTOSIZE_METHOD_APPROX;
+
+ /**
+ * Path to folder containing TrueType font .ttf files.
+ *
+ * @var string
+ */
+ private static $trueTypeFontPath = '';
+
+ /**
+ * How wide is a default column for a given default font and size?
+ * Empirical data found by inspecting real Excel files and reading off the pixel width
+ * in Microsoft Office Excel 2007.
+ * Added height in points.
+ */
+ public const DEFAULT_COLUMN_WIDTHS = [
+ 'Arial' => [
+ 1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
+ 2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
+ 3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.0],
+
+ 4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75],
+ 5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25],
+ 6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25],
+ 7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0],
+ 8 => ['px' => 56, 'width' => 9.33203125, 'height' => 11.25],
+ 9 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.0],
+ 10 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.75],
+ ],
+ 'Calibri' => [
+ 1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
+ 2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
+ 3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.00],
+ 4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75],
+ 5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25],
+ 6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25],
+ 7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0],
+ 8 => ['px' => 56, 'width' => 9.33203125, 'height' => 11.25],
+ 9 => ['px' => 56, 'width' => 9.33203125, 'height' => 12.0],
+ 10 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.75],
+ 11 => ['px' => 64, 'width' => 9.14062500, 'height' => 15.0],
+ ],
+ 'Verdana' => [
+ 1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
+ 2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
+ 3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.0],
+ 4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75],
+ 5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25],
+ 6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25],
+ 7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0],
+ 8 => ['px' => 64, 'width' => 9.14062500, 'height' => 10.5],
+ 9 => ['px' => 72, 'width' => 9.00000000, 'height' => 11.25],
+ 10 => ['px' => 72, 'width' => 9.00000000, 'height' => 12.75],
+ ],
+ ];
+
+ /**
+ * List of column widths. Replaced by constant;
+ * previously it was public and updateable, allowing
+ * user to make inappropriate alterations.
+ *
+ * @deprecated 1.25.0 Use DEFAULT_COLUMN_WIDTHS constant instead.
+ *
+ * @var array
+ */
+ public static $defaultColumnWidths = self::DEFAULT_COLUMN_WIDTHS;
+
+ /**
+ * Set autoSize method.
+ *
+ * @param string $method see self::AUTOSIZE_METHOD_*
+ *
+ * @return bool Success or failure
+ */
+ public static function setAutoSizeMethod($method): bool
+ {
+ if (!in_array($method, self::AUTOSIZE_METHODS)) {
+ return false;
+ }
+ self::$autoSizeMethod = $method;
+
+ return true;
+ }
+
+ /**
+ * Get autoSize method.
+ *
+ * @return string
+ */
+ public static function getAutoSizeMethod()
+ {
+ return self::$autoSizeMethod;
+ }
+
+ /**
+ * Set the path to the folder containing .ttf files. There should be a trailing slash.
+ * Path will be recursively searched for font file.
+ * Typical locations on various platforms:
+ *
+ * C:/Windows/Fonts/
+ * /usr/share/fonts/truetype/
+ * ~/.fonts/
+ * .
+ *
+ * @param string $folderPath
+ */
+ public static function setTrueTypeFontPath($folderPath): void
+ {
+ self::$trueTypeFontPath = $folderPath;
+ }
+
+ /**
+ * Get the path to the folder containing .ttf files.
+ *
+ * @return string
+ */
+ public static function getTrueTypeFontPath()
+ {
+ return self::$trueTypeFontPath;
+ }
+
+ /**
+ * Pad amount for exact in pixels; use best guess if null.
+ *
+ * @var null|float|int
+ */
+ private static $paddingAmountExact;
+
+ /**
+ * Set pad amount for exact in pixels; use best guess if null.
+ *
+ * @param null|float|int $paddingAmountExact
+ */
+ public static function setPaddingAmountExact($paddingAmountExact): void
+ {
+ self::$paddingAmountExact = $paddingAmountExact;
+ }
+
+ /**
+ * Get pad amount for exact in pixels; or null if using best guess.
+ *
+ * @return null|float|int
+ */
+ public static function getPaddingAmountExact()
+ {
+ return self::$paddingAmountExact;
+ }
+
+ /**
+ * Calculate an (approximate) OpenXML column width, based on font size and text contained.
+ *
+ * @param FontStyle $font Font object
+ * @param null|RichText|string $cellText Text to calculate width
+ * @param int $rotation Rotation angle
+ * @param null|FontStyle $defaultFont Font object
+ * @param bool $filterAdjustment Add space for Autofilter or Table dropdown
+ */
+ public static function calculateColumnWidth(
+ FontStyle $font,
+ $cellText = '',
+ $rotation = 0,
+ ?FontStyle $defaultFont = null,
+ bool $filterAdjustment = false,
+ int $indentAdjustment = 0
+ ): float {
+ // If it is rich text, use plain text
+ if ($cellText instanceof RichText) {
+ $cellText = $cellText->getPlainText();
+ }
+
+ // Special case if there are one or more newline characters ("\n")
+ $cellText = (string) $cellText;
+ if (str_contains($cellText, "\n")) {
+ $lineTexts = explode("\n", $cellText);
+ $lineWidths = [];
+ foreach ($lineTexts as $lineText) {
+ $lineWidths[] = self::calculateColumnWidth($font, $lineText, $rotation = 0, $defaultFont, $filterAdjustment);
+ }
+
+ return max($lineWidths); // width of longest line in cell
+ }
+
+ // Try to get the exact text width in pixels
+ $approximate = self::$autoSizeMethod === self::AUTOSIZE_METHOD_APPROX;
+ $columnWidth = 0;
+ if (!$approximate) {
+ try {
+ $columnWidthAdjust = ceil(
+ self::getTextWidthPixelsExact(
+ str_repeat('n', 1 * (($filterAdjustment ? 3 : 1) + ($indentAdjustment * 2))),
+ $font,
+ 0
+ ) * 1.07
+ );
+
+ // Width of text in pixels excl. padding
+ // and addition because Excel adds some padding, just use approx width of 'n' glyph
+ $columnWidth = self::getTextWidthPixelsExact($cellText, $font, $rotation) + (self::$paddingAmountExact ?? $columnWidthAdjust);
+ } catch (PhpSpreadsheetException) {
+ $approximate = true;
+ }
+ }
+
+ if ($approximate) {
+ $columnWidthAdjust = self::getTextWidthPixelsApprox(
+ str_repeat('n', 1 * (($filterAdjustment ? 3 : 1) + ($indentAdjustment * 2))),
+ $font,
+ 0
+ );
+ // Width of text in pixels excl. padding, approximation
+ // and addition because Excel adds some padding, just use approx width of 'n' glyph
+ $columnWidth = self::getTextWidthPixelsApprox($cellText, $font, $rotation) + $columnWidthAdjust;
+ }
+
+ // Convert from pixel width to column width
+ $columnWidth = Drawing::pixelsToCellDimension((int) $columnWidth, $defaultFont ?? new FontStyle());
+
+ // Return
+ return round($columnWidth, 4);
+ }
+
+ /**
+ * Get GD text width in pixels for a string of text in a certain font at a certain rotation angle.
+ */
+ public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): float
+ {
+ // font size should really be supplied in pixels in GD2,
+ // but since GD2 seems to assume 72dpi, pixels and points are the same
+ $fontFile = self::getTrueTypeFontFileFromFont($font);
+ $textBox = imagettfbbox($font->getSize() ?? 10.0, $rotation, $fontFile, $text);
+ if ($textBox === false) {
+ // @codeCoverageIgnoreStart
+ throw new PhpSpreadsheetException('imagettfbbox failed');
+ // @codeCoverageIgnoreEnd
+ }
+
+ // Get corners positions
+ $lowerLeftCornerX = $textBox[0];
+ $lowerRightCornerX = $textBox[2];
+ $upperRightCornerX = $textBox[4];
+ $upperLeftCornerX = $textBox[6];
+
+ // Consider the rotation when calculating the width
+ return round(max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX), 4);
+ }
+
+ /**
+ * Get approximate width in pixels for a string of text in a certain font at a certain rotation angle.
+ *
+ * @param int $rotation
+ *
+ * @return int Text width in pixels (no padding added)
+ */
+ public static function getTextWidthPixelsApprox(string $columnText, FontStyle $font, $rotation = 0): int
+ {
+ $fontName = $font->getName();
+ $fontSize = $font->getSize();
+
+ // Calculate column width in pixels.
+ // We assume fixed glyph width, but count double for "fullwidth" characters.
+ // Result varies with font name and size.
+ switch ($fontName) {
+ case 'Arial':
+ // value 8 was set because of experience in different exports at Arial 10 font.
+ $columnWidth = (int) (8 * StringHelper::countCharactersDbcs($columnText));
+ $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
+
+ break;
+ case 'Verdana':
+ // value 8 was found via interpolation by inspecting real Excel files with Verdana 10 font.
+ $columnWidth = (int) (8 * StringHelper::countCharactersDbcs($columnText));
+ $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
+
+ break;
+ default:
+ // just assume Calibri
+ // value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font.
+ $columnWidth = (int) (8.26 * StringHelper::countCharactersDbcs($columnText));
+ $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
+
+ break;
+ }
+
+ // Calculate approximate rotated column width
+ if ($rotation !== 0) {
+ if ($rotation == Alignment::TEXTROTATION_STACK_PHPSPREADSHEET) {
+ // stacked text
+ $columnWidth = 4; // approximation
+ } else {
+ // rotated text
+ $columnWidth = $columnWidth * cos(deg2rad($rotation))
+ + $fontSize * abs(sin(deg2rad($rotation))) / 5; // approximation
+ }
+ }
+
+ // pixel width is an integer
+ return (int) $columnWidth;
+ }
+
+ /**
+ * Calculate an (approximate) pixel size, based on a font points size.
+ *
+ * @param int $fontSizeInPoints Font size (in points)
+ *
+ * @return int Font size (in pixels)
+ */
+ public static function fontSizeToPixels($fontSizeInPoints): int
+ {
+ return (int) ((4 / 3) * $fontSizeInPoints);
+ }
+
+ /**
+ * Calculate an (approximate) pixel size, based on inch size.
+ *
+ * @param int $sizeInInch Font size (in inch)
+ *
+ * @return int Size (in pixels)
+ */
+ public static function inchSizeToPixels($sizeInInch): int|float
+ {
+ return $sizeInInch * 96;
+ }
+
+ /**
+ * Calculate an (approximate) pixel size, based on centimeter size.
+ *
+ * @param int $sizeInCm Font size (in centimeters)
+ *
+ * @return float Size (in pixels)
+ */
+ public static function centimeterSizeToPixels($sizeInCm): float
+ {
+ return $sizeInCm * 37.795275591;
+ }
+
+ /**
+ * Returns the font path given the font.
+ *
+ * @return string Path to TrueType font file
+ */
+ public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkPath = true)
+ {
+ if ($checkPath && (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath))) {
+ throw new PhpSpreadsheetException('Valid directory to TrueType Font files not specified');
+ }
+
+ $name = $font->getName();
+ $fontArray = array_merge(self::FONT_FILE_NAMES, self::$extraFontArray);
+ if (!isset($fontArray[$name])) {
+ throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file');
+ }
+ $bold = $font->getBold();
+ $italic = $font->getItalic();
+ $index = 'x';
+ if ($bold) {
+ $index .= 'b';
+ }
+ if ($italic) {
+ $index .= 'i';
+ }
+ $fontFile = $fontArray[$name][$index];
+
+ $separator = '';
+ if (mb_strlen(self::$trueTypeFontPath) > 1 && mb_substr(self::$trueTypeFontPath, -1) !== '/' && mb_substr(self::$trueTypeFontPath, -1) !== '\\') {
+ $separator = DIRECTORY_SEPARATOR;
+ }
+ $fontFileAbsolute = preg_match('~^([A-Za-z]:)?[/\\\\]~', $fontFile) === 1;
+ if (!$fontFileAbsolute) {
+ $fontFile = self::findFontFile(self::$trueTypeFontPath, $fontFile) ?? self::$trueTypeFontPath . $separator . $fontFile;
+ }
+
+ // Check if file actually exists
+ if ($checkPath && !file_exists($fontFile) && !$fontFileAbsolute) {
+ $alternateName = $name;
+ if ($index !== 'x' && $fontArray[$name][$index] !== $fontArray[$name]['x']) {
+ // Bold but no italic:
+ // Comic Sans
+ // Tahoma
+ // Neither bold nor italic:
+ // Impact
+ // Lucida Console
+ // Lucida Sans Unicode
+ // Microsoft Sans Serif
+ // Symbol
+ if ($index === 'xb') {
+ $alternateName .= ' Bold';
+ } elseif ($index === 'xi') {
+ $alternateName .= ' Italic';
+ } elseif ($fontArray[$name]['xb'] === $fontArray[$name]['xbi']) {
+ $alternateName .= ' Bold';
+ } else {
+ $alternateName .= ' Bold Italic';
+ }
+ }
+ $fontFile = self::$trueTypeFontPath . $separator . $alternateName . '.ttf';
+ if (!file_exists($fontFile)) {
+ throw new PhpSpreadsheetException('TrueType Font file not found');
+ }
+ }
+
+ return $fontFile;
+ }
+
+ public const CHARSET_FROM_FONT_NAME = [
+ 'EucrosiaUPC' => self::CHARSET_ANSI_THAI,
+ 'Wingdings' => self::CHARSET_SYMBOL,
+ 'Wingdings 2' => self::CHARSET_SYMBOL,
+ 'Wingdings 3' => self::CHARSET_SYMBOL,
+ ];
+
+ /**
+ * Returns the associated charset for the font name.
+ *
+ * @param string $fontName Font name
+ *
+ * @return int Character set code
+ */
+ public static function getCharsetFromFontName($fontName): int
+ {
+ return self::CHARSET_FROM_FONT_NAME[$fontName] ?? self::CHARSET_ANSI_LATIN;
+ }
+
+ /**
+ * Get the effective column width for columns without a column dimension or column with width -1
+ * For example, for Calibri 11 this is 9.140625 (64 px).
+ *
+ * @param FontStyle $font The workbooks default font
+ * @param bool $returnAsPixels true = return column width in pixels, false = return in OOXML units
+ *
+ * @return ($returnAsPixels is true ? int : float) Column width
+ */
+ public static function getDefaultColumnWidthByFont(FontStyle $font, bool $returnAsPixels = false): float|int
+ {
+ if (isset(self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()])) {
+ // Exact width can be determined
+ $columnWidth = $returnAsPixels
+ ? self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['px']
+ : self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['width'];
+ } else {
+ // We don't have data for this particular font and size, use approximation by
+ // extrapolating from Calibri 11
+ $columnWidth = $returnAsPixels
+ ? self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px']
+ : self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['width'];
+ $columnWidth = $columnWidth * $font->getSize() / 11;
+
+ // Round pixels to closest integer
+ if ($returnAsPixels) {
+ $columnWidth = (int) round($columnWidth);
+ }
+ }
+
+ return $columnWidth;
+ }
+
+ /**
+ * Get the effective row height for rows without a row dimension or rows with height -1
+ * For example, for Calibri 11 this is 15 points.
+ *
+ * @param FontStyle $font The workbooks default font
+ *
+ * @return float Row height in points
+ */
+ public static function getDefaultRowHeightByFont(FontStyle $font): float
+ {
+ $name = $font->getName();
+ $size = $font->getSize();
+ if (isset(self::DEFAULT_COLUMN_WIDTHS[$name][$size])) {
+ $rowHeight = self::DEFAULT_COLUMN_WIDTHS[$name][$size]['height'];
+ } elseif ($name === 'Arial' || $name === 'Verdana') {
+ $rowHeight = self::DEFAULT_COLUMN_WIDTHS[$name][10]['height'] * $size / 10.0;
+ } else {
+ $rowHeight = self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['height'] * $size / 11.0;
+ }
+
+ return $rowHeight;
+ }
+
+ private static function findFontFile(string $startDirectory, string $desiredFont): ?string
+ {
+ $fontPath = null;
+ if ($startDirectory === '') {
+ return null;
+ }
+ if (file_exists("$startDirectory/$desiredFont")) {
+ $fontPath = "$startDirectory/$desiredFont";
+ } else {
+ $iterations = 0;
+ $it = new RecursiveDirectoryIterator(
+ $startDirectory,
+ RecursiveDirectoryIterator::SKIP_DOTS
+ | RecursiveDirectoryIterator::FOLLOW_SYMLINKS
+ );
+ foreach (
+ new RecursiveIteratorIterator(
+ $it,
+ RecursiveIteratorIterator::LEAVES_ONLY,
+ RecursiveIteratorIterator::CATCH_GET_CHILD
+ ) as $file
+ ) {
+ if (basename($file) === $desiredFont) {
+ $fontPath = $file;
+
+ break;
+ }
+ ++$iterations;
+ if ($iterations > 5000) {
+ // @codeCoverageIgnoreStart
+ break;
+ // @codeCoverageIgnoreEnd
+ }
+ }
+ }
+
+ return $fontPath;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php
new file mode 100644
index 0000000..060f09c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php
@@ -0,0 +1,21 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root;
+
+/*
+ * Array for storing OLE instances that are accessed from
+ * OLE_ChainedBlockStream::stream_open().
+ *
+ * @var array
+ */
+$GLOBALS['_OLE_INSTANCES'] = [];
+
+/**
+ * OLE package base class.
+ *
+ * @author Xavier Noguer
+ * @author Christian Schmidt
+ */
+class OLE
+{
+ const OLE_PPS_TYPE_ROOT = 5;
+ const OLE_PPS_TYPE_DIR = 1;
+ const OLE_PPS_TYPE_FILE = 2;
+ const OLE_DATA_SIZE_SMALL = 0x1000;
+ const OLE_LONG_INT_SIZE = 4;
+ const OLE_PPS_SIZE = 0x80;
+
+ /**
+ * The file handle for reading an OLE container.
+ *
+ * @var resource
+ */
+ public $_file_handle;
+
+ /**
+ * Array of PPS's found on the OLE container.
+ *
+ * @var array
+ */
+ public $_list = [];
+
+ /**
+ * Root directory of OLE container.
+ *
+ * @var Root
+ */
+ public $root;
+
+ /**
+ * Big Block Allocation Table.
+ *
+ * @var array (blockId => nextBlockId)
+ */
+ public $bbat;
+
+ /**
+ * Short Block Allocation Table.
+ *
+ * @var array (blockId => nextBlockId)
+ */
+ public $sbat;
+
+ /**
+ * Size of big blocks. This is usually 512.
+ *
+ * @var int number of octets per block
+ */
+ public $bigBlockSize;
+
+ /**
+ * Size of small blocks. This is usually 64.
+ *
+ * @var int number of octets per block
+ */
+ public $smallBlockSize;
+
+ /**
+ * Threshold for big blocks.
+ *
+ * @var int
+ */
+ public $bigBlockThreshold;
+
+ /**
+ * Reads an OLE container from the contents of the file given.
+ *
+ * @acces public
+ *
+ * @param string $filename
+ *
+ * @return bool true on success, PEAR_Error on failure
+ */
+ public function read($filename): bool
+ {
+ $fh = @fopen($filename, 'rb');
+ if ($fh === false) {
+ throw new ReaderException("Can't open file $filename");
+ }
+ $this->_file_handle = $fh;
+
+ $signature = fread($fh, 8);
+ if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
+ throw new ReaderException("File doesn't seem to be an OLE container.");
+ }
+ fseek($fh, 28);
+ if (fread($fh, 2) != "\xFE\xFF") {
+ // This shouldn't be a problem in practice
+ throw new ReaderException('Only Little-Endian encoding is supported.');
+ }
+ // Size of blocks and short blocks in bytes
+ $this->bigBlockSize = 2 ** self::readInt2($fh);
+ $this->smallBlockSize = 2 ** self::readInt2($fh);
+
+ // Skip UID, revision number and version number
+ fseek($fh, 44);
+ // Number of blocks in Big Block Allocation Table
+ $bbatBlockCount = self::readInt4($fh);
+
+ // Root chain 1st block
+ $directoryFirstBlockId = self::readInt4($fh);
+
+ // Skip unused bytes
+ fseek($fh, 56);
+ // Streams shorter than this are stored using small blocks
+ $this->bigBlockThreshold = self::readInt4($fh);
+ // Block id of first sector in Short Block Allocation Table
+ $sbatFirstBlockId = self::readInt4($fh);
+ // Number of blocks in Short Block Allocation Table
+ $sbbatBlockCount = self::readInt4($fh);
+ // Block id of first sector in Master Block Allocation Table
+ $mbatFirstBlockId = self::readInt4($fh);
+ // Number of blocks in Master Block Allocation Table
+ $mbbatBlockCount = self::readInt4($fh);
+ $this->bbat = [];
+
+ // Remaining 4 * 109 bytes of current block is beginning of Master
+ // Block Allocation Table
+ $mbatBlocks = [];
+ for ($i = 0; $i < 109; ++$i) {
+ $mbatBlocks[] = self::readInt4($fh);
+ }
+
+ // Read rest of Master Block Allocation Table (if any is left)
+ $pos = $this->getBlockOffset($mbatFirstBlockId);
+ for ($i = 0; $i < $mbbatBlockCount; ++$i) {
+ fseek($fh, $pos);
+ for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
+ $mbatBlocks[] = self::readInt4($fh);
+ }
+ // Last block id in each block points to next block
+ $pos = $this->getBlockOffset(self::readInt4($fh));
+ }
+
+ // Read Big Block Allocation Table according to chain specified by $mbatBlocks
+ for ($i = 0; $i < $bbatBlockCount; ++$i) {
+ $pos = $this->getBlockOffset($mbatBlocks[$i]);
+ fseek($fh, $pos);
+ for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) {
+ $this->bbat[] = self::readInt4($fh);
+ }
+ }
+
+ // Read short block allocation table (SBAT)
+ $this->sbat = [];
+ $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
+ $sbatFh = $this->getStream($sbatFirstBlockId);
+ for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
+ $this->sbat[$blockId] = self::readInt4($sbatFh);
+ }
+ fclose($sbatFh);
+
+ $this->readPpsWks($directoryFirstBlockId);
+
+ return true;
+ }
+
+ /**
+ * @param int $blockId byte offset from beginning of file
+ */
+ public function getBlockOffset(int $blockId): int
+ {
+ return 512 + $blockId * $this->bigBlockSize;
+ }
+
+ /**
+ * Returns a stream for use with fread() etc. External callers should
+ * use \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File::getStream().
+ *
+ * @param int|OLE\PPS $blockIdOrPps block id or PPS
+ *
+ * @return resource read-only stream
+ */
+ public function getStream($blockIdOrPps)
+ {
+ static $isRegistered = false;
+ if (!$isRegistered) {
+ stream_wrapper_register('ole-chainedblockstream', ChainedBlockStream::class);
+ $isRegistered = true;
+ }
+
+ // Store current instance in global array, so that it can be accessed
+ // in OLE_ChainedBlockStream::stream_open().
+ // Object is removed from self::$instances in OLE_Stream::close().
+ $GLOBALS['_OLE_INSTANCES'][] = $this;
+ $keys = array_keys($GLOBALS['_OLE_INSTANCES']);
+ $instanceId = end($keys);
+
+ $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
+ if ($blockIdOrPps instanceof OLE\PPS) {
+ $path .= '&blockId=' . $blockIdOrPps->startBlock;
+ $path .= '&size=' . $blockIdOrPps->Size;
+ } else {
+ $path .= '&blockId=' . $blockIdOrPps;
+ }
+
+ $resource = fopen($path, 'rb');
+ if ($resource === false) {
+ throw new Exception("Unable to open stream $path");
+ }
+
+ return $resource;
+ }
+
+ /**
+ * Reads a signed char.
+ *
+ * @param resource $fileHandle file handle
+ *
+ * @return int
+ */
+ private static function readInt1($fileHandle)
+ {
+ [, $tmp] = unpack('c', fread($fileHandle, 1) ?: '') ?: [0, 0];
+
+ return $tmp;
+ }
+
+ /**
+ * Reads an unsigned short (2 octets).
+ *
+ * @param resource $fileHandle file handle
+ *
+ * @return int
+ */
+ private static function readInt2($fileHandle)
+ {
+ [, $tmp] = unpack('v', fread($fileHandle, 2) ?: '') ?: [0, 0];
+
+ return $tmp;
+ }
+
+ private const SIGNED_4OCTET_LIMIT = 2147483648;
+
+ private const SIGNED_4OCTET_SUBTRACT = 2 * self::SIGNED_4OCTET_LIMIT;
+
+ /**
+ * Reads long (4 octets), interpreted as if signed on 32-bit system.
+ *
+ * @param resource $fileHandle file handle
+ *
+ * @return int
+ */
+ private static function readInt4($fileHandle)
+ {
+ [, $tmp] = unpack('V', fread($fileHandle, 4) ?: '') ?: [0, 0];
+ if ($tmp >= self::SIGNED_4OCTET_LIMIT) {
+ $tmp -= self::SIGNED_4OCTET_SUBTRACT;
+ }
+
+ return $tmp;
+ }
+
+ /**
+ * Gets information about all PPS's on the OLE container from the PPS WK's
+ * creates an OLE_PPS object for each one.
+ *
+ * @param int $blockId the block id of the first block
+ *
+ * @return bool true on success, PEAR_Error on failure
+ */
+ public function readPpsWks($blockId): bool
+ {
+ $fh = $this->getStream($blockId);
+ for ($pos = 0; true; $pos += 128) {
+ fseek($fh, $pos, SEEK_SET);
+ $nameUtf16 = (string) fread($fh, 64);
+ $nameLength = self::readInt2($fh);
+ $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
+ // Simple conversion from UTF-16LE to ISO-8859-1
+ $name = str_replace("\x00", '', $nameUtf16);
+ $type = self::readInt1($fh);
+ switch ($type) {
+ case self::OLE_PPS_TYPE_ROOT:
+ $pps = new OLE\PPS\Root(null, null, []);
+ $this->root = $pps;
+
+ break;
+ case self::OLE_PPS_TYPE_DIR:
+ $pps = new OLE\PPS(null, null, null, null, null, null, null, null, null, []);
+
+ break;
+ case self::OLE_PPS_TYPE_FILE:
+ $pps = new OLE\PPS\File($name);
+
+ break;
+ default:
+ throw new Exception('Unsupported PPS type');
+ }
+ fseek($fh, 1, SEEK_CUR);
+ $pps->Type = $type;
+ $pps->Name = $name;
+ $pps->PrevPps = self::readInt4($fh);
+ $pps->NextPps = self::readInt4($fh);
+ $pps->DirPps = self::readInt4($fh);
+ fseek($fh, 20, SEEK_CUR);
+ $pps->Time1st = self::OLE2LocalDate((string) fread($fh, 8));
+ $pps->Time2nd = self::OLE2LocalDate((string) fread($fh, 8));
+ $pps->startBlock = self::readInt4($fh);
+ $pps->Size = self::readInt4($fh);
+ $pps->No = count($this->_list);
+ $this->_list[] = $pps;
+
+ // check if the PPS tree (starting from root) is complete
+ if (isset($this->root) && $this->ppsTreeComplete($this->root->No)) { //* @phpstan-ignore-line
+ break;
+ }
+ }
+ fclose($fh);
+
+ // Initialize $pps->children on directories
+ foreach ($this->_list as $pps) {
+ if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
+ $nos = [$pps->DirPps];
+ $pps->children = [];
+ while (!empty($nos)) {
+ $no = array_pop($nos);
+ if ($no != -1) {
+ $childPps = $this->_list[$no];
+ $nos[] = $childPps->PrevPps;
+ $nos[] = $childPps->NextPps;
+ $pps->children[] = $childPps;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * It checks whether the PPS tree is complete (all PPS's read)
+ * starting with the given PPS (not necessarily root).
+ *
+ * @param int $index The index of the PPS from which we are checking
+ *
+ * @return bool Whether the PPS tree for the given PPS is complete
+ */
+ private function ppsTreeComplete($index): bool
+ {
+ return isset($this->_list[$index])
+ && ($pps = $this->_list[$index])
+ && ($pps->PrevPps == -1
+ || $this->ppsTreeComplete($pps->PrevPps))
+ && ($pps->NextPps == -1
+ || $this->ppsTreeComplete($pps->NextPps))
+ && ($pps->DirPps == -1
+ || $this->ppsTreeComplete($pps->DirPps));
+ }
+
+ /**
+ * Checks whether a PPS is a File PPS or not.
+ * If there is no PPS for the index given, it will return false.
+ *
+ * @param int $index The index for the PPS
+ *
+ * @return bool true if it's a File PPS, false otherwise
+ */
+ public function isFile($index): bool
+ {
+ if (isset($this->_list[$index])) {
+ return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether a PPS is a Root PPS or not.
+ * If there is no PPS for the index given, it will return false.
+ *
+ * @param int $index the index for the PPS
+ *
+ * @return bool true if it's a Root PPS, false otherwise
+ */
+ public function isRoot($index): bool
+ {
+ if (isset($this->_list[$index])) {
+ return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT;
+ }
+
+ return false;
+ }
+
+ /**
+ * Gives the total number of PPS's found in the OLE container.
+ *
+ * @return int The total number of PPS's found in the OLE container
+ */
+ public function ppsTotal(): int
+ {
+ return count($this->_list);
+ }
+
+ /**
+ * Gets data from a PPS
+ * If there is no PPS for the index given, it will return an empty string.
+ *
+ * @param int $index The index for the PPS
+ * @param int $position The position from which to start reading
+ * (relative to the PPS)
+ * @param int $length The amount of bytes to read (at most)
+ *
+ * @return string The binary string containing the data requested
+ *
+ * @see OLE_PPS_File::getStream()
+ */
+ public function getData($index, $position, $length): string
+ {
+ // if position is not valid return empty string
+ if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
+ return '';
+ }
+ $fh = $this->getStream($this->_list[$index]);
+ $data = (string) stream_get_contents($fh, $length, $position);
+ fclose($fh);
+
+ return $data;
+ }
+
+ /**
+ * Gets the data length from a PPS
+ * If there is no PPS for the index given, it will return 0.
+ *
+ * @param int $index The index for the PPS
+ *
+ * @return int The amount of bytes in data the PPS has
+ */
+ public function getDataLength($index)
+ {
+ if (isset($this->_list[$index])) {
+ return $this->_list[$index]->Size;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Utility function to transform ASCII text to Unicode.
+ *
+ * @param string $ascii The ASCII string to transform
+ *
+ * @return string The string in Unicode
+ */
+ public static function ascToUcs($ascii): string
+ {
+ $rawname = '';
+ $iMax = strlen($ascii);
+ for ($i = 0; $i < $iMax; ++$i) {
+ $rawname .= $ascii[$i]
+ . "\x00";
+ }
+
+ return $rawname;
+ }
+
+ /**
+ * Utility function
+ * Returns a string for the OLE container with the date given.
+ *
+ * @param float|int $date A timestamp
+ *
+ * @return string The string for the OLE container
+ */
+ public static function localDateToOLE($date): string
+ {
+ if (!$date) {
+ return "\x00\x00\x00\x00\x00\x00\x00\x00";
+ }
+ $dateTime = Date::dateTimeFromTimestamp("$date");
+
+ // days from 1-1-1601 until the beggining of UNIX era
+ $days = 134774;
+ // calculate seconds
+ $big_date = $days * 24 * 3600 + (float) $dateTime->format('U');
+ // multiply just to make MS happy
+ $big_date *= 10000000;
+
+ // Make HEX string
+ $res = '';
+
+ $factor = 2 ** 56;
+ while ($factor >= 1) {
+ $hex = (int) floor($big_date / $factor);
+ $res = pack('c', $hex) . $res;
+ $big_date = fmod($big_date, $factor);
+ $factor /= 256;
+ }
+
+ return $res;
+ }
+
+ /**
+ * Returns a timestamp from an OLE container's date.
+ *
+ * @param string $oleTimestamp A binary string with the encoded date
+ *
+ * @return float|int The Unix timestamp corresponding to the string
+ */
+ public static function OLE2LocalDate($oleTimestamp)
+ {
+ if (strlen($oleTimestamp) != 8) {
+ throw new ReaderException('Expecting 8 byte string');
+ }
+
+ // convert to units of 100 ns since 1601:
+ $unpackedTimestamp = unpack('v4', $oleTimestamp) ?: [];
+ $timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];
+ $timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];
+
+ // translate to seconds since 1601:
+ $timestampHigh /= 10000000;
+ $timestampLow /= 10000000;
+
+ // days from 1601 to 1970:
+ $days = 134774;
+
+ // translate to seconds since 1970:
+ $unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5);
+
+ return IntOrFloat::evaluate($unixTimestamp);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
new file mode 100644
index 0000000..f95c902
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
@@ -0,0 +1,195 @@
+params);
+ if (!isset($this->params['oleInstanceId'], $this->params['blockId'], $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) {
+ if ($options & STREAM_REPORT_ERRORS) {
+ trigger_error('OLE stream not found', E_USER_WARNING);
+ }
+
+ return false;
+ }
+ $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']];
+
+ $blockId = $this->params['blockId'];
+ $this->data = '';
+ if (isset($this->params['size']) && $this->params['size'] < $this->ole->bigBlockThreshold && $blockId != $this->ole->root->startBlock) {
+ // Block id refers to small blocks
+ $rootPos = $this->ole->getBlockOffset($this->ole->root->startBlock);
+ while ($blockId != -2) {
+ $pos = $rootPos + $blockId * $this->ole->bigBlockSize;
+ $blockId = $this->ole->sbat[$blockId];
+ fseek($this->ole->_file_handle, $pos);
+ $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
+ }
+ } else {
+ // Block id refers to big blocks
+ while ($blockId != -2) {
+ $pos = $this->ole->getBlockOffset($blockId);
+ fseek($this->ole->_file_handle, $pos);
+ $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
+ $blockId = $this->ole->bbat[$blockId];
+ }
+ }
+ if (isset($this->params['size'])) {
+ $this->data = substr($this->data, 0, $this->params['size']);
+ }
+
+ if ($options & STREAM_USE_PATH) {
+ $openedPath = $path;
+ }
+
+ return true;
+ }
+
+ /**
+ * Implements support for fclose().
+ */
+ public function stream_close(): void // @codingStandardsIgnoreLine
+ {
+ $this->ole = null;
+ unset($GLOBALS['_OLE_INSTANCES']);
+ }
+
+ /**
+ * Implements support for fread(), fgets() etc.
+ *
+ * @param int $count maximum number of bytes to read
+ *
+ * @return false|string
+ */
+ public function stream_read($count): bool|string // @codingStandardsIgnoreLine
+ {
+ if ($this->stream_eof()) {
+ return false;
+ }
+ $s = substr($this->data, (int) $this->pos, $count);
+ $this->pos += $count;
+
+ return $s;
+ }
+
+ /**
+ * Implements support for feof().
+ *
+ * @return bool TRUE if the file pointer is at EOF; otherwise FALSE
+ */
+ public function stream_eof(): bool // @codingStandardsIgnoreLine
+ {
+ return $this->pos >= strlen($this->data);
+ }
+
+ /**
+ * Returns the position of the file pointer, i.e. its offset into the file
+ * stream. Implements support for ftell().
+ *
+ * @return int
+ */
+ public function stream_tell() // @codingStandardsIgnoreLine
+ {
+ return $this->pos;
+ }
+
+ /**
+ * Implements support for fseek().
+ *
+ * @param int $offset byte offset
+ * @param int $whence SEEK_SET, SEEK_CUR or SEEK_END
+ */
+ public function stream_seek($offset, $whence): bool // @codingStandardsIgnoreLine
+ {
+ if ($whence == SEEK_SET && $offset >= 0) {
+ $this->pos = $offset;
+ } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) {
+ $this->pos += $offset;
+ } elseif ($whence == SEEK_END && -$offset <= count($this->data)) { // @phpstan-ignore-line
+ $this->pos = strlen($this->data) + $offset;
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Implements support for fstat(). Currently the only supported field is
+ * "size".
+ */
+ public function stream_stat(): array // @codingStandardsIgnoreLine
+ {
+ return [
+ 'size' => strlen($this->data),
+ ];
+ }
+
+ // Methods used by stream_wrapper_register() that are not implemented:
+ // bool stream_flush ( void )
+ // int stream_write ( string data )
+ // bool rename ( string path_from, string path_to )
+ // bool mkdir ( string path, int mode, int options )
+ // bool rmdir ( string path, int options )
+ // bool dir_opendir ( string path, int options )
+ // array url_stat ( string path, int flags )
+ // string dir_readdir ( void )
+ // bool dir_rewinddir ( void )
+ // bool dir_closedir ( void )
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
new file mode 100644
index 0000000..76f6bf8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
@@ -0,0 +1,231 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+
+/**
+ * Class for creating PPS's for OLE containers.
+ *
+ * @author Xavier Noguer
+ */
+class PPS
+{
+ /**
+ * The PPS index.
+ *
+ * @var int
+ */
+ public $No;
+
+ /**
+ * The PPS name (in Unicode).
+ *
+ * @var string
+ */
+ public $Name;
+
+ /**
+ * The PPS type. Dir, Root or File.
+ *
+ * @var int
+ */
+ public $Type;
+
+ /**
+ * The index of the previous PPS.
+ *
+ * @var int
+ */
+ public $PrevPps;
+
+ /**
+ * The index of the next PPS.
+ *
+ * @var int
+ */
+ public $NextPps;
+
+ /**
+ * The index of it's first child if this is a Dir or Root PPS.
+ *
+ * @var int
+ */
+ public $DirPps;
+
+ /**
+ * A timestamp.
+ *
+ * @var float|int
+ */
+ public $Time1st;
+
+ /**
+ * A timestamp.
+ *
+ * @var float|int
+ */
+ public $Time2nd;
+
+ /**
+ * Starting block (small or big) for this PPS's data inside the container.
+ *
+ * @var ?int
+ */
+ public $startBlock;
+
+ /**
+ * The size of the PPS's data (in bytes).
+ *
+ * @var int
+ */
+ public $Size;
+
+ /**
+ * The PPS's data (only used if it's not using a temporary file).
+ *
+ * @var string
+ */
+ public $_data = '';
+
+ /**
+ * Array of child PPS's (only used by Root and Dir PPS's).
+ *
+ * @var array
+ */
+ public $children = [];
+
+ /**
+ * Pointer to OLE container.
+ *
+ * @var OLE
+ */
+ public $ole;
+
+ /**
+ * The constructor.
+ *
+ * @param ?int $No The PPS index
+ * @param ?string $name The PPS name
+ * @param ?int $type The PPS type. Dir, Root or File
+ * @param ?int $prev The index of the previous PPS
+ * @param ?int $next The index of the next PPS
+ * @param ?int $dir The index of it's first child if this is a Dir or Root PPS
+ * @param null|float|int $time_1st A timestamp
+ * @param null|float|int $time_2nd A timestamp
+ * @param ?string $data The (usually binary) source data of the PPS
+ * @param array $children Array containing children PPS for this PPS
+ */
+ public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
+ {
+ $this->No = (int) $No;
+ $this->Name = (string) $name;
+ $this->Type = (int) $type;
+ $this->PrevPps = (int) $prev;
+ $this->NextPps = (int) $next;
+ $this->DirPps = (int) $dir;
+ $this->Time1st = $time_1st ?? 0;
+ $this->Time2nd = $time_2nd ?? 0;
+ $this->_data = (string) $data;
+ $this->children = $children;
+ $this->Size = strlen((string) $data);
+ }
+
+ /**
+ * Returns the amount of data saved for this PPS.
+ *
+ * @return int The amount of data (in bytes)
+ */
+ public function getDataLen()
+ {
+ //if (!isset($this->_data)) {
+ // return 0;
+ //}
+
+ return strlen($this->_data);
+ }
+
+ /**
+ * Returns a string with the PPS's WK (What is a WK?).
+ *
+ * @return string The binary string
+ */
+ public function getPpsWk()
+ {
+ $ret = str_pad($this->Name, 64, "\x00");
+
+ $ret .= pack('v', strlen($this->Name) + 2) // 66
+ . pack('c', $this->Type) // 67
+ . pack('c', 0x00) //UK // 68
+ . pack('V', $this->PrevPps) //Prev // 72
+ . pack('V', $this->NextPps) //Next // 76
+ . pack('V', $this->DirPps) //Dir // 80
+ . "\x00\x09\x02\x00" // 84
+ . "\x00\x00\x00\x00" // 88
+ . "\xc0\x00\x00\x00" // 92
+ . "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root
+ . "\x00\x00\x00\x00" // 100
+ . OLE::localDateToOLE($this->Time1st) // 108
+ . OLE::localDateToOLE($this->Time2nd) // 116
+ . pack('V', $this->startBlock ?? 0) // 120
+ . pack('V', $this->Size) // 124
+ . pack('V', 0); // 128
+
+ return $ret;
+ }
+
+ /**
+ * Updates index and pointers to previous, next and children PPS's for this
+ * PPS. I don't think it'll work with Dir PPS's.
+ *
+ * @param array $raList Reference to the array of PPS's for the whole OLE
+ * container
+ *
+ * @return int The index for this PPS
+ */
+ public static function savePpsSetPnt(array &$raList, mixed $to_save, mixed $depth = 0)
+ {
+ if (!is_array($to_save) || (empty($to_save))) {
+ return 0xFFFFFFFF;
+ } elseif (count($to_save) == 1) {
+ $cnt = count($raList);
+ // If the first entry, it's the root... Don't clone it!
+ $raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0];
+ $raList[$cnt]->No = $cnt;
+ $raList[$cnt]->PrevPps = 0xFFFFFFFF;
+ $raList[$cnt]->NextPps = 0xFFFFFFFF;
+ $raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
+ } else {
+ $iPos = (int) floor(count($to_save) / 2);
+ $aPrev = array_slice($to_save, 0, $iPos);
+ $aNext = array_slice($to_save, $iPos + 1);
+ $cnt = count($raList);
+ // If the first entry, it's the root... Don't clone it!
+ $raList[$cnt] = ($depth == 0) ? $to_save[$iPos] : clone $to_save[$iPos];
+ $raList[$cnt]->No = $cnt;
+ $raList[$cnt]->PrevPps = self::savePpsSetPnt($raList, $aPrev, $depth++);
+ $raList[$cnt]->NextPps = self::savePpsSetPnt($raList, $aNext, $depth++);
+ $raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
+ }
+
+ return $cnt;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
new file mode 100644
index 0000000..8a39281
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
@@ -0,0 +1,62 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
+
+/**
+ * Class for creating File PPS's for OLE containers.
+ *
+ * @author Xavier Noguer
+ */
+class File extends PPS
+{
+ /**
+ * The constructor.
+ *
+ * @param string $name The name of the file (in Unicode)
+ *
+ * @see OLE::ascToUcs()
+ */
+ public function __construct($name)
+ {
+ parent::__construct(null, $name, OLE::OLE_PPS_TYPE_FILE, null, null, null, null, null, '', []);
+ }
+
+ /**
+ * Initialization method. Has to be called right after OLE_PPS_File().
+ */
+ public function init(): bool
+ {
+ return true;
+ }
+
+ /**
+ * Append data to PPS.
+ *
+ * @param string $data The data to append
+ */
+ public function append(string $data): void
+ {
+ $this->_data .= $data;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
new file mode 100644
index 0000000..5cd43c1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
@@ -0,0 +1,412 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+use PhpOffice\PhpSpreadsheet\Shared\OLE;
+use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
+
+/**
+ * Class for creating Root PPS's for OLE containers.
+ *
+ * @author Xavier Noguer
+ */
+class Root extends PPS
+{
+ /**
+ * @var resource
+ */
+ private $fileHandle;
+
+ /**
+ * @var ?int
+ */
+ private $smallBlockSize;
+
+ /**
+ * @var ?int
+ */
+ private $bigBlockSize;
+
+ /**
+ * @param null|float|int $time_1st A timestamp
+ * @param null|float|int $time_2nd A timestamp
+ * @param File[] $raChild
+ */
+ public function __construct($time_1st, $time_2nd, $raChild)
+ {
+ parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
+ }
+
+ /**
+ * Method for saving the whole OLE container (including files).
+ * In fact, if called with an empty argument (or '-'), it saves to a
+ * temporary file and then outputs it's contents to stdout.
+ * If a resource pointer to a stream created by fopen() is passed
+ * it will be used, but you have to close such stream by yourself.
+ *
+ * @param resource $fileHandle the name of the file or stream where to save the OLE container
+ *
+ * @return bool true on success
+ */
+ public function save($fileHandle): bool
+ {
+ $this->fileHandle = $fileHandle;
+
+ // Initial Setting for saving
+ $this->bigBlockSize = (int) (2 ** (
+ (isset($this->bigBlockSize)) ? self::adjust2($this->bigBlockSize) : 9
+ ));
+ $this->smallBlockSize = (int) (2 ** (
+ (isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6
+ ));
+
+ // Make an array of PPS's (for Save)
+ $aList = [];
+ PPS::savePpsSetPnt($aList, [$this]);
+ // calculate values for header
+ [$iSBDcnt, $iBBcnt, $iPPScnt] = $this->calcSize($aList); //, $rhInfo);
+ // Save Header
+ $this->saveHeader((int) $iSBDcnt, (int) $iBBcnt, (int) $iPPScnt);
+
+ // Make Small Data string (write SBD)
+ $this->_data = $this->makeSmallData($aList);
+
+ // Write BB
+ $this->saveBigData((int) $iSBDcnt, $aList);
+ // Write PPS
+ $this->savePps($aList);
+ // Write Big Block Depot and BDList and Adding Header informations
+ $this->saveBbd((int) $iSBDcnt, (int) $iBBcnt, (int) $iPPScnt);
+
+ return true;
+ }
+
+ /**
+ * Calculate some numbers.
+ *
+ * @param array $raList Reference to an array of PPS's
+ *
+ * @return float[] The array of numbers
+ */
+ private function calcSize(array &$raList): array
+ {
+ // Calculate Basic Setting
+ [$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
+ $iSBcnt = 0;
+ $iCount = count($raList);
+ for ($i = 0; $i < $iCount; ++$i) {
+ if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
+ $raList[$i]->Size = $raList[$i]->getDataLen();
+ if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
+ $iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize)
+ + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
+ } else {
+ $iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize)
+ + (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
+ }
+ }
+ }
+ $iSmallLen = $iSBcnt * $this->smallBlockSize;
+ $iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
+ $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0);
+ $iBBcnt += (floor($iSmallLen / $this->bigBlockSize)
+ + (($iSmallLen % $this->bigBlockSize) ? 1 : 0));
+ $iCnt = count($raList);
+ $iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
+ $iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));
+
+ return [$iSBDcnt, $iBBcnt, $iPPScnt];
+ }
+
+ /**
+ * Helper function for caculating a magic value for block sizes.
+ *
+ * @param int $i2 The argument
+ *
+ * @see save()
+ */
+ private static function adjust2($i2): float
+ {
+ $iWk = log($i2) / log(2);
+
+ return ($iWk > floor($iWk)) ? floor($iWk) + 1 : $iWk;
+ }
+
+ /**
+ * Save OLE header.
+ */
+ private function saveHeader(int $iSBDcnt, int $iBBcnt, int $iPPScnt): void
+ {
+ $FILE = $this->fileHandle;
+
+ // Calculate Basic Setting
+ $iBlCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
+ $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
+
+ $iBdExL = 0;
+ $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
+ $iAllW = $iAll;
+ $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
+
+ // Calculate BD count
+ if ($iBdCnt > $i1stBdL) {
+ while (1) {
+ ++$iBdExL;
+ ++$iAllW;
+ $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
+ if ($iBdCnt <= ($iBdExL * $iBlCnt + $i1stBdL)) {
+ break;
+ }
+ }
+ }
+
+ // Save Header
+ fwrite(
+ $FILE,
+ "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . pack('v', 0x3B)
+ . pack('v', 0x03)
+ . pack('v', -2)
+ . pack('v', 9)
+ . pack('v', 6)
+ . pack('v', 0)
+ . "\x00\x00\x00\x00"
+ . "\x00\x00\x00\x00"
+ . pack('V', $iBdCnt)
+ . pack('V', $iBBcnt + $iSBDcnt) //ROOT START
+ . pack('V', 0)
+ . pack('V', 0x1000)
+ . pack('V', $iSBDcnt ? 0 : -2) //Small Block Depot
+ . pack('V', $iSBDcnt)
+ );
+ // Extra BDList Start, Count
+ if ($iBdCnt < $i1stBdL) {
+ fwrite(
+ $FILE,
+ pack('V', -2) // Extra BDList Start
+ . pack('V', 0)// Extra BDList Count
+ );
+ } else {
+ fwrite($FILE, pack('V', $iAll + $iBdCnt) . pack('V', $iBdExL));
+ }
+
+ // BDList
+ for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
+ fwrite($FILE, pack('V', $iAll + $i));
+ }
+ if ($i < $i1stBdL) {
+ $jB = $i1stBdL - $i;
+ for ($j = 0; $j < $jB; ++$j) {
+ fwrite($FILE, (pack('V', -1)));
+ }
+ }
+ }
+
+ /**
+ * Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
+ *
+ * @param array $raList Reference to array of PPS's
+ */
+ private function saveBigData(int $iStBlk, array &$raList): void
+ {
+ $FILE = $this->fileHandle;
+
+ // cycle through PPS's
+ $iCount = count($raList);
+ for ($i = 0; $i < $iCount; ++$i) {
+ if ($raList[$i]->Type != OLE::OLE_PPS_TYPE_DIR) {
+ $raList[$i]->Size = $raList[$i]->getDataLen();
+ if (($raList[$i]->Size >= OLE::OLE_DATA_SIZE_SMALL) || (($raList[$i]->Type == OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) {
+ fwrite($FILE, $raList[$i]->_data);
+
+ if ($raList[$i]->Size % $this->bigBlockSize) {
+ fwrite($FILE, str_repeat("\x00", $this->bigBlockSize - ($raList[$i]->Size % $this->bigBlockSize)));
+ }
+ // Set For PPS
+ $raList[$i]->startBlock = $iStBlk;
+ $iStBlk
+ += (floor($raList[$i]->Size / $this->bigBlockSize)
+ + (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
+ }
+ }
+ }
+ }
+
+ /**
+ * get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
+ *
+ * @param array $raList Reference to array of PPS's
+ */
+ private function makeSmallData(array &$raList): string
+ {
+ $sRes = '';
+ $FILE = $this->fileHandle;
+ $iSmBlk = 0;
+
+ $iCount = count($raList);
+ for ($i = 0; $i < $iCount; ++$i) {
+ // Make SBD, small data string
+ if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
+ if ($raList[$i]->Size <= 0) {
+ continue;
+ }
+ if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
+ $iSmbCnt = floor($raList[$i]->Size / $this->smallBlockSize)
+ + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
+ // Add to SBD
+ $jB = $iSmbCnt - 1;
+ for ($j = 0; $j < $jB; ++$j) {
+ fwrite($FILE, pack('V', $j + $iSmBlk + 1));
+ }
+ fwrite($FILE, pack('V', -2));
+
+ // Add to Data String(this will be written for RootEntry)
+ $sRes .= $raList[$i]->_data;
+ if ($raList[$i]->Size % $this->smallBlockSize) {
+ $sRes .= str_repeat("\x00", $this->smallBlockSize - ($raList[$i]->Size % $this->smallBlockSize));
+ }
+ // Set for PPS
+ $raList[$i]->startBlock = $iSmBlk;
+ $iSmBlk += $iSmbCnt;
+ }
+ }
+ }
+ $iSbCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
+ if ($iSmBlk % $iSbCnt) {
+ $iB = $iSbCnt - ($iSmBlk % $iSbCnt);
+ for ($i = 0; $i < $iB; ++$i) {
+ fwrite($FILE, pack('V', -1));
+ }
+ }
+
+ return $sRes;
+ }
+
+ /**
+ * Saves all the PPS's WKs.
+ *
+ * @param array $raList Reference to an array with all PPS's
+ */
+ private function savePps(array &$raList): void
+ {
+ // Save each PPS WK
+ $iC = count($raList);
+ for ($i = 0; $i < $iC; ++$i) {
+ fwrite($this->fileHandle, $raList[$i]->getPpsWk());
+ }
+ // Adjust for Block
+ $iCnt = count($raList);
+ $iBCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
+ if ($iCnt % $iBCnt) {
+ fwrite($this->fileHandle, str_repeat("\x00", ($iBCnt - ($iCnt % $iBCnt)) * OLE::OLE_PPS_SIZE));
+ }
+ }
+
+ /**
+ * Saving Big Block Depot.
+ */
+ private function saveBbd(int $iSbdSize, int $iBsize, int $iPpsCnt): void
+ {
+ $FILE = $this->fileHandle;
+ // Calculate Basic Setting
+ $iBbCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
+ $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
+
+ $iBdExL = 0;
+ $iAll = $iBsize + $iPpsCnt + $iSbdSize;
+ $iAllW = $iAll;
+ $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
+ // Calculate BD count
+ if ($iBdCnt > $i1stBdL) {
+ while (1) {
+ ++$iBdExL;
+ ++$iAllW;
+ $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
+ if ($iBdCnt <= ($iBdExL * $iBbCnt + $i1stBdL)) {
+ break;
+ }
+ }
+ }
+
+ // Making BD
+ // Set for SBD
+ if ($iSbdSize > 0) {
+ for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
+ fwrite($FILE, pack('V', $i + 1));
+ }
+ fwrite($FILE, pack('V', -2));
+ }
+ // Set for B
+ for ($i = 0; $i < ($iBsize - 1); ++$i) {
+ fwrite($FILE, pack('V', $i + $iSbdSize + 1));
+ }
+ fwrite($FILE, pack('V', -2));
+
+ // Set for PPS
+ for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
+ fwrite($FILE, pack('V', $i + $iSbdSize + $iBsize + 1));
+ }
+ fwrite($FILE, pack('V', -2));
+ // Set for BBD itself ( 0xFFFFFFFD : BBD)
+ for ($i = 0; $i < $iBdCnt; ++$i) {
+ fwrite($FILE, pack('V', 0xFFFFFFFD));
+ }
+ // Set for ExtraBDList
+ for ($i = 0; $i < $iBdExL; ++$i) {
+ fwrite($FILE, pack('V', 0xFFFFFFFC));
+ }
+ // Adjust for Block
+ if (($iAllW + $iBdCnt) % $iBbCnt) {
+ $iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt));
+ for ($i = 0; $i < $iBlock; ++$i) {
+ fwrite($FILE, pack('V', -1));
+ }
+ }
+ // Extra BDList
+ if ($iBdCnt > $i1stBdL) {
+ $iN = 0;
+ $iNb = 0;
+ for ($i = $i1stBdL; $i < $iBdCnt; $i++, ++$iN) {
+ if ($iN >= ($iBbCnt - 1)) {
+ $iN = 0;
+ ++$iNb;
+ fwrite($FILE, pack('V', $iAll + $iBdCnt + $iNb));
+ }
+ fwrite($FILE, pack('V', $iBsize + $iSbdSize + $iPpsCnt + $i));
+ }
+ if (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)) {
+ $iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1));
+ for ($i = 0; $i < $iB; ++$i) {
+ fwrite($FILE, pack('V', -1));
+ }
+ }
+ fwrite($FILE, pack('V', -2));
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
new file mode 100644
index 0000000..f2718fc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
@@ -0,0 +1,345 @@
+data = (string) file_get_contents($filename, false, null, 0, 8);
+
+ // Check OLE identifier
+ $identifierOle = pack('CCCCCCCC', 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1);
+ if ($this->data != $identifierOle) {
+ throw new ReaderException('The filename ' . $filename . ' is not recognised as an OLE file');
+ }
+
+ // Get the file data
+ $this->data = (string) file_get_contents($filename);
+
+ // Total number of sectors used for the SAT
+ $this->numBigBlockDepotBlocks = self::getInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
+
+ // SecID of the first sector of the directory stream
+ $this->rootStartBlock = self::getInt4d($this->data, self::ROOT_START_BLOCK_POS);
+
+ // SecID of the first sector of the SSAT (or -2 if not extant)
+ $this->sbdStartBlock = self::getInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
+
+ // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
+ $this->extensionBlock = self::getInt4d($this->data, self::EXTENSION_BLOCK_POS);
+
+ // Total number of sectors used by MSAT
+ $this->numExtensionBlocks = self::getInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
+
+ $bigBlockDepotBlocks = [];
+ $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
+
+ $bbdBlocks = $this->numBigBlockDepotBlocks;
+
+ if ($this->numExtensionBlocks !== 0) {
+ $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
+ }
+
+ for ($i = 0; $i < $bbdBlocks; ++$i) {
+ $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
+ $pos += 4;
+ }
+
+ for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
+ $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
+ $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
+
+ for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
+ $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
+ $pos += 4;
+ }
+
+ $bbdBlocks += $blocksToRead;
+ if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
+ $this->extensionBlock = self::getInt4d($this->data, $pos);
+ }
+ }
+
+ $pos = 0;
+ $this->bigBlockChain = '';
+ $bbs = self::BIG_BLOCK_SIZE / 4;
+ for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
+ $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
+
+ $this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
+ $pos += 4 * $bbs;
+ }
+
+ $sbdBlock = $this->sbdStartBlock;
+ $this->smallBlockChain = '';
+ while ($sbdBlock != -2) {
+ $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
+
+ $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
+ $pos += 4 * $bbs;
+
+ $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
+ }
+
+ // read the directory stream
+ $block = $this->rootStartBlock;
+ $this->entry = $this->readData($block);
+
+ $this->readPropertySets();
+ }
+
+ /**
+ * Extract binary stream data.
+ *
+ * @param ?int $stream
+ */
+ public function getStream($stream): ?string
+ {
+ if ($stream === null) {
+ return null;
+ }
+
+ $streamData = '';
+
+ if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
+ $rootdata = $this->readData($this->props[$this->rootentry]['startBlock']);
+
+ $block = $this->props[$stream]['startBlock'];
+
+ while ($block != -2) {
+ $pos = $block * self::SMALL_BLOCK_SIZE;
+ $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
+
+ $block = self::getInt4d($this->smallBlockChain, $block * 4);
+ }
+
+ return $streamData;
+ }
+ $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
+ if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
+ ++$numBlocks;
+ }
+
+ if ($numBlocks == 0) {
+ return '';
+ }
+
+ $block = $this->props[$stream]['startBlock'];
+
+ while ($block != -2) {
+ $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+ $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+ $block = self::getInt4d($this->bigBlockChain, $block * 4);
+ }
+
+ return $streamData;
+ }
+
+ /**
+ * Read a standard stream (by joining sectors using information from SAT).
+ *
+ * @param int $block Sector ID where the stream starts
+ *
+ * @return string Data for standard stream
+ */
+ private function readData($block): string
+ {
+ $data = '';
+
+ while ($block != -2) {
+ $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
+ $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
+ $block = self::getInt4d($this->bigBlockChain, $block * 4);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Read entries in the directory stream.
+ */
+ private function readPropertySets(): void
+ {
+ $offset = 0;
+
+ // loop through entires, each entry is 128 bytes
+ $entryLen = strlen($this->entry);
+ while ($offset < $entryLen) {
+ // entry data (128 bytes)
+ $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
+
+ // size in bytes of name
+ $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS + 1]) << 8);
+
+ // type of entry
+ $type = ord($d[self::TYPE_POS]);
+
+ // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
+ // sectorID of first sector of the short-stream container stream, if this entry is root entry
+ $startBlock = self::getInt4d($d, self::START_BLOCK_POS);
+
+ $size = self::getInt4d($d, self::SIZE_POS);
+
+ $name = str_replace("\x00", '', substr($d, 0, $nameSize));
+
+ $this->props[] = [
+ 'name' => $name,
+ 'type' => $type,
+ 'startBlock' => $startBlock,
+ 'size' => $size,
+ ];
+
+ // tmp helper to simplify checks
+ $upName = strtoupper($name);
+
+ // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
+ if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
+ $this->wrkbook = count($this->props) - 1;
+ } elseif ($upName === 'ROOT ENTRY' || $upName === 'R') {
+ // Root entry
+ $this->rootentry = count($this->props) - 1;
+ }
+
+ // Summary information
+ if ($name == chr(5) . 'SummaryInformation') {
+ $this->summaryInformation = count($this->props) - 1;
+ }
+
+ // Additional Document Summary information
+ if ($name == chr(5) . 'DocumentSummaryInformation') {
+ $this->documentSummaryInformation = count($this->props) - 1;
+ }
+
+ $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
+ }
+ }
+
+ /**
+ * Read 4 bytes of data at specified position.
+ *
+ * @param int $pos
+ */
+ private static function getInt4d(string $data, $pos): int
+ {
+ if ($pos < 0) {
+ // Invalid position
+ throw new ReaderException('Parameter pos=' . $pos . ' is invalid.');
+ }
+
+ $len = strlen($data);
+ if ($len < $pos + 4) {
+ $data .= str_repeat("\0", $pos + 4 - $len);
+ }
+
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
new file mode 100644
index 0000000..fcdbc98
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
@@ -0,0 +1,106 @@
+ 'md2',
+ Protection::ALGORITHM_MD4 => 'md4',
+ Protection::ALGORITHM_MD5 => 'md5',
+ Protection::ALGORITHM_SHA_1 => 'sha1',
+ Protection::ALGORITHM_SHA_256 => 'sha256',
+ Protection::ALGORITHM_SHA_384 => 'sha384',
+ Protection::ALGORITHM_SHA_512 => 'sha512',
+ Protection::ALGORITHM_RIPEMD_128 => 'ripemd128',
+ Protection::ALGORITHM_RIPEMD_160 => 'ripemd160',
+ Protection::ALGORITHM_WHIRLPOOL => 'whirlpool',
+ ];
+
+ if (array_key_exists($algorithmName, $mapping)) {
+ return $mapping[$algorithmName];
+ }
+
+ throw new SpException('Unsupported password algorithm: ' . $algorithmName);
+ }
+
+ /**
+ * Create a password hash from a given string.
+ *
+ * This method is based on the spec at:
+ * https://interoperability.blob.core.windows.net/files/MS-OFFCRYPTO/[MS-OFFCRYPTO].pdf
+ * 2.3.7.1 Binary Document Password Verifier Derivation Method 1
+ *
+ * It replaces a method based on the algorithm provided by
+ * Daniel Rentz of OpenOffice and the PEAR package
+ * Spreadsheet_Excel_Writer by Xavier Noguer .
+ *
+ * @param string $password Password to hash
+ */
+ private static function defaultHashPassword(string $password): string
+ {
+ $verifier = 0;
+ $pwlen = strlen($password);
+ $passwordArray = pack('c', $pwlen) . $password;
+ for ($i = $pwlen; $i >= 0; --$i) {
+ $intermediate1 = (($verifier & 0x4000) === 0) ? 0 : 1;
+ $intermediate2 = 2 * $verifier;
+ $intermediate2 = $intermediate2 & 0x7FFF;
+ $intermediate3 = $intermediate1 | $intermediate2;
+ $verifier = $intermediate3 ^ ord($passwordArray[$i]);
+ }
+ $verifier ^= 0xCE4B;
+
+ return strtoupper(dechex($verifier));
+ }
+
+ /**
+ * Create a password hash from a given string by a specific algorithm.
+ *
+ * 2.4.2.4 ISO Write Protection Method
+ *
+ * @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f
+ *
+ * @param string $password Password to hash
+ * @param string $algorithm Hash algorithm used to compute the password hash value
+ * @param string $salt Pseudorandom string
+ * @param int $spinCount Number of times to iterate on a hash of a password
+ *
+ * @return string Hashed password
+ */
+ public static function hashPassword(string $password, string $algorithm = '', string $salt = '', int $spinCount = 10000): string
+ {
+ if (strlen($password) > self::MAX_PASSWORD_LENGTH) {
+ throw new SpException('Password exceeds ' . self::MAX_PASSWORD_LENGTH . ' characters');
+ }
+ $phpAlgorithm = self::getAlgorithm($algorithm);
+ if (!$phpAlgorithm) {
+ return self::defaultHashPassword($password);
+ }
+
+ $saltValue = base64_decode($salt);
+ $encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
+
+ $hashValue = hash($phpAlgorithm, $saltValue . $encodedPassword, true);
+ for ($i = 0; $i < $spinCount; ++$i) {
+ $hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true);
+ }
+
+ return base64_encode($hashValue);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
new file mode 100644
index 0000000..66800f1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
@@ -0,0 +1,668 @@
+ chr(0),
+ "\x1B 1" => chr(1),
+ "\x1B 2" => chr(2),
+ "\x1B 3" => chr(3),
+ "\x1B 4" => chr(4),
+ "\x1B 5" => chr(5),
+ "\x1B 6" => chr(6),
+ "\x1B 7" => chr(7),
+ "\x1B 8" => chr(8),
+ "\x1B 9" => chr(9),
+ "\x1B :" => chr(10),
+ "\x1B ;" => chr(11),
+ "\x1B <" => chr(12),
+ "\x1B =" => chr(13),
+ "\x1B >" => chr(14),
+ "\x1B ?" => chr(15),
+ "\x1B!0" => chr(16),
+ "\x1B!1" => chr(17),
+ "\x1B!2" => chr(18),
+ "\x1B!3" => chr(19),
+ "\x1B!4" => chr(20),
+ "\x1B!5" => chr(21),
+ "\x1B!6" => chr(22),
+ "\x1B!7" => chr(23),
+ "\x1B!8" => chr(24),
+ "\x1B!9" => chr(25),
+ "\x1B!:" => chr(26),
+ "\x1B!;" => chr(27),
+ "\x1B!<" => chr(28),
+ "\x1B!=" => chr(29),
+ "\x1B!>" => chr(30),
+ "\x1B!?" => chr(31),
+ "\x1B'?" => chr(127),
+ "\x1B(0" => '€', // 128 in CP1252
+ "\x1B(2" => '‚', // 130 in CP1252
+ "\x1B(3" => 'ƒ', // 131 in CP1252
+ "\x1B(4" => '„', // 132 in CP1252
+ "\x1B(5" => '…', // 133 in CP1252
+ "\x1B(6" => '†', // 134 in CP1252
+ "\x1B(7" => '‡', // 135 in CP1252
+ "\x1B(8" => 'ˆ', // 136 in CP1252
+ "\x1B(9" => '‰', // 137 in CP1252
+ "\x1B(:" => 'Š', // 138 in CP1252
+ "\x1B(;" => '‹', // 139 in CP1252
+ "\x1BNj" => 'Œ', // 140 in CP1252
+ "\x1B(>" => 'Ž', // 142 in CP1252
+ "\x1B)1" => '‘', // 145 in CP1252
+ "\x1B)2" => '’', // 146 in CP1252
+ "\x1B)3" => '“', // 147 in CP1252
+ "\x1B)4" => '”', // 148 in CP1252
+ "\x1B)5" => '•', // 149 in CP1252
+ "\x1B)6" => '–', // 150 in CP1252
+ "\x1B)7" => '—', // 151 in CP1252
+ "\x1B)8" => '˜', // 152 in CP1252
+ "\x1B)9" => '™', // 153 in CP1252
+ "\x1B):" => 'š', // 154 in CP1252
+ "\x1B);" => '›', // 155 in CP1252
+ "\x1BNz" => 'œ', // 156 in CP1252
+ "\x1B)>" => 'ž', // 158 in CP1252
+ "\x1B)?" => 'Ÿ', // 159 in CP1252
+ "\x1B*0" => ' ', // 160 in CP1252
+ "\x1BN!" => '¡', // 161 in CP1252
+ "\x1BN\"" => '¢', // 162 in CP1252
+ "\x1BN#" => '£', // 163 in CP1252
+ "\x1BN(" => '¤', // 164 in CP1252
+ "\x1BN%" => '¥', // 165 in CP1252
+ "\x1B*6" => '¦', // 166 in CP1252
+ "\x1BN'" => '§', // 167 in CP1252
+ "\x1BNH " => '¨', // 168 in CP1252
+ "\x1BNS" => '©', // 169 in CP1252
+ "\x1BNc" => 'ª', // 170 in CP1252
+ "\x1BN+" => '«', // 171 in CP1252
+ "\x1B*<" => '¬', // 172 in CP1252
+ "\x1B*=" => '', // 173 in CP1252
+ "\x1BNR" => '®', // 174 in CP1252
+ "\x1B*?" => '¯', // 175 in CP1252
+ "\x1BN0" => '°', // 176 in CP1252
+ "\x1BN1" => '±', // 177 in CP1252
+ "\x1BN2" => '²', // 178 in CP1252
+ "\x1BN3" => '³', // 179 in CP1252
+ "\x1BNB " => '´', // 180 in CP1252
+ "\x1BN5" => 'µ', // 181 in CP1252
+ "\x1BN6" => '¶', // 182 in CP1252
+ "\x1BN7" => '·', // 183 in CP1252
+ "\x1B+8" => '¸', // 184 in CP1252
+ "\x1BNQ" => '¹', // 185 in CP1252
+ "\x1BNk" => 'º', // 186 in CP1252
+ "\x1BN;" => '»', // 187 in CP1252
+ "\x1BN<" => '¼', // 188 in CP1252
+ "\x1BN=" => '½', // 189 in CP1252
+ "\x1BN>" => '¾', // 190 in CP1252
+ "\x1BN?" => '¿', // 191 in CP1252
+ "\x1BNAA" => 'À', // 192 in CP1252
+ "\x1BNBA" => 'Á', // 193 in CP1252
+ "\x1BNCA" => 'Â', // 194 in CP1252
+ "\x1BNDA" => 'Ã', // 195 in CP1252
+ "\x1BNHA" => 'Ä', // 196 in CP1252
+ "\x1BNJA" => 'Å', // 197 in CP1252
+ "\x1BNa" => 'Æ', // 198 in CP1252
+ "\x1BNKC" => 'Ç', // 199 in CP1252
+ "\x1BNAE" => 'È', // 200 in CP1252
+ "\x1BNBE" => 'É', // 201 in CP1252
+ "\x1BNCE" => 'Ê', // 202 in CP1252
+ "\x1BNHE" => 'Ë', // 203 in CP1252
+ "\x1BNAI" => 'Ì', // 204 in CP1252
+ "\x1BNBI" => 'Í', // 205 in CP1252
+ "\x1BNCI" => 'Î', // 206 in CP1252
+ "\x1BNHI" => 'Ï', // 207 in CP1252
+ "\x1BNb" => 'Ð', // 208 in CP1252
+ "\x1BNDN" => 'Ñ', // 209 in CP1252
+ "\x1BNAO" => 'Ò', // 210 in CP1252
+ "\x1BNBO" => 'Ó', // 211 in CP1252
+ "\x1BNCO" => 'Ô', // 212 in CP1252
+ "\x1BNDO" => 'Õ', // 213 in CP1252
+ "\x1BNHO" => 'Ö', // 214 in CP1252
+ "\x1B-7" => '×', // 215 in CP1252
+ "\x1BNi" => 'Ø', // 216 in CP1252
+ "\x1BNAU" => 'Ù', // 217 in CP1252
+ "\x1BNBU" => 'Ú', // 218 in CP1252
+ "\x1BNCU" => 'Û', // 219 in CP1252
+ "\x1BNHU" => 'Ü', // 220 in CP1252
+ "\x1B-=" => 'Ý', // 221 in CP1252
+ "\x1BNl" => 'Þ', // 222 in CP1252
+ "\x1BN{" => 'ß', // 223 in CP1252
+ "\x1BNAa" => 'à', // 224 in CP1252
+ "\x1BNBa" => 'á', // 225 in CP1252
+ "\x1BNCa" => 'â', // 226 in CP1252
+ "\x1BNDa" => 'ã', // 227 in CP1252
+ "\x1BNHa" => 'ä', // 228 in CP1252
+ "\x1BNJa" => 'å', // 229 in CP1252
+ "\x1BNq" => 'æ', // 230 in CP1252
+ "\x1BNKc" => 'ç', // 231 in CP1252
+ "\x1BNAe" => 'è', // 232 in CP1252
+ "\x1BNBe" => 'é', // 233 in CP1252
+ "\x1BNCe" => 'ê', // 234 in CP1252
+ "\x1BNHe" => 'ë', // 235 in CP1252
+ "\x1BNAi" => 'ì', // 236 in CP1252
+ "\x1BNBi" => 'í', // 237 in CP1252
+ "\x1BNCi" => 'î', // 238 in CP1252
+ "\x1BNHi" => 'ï', // 239 in CP1252
+ "\x1BNs" => 'ð', // 240 in CP1252
+ "\x1BNDn" => 'ñ', // 241 in CP1252
+ "\x1BNAo" => 'ò', // 242 in CP1252
+ "\x1BNBo" => 'ó', // 243 in CP1252
+ "\x1BNCo" => 'ô', // 244 in CP1252
+ "\x1BNDo" => 'õ', // 245 in CP1252
+ "\x1BNHo" => 'ö', // 246 in CP1252
+ "\x1B/7" => '÷', // 247 in CP1252
+ "\x1BNy" => 'ø', // 248 in CP1252
+ "\x1BNAu" => 'ù', // 249 in CP1252
+ "\x1BNBu" => 'ú', // 250 in CP1252
+ "\x1BNCu" => 'û', // 251 in CP1252
+ "\x1BNHu" => 'ü', // 252 in CP1252
+ "\x1B/=" => 'ý', // 253 in CP1252
+ "\x1BN|" => 'þ', // 254 in CP1252
+ "\x1BNHy" => 'ÿ', // 255 in CP1252
+ ];
+ }
+
+ /**
+ * Get whether iconv extension is available.
+ *
+ * @return bool
+ */
+ public static function getIsIconvEnabled()
+ {
+ if (isset(self::$isIconvEnabled)) {
+ return self::$isIconvEnabled;
+ }
+
+ // Assume no problems with iconv
+ self::$isIconvEnabled = true;
+
+ // Fail if iconv doesn't exist
+ if (!function_exists('iconv')) {
+ self::$isIconvEnabled = false;
+ } elseif (!@iconv('UTF-8', 'UTF-16LE', 'x')) {
+ // Sometimes iconv is not working, and e.g. iconv('UTF-8', 'UTF-16LE', 'x') just returns false,
+ self::$isIconvEnabled = false;
+ } elseif (defined('PHP_OS') && @stristr(PHP_OS, 'AIX') && defined('ICONV_IMPL') && (@strcasecmp(ICONV_IMPL, 'unknown') == 0) && defined('ICONV_VERSION') && (@strcasecmp(ICONV_VERSION, 'unknown') == 0)) {
+ // CUSTOM: IBM AIX iconv() does not work
+ self::$isIconvEnabled = false;
+ }
+
+ // Deactivate iconv default options if they fail (as seen on IMB i)
+ if (self::$isIconvEnabled && !@iconv('UTF-8', 'UTF-16LE' . self::$iconvOptions, 'x')) {
+ self::$iconvOptions = '';
+ }
+
+ return self::$isIconvEnabled;
+ }
+
+ private static function buildCharacterSets(): void
+ {
+ if (empty(self::$controlCharacters)) {
+ self::buildControlCharacters();
+ }
+
+ if (empty(self::$SYLKCharacters)) {
+ self::buildSYLKCharacters();
+ }
+ }
+
+ /**
+ * Convert from OpenXML escaped control character to PHP control character.
+ *
+ * Excel 2007 team:
+ * ----------------
+ * That's correct, control characters are stored directly in the shared-strings table.
+ * We do encode characters that cannot be represented in XML using the following escape sequence:
+ * _xHHHH_ where H represents a hexadecimal character in the character's value...
+ * So you could end up with something like _x0008_ in a string (either in a cell value ()
+ * element or in the shared string element.
+ *
+ * @param string $textValue Value to unescape
+ */
+ public static function controlCharacterOOXML2PHP($textValue): string
+ {
+ self::buildCharacterSets();
+
+ return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $textValue);
+ }
+
+ /**
+ * Convert from PHP control character to OpenXML escaped control character.
+ *
+ * Excel 2007 team:
+ * ----------------
+ * That's correct, control characters are stored directly in the shared-strings table.
+ * We do encode characters that cannot be represented in XML using the following escape sequence:
+ * _xHHHH_ where H represents a hexadecimal character in the character's value...
+ * So you could end up with something like _x0008_ in a string (either in a cell value ()
+ * element or in the shared string element.
+ *
+ * @param string $textValue Value to escape
+ */
+ public static function controlCharacterPHP2OOXML($textValue): string
+ {
+ self::buildCharacterSets();
+
+ return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $textValue);
+ }
+
+ /**
+ * Try to sanitize UTF8, replacing invalid sequences with Unicode substitution characters.
+ */
+ public static function sanitizeUTF8(string $textValue): string
+ {
+ $textValue = str_replace(["\xef\xbf\xbe", "\xef\xbf\xbf"], "\xef\xbf\xbd", $textValue);
+ $subst = mb_substitute_character(); // default is question mark
+ mb_substitute_character(65533); // Unicode substitution character
+ // Phpstan does not think this can return false.
+ $returnValue = mb_convert_encoding($textValue, 'UTF-8', 'UTF-8');
+ mb_substitute_character($subst);
+
+ return $returnValue;
+ }
+
+ /**
+ * Check if a string contains UTF8 data.
+ */
+ public static function isUTF8(string $textValue): bool
+ {
+ return $textValue === self::sanitizeUTF8($textValue);
+ }
+
+ /**
+ * Formats a numeric value as a string for output in various output writers forcing
+ * point as decimal separator in case locale is other than English.
+ *
+ * @param float|int|string $numericValue
+ */
+ public static function formatNumber($numericValue): string
+ {
+ if (is_float($numericValue)) {
+ return str_replace(',', '.', (string) $numericValue);
+ }
+
+ return (string) $numericValue;
+ }
+
+ /**
+ * Converts a UTF-8 string into BIFF8 Unicode string data (8-bit string length)
+ * Writes the string using uncompressed notation, no rich text, no Asian phonetics
+ * If mbstring extension is not available, ASCII is assumed, and compressed notation is used
+ * although this will give wrong results for non-ASCII strings
+ * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
+ *
+ * @param string $textValue UTF-8 encoded string
+ * @param mixed[] $arrcRuns Details of rich text runs in $value
+ */
+ public static function UTF8toBIFF8UnicodeShort(string $textValue, array $arrcRuns = []): string
+ {
+ // character count
+ $ln = self::countCharacters($textValue, 'UTF-8');
+ // option flags
+ if (empty($arrcRuns)) {
+ $data = pack('CC', $ln, 0x0001);
+ // characters
+ $data .= self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
+ } else {
+ $data = pack('vC', $ln, 0x09);
+ $data .= pack('v', count($arrcRuns));
+ // characters
+ $data .= self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
+ foreach ($arrcRuns as $cRun) {
+ $data .= pack('v', $cRun['strlen']);
+ $data .= pack('v', $cRun['fontidx']);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Converts a UTF-8 string into BIFF8 Unicode string data (16-bit string length)
+ * Writes the string using uncompressed notation, no rich text, no Asian phonetics
+ * If mbstring extension is not available, ASCII is assumed, and compressed notation is used
+ * although this will give wrong results for non-ASCII strings
+ * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
+ *
+ * @param string $textValue UTF-8 encoded string
+ */
+ public static function UTF8toBIFF8UnicodeLong(string $textValue): string
+ {
+ // characters
+ $chars = self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
+ $ln = (int) (strlen($chars) / 2); // N.B. - strlen, not mb_strlen issue #642
+
+ return pack('vC', $ln, 0x0001) . $chars;
+ }
+
+ /**
+ * Convert string from one encoding to another.
+ *
+ * @param string $to Encoding to convert to, e.g. 'UTF-8'
+ * @param string $from Encoding to convert from, e.g. 'UTF-16LE'
+ */
+ public static function convertEncoding(string $textValue, string $to, string $from): string
+ {
+ if (self::getIsIconvEnabled()) {
+ $result = iconv($from, $to . self::$iconvOptions, $textValue);
+ if (false !== $result) {
+ return $result;
+ }
+ }
+
+ return mb_convert_encoding($textValue, $to, $from);
+ }
+
+ /**
+ * Get character count.
+ *
+ * @param string $encoding Encoding
+ *
+ * @return int Character count
+ */
+ public static function countCharacters(string $textValue, string $encoding = 'UTF-8'): int
+ {
+ return mb_strlen($textValue, $encoding);
+ }
+
+ /**
+ * Get character count using mb_strwidth rather than mb_strlen.
+ *
+ * @param string $encoding Encoding
+ *
+ * @return int Character count
+ */
+ public static function countCharactersDbcs(string $textValue, string $encoding = 'UTF-8'): int
+ {
+ return mb_strwidth($textValue, $encoding);
+ }
+
+ /**
+ * Get a substring of a UTF-8 encoded string.
+ *
+ * @param string $textValue UTF-8 encoded string
+ * @param int $offset Start offset
+ * @param ?int $length Maximum number of characters in substring
+ */
+ public static function substring(string $textValue, int $offset, ?int $length = 0): string
+ {
+ return mb_substr($textValue, $offset, $length, 'UTF-8');
+ }
+
+ /**
+ * Convert a UTF-8 encoded string to upper case.
+ *
+ * @param string $textValue UTF-8 encoded string
+ */
+ public static function strToUpper(string $textValue): string
+ {
+ return mb_convert_case($textValue, MB_CASE_UPPER, 'UTF-8');
+ }
+
+ /**
+ * Convert a UTF-8 encoded string to lower case.
+ *
+ * @param string $textValue UTF-8 encoded string
+ */
+ public static function strToLower(string $textValue): string
+ {
+ return mb_convert_case($textValue, MB_CASE_LOWER, 'UTF-8');
+ }
+
+ /**
+ * Convert a UTF-8 encoded string to title/proper case
+ * (uppercase every first character in each word, lower case all other characters).
+ *
+ * @param string $textValue UTF-8 encoded string
+ */
+ public static function strToTitle(string $textValue): string
+ {
+ return mb_convert_case($textValue, MB_CASE_TITLE, 'UTF-8');
+ }
+
+ public static function mbIsUpper(string $character): bool
+ {
+ return mb_strtolower($character, 'UTF-8') !== $character;
+ }
+
+ /**
+ * Splits a UTF-8 string into an array of individual characters.
+ */
+ public static function mbStrSplit(string $string): array
+ {
+ // Split at all position not after the start: ^
+ // and not before the end: $
+ $split = preg_split('/(? $v) {
+ $textValue = str_replace($k, $v, $textValue);
+ }
+
+ return $textValue;
+ }
+
+ /**
+ * Retrieve any leading numeric part of a string, or return the full string if no leading numeric
+ * (handles basic integer or float, but not exponent or non decimal).
+ *
+ * @param string $textValue
+ *
+ * @return mixed string or only the leading numeric part of the string
+ */
+ public static function testStringAsNumeric($textValue)
+ {
+ if (is_numeric($textValue)) {
+ return $textValue;
+ }
+ $v = (float) $textValue;
+
+ return (is_numeric(substr($textValue, 0, strlen((string) $v)))) ? $v : $textValue;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
new file mode 100644
index 0000000..324e342
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
@@ -0,0 +1,77 @@
+setTimeZone(new DateTimeZone($timezoneName));
+
+ return $dtobj->getOffset();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
new file mode 100644
index 0000000..b2eb033
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
@@ -0,0 +1,485 @@
+error;
+ }
+
+ /** @return string */
+ public function getBestFitType()
+ {
+ return $this->bestFitType;
+ }
+
+ /**
+ * Return the Y-Value for a specified value of X.
+ *
+ * @param float $xValue X-Value
+ *
+ * @return float Y-Value
+ */
+ abstract public function getValueOfYForX($xValue);
+
+ /**
+ * Return the X-Value for a specified value of Y.
+ *
+ * @param float $yValue Y-Value
+ *
+ * @return float X-Value
+ */
+ abstract public function getValueOfXForY($yValue);
+
+ /**
+ * Return the original set of X-Values.
+ *
+ * @return float[] X-Values
+ */
+ public function getXValues()
+ {
+ return $this->xValues;
+ }
+
+ /**
+ * Return the Equation of the best-fit line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ *
+ * @return string
+ */
+ abstract public function getEquation($dp = 0);
+
+ /**
+ * Return the Slope of the line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ *
+ * @return float
+ */
+ public function getSlope($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->slope, $dp);
+ }
+
+ return $this->slope;
+ }
+
+ /**
+ * Return the standard error of the Slope.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ *
+ * @return float
+ */
+ public function getSlopeSE($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->slopeSE, $dp);
+ }
+
+ return $this->slopeSE;
+ }
+
+ /**
+ * Return the Value of X where it intersects Y = 0.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ *
+ * @return float
+ */
+ public function getIntersect($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->intersect, $dp);
+ }
+
+ return $this->intersect;
+ }
+
+ /**
+ * Return the standard error of the Intersect.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ *
+ * @return float
+ */
+ public function getIntersectSE($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->intersectSE, $dp);
+ }
+
+ return $this->intersectSE;
+ }
+
+ /**
+ * Return the goodness of fit for this regression.
+ *
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getGoodnessOfFit($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->goodnessOfFit, $dp);
+ }
+
+ return $this->goodnessOfFit;
+ }
+
+ /**
+ * Return the goodness of fit for this regression.
+ *
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getGoodnessOfFitPercent($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->goodnessOfFit * 100, $dp);
+ }
+
+ return $this->goodnessOfFit * 100;
+ }
+
+ /**
+ * Return the standard deviation of the residuals for this regression.
+ *
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getStdevOfResiduals($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->stdevOfResiduals, $dp);
+ }
+
+ return $this->stdevOfResiduals;
+ }
+
+ /**
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getSSRegression($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->SSRegression, $dp);
+ }
+
+ return $this->SSRegression;
+ }
+
+ /**
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getSSResiduals($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->SSResiduals, $dp);
+ }
+
+ return $this->SSResiduals;
+ }
+
+ /**
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getDFResiduals($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->DFResiduals, $dp);
+ }
+
+ return $this->DFResiduals;
+ }
+
+ /**
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getF($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->f, $dp);
+ }
+
+ return $this->f;
+ }
+
+ /**
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getCovariance($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->covariance, $dp);
+ }
+
+ return $this->covariance;
+ }
+
+ /**
+ * @param int $dp Number of places of decimal precision to return
+ *
+ * @return float
+ */
+ public function getCorrelation($dp = 0)
+ {
+ if ($dp != 0) {
+ return round($this->correlation, $dp);
+ }
+
+ return $this->correlation;
+ }
+
+ /**
+ * @return float[]
+ */
+ public function getYBestFitValues()
+ {
+ return $this->yBestFitValues;
+ }
+
+ /**
+ * @param float $sumX
+ * @param float $sumY
+ * @param float $sumX2
+ * @param float $sumY2
+ * @param float $sumXY
+ * @param float $meanX
+ * @param float $meanY
+ * @param bool|int $const
+ */
+ protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void
+ {
+ $SSres = $SScov = $SStot = $SSsex = 0.0;
+ foreach ($this->xValues as $xKey => $xValue) {
+ $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
+
+ $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
+ if ($const === true) {
+ $SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
+ } else {
+ $SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
+ }
+ $SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
+ if ($const === true) {
+ $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
+ } else {
+ $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
+ }
+ }
+
+ $this->SSResiduals = $SSres;
+ $this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0);
+
+ if ($this->DFResiduals == 0.0) {
+ $this->stdevOfResiduals = 0.0;
+ } else {
+ $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals);
+ }
+
+ if ($SStot == 0.0 || $SSres == $SStot) {
+ $this->goodnessOfFit = 1;
+ } else {
+ $this->goodnessOfFit = 1 - ($SSres / $SStot);
+ }
+
+ $this->SSRegression = $this->goodnessOfFit * $SStot;
+ $this->covariance = $SScov / $this->valueCount;
+ $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2));
+ $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex);
+ $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2));
+ if ($this->SSResiduals != 0.0) {
+ if ($this->DFResiduals == 0.0) {
+ $this->f = 0.0;
+ } else {
+ $this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals);
+ }
+ } else {
+ if ($this->DFResiduals == 0.0) {
+ $this->f = 0.0;
+ } else {
+ $this->f = $this->SSRegression / $this->DFResiduals;
+ }
+ }
+ }
+
+ /** @return float|int */
+ private function sumSquares(array $values)
+ {
+ return array_sum(
+ array_map(
+ fn ($value): float|int => $value ** 2,
+ $values
+ )
+ );
+ }
+
+ /**
+ * @param float[] $yValues
+ * @param float[] $xValues
+ */
+ protected function leastSquareFit(array $yValues, array $xValues, bool $const): void
+ {
+ // calculate sums
+ $sumValuesX = array_sum($xValues);
+ $sumValuesY = array_sum($yValues);
+ $meanValueX = $sumValuesX / $this->valueCount;
+ $meanValueY = $sumValuesY / $this->valueCount;
+ $sumSquaresX = $this->sumSquares($xValues);
+ $sumSquaresY = $this->sumSquares($yValues);
+ $mBase = $mDivisor = 0.0;
+ $xy_sum = 0.0;
+ for ($i = 0; $i < $this->valueCount; ++$i) {
+ $xy_sum += $xValues[$i] * $yValues[$i];
+
+ if ($const === true) {
+ $mBase += ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY);
+ $mDivisor += ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX);
+ } else {
+ $mBase += $xValues[$i] * $yValues[$i];
+ $mDivisor += $xValues[$i] * $xValues[$i];
+ }
+ }
+
+ // calculate slope
+ $this->slope = $mBase / $mDivisor;
+
+ // calculate intersect
+ $this->intersect = ($const === true) ? $meanValueY - ($this->slope * $meanValueX) : 0.0;
+
+ $this->calculateGoodnessOfFit($sumValuesX, $sumValuesY, $sumSquaresX, $sumSquaresY, $xy_sum, $meanValueX, $meanValueY, $const);
+ }
+
+ /**
+ * Define the regression.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ public function __construct($yValues, $xValues = [])
+ {
+ // Calculate number of points
+ $yValueCount = count($yValues);
+ $xValueCount = count($xValues);
+
+ // Define X Values if necessary
+ if ($xValueCount === 0) {
+ $xValues = range(1, $yValueCount);
+ } elseif ($yValueCount !== $xValueCount) {
+ // Ensure both arrays of points are the same size
+ $this->error = true;
+ }
+
+ $this->valueCount = $yValueCount;
+ $this->xValues = $xValues;
+ $this->yValues = $yValues;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
new file mode 100644
index 0000000..67b96f5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
@@ -0,0 +1,111 @@
+getIntersect() * $this->getSlope() ** ($xValue - $this->xOffset);
+ }
+
+ /**
+ * Return the X-Value for a specified value of Y.
+ *
+ * @param float $yValue Y-Value
+ *
+ * @return float X-Value
+ */
+ public function getValueOfXForY($yValue): float
+ {
+ return log(($yValue + $this->yOffset) / $this->getIntersect()) / log($this->getSlope());
+ }
+
+ /**
+ * Return the Equation of the best-fit line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getEquation($dp = 0): string
+ {
+ $slope = $this->getSlope($dp);
+ $intersect = $this->getIntersect($dp);
+
+ return 'Y = ' . $intersect . ' * ' . $slope . '^X';
+ }
+
+ /**
+ * Return the Slope of the line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getSlope($dp = 0): float
+ {
+ if ($dp != 0) {
+ return round(exp($this->slope), $dp);
+ }
+
+ return exp($this->slope);
+ }
+
+ /**
+ * Return the Value of X where it intersects Y = 0.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getIntersect($dp = 0): float
+ {
+ if ($dp != 0) {
+ return round(exp($this->intersect), $dp);
+ }
+
+ return exp($this->intersect);
+ }
+
+ /**
+ * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ private function exponentialRegression(array $yValues, array $xValues, bool $const): void
+ {
+ $adjustedYValues = array_map(
+ fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
+ $yValues
+ );
+
+ $this->leastSquareFit($adjustedYValues, $xValues, $const);
+ }
+
+ /**
+ * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ * @param bool $const
+ */
+ public function __construct($yValues, $xValues = [], $const = true)
+ {
+ parent::__construct($yValues, $xValues);
+
+ if (!$this->error) {
+ $this->exponentialRegression($yValues, $xValues, (bool) $const);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
new file mode 100644
index 0000000..ef03b84
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
@@ -0,0 +1,78 @@
+getIntersect() + $this->getSlope() * $xValue;
+ }
+
+ /**
+ * Return the X-Value for a specified value of Y.
+ *
+ * @param float $yValue Y-Value
+ *
+ * @return float X-Value
+ */
+ public function getValueOfXForY($yValue): int|float
+ {
+ return ($yValue - $this->getIntersect()) / $this->getSlope();
+ }
+
+ /**
+ * Return the Equation of the best-fit line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getEquation($dp = 0): string
+ {
+ $slope = $this->getSlope($dp);
+ $intersect = $this->getIntersect($dp);
+
+ return 'Y = ' . $intersect . ' + ' . $slope . ' * X';
+ }
+
+ /**
+ * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ private function linearRegression(array $yValues, array $xValues, bool $const): void
+ {
+ $this->leastSquareFit($yValues, $xValues, $const);
+ }
+
+ /**
+ * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ * @param bool $const
+ */
+ public function __construct($yValues, $xValues = [], $const = true)
+ {
+ parent::__construct($yValues, $xValues);
+
+ if (!$this->error) {
+ $this->linearRegression($yValues, $xValues, (bool) $const);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
new file mode 100644
index 0000000..e6981d5
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
@@ -0,0 +1,83 @@
+getIntersect() + $this->getSlope() * log($xValue - $this->xOffset);
+ }
+
+ /**
+ * Return the X-Value for a specified value of Y.
+ *
+ * @param float $yValue Y-Value
+ *
+ * @return float X-Value
+ */
+ public function getValueOfXForY($yValue): float
+ {
+ return exp(($yValue - $this->getIntersect()) / $this->getSlope());
+ }
+
+ /**
+ * Return the Equation of the best-fit line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getEquation($dp = 0): string
+ {
+ $slope = $this->getSlope($dp);
+ $intersect = $this->getIntersect($dp);
+
+ return 'Y = ' . $slope . ' * log(' . $intersect . ' * X)';
+ }
+
+ /**
+ * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ private function logarithmicRegression(array $yValues, array $xValues, bool $const): void
+ {
+ $adjustedYValues = array_map(
+ fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
+ $yValues
+ );
+
+ $this->leastSquareFit($adjustedYValues, $xValues, $const);
+ }
+
+ /**
+ * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ * @param bool $const
+ */
+ public function __construct($yValues, $xValues = [], $const = true)
+ {
+ parent::__construct($yValues, $xValues);
+
+ if (!$this->error) {
+ $this->logarithmicRegression($yValues, $xValues, (bool) $const);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
new file mode 100644
index 0000000..4089afc
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
@@ -0,0 +1,214 @@
+slope is specified where an array is expected in several places.
+// But it seems that it should always be float.
+// This code is probably not exercised at all in unit tests.
+class PolynomialBestFit extends BestFit
+{
+ /**
+ * Algorithm type to use for best-fit
+ * (Name of this Trend class).
+ *
+ * @var string
+ */
+ protected $bestFitType = 'polynomial';
+
+ /**
+ * Polynomial order.
+ *
+ * @var int
+ */
+ protected $order = 0;
+
+ /**
+ * Return the order of this polynomial.
+ *
+ * @return int
+ */
+ public function getOrder()
+ {
+ return $this->order;
+ }
+
+ /**
+ * Return the Y-Value for a specified value of X.
+ *
+ * @param float $xValue X-Value
+ *
+ * @return float Y-Value
+ */
+ public function getValueOfYForX($xValue)
+ {
+ $retVal = $this->getIntersect();
+ $slope = $this->getSlope();
+ // Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
+ // @phpstan-ignore-next-line
+ foreach ($slope as $key => $value) {
+ if ($value != 0.0) {
+ $retVal += $value * $xValue ** ($key + 1);
+ }
+ }
+
+ return $retVal;
+ }
+
+ /**
+ * Return the X-Value for a specified value of Y.
+ *
+ * @param float $yValue Y-Value
+ *
+ * @return float X-Value
+ */
+ public function getValueOfXForY($yValue): int|float
+ {
+ return ($yValue - $this->getIntersect()) / $this->getSlope();
+ }
+
+ /**
+ * Return the Equation of the best-fit line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getEquation($dp = 0): string
+ {
+ $slope = $this->getSlope($dp);
+ $intersect = $this->getIntersect($dp);
+
+ $equation = 'Y = ' . $intersect;
+ // Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
+ // @phpstan-ignore-next-line
+ foreach ($slope as $key => $value) {
+ if ($value != 0.0) {
+ $equation .= ' + ' . $value . ' * X';
+ if ($key > 0) {
+ $equation .= '^' . ($key + 1);
+ }
+ }
+ }
+
+ return $equation;
+ }
+
+ /**
+ * Return the Slope of the line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ *
+ * @return float
+ */
+ public function getSlope($dp = 0)
+ {
+ if ($dp != 0) {
+ $coefficients = [];
+ //* @phpstan-ignore-next-line
+ foreach ($this->slope as $coefficient) {
+ $coefficients[] = round($coefficient, $dp);
+ }
+
+ // @phpstan-ignore-next-line
+ return $coefficients;
+ }
+
+ return $this->slope;
+ }
+
+ /**
+ * @param int $dp
+ */
+ public function getCoefficients($dp = 0): array
+ {
+ // Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
+ // @phpstan-ignore-next-line
+ return array_merge([$this->getIntersect($dp)], $this->getSlope($dp));
+ }
+
+ /**
+ * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param int $order Order of Polynomial for this regression
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ private function polynomialRegression($order, array $yValues, array $xValues): void
+ {
+ // calculate sums
+ $x_sum = array_sum($xValues);
+ $y_sum = array_sum($yValues);
+ $xx_sum = $xy_sum = $yy_sum = 0;
+ for ($i = 0; $i < $this->valueCount; ++$i) {
+ $xy_sum += $xValues[$i] * $yValues[$i];
+ $xx_sum += $xValues[$i] * $xValues[$i];
+ $yy_sum += $yValues[$i] * $yValues[$i];
+ }
+ /*
+ * This routine uses logic from the PHP port of polyfit version 0.1
+ * written by Michael Bommarito and Paul Meagher
+ *
+ * The function fits a polynomial function of order $order through
+ * a series of x-y data points using least squares.
+ *
+ */
+ $A = [];
+ $B = [];
+ for ($i = 0; $i < $this->valueCount; ++$i) {
+ for ($j = 0; $j <= $order; ++$j) {
+ $A[$i][$j] = $xValues[$i] ** $j;
+ }
+ }
+ for ($i = 0; $i < $this->valueCount; ++$i) {
+ $B[$i] = [$yValues[$i]];
+ }
+ $matrixA = new Matrix($A);
+ $matrixB = new Matrix($B);
+ $C = $matrixA->solve($matrixB);
+
+ $coefficients = [];
+ for ($i = 0; $i < $C->rows; ++$i) {
+ $r = $C->getValue($i + 1, 1); // row and column are origin-1
+ if (abs($r) <= 10 ** (-9)) {
+ $r = 0;
+ }
+ $coefficients[] = $r;
+ }
+
+ $this->intersect = array_shift($coefficients);
+ // Phpstan is correct
+ //* @phpstan-ignore-next-line
+ $this->slope = $coefficients;
+
+ $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0);
+ foreach ($this->xValues as $xKey => $xValue) {
+ $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
+ }
+ }
+
+ /**
+ * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param int $order Order of Polynomial for this regression
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ public function __construct($order, $yValues, $xValues = [])
+ {
+ parent::__construct($yValues, $xValues);
+
+ if (!$this->error) {
+ if ($order < $this->valueCount) {
+ $this->bestFitType .= '_' . $order;
+ $this->order = $order;
+ $this->polynomialRegression($order, $yValues, $xValues);
+ if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) {
+ $this->error = true;
+ }
+ } else {
+ $this->error = true;
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
new file mode 100644
index 0000000..bb3bf4f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
@@ -0,0 +1,101 @@
+getIntersect() * ($xValue - $this->xOffset) ** $this->getSlope();
+ }
+
+ /**
+ * Return the X-Value for a specified value of Y.
+ *
+ * @param float $yValue Y-Value
+ *
+ * @return float X-Value
+ */
+ public function getValueOfXForY($yValue): float
+ {
+ return (($yValue + $this->yOffset) / $this->getIntersect()) ** (1 / $this->getSlope());
+ }
+
+ /**
+ * Return the Equation of the best-fit line.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getEquation($dp = 0): string
+ {
+ $slope = $this->getSlope($dp);
+ $intersect = $this->getIntersect($dp);
+
+ return 'Y = ' . $intersect . ' * X^' . $slope;
+ }
+
+ /**
+ * Return the Value of X where it intersects Y = 0.
+ *
+ * @param int $dp Number of places of decimal precision to display
+ */
+ public function getIntersect($dp = 0): float
+ {
+ if ($dp != 0) {
+ return round(exp($this->intersect), $dp);
+ }
+
+ return exp($this->intersect);
+ }
+
+ /**
+ * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ */
+ private function powerRegression(array $yValues, array $xValues, bool $const): void
+ {
+ $adjustedYValues = array_map(
+ fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
+ $yValues
+ );
+ $adjustedXValues = array_map(
+ fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
+ $xValues
+ );
+
+ $this->leastSquareFit($adjustedYValues, $adjustedXValues, $const);
+ }
+
+ /**
+ * Define the regression and calculate the goodness of fit for a set of X and Y data values.
+ *
+ * @param float[] $yValues The set of Y-values for this regression
+ * @param float[] $xValues The set of X-values for this regression
+ * @param bool $const
+ */
+ public function __construct($yValues, $xValues = [], $const = true)
+ {
+ parent::__construct($yValues, $xValues);
+
+ if (!$this->error) {
+ $this->powerRegression($yValues, $xValues, (bool) $const);
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
new file mode 100644
index 0000000..62399ca
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
@@ -0,0 +1,129 @@
+getGoodnessOfFit();
+ }
+ if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
+ foreach (self::$trendTypePolynomialOrders as $trendMethod) {
+ $order = (int) substr($trendMethod, -1);
+ $bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues);
+ if ($bestFit[$trendMethod]->getError()) {
+ unset($bestFit[$trendMethod]);
+ } else {
+ $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
+ }
+ }
+ }
+ // Determine which of our Trend lines is the best fit, and then we return the instance of that Trend class
+ arsort($bestFitValue);
+ $bestFitType = key($bestFitValue);
+
+ return $bestFit[$bestFitType];
+ default:
+ return false;
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
new file mode 100644
index 0000000..28a567f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
@@ -0,0 +1,99 @@
+openMemory();
+ } else {
+ // Create temporary filename
+ if ($temporaryStorageFolder === null) {
+ $temporaryStorageFolder = File::sysGetTempDir();
+ }
+ $this->tempFileName = (string) @tempnam($temporaryStorageFolder, 'xml');
+
+ // Open storage
+ if (empty($this->tempFileName) || $this->openUri($this->tempFileName) === false) {
+ // Fallback to memory...
+ $this->openMemory();
+ }
+ }
+
+ // Set default values
+ if (self::$debugEnabled) {
+ $this->setIndent(true);
+ }
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ // Unlink temporary files
+ // There is nothing reasonable to do if unlink fails.
+ if ($this->tempFileName != '') {
+ @unlink($this->tempFileName);
+ }
+ }
+
+ public function __wakeup(): void
+ {
+ $this->tempFileName = '';
+
+ throw new SpreadsheetException('Unserialize not permitted');
+ }
+
+ /**
+ * Get written data.
+ *
+ * @return string
+ */
+ public function getData()
+ {
+ if ($this->tempFileName == '') {
+ return $this->outputMemory(true);
+ }
+ $this->flush();
+
+ return file_get_contents($this->tempFileName) ?: '';
+ }
+
+ /**
+ * Wrapper method for writeRaw.
+ *
+ * @param null|string|string[] $rawTextData
+ */
+ public function writeRawData($rawTextData): bool
+ {
+ if (is_array($rawTextData)) {
+ $rawTextData = implode("\n", $rawTextData);
+ }
+
+ return $this->writeRaw(htmlspecialchars($rawTextData ?? ''));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
new file mode 100644
index 0000000..8c16e3c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
@@ -0,0 +1,275 @@
+getParentOrThrow()->getDefaultStyle()->getFont();
+
+ $columnDimensions = $worksheet->getColumnDimensions();
+
+ // first find the true column width in pixels (uncollapsed and unhidden)
+ if (isset($columnDimensions[$col]) && $columnDimensions[$col]->getWidth() != -1) {
+ // then we have column dimension with explicit width
+ $columnDimension = $columnDimensions[$col];
+ $width = $columnDimension->getWidth();
+ $pixelWidth = Drawing::cellDimensionToPixels($width, $font);
+ } elseif ($worksheet->getDefaultColumnDimension()->getWidth() != -1) {
+ // then we have default column dimension with explicit width
+ $defaultColumnDimension = $worksheet->getDefaultColumnDimension();
+ $width = $defaultColumnDimension->getWidth();
+ $pixelWidth = Drawing::cellDimensionToPixels($width, $font);
+ } else {
+ // we don't even have any default column dimension. Width depends on default font
+ $pixelWidth = Font::getDefaultColumnWidthByFont($font, true);
+ }
+
+ // now find the effective column width in pixels
+ if (isset($columnDimensions[$col]) && !$columnDimensions[$col]->getVisible()) {
+ $effectivePixelWidth = 0;
+ } else {
+ $effectivePixelWidth = $pixelWidth;
+ }
+
+ return $effectivePixelWidth;
+ }
+
+ /**
+ * Convert the height of a cell from user's units to pixels. By interpolation
+ * the relationship is: y = 4/3x. If the height hasn't been set by the user we
+ * use the default value. If the row is hidden we use a value of zero.
+ *
+ * @param Worksheet $worksheet The sheet
+ * @param int $row The row index (1-based)
+ *
+ * @return int The width in pixels
+ */
+ public static function sizeRow(Worksheet $worksheet, $row = 1): int
+ {
+ // default font of the workbook
+ $font = $worksheet->getParentOrThrow()->getDefaultStyle()->getFont();
+
+ $rowDimensions = $worksheet->getRowDimensions();
+
+ // first find the true row height in pixels (uncollapsed and unhidden)
+ if (isset($rowDimensions[$row]) && $rowDimensions[$row]->getRowHeight() != -1) {
+ // then we have a row dimension
+ $rowDimension = $rowDimensions[$row];
+ $rowHeight = $rowDimension->getRowHeight();
+ $pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10
+ } elseif ($worksheet->getDefaultRowDimension()->getRowHeight() != -1) {
+ // then we have a default row dimension with explicit height
+ $defaultRowDimension = $worksheet->getDefaultRowDimension();
+ $pixelRowHeight = $defaultRowDimension->getRowHeight(Dimension::UOM_PIXELS);
+ } else {
+ // we don't even have any default row dimension. Height depends on default font
+ $pointRowHeight = Font::getDefaultRowHeightByFont($font);
+ $pixelRowHeight = Font::fontSizeToPixels((int) $pointRowHeight);
+ }
+
+ // now find the effective row height in pixels
+ if (isset($rowDimensions[$row]) && !$rowDimensions[$row]->getVisible()) {
+ $effectivePixelRowHeight = 0;
+ } else {
+ $effectivePixelRowHeight = $pixelRowHeight;
+ }
+
+ return (int) $effectivePixelRowHeight;
+ }
+
+ /**
+ * Get the horizontal distance in pixels between two anchors
+ * The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets.
+ *
+ * @param string $startColumn
+ * @param int $startOffsetX Offset within start cell measured in 1/1024 of the cell width
+ * @param string $endColumn
+ * @param int $endOffsetX Offset within end cell measured in 1/1024 of the cell width
+ *
+ * @return int Horizontal measured in pixels
+ */
+ public static function getDistanceX(Worksheet $worksheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0): int
+ {
+ $distanceX = 0;
+
+ // add the widths of the spanning columns
+ $startColumnIndex = Coordinate::columnIndexFromString($startColumn);
+ $endColumnIndex = Coordinate::columnIndexFromString($endColumn);
+ for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) {
+ $distanceX += self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($i));
+ }
+
+ // correct for offsetX in startcell
+ $distanceX -= (int) floor(self::sizeCol($worksheet, $startColumn) * $startOffsetX / 1024);
+
+ // correct for offsetX in endcell
+ $distanceX -= (int) floor(self::sizeCol($worksheet, $endColumn) * (1 - $endOffsetX / 1024));
+
+ return $distanceX;
+ }
+
+ /**
+ * Get the vertical distance in pixels between two anchors
+ * The distanceY is found as sum of all the spanning rows minus two offsets.
+ *
+ * @param int $startRow (1-based)
+ * @param int $startOffsetY Offset within start cell measured in 1/256 of the cell height
+ * @param int $endRow (1-based)
+ * @param int $endOffsetY Offset within end cell measured in 1/256 of the cell height
+ *
+ * @return int Vertical distance measured in pixels
+ */
+ public static function getDistanceY(Worksheet $worksheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0): int
+ {
+ $distanceY = 0;
+
+ // add the widths of the spanning rows
+ for ($row = $startRow; $row <= $endRow; ++$row) {
+ $distanceY += self::sizeRow($worksheet, $row);
+ }
+
+ // correct for offsetX in startcell
+ $distanceY -= (int) floor(self::sizeRow($worksheet, $startRow) * $startOffsetY / 256);
+
+ // correct for offsetX in endcell
+ $distanceY -= (int) floor(self::sizeRow($worksheet, $endRow) * (1 - $endOffsetY / 256));
+
+ return $distanceY;
+ }
+
+ /**
+ * Convert 1-cell anchor coordinates to 2-cell anchor coordinates
+ * This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications.
+ *
+ * Calculate the vertices that define the position of the image as required by
+ * the OBJ record.
+ *
+ * +------------+------------+
+ * | A | B |
+ * +-----+------------+------------+
+ * | |(x1,y1) | |
+ * | 1 |(A1)._______|______ |
+ * | | | | |
+ * | | | | |
+ * +-----+----| BITMAP |-----+
+ * | | | | |
+ * | 2 | |______________. |
+ * | | | (B2)|
+ * | | | (x2,y2)|
+ * +---- +------------+------------+
+ *
+ * Example of a bitmap that covers some of the area from cell A1 to cell B2.
+ *
+ * Based on the width and height of the bitmap we need to calculate 8 vars:
+ * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
+ * The width and height of the cells are also variable and have to be taken into
+ * account.
+ * The values of $col_start and $row_start are passed in from the calling
+ * function. The values of $col_end and $row_end are calculated by subtracting
+ * the width and height of the bitmap from the width and height of the
+ * underlying cells.
+ * The vertices are expressed as a percentage of the underlying cell width as
+ * follows (rhs values are in pixels):
+ *
+ * x1 = X / W *1024
+ * y1 = Y / H *256
+ * x2 = (X-1) / W *1024
+ * y2 = (Y-1) / H *256
+ *
+ * Where: X is distance from the left side of the underlying cell
+ * Y is distance from the top of the underlying cell
+ * W is the width of the cell
+ * H is the height of the cell
+ *
+ * @param string $coordinates E.g. 'A1'
+ * @param int $offsetX Horizontal offset in pixels
+ * @param int $offsetY Vertical offset in pixels
+ * @param int $width Width in pixels
+ * @param int $height Height in pixels
+ */
+ public static function oneAnchor2twoAnchor(Worksheet $worksheet, string $coordinates, $offsetX, $offsetY, $width, $height): ?array
+ {
+ [$col_start, $row] = Coordinate::indexesFromString($coordinates);
+ $row_start = $row - 1;
+
+ $x1 = $offsetX;
+ $y1 = $offsetY;
+
+ // Initialise end cell to the same as the start cell
+ $col_end = $col_start; // Col containing lower right corner of object
+ $row_end = $row_start; // Row containing bottom right corner of object
+
+ // Zero the specified offset if greater than the cell dimensions
+ if ($x1 >= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start))) {
+ $x1 = 0;
+ }
+ if ($y1 >= self::sizeRow($worksheet, $row_start + 1)) {
+ $y1 = 0;
+ }
+
+ $width = $width + $x1 - 1;
+ $height = $height + $y1 - 1;
+
+ // Subtract the underlying cell widths to find the end cell of the image
+ while ($width >= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end))) {
+ $width -= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end));
+ ++$col_end;
+ }
+
+ // Subtract the underlying cell heights to find the end cell of the image
+ while ($height >= self::sizeRow($worksheet, $row_end + 1)) {
+ $height -= self::sizeRow($worksheet, $row_end + 1);
+ ++$row_end;
+ }
+
+ // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
+ // with zero height or width.
+ if (self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start)) == 0) {
+ return null;
+ }
+ if (self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end)) == 0) {
+ return null;
+ }
+ if (self::sizeRow($worksheet, $row_start + 1) == 0) {
+ return null;
+ }
+ if (self::sizeRow($worksheet, $row_end + 1) == 0) {
+ return null;
+ }
+
+ // Convert the pixel values to the percentage value expected by Excel
+ $x1 = $x1 / self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start)) * 1024;
+ $y1 = $y1 / self::sizeRow($worksheet, $row_start + 1) * 256;
+ $x2 = ($width + 1) / self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
+ $y2 = ($height + 1) / self::sizeRow($worksheet, $row_end + 1) * 256; // Distance to bottom of object
+
+ $startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1);
+ $endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1);
+
+ return [
+ 'startCoordinates' => $startCoordinates,
+ 'startOffsetX' => $x1,
+ 'startOffsetY' => $y1,
+ 'endCoordinates' => $endCoordinates,
+ 'endOffsetX' => $x2,
+ 'endOffsetY' => $y2,
+ ];
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
new file mode 100644
index 0000000..bc1a103
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
@@ -0,0 +1,1654 @@
+theme;
+ }
+
+ /**
+ * The workbook has macros ?
+ *
+ * @return bool
+ */
+ public function hasMacros()
+ {
+ return $this->hasMacros;
+ }
+
+ /**
+ * Define if a workbook has macros.
+ *
+ * @param bool $hasMacros true|false
+ */
+ public function setHasMacros($hasMacros): void
+ {
+ $this->hasMacros = (bool) $hasMacros;
+ }
+
+ /**
+ * Set the macros code.
+ *
+ * @param string $macroCode string|null
+ */
+ public function setMacrosCode($macroCode): void
+ {
+ $this->macrosCode = $macroCode;
+ $this->setHasMacros($macroCode !== null);
+ }
+
+ /**
+ * Return the macros code.
+ *
+ * @return null|string
+ */
+ public function getMacrosCode()
+ {
+ return $this->macrosCode;
+ }
+
+ /**
+ * Set the macros certificate.
+ *
+ * @param null|string $certificate
+ */
+ public function setMacrosCertificate($certificate): void
+ {
+ $this->macrosCertificate = $certificate;
+ }
+
+ /**
+ * Is the project signed ?
+ *
+ * @return bool true|false
+ */
+ public function hasMacrosCertificate(): bool
+ {
+ return $this->macrosCertificate !== null;
+ }
+
+ /**
+ * Return the macros certificate.
+ *
+ * @return null|string
+ */
+ public function getMacrosCertificate()
+ {
+ return $this->macrosCertificate;
+ }
+
+ /**
+ * Remove all macros, certificate from spreadsheet.
+ */
+ public function discardMacros(): void
+ {
+ $this->hasMacros = false;
+ $this->macrosCode = null;
+ $this->macrosCertificate = null;
+ }
+
+ /**
+ * set ribbon XML data.
+ *
+ * @param null|mixed $target
+ * @param null|mixed $xmlData
+ */
+ public function setRibbonXMLData($target, $xmlData): void
+ {
+ if ($target !== null && $xmlData !== null) {
+ $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData];
+ } else {
+ $this->ribbonXMLData = null;
+ }
+ }
+
+ /**
+ * retrieve ribbon XML Data.
+ *
+ * @param string $what
+ *
+ * @return null|array|string
+ */
+ public function getRibbonXMLData($what = 'all') //we need some constants here...
+ {
+ $returnData = null;
+ $what = strtolower($what);
+ switch ($what) {
+ case 'all':
+ $returnData = $this->ribbonXMLData;
+
+ break;
+ case 'target':
+ case 'data':
+ if (is_array($this->ribbonXMLData)) {
+ $returnData = $this->ribbonXMLData[$what];
+ }
+
+ break;
+ }
+
+ return $returnData;
+ }
+
+ /**
+ * store binaries ribbon objects (pictures).
+ *
+ * @param null|mixed $BinObjectsNames
+ * @param null|mixed $BinObjectsData
+ */
+ public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void
+ {
+ if ($BinObjectsNames !== null && $BinObjectsData !== null) {
+ $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
+ } else {
+ $this->ribbonBinObjects = null;
+ }
+ }
+
+ /**
+ * List of unparsed loaded data for export to same format with better compatibility.
+ * It has to be minimized when the library start to support currently unparsed data.
+ *
+ * @internal
+ *
+ * @return array
+ */
+ public function getUnparsedLoadedData()
+ {
+ return $this->unparsedLoadedData;
+ }
+
+ /**
+ * List of unparsed loaded data for export to same format with better compatibility.
+ * It has to be minimized when the library start to support currently unparsed data.
+ *
+ * @internal
+ */
+ public function setUnparsedLoadedData(array $unparsedLoadedData): void
+ {
+ $this->unparsedLoadedData = $unparsedLoadedData;
+ }
+
+ /**
+ * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
+ */
+ private function getExtensionOnly(mixed $path): string
+ {
+ $extension = pathinfo($path, PATHINFO_EXTENSION);
+
+ return substr($extension, 0);
+ }
+
+ /**
+ * retrieve Binaries Ribbon Objects.
+ *
+ * @param string $what
+ *
+ * @return null|array
+ */
+ public function getRibbonBinObjects($what = 'all')
+ {
+ $ReturnData = null;
+ $what = strtolower($what);
+ switch ($what) {
+ case 'all':
+ return $this->ribbonBinObjects;
+ case 'names':
+ case 'data':
+ if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) {
+ $ReturnData = $this->ribbonBinObjects[$what];
+ }
+
+ break;
+ case 'types':
+ if (
+ is_array($this->ribbonBinObjects)
+ && isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])
+ ) {
+ $tmpTypes = array_keys($this->ribbonBinObjects['data']);
+ $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
+ } else {
+ $ReturnData = []; // the caller want an array... not null if empty
+ }
+
+ break;
+ }
+
+ return $ReturnData;
+ }
+
+ /**
+ * This workbook have a custom UI ?
+ */
+ public function hasRibbon(): bool
+ {
+ return $this->ribbonXMLData !== null;
+ }
+
+ /**
+ * This workbook have additionnal object for the ribbon ?
+ */
+ public function hasRibbonBinObjects(): bool
+ {
+ return $this->ribbonBinObjects !== null;
+ }
+
+ /**
+ * Check if a sheet with a specified code name already exists.
+ *
+ * @param string $codeName Name of the worksheet to check
+ */
+ public function sheetCodeNameExists($codeName): bool
+ {
+ return $this->getSheetByCodeName($codeName) !== null;
+ }
+
+ /**
+ * Get sheet by code name. Warning : sheet don't have always a code name !
+ *
+ * @param string $codeName Sheet name
+ *
+ * @return null|Worksheet
+ */
+ public function getSheetByCodeName($codeName)
+ {
+ $worksheetCount = count($this->workSheetCollection);
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ if ($this->workSheetCollection[$i]->getCodeName() == $codeName) {
+ return $this->workSheetCollection[$i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a new PhpSpreadsheet with one Worksheet.
+ */
+ public function __construct()
+ {
+ $this->uniqueID = uniqid('', true);
+ $this->calculationEngine = new Calculation($this);
+ $this->theme = new Theme();
+
+ // Initialise worksheet collection and add one worksheet
+ $this->workSheetCollection = [];
+ $this->workSheetCollection[] = new Worksheet($this);
+ $this->activeSheetIndex = 0;
+
+ // Create document properties
+ $this->properties = new Document\Properties();
+
+ // Create document security
+ $this->security = new Document\Security();
+
+ // Set defined names
+ $this->definedNames = [];
+
+ // Create the cellXf supervisor
+ $this->cellXfSupervisor = new Style(true);
+ $this->cellXfSupervisor->bindParent($this);
+
+ // Create the default style
+ $this->addCellXf(new Style());
+ $this->addCellStyleXf(new Style());
+ }
+
+ /**
+ * Code to execute when this worksheet is unset().
+ */
+ public function __destruct()
+ {
+ $this->disconnectWorksheets();
+ $this->calculationEngine = null;
+ $this->cellXfCollection = [];
+ $this->cellStyleXfCollection = [];
+ $this->definedNames = [];
+ }
+
+ /**
+ * Disconnect all worksheets from this PhpSpreadsheet workbook object,
+ * typically so that the PhpSpreadsheet object can be unset.
+ */
+ public function disconnectWorksheets(): void
+ {
+ foreach ($this->workSheetCollection as $worksheet) {
+ $worksheet->disconnectCells();
+ unset($worksheet);
+ }
+ $this->workSheetCollection = [];
+ }
+
+ /**
+ * Return the calculation engine for this worksheet.
+ *
+ * @return null|Calculation
+ */
+ public function getCalculationEngine()
+ {
+ return $this->calculationEngine;
+ }
+
+ /**
+ * Get properties.
+ *
+ * @return Document\Properties
+ */
+ public function getProperties()
+ {
+ return $this->properties;
+ }
+
+ /**
+ * Set properties.
+ */
+ public function setProperties(Document\Properties $documentProperties): void
+ {
+ $this->properties = $documentProperties;
+ }
+
+ /**
+ * Get security.
+ *
+ * @return Document\Security
+ */
+ public function getSecurity()
+ {
+ return $this->security;
+ }
+
+ /**
+ * Set security.
+ */
+ public function setSecurity(Document\Security $documentSecurity): void
+ {
+ $this->security = $documentSecurity;
+ }
+
+ /**
+ * Get active sheet.
+ *
+ * @return Worksheet
+ */
+ public function getActiveSheet()
+ {
+ return $this->getSheet($this->activeSheetIndex);
+ }
+
+ /**
+ * Create sheet and add it to this workbook.
+ *
+ * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
+ */
+ public function createSheet($sheetIndex = null): Worksheet
+ {
+ $newSheet = new Worksheet($this);
+ $this->addSheet($newSheet, $sheetIndex);
+
+ return $newSheet;
+ }
+
+ /**
+ * Check if a sheet with a specified name already exists.
+ *
+ * @param string $worksheetName Name of the worksheet to check
+ */
+ public function sheetNameExists($worksheetName): bool
+ {
+ return $this->getSheetByName($worksheetName) !== null;
+ }
+
+ /**
+ * Add sheet.
+ *
+ * @param Worksheet $worksheet The worksheet to add
+ * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
+ */
+ public function addSheet(Worksheet $worksheet, $sheetIndex = null): Worksheet
+ {
+ if ($this->sheetNameExists($worksheet->getTitle())) {
+ throw new Exception(
+ "Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename this worksheet first."
+ );
+ }
+
+ if ($sheetIndex === null) {
+ if ($this->activeSheetIndex < 0) {
+ $this->activeSheetIndex = 0;
+ }
+ $this->workSheetCollection[] = $worksheet;
+ } else {
+ // Insert the sheet at the requested index
+ array_splice(
+ $this->workSheetCollection,
+ $sheetIndex,
+ 0,
+ [$worksheet]
+ );
+
+ // Adjust active sheet index if necessary
+ if ($this->activeSheetIndex >= $sheetIndex) {
+ ++$this->activeSheetIndex;
+ }
+ }
+
+ if ($worksheet->getParent() === null) {
+ $worksheet->rebindParent($this);
+ }
+
+ return $worksheet;
+ }
+
+ /**
+ * Remove sheet by index.
+ *
+ * @param int $sheetIndex Index position of the worksheet to remove
+ */
+ public function removeSheetByIndex($sheetIndex): void
+ {
+ $numSheets = count($this->workSheetCollection);
+ if ($sheetIndex > $numSheets - 1) {
+ throw new Exception(
+ "You tried to remove a sheet by the out of bounds index: {$sheetIndex}. The actual number of sheets is {$numSheets}."
+ );
+ }
+ array_splice($this->workSheetCollection, $sheetIndex, 1);
+
+ // Adjust active sheet index if necessary
+ if (
+ ($this->activeSheetIndex >= $sheetIndex)
+ && ($this->activeSheetIndex > 0 || $numSheets <= 1)
+ ) {
+ --$this->activeSheetIndex;
+ }
+ }
+
+ /**
+ * Get sheet by index.
+ *
+ * @param int $sheetIndex Sheet index
+ *
+ * @return Worksheet
+ */
+ public function getSheet($sheetIndex)
+ {
+ if (!isset($this->workSheetCollection[$sheetIndex])) {
+ $numSheets = $this->getSheetCount();
+
+ throw new Exception(
+ "Your requested sheet index: {$sheetIndex} is out of bounds. The actual number of sheets is {$numSheets}."
+ );
+ }
+
+ return $this->workSheetCollection[$sheetIndex];
+ }
+
+ /**
+ * Get all sheets.
+ *
+ * @return Worksheet[]
+ */
+ public function getAllSheets()
+ {
+ return $this->workSheetCollection;
+ }
+
+ /**
+ * Get sheet by name.
+ *
+ * @param string $worksheetName Sheet name
+ *
+ * @return null|Worksheet
+ */
+ public function getSheetByName($worksheetName)
+ {
+ $worksheetCount = count($this->workSheetCollection);
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ if (strcasecmp($this->workSheetCollection[$i]->getTitle(), trim($worksheetName, "'")) === 0) {
+ return $this->workSheetCollection[$i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get sheet by name, throwing exception if not found.
+ */
+ public function getSheetByNameOrThrow(string $worksheetName): Worksheet
+ {
+ $worksheet = $this->getSheetByName($worksheetName);
+ if ($worksheet === null) {
+ throw new Exception("Sheet $worksheetName does not exist.");
+ }
+
+ return $worksheet;
+ }
+
+ /**
+ * Get index for sheet.
+ *
+ * @return int index
+ */
+ public function getIndex(Worksheet $worksheet)
+ {
+ foreach ($this->workSheetCollection as $key => $value) {
+ if ($value->getHashCode() === $worksheet->getHashCode()) {
+ return $key;
+ }
+ }
+
+ throw new Exception('Sheet does not exist.');
+ }
+
+ /**
+ * Set index for sheet by sheet name.
+ *
+ * @param string $worksheetName Sheet name to modify index for
+ * @param int $newIndexPosition New index for the sheet
+ *
+ * @return int New sheet index
+ */
+ public function setIndexByName(string $worksheetName, $newIndexPosition)
+ {
+ $oldIndex = $this->getIndex($this->getSheetByNameOrThrow($worksheetName));
+ $worksheet = array_splice(
+ $this->workSheetCollection,
+ $oldIndex,
+ 1
+ );
+ array_splice(
+ $this->workSheetCollection,
+ $newIndexPosition,
+ 0,
+ $worksheet
+ );
+
+ return $newIndexPosition;
+ }
+
+ /**
+ * Get sheet count.
+ */
+ public function getSheetCount(): int
+ {
+ return count($this->workSheetCollection);
+ }
+
+ /**
+ * Get active sheet index.
+ *
+ * @return int Active sheet index
+ */
+ public function getActiveSheetIndex()
+ {
+ return $this->activeSheetIndex;
+ }
+
+ /**
+ * Set active sheet index.
+ *
+ * @param int $worksheetIndex Active sheet index
+ *
+ * @return Worksheet
+ */
+ public function setActiveSheetIndex($worksheetIndex)
+ {
+ $numSheets = count($this->workSheetCollection);
+
+ if ($worksheetIndex > $numSheets - 1) {
+ throw new Exception(
+ "You tried to set a sheet active by the out of bounds index: {$worksheetIndex}. The actual number of sheets is {$numSheets}."
+ );
+ }
+ $this->activeSheetIndex = $worksheetIndex;
+
+ return $this->getActiveSheet();
+ }
+
+ /**
+ * Set active sheet index by name.
+ *
+ * @param string $worksheetName Sheet title
+ *
+ * @return Worksheet
+ */
+ public function setActiveSheetIndexByName(string $worksheetName)
+ {
+ if (($worksheet = $this->getSheetByName($worksheetName)) instanceof Worksheet) {
+ $this->setActiveSheetIndex($this->getIndex($worksheet));
+
+ return $worksheet;
+ }
+
+ throw new Exception('Workbook does not contain sheet:' . $worksheetName);
+ }
+
+ /**
+ * Get sheet names.
+ *
+ * @return string[]
+ */
+ public function getSheetNames(): array
+ {
+ $returnValue = [];
+ $worksheetCount = $this->getSheetCount();
+ for ($i = 0; $i < $worksheetCount; ++$i) {
+ $returnValue[] = $this->getSheet($i)->getTitle();
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Add external sheet.
+ *
+ * @param Worksheet $worksheet External sheet to add
+ * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
+ */
+ public function addExternalSheet(Worksheet $worksheet, $sheetIndex = null): Worksheet
+ {
+ if ($this->sheetNameExists($worksheet->getTitle())) {
+ throw new Exception("Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename the external sheet first.");
+ }
+
+ // count how many cellXfs there are in this workbook currently, we will need this below
+ $countCellXfs = count($this->cellXfCollection);
+
+ // copy all the shared cellXfs from the external workbook and append them to the current
+ foreach ($worksheet->getParentOrThrow()->getCellXfCollection() as $cellXf) {
+ $this->addCellXf(clone $cellXf);
+ }
+
+ // move sheet to this workbook
+ $worksheet->rebindParent($this);
+
+ // update the cellXfs
+ foreach ($worksheet->getCoordinates(false) as $coordinate) {
+ $cell = $worksheet->getCell($coordinate);
+ $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
+ }
+
+ // update the column dimensions Xfs
+ foreach ($worksheet->getColumnDimensions() as $columnDimension) {
+ $columnDimension->setXfIndex($columnDimension->getXfIndex() + $countCellXfs);
+ }
+
+ // update the row dimensions Xfs
+ foreach ($worksheet->getRowDimensions() as $rowDimension) {
+ $xfIndex = $rowDimension->getXfIndex();
+ if ($xfIndex !== null) {
+ $rowDimension->setXfIndex($xfIndex + $countCellXfs);
+ }
+ }
+
+ return $this->addSheet($worksheet, $sheetIndex);
+ }
+
+ /**
+ * Get an array of all Named Ranges.
+ *
+ * @return DefinedName[]
+ */
+ public function getNamedRanges(): array
+ {
+ return array_filter(
+ $this->definedNames,
+ fn (DefinedName $definedName): bool => $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE
+ );
+ }
+
+ /**
+ * Get an array of all Named Formulae.
+ *
+ * @return DefinedName[]
+ */
+ public function getNamedFormulae(): array
+ {
+ return array_filter(
+ $this->definedNames,
+ fn (DefinedName $definedName): bool => $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA
+ );
+ }
+
+ /**
+ * Get an array of all Defined Names (both named ranges and named formulae).
+ *
+ * @return DefinedName[]
+ */
+ public function getDefinedNames(): array
+ {
+ return $this->definedNames;
+ }
+
+ /**
+ * Add a named range.
+ * If a named range with this name already exists, then this will replace the existing value.
+ */
+ public function addNamedRange(NamedRange $namedRange): void
+ {
+ $this->addDefinedName($namedRange);
+ }
+
+ /**
+ * Add a named formula.
+ * If a named formula with this name already exists, then this will replace the existing value.
+ */
+ public function addNamedFormula(NamedFormula $namedFormula): void
+ {
+ $this->addDefinedName($namedFormula);
+ }
+
+ /**
+ * Add a defined name (either a named range or a named formula).
+ * If a defined named with this name already exists, then this will replace the existing value.
+ */
+ public function addDefinedName(DefinedName $definedName): void
+ {
+ $upperCaseName = StringHelper::strToUpper($definedName->getName());
+ if ($definedName->getScope() == null) {
+ // global scope
+ $this->definedNames[$upperCaseName] = $definedName;
+ } else {
+ // local scope
+ $this->definedNames[$definedName->getScope()->getTitle() . '!' . $upperCaseName] = $definedName;
+ }
+ }
+
+ /**
+ * Get named range.
+ *
+ * @param null|Worksheet $worksheet Scope. Use null for global scope
+ */
+ public function getNamedRange(string $namedRange, ?Worksheet $worksheet = null): ?NamedRange
+ {
+ $returnValue = null;
+
+ if ($namedRange !== '') {
+ $namedRange = StringHelper::strToUpper($namedRange);
+ // first look for global named range
+ $returnValue = $this->getGlobalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE);
+ // then look for local named range (has priority over global named range if both names exist)
+ $returnValue = $this->getLocalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE, $worksheet) ?: $returnValue;
+ }
+
+ return $returnValue instanceof NamedRange ? $returnValue : null;
+ }
+
+ /**
+ * Get named formula.
+ *
+ * @param null|Worksheet $worksheet Scope. Use null for global scope
+ */
+ public function getNamedFormula(string $namedFormula, ?Worksheet $worksheet = null): ?NamedFormula
+ {
+ $returnValue = null;
+
+ if ($namedFormula !== '') {
+ $namedFormula = StringHelper::strToUpper($namedFormula);
+ // first look for global named formula
+ $returnValue = $this->getGlobalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA);
+ // then look for local named formula (has priority over global named formula if both names exist)
+ $returnValue = $this->getLocalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA, $worksheet) ?: $returnValue;
+ }
+
+ return $returnValue instanceof NamedFormula ? $returnValue : null;
+ }
+
+ private function getGlobalDefinedNameByType(string $name, bool $type): ?DefinedName
+ {
+ if (isset($this->definedNames[$name]) && $this->definedNames[$name]->isFormula() === $type) {
+ return $this->definedNames[$name];
+ }
+
+ return null;
+ }
+
+ private function getLocalDefinedNameByType(string $name, bool $type, ?Worksheet $worksheet = null): ?DefinedName
+ {
+ if (
+ ($worksheet !== null) && isset($this->definedNames[$worksheet->getTitle() . '!' . $name])
+ && $this->definedNames[$worksheet->getTitle() . '!' . $name]->isFormula() === $type
+ ) {
+ return $this->definedNames[$worksheet->getTitle() . '!' . $name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get named range.
+ *
+ * @param null|Worksheet $worksheet Scope. Use null for global scope
+ */
+ public function getDefinedName(string $definedName, ?Worksheet $worksheet = null): ?DefinedName
+ {
+ $returnValue = null;
+
+ if ($definedName !== '') {
+ $definedName = StringHelper::strToUpper($definedName);
+ // first look for global defined name
+ if (isset($this->definedNames[$definedName])) {
+ $returnValue = $this->definedNames[$definedName];
+ }
+
+ // then look for local defined name (has priority over global defined name if both names exist)
+ if (($worksheet !== null) && isset($this->definedNames[$worksheet->getTitle() . '!' . $definedName])) {
+ $returnValue = $this->definedNames[$worksheet->getTitle() . '!' . $definedName];
+ }
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Remove named range.
+ *
+ * @param null|Worksheet $worksheet scope: use null for global scope
+ *
+ * @return $this
+ */
+ public function removeNamedRange(string $namedRange, ?Worksheet $worksheet = null): self
+ {
+ if ($this->getNamedRange($namedRange, $worksheet) === null) {
+ return $this;
+ }
+
+ return $this->removeDefinedName($namedRange, $worksheet);
+ }
+
+ /**
+ * Remove named formula.
+ *
+ * @param null|Worksheet $worksheet scope: use null for global scope
+ *
+ * @return $this
+ */
+ public function removeNamedFormula(string $namedFormula, ?Worksheet $worksheet = null): self
+ {
+ if ($this->getNamedFormula($namedFormula, $worksheet) === null) {
+ return $this;
+ }
+
+ return $this->removeDefinedName($namedFormula, $worksheet);
+ }
+
+ /**
+ * Remove defined name.
+ *
+ * @param null|Worksheet $worksheet scope: use null for global scope
+ *
+ * @return $this
+ */
+ public function removeDefinedName(string $definedName, ?Worksheet $worksheet = null): self
+ {
+ $definedName = StringHelper::strToUpper($definedName);
+
+ if ($worksheet === null) {
+ if (isset($this->definedNames[$definedName])) {
+ unset($this->definedNames[$definedName]);
+ }
+ } else {
+ if (isset($this->definedNames[$worksheet->getTitle() . '!' . $definedName])) {
+ unset($this->definedNames[$worksheet->getTitle() . '!' . $definedName]);
+ } elseif (isset($this->definedNames[$definedName])) {
+ unset($this->definedNames[$definedName]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get worksheet iterator.
+ */
+ public function getWorksheetIterator(): Iterator
+ {
+ return new Iterator($this);
+ }
+
+ /**
+ * Copy workbook (!= clone!).
+ */
+ public function copy(): self
+ {
+ $filename = File::temporaryFilename();
+ $writer = new XlsxWriter($this);
+ $writer->setIncludeCharts(true);
+ $writer->save($filename);
+
+ $reader = new XlsxReader();
+ $reader->setIncludeCharts(true);
+ $reloadedSpreadsheet = $reader->load($filename);
+ unlink($filename);
+
+ return $reloadedSpreadsheet;
+ }
+
+ public function __clone()
+ {
+ throw new Exception(
+ 'Do not use clone on spreadsheet. Use spreadsheet->copy() instead.'
+ );
+ }
+
+ /**
+ * Get the workbook collection of cellXfs.
+ *
+ * @return Style[]
+ */
+ public function getCellXfCollection()
+ {
+ return $this->cellXfCollection;
+ }
+
+ /**
+ * Get cellXf by index.
+ *
+ * @param int $cellStyleIndex
+ *
+ * @return Style
+ */
+ public function getCellXfByIndex($cellStyleIndex)
+ {
+ return $this->cellXfCollection[$cellStyleIndex];
+ }
+
+ /**
+ * Get cellXf by hash code.
+ *
+ * @param string $hashcode
+ *
+ * @return false|Style
+ */
+ public function getCellXfByHashCode($hashcode)
+ {
+ foreach ($this->cellXfCollection as $cellXf) {
+ if ($cellXf->getHashCode() === $hashcode) {
+ return $cellXf;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if style exists in style collection.
+ */
+ public function cellXfExists(Style $cellStyleIndex): bool
+ {
+ return in_array($cellStyleIndex, $this->cellXfCollection, true);
+ }
+
+ /**
+ * Get default style.
+ *
+ * @return Style
+ */
+ public function getDefaultStyle()
+ {
+ if (isset($this->cellXfCollection[0])) {
+ return $this->cellXfCollection[0];
+ }
+
+ throw new Exception('No default style found for this workbook');
+ }
+
+ /**
+ * Add a cellXf to the workbook.
+ */
+ public function addCellXf(Style $style): void
+ {
+ $this->cellXfCollection[] = $style;
+ $style->setIndex(count($this->cellXfCollection) - 1);
+ }
+
+ /**
+ * Remove cellXf by index. It is ensured that all cells get their xf index updated.
+ *
+ * @param int $cellStyleIndex Index to cellXf
+ */
+ public function removeCellXfByIndex($cellStyleIndex): void
+ {
+ if ($cellStyleIndex > count($this->cellXfCollection) - 1) {
+ throw new Exception('CellXf index is out of bounds.');
+ }
+
+ // first remove the cellXf
+ array_splice($this->cellXfCollection, $cellStyleIndex, 1);
+
+ // then update cellXf indexes for cells
+ foreach ($this->workSheetCollection as $worksheet) {
+ foreach ($worksheet->getCoordinates(false) as $coordinate) {
+ $cell = $worksheet->getCell($coordinate);
+ $xfIndex = $cell->getXfIndex();
+ if ($xfIndex > $cellStyleIndex) {
+ // decrease xf index by 1
+ $cell->setXfIndex($xfIndex - 1);
+ } elseif ($xfIndex == $cellStyleIndex) {
+ // set to default xf index 0
+ $cell->setXfIndex(0);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the cellXf supervisor.
+ *
+ * @return Style
+ */
+ public function getCellXfSupervisor()
+ {
+ return $this->cellXfSupervisor;
+ }
+
+ /**
+ * Get the workbook collection of cellStyleXfs.
+ *
+ * @return Style[]
+ */
+ public function getCellStyleXfCollection()
+ {
+ return $this->cellStyleXfCollection;
+ }
+
+ /**
+ * Get cellStyleXf by index.
+ *
+ * @param int $cellStyleIndex Index to cellXf
+ *
+ * @return Style
+ */
+ public function getCellStyleXfByIndex($cellStyleIndex)
+ {
+ return $this->cellStyleXfCollection[$cellStyleIndex];
+ }
+
+ /**
+ * Get cellStyleXf by hash code.
+ *
+ * @param string $hashcode
+ *
+ * @return false|Style
+ */
+ public function getCellStyleXfByHashCode($hashcode)
+ {
+ foreach ($this->cellStyleXfCollection as $cellStyleXf) {
+ if ($cellStyleXf->getHashCode() === $hashcode) {
+ return $cellStyleXf;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add a cellStyleXf to the workbook.
+ */
+ public function addCellStyleXf(Style $style): void
+ {
+ $this->cellStyleXfCollection[] = $style;
+ $style->setIndex(count($this->cellStyleXfCollection) - 1);
+ }
+
+ /**
+ * Remove cellStyleXf by index.
+ *
+ * @param int $cellStyleIndex Index to cellXf
+ */
+ public function removeCellStyleXfByIndex($cellStyleIndex): void
+ {
+ if ($cellStyleIndex > count($this->cellStyleXfCollection) - 1) {
+ throw new Exception('CellStyleXf index is out of bounds.');
+ }
+ array_splice($this->cellStyleXfCollection, $cellStyleIndex, 1);
+ }
+
+ /**
+ * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
+ * and columns in the workbook.
+ */
+ public function garbageCollect(): void
+ {
+ // how many references are there to each cellXf ?
+ $countReferencesCellXf = [];
+ foreach ($this->cellXfCollection as $index => $cellXf) {
+ $countReferencesCellXf[$index] = 0;
+ }
+
+ foreach ($this->getWorksheetIterator() as $sheet) {
+ // from cells
+ foreach ($sheet->getCoordinates(false) as $coordinate) {
+ $cell = $sheet->getCell($coordinate);
+ ++$countReferencesCellXf[$cell->getXfIndex()];
+ }
+
+ // from row dimensions
+ foreach ($sheet->getRowDimensions() as $rowDimension) {
+ if ($rowDimension->getXfIndex() !== null) {
+ ++$countReferencesCellXf[$rowDimension->getXfIndex()];
+ }
+ }
+
+ // from column dimensions
+ foreach ($sheet->getColumnDimensions() as $columnDimension) {
+ ++$countReferencesCellXf[$columnDimension->getXfIndex()];
+ }
+ }
+
+ // remove cellXfs without references and create mapping so we can update xfIndex
+ // for all cells and columns
+ $countNeededCellXfs = 0;
+ $map = [];
+ foreach ($this->cellXfCollection as $index => $cellXf) {
+ if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
+ ++$countNeededCellXfs;
+ } else {
+ unset($this->cellXfCollection[$index]);
+ }
+ $map[$index] = $countNeededCellXfs - 1;
+ }
+ $this->cellXfCollection = array_values($this->cellXfCollection);
+
+ // update the index for all cellXfs
+ foreach ($this->cellXfCollection as $i => $cellXf) {
+ $cellXf->setIndex($i);
+ }
+
+ // make sure there is always at least one cellXf (there should be)
+ if (empty($this->cellXfCollection)) {
+ $this->cellXfCollection[] = new Style();
+ }
+
+ // update the xfIndex for all cells, row dimensions, column dimensions
+ foreach ($this->getWorksheetIterator() as $sheet) {
+ // for all cells
+ foreach ($sheet->getCoordinates(false) as $coordinate) {
+ $cell = $sheet->getCell($coordinate);
+ $cell->setXfIndex($map[$cell->getXfIndex()]);
+ }
+
+ // for all row dimensions
+ foreach ($sheet->getRowDimensions() as $rowDimension) {
+ if ($rowDimension->getXfIndex() !== null) {
+ $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]);
+ }
+ }
+
+ // for all column dimensions
+ foreach ($sheet->getColumnDimensions() as $columnDimension) {
+ $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]);
+ }
+
+ // also do garbage collection for all the sheets
+ $sheet->garbageCollect();
+ }
+ }
+
+ /**
+ * Return the unique ID value assigned to this spreadsheet workbook.
+ *
+ * @return string
+ */
+ public function getID()
+ {
+ return $this->uniqueID;
+ }
+
+ /**
+ * Get the visibility of the horizonal scroll bar in the application.
+ *
+ * @return bool True if horizonal scroll bar is visible
+ */
+ public function getShowHorizontalScroll()
+ {
+ return $this->showHorizontalScroll;
+ }
+
+ /**
+ * Set the visibility of the horizonal scroll bar in the application.
+ *
+ * @param bool $showHorizontalScroll True if horizonal scroll bar is visible
+ */
+ public function setShowHorizontalScroll($showHorizontalScroll): void
+ {
+ $this->showHorizontalScroll = (bool) $showHorizontalScroll;
+ }
+
+ /**
+ * Get the visibility of the vertical scroll bar in the application.
+ *
+ * @return bool True if vertical scroll bar is visible
+ */
+ public function getShowVerticalScroll()
+ {
+ return $this->showVerticalScroll;
+ }
+
+ /**
+ * Set the visibility of the vertical scroll bar in the application.
+ *
+ * @param bool $showVerticalScroll True if vertical scroll bar is visible
+ */
+ public function setShowVerticalScroll($showVerticalScroll): void
+ {
+ $this->showVerticalScroll = (bool) $showVerticalScroll;
+ }
+
+ /**
+ * Get the visibility of the sheet tabs in the application.
+ *
+ * @return bool True if the sheet tabs are visible
+ */
+ public function getShowSheetTabs()
+ {
+ return $this->showSheetTabs;
+ }
+
+ /**
+ * Set the visibility of the sheet tabs in the application.
+ *
+ * @param bool $showSheetTabs True if sheet tabs are visible
+ */
+ public function setShowSheetTabs($showSheetTabs): void
+ {
+ $this->showSheetTabs = (bool) $showSheetTabs;
+ }
+
+ /**
+ * Return whether the workbook window is minimized.
+ *
+ * @return bool true if workbook window is minimized
+ */
+ public function getMinimized()
+ {
+ return $this->minimized;
+ }
+
+ /**
+ * Set whether the workbook window is minimized.
+ *
+ * @param bool $minimized true if workbook window is minimized
+ */
+ public function setMinimized($minimized): void
+ {
+ $this->minimized = (bool) $minimized;
+ }
+
+ /**
+ * Return whether to group dates when presenting the user with
+ * filtering optiomd in the user interface.
+ *
+ * @return bool true if workbook window is minimized
+ */
+ public function getAutoFilterDateGrouping()
+ {
+ return $this->autoFilterDateGrouping;
+ }
+
+ /**
+ * Set whether to group dates when presenting the user with
+ * filtering optiomd in the user interface.
+ *
+ * @param bool $autoFilterDateGrouping true if workbook window is minimized
+ */
+ public function setAutoFilterDateGrouping($autoFilterDateGrouping): void
+ {
+ $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping;
+ }
+
+ /**
+ * Return the first sheet in the book view.
+ *
+ * @return int First sheet in book view
+ */
+ public function getFirstSheetIndex()
+ {
+ return $this->firstSheetIndex;
+ }
+
+ /**
+ * Set the first sheet in the book view.
+ *
+ * @param int $firstSheetIndex First sheet in book view
+ */
+ public function setFirstSheetIndex($firstSheetIndex): void
+ {
+ if ($firstSheetIndex >= 0) {
+ $this->firstSheetIndex = (int) $firstSheetIndex;
+ } else {
+ throw new Exception('First sheet index must be a positive integer.');
+ }
+ }
+
+ /**
+ * Return the visibility status of the workbook.
+ *
+ * This may be one of the following three values:
+ * - visibile
+ *
+ * @return string Visible status
+ */
+ public function getVisibility()
+ {
+ return $this->visibility;
+ }
+
+ /**
+ * Set the visibility status of the workbook.
+ *
+ * Valid values are:
+ * - 'visible' (self::VISIBILITY_VISIBLE):
+ * Workbook window is visible
+ * - 'hidden' (self::VISIBILITY_HIDDEN):
+ * Workbook window is hidden, but can be shown by the user
+ * via the user interface
+ * - 'veryHidden' (self::VISIBILITY_VERY_HIDDEN):
+ * Workbook window is hidden and cannot be shown in the
+ * user interface.
+ *
+ * @param null|string $visibility visibility status of the workbook
+ */
+ public function setVisibility($visibility): void
+ {
+ if ($visibility === null) {
+ $visibility = self::VISIBILITY_VISIBLE;
+ }
+
+ if (in_array($visibility, self::WORKBOOK_VIEW_VISIBILITY_VALUES)) {
+ $this->visibility = $visibility;
+ } else {
+ throw new Exception('Invalid visibility value.');
+ }
+ }
+
+ /**
+ * Get the ratio between the workbook tabs bar and the horizontal scroll bar.
+ * TabRatio is assumed to be out of 1000 of the horizontal window width.
+ *
+ * @return int Ratio between the workbook tabs bar and the horizontal scroll bar
+ */
+ public function getTabRatio()
+ {
+ return $this->tabRatio;
+ }
+
+ /**
+ * Set the ratio between the workbook tabs bar and the horizontal scroll bar
+ * TabRatio is assumed to be out of 1000 of the horizontal window width.
+ *
+ * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar
+ */
+ public function setTabRatio($tabRatio): void
+ {
+ if ($tabRatio >= 0 && $tabRatio <= 1000) {
+ $this->tabRatio = (int) $tabRatio;
+ } else {
+ throw new Exception('Tab ratio must be between 0 and 1000.');
+ }
+ }
+
+ public function reevaluateAutoFilters(bool $resetToMax): void
+ {
+ foreach ($this->workSheetCollection as $sheet) {
+ $filter = $sheet->getAutoFilter();
+ if (!empty($filter->getRange())) {
+ if ($resetToMax) {
+ $filter->setRangeToMaxRow();
+ }
+ $filter->showHideRows();
+ }
+ }
+ }
+
+ /**
+ * @throws Exception
+ *
+ * @return mixed
+ */
+ public function __serialize()
+ {
+ throw new Exception('Spreadsheet objects cannot be serialized');
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function jsonSerialize(): mixed
+ {
+ throw new Exception('Spreadsheet objects cannot be json encoded');
+ }
+
+ public function resetThemeFonts(): void
+ {
+ $majorFontLatin = $this->theme->getMajorFontLatin();
+ $minorFontLatin = $this->theme->getMinorFontLatin();
+ foreach ($this->cellXfCollection as $cellStyleXf) {
+ $scheme = $cellStyleXf->getFont()->getScheme();
+ if ($scheme === 'major') {
+ $cellStyleXf->getFont()->setName($majorFontLatin)->setScheme($scheme);
+ } elseif ($scheme === 'minor') {
+ $cellStyleXf->getFont()->setName($minorFontLatin)->setScheme($scheme);
+ }
+ }
+ foreach ($this->cellStyleXfCollection as $cellStyleXf) {
+ $scheme = $cellStyleXf->getFont()->getScheme();
+ if ($scheme === 'major') {
+ $cellStyleXf->getFont()->setName($majorFontLatin)->setScheme($scheme);
+ } elseif ($scheme === 'minor') {
+ $cellStyleXf->getFont()->setName($minorFontLatin)->setScheme($scheme);
+ }
+ }
+ }
+
+ public function getTableByName(string $tableName): ?Table
+ {
+ $table = null;
+ foreach ($this->workSheetCollection as $sheet) {
+ $table = $sheet->getTableByName($tableName);
+ if ($table !== null) {
+ break;
+ }
+ }
+
+ return $table;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
new file mode 100644
index 0000000..90d607e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
@@ -0,0 +1,543 @@
+ self::HORIZONTAL_LEFT,
+ self::HORIZONTAL_RIGHT => self::HORIZONTAL_RIGHT,
+ self::HORIZONTAL_CENTER => self::HORIZONTAL_CENTER,
+ self::HORIZONTAL_CENTER_CONTINUOUS => self::HORIZONTAL_CENTER_CONTINUOUS,
+ self::HORIZONTAL_JUSTIFY => self::HORIZONTAL_JUSTIFY,
+ self::HORIZONTAL_FILL => self::HORIZONTAL_FILL,
+ self::HORIZONTAL_DISTRIBUTED => self::HORIZONTAL_DISTRIBUTED,
+ ];
+ // Mapping for horizontal alignment CSS
+ const HORIZONTAL_ALIGNMENT_FOR_HTML = [
+ self::HORIZONTAL_LEFT => self::HORIZONTAL_LEFT,
+ self::HORIZONTAL_RIGHT => self::HORIZONTAL_RIGHT,
+ self::HORIZONTAL_CENTER => self::HORIZONTAL_CENTER,
+ self::HORIZONTAL_CENTER_CONTINUOUS => self::HORIZONTAL_CENTER,
+ self::HORIZONTAL_JUSTIFY => self::HORIZONTAL_JUSTIFY,
+ //self::HORIZONTAL_FILL => self::HORIZONTAL_FILL, // no reasonable equivalent for fill
+ self::HORIZONTAL_DISTRIBUTED => self::HORIZONTAL_JUSTIFY,
+ ];
+
+ // Vertical alignment styles
+ const VERTICAL_BOTTOM = 'bottom';
+ const VERTICAL_TOP = 'top';
+ const VERTICAL_CENTER = 'center';
+ const VERTICAL_JUSTIFY = 'justify';
+ const VERTICAL_DISTRIBUTED = 'distributed'; // Excel2007 only
+ // Vertical alignment CSS
+ private const VERTICAL_BASELINE = 'baseline';
+ private const VERTICAL_MIDDLE = 'middle';
+ private const VERTICAL_SUB = 'sub';
+ private const VERTICAL_SUPER = 'super';
+ private const VERTICAL_TEXT_BOTTOM = 'text-bottom';
+ private const VERTICAL_TEXT_TOP = 'text-top';
+
+ // Mapping for vertical alignment
+ const VERTICAL_ALIGNMENT_FOR_XLSX = [
+ self::VERTICAL_BOTTOM => self::VERTICAL_BOTTOM,
+ self::VERTICAL_TOP => self::VERTICAL_TOP,
+ self::VERTICAL_CENTER => self::VERTICAL_CENTER,
+ self::VERTICAL_JUSTIFY => self::VERTICAL_JUSTIFY,
+ self::VERTICAL_DISTRIBUTED => self::VERTICAL_DISTRIBUTED,
+ // css settings that arent't in sync with Excel
+ self::VERTICAL_BASELINE => self::VERTICAL_BOTTOM,
+ self::VERTICAL_MIDDLE => self::VERTICAL_CENTER,
+ self::VERTICAL_SUB => self::VERTICAL_BOTTOM,
+ self::VERTICAL_SUPER => self::VERTICAL_TOP,
+ self::VERTICAL_TEXT_BOTTOM => self::VERTICAL_BOTTOM,
+ self::VERTICAL_TEXT_TOP => self::VERTICAL_TOP,
+ ];
+
+ // Mapping for vertical alignment for Html
+ const VERTICAL_ALIGNMENT_FOR_HTML = [
+ self::VERTICAL_BOTTOM => self::VERTICAL_BOTTOM,
+ self::VERTICAL_TOP => self::VERTICAL_TOP,
+ self::VERTICAL_CENTER => self::VERTICAL_MIDDLE,
+ self::VERTICAL_JUSTIFY => self::VERTICAL_MIDDLE,
+ self::VERTICAL_DISTRIBUTED => self::VERTICAL_MIDDLE,
+ // css settings that arent't in sync with Excel
+ self::VERTICAL_BASELINE => self::VERTICAL_BASELINE,
+ self::VERTICAL_MIDDLE => self::VERTICAL_MIDDLE,
+ self::VERTICAL_SUB => self::VERTICAL_SUB,
+ self::VERTICAL_SUPER => self::VERTICAL_SUPER,
+ self::VERTICAL_TEXT_BOTTOM => self::VERTICAL_TEXT_BOTTOM,
+ self::VERTICAL_TEXT_TOP => self::VERTICAL_TEXT_TOP,
+ ];
+
+ // Read order
+ const READORDER_CONTEXT = 0;
+ const READORDER_LTR = 1;
+ const READORDER_RTL = 2;
+
+ // Special value for Text Rotation
+ const TEXTROTATION_STACK_EXCEL = 255;
+ const TEXTROTATION_STACK_PHPSPREADSHEET = -165; // 90 - 255
+
+ /**
+ * Horizontal alignment.
+ *
+ * @var null|string
+ */
+ protected $horizontal = self::HORIZONTAL_GENERAL;
+
+ /**
+ * Vertical alignment.
+ *
+ * @var null|string
+ */
+ protected $vertical = self::VERTICAL_BOTTOM;
+
+ /**
+ * Text rotation.
+ *
+ * @var null|int
+ */
+ protected $textRotation = 0;
+
+ /**
+ * Wrap text.
+ *
+ * @var bool
+ */
+ protected $wrapText = false;
+
+ /**
+ * Shrink to fit.
+ *
+ * @var bool
+ */
+ protected $shrinkToFit = false;
+
+ /**
+ * Indent - only possible with horizontal alignment left and right.
+ *
+ * @var int
+ */
+ protected $indent = 0;
+
+ /**
+ * Read order.
+ *
+ * @var int
+ */
+ protected $readOrder = 0;
+
+ /**
+ * Create a new Alignment.
+ *
+ * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+ * Leave this value at default unless you understand exactly what
+ * its ramifications are
+ * @param bool $isConditional Flag indicating if this is a conditional style or not
+ * Leave this value at default unless you understand exactly what
+ * its ramifications are
+ */
+ public function __construct($isSupervisor = false, $isConditional = false)
+ {
+ // Supervisor?
+ parent::__construct($isSupervisor);
+
+ if ($isConditional) {
+ $this->horizontal = null;
+ $this->vertical = null;
+ $this->textRotation = null;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return Alignment
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getSharedComponent()->getAlignment();
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ return ['alignment' => $array];
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getAlignment()->applyFromArray(
+ * [
+ * 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+ * 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
+ * 'textRotation' => 0,
+ * 'wrapText' => TRUE
+ * ]
+ * );
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())
+ ->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['horizontal'])) {
+ $this->setHorizontal($styleArray['horizontal']);
+ }
+ if (isset($styleArray['vertical'])) {
+ $this->setVertical($styleArray['vertical']);
+ }
+ if (isset($styleArray['textRotation'])) {
+ $this->setTextRotation($styleArray['textRotation']);
+ }
+ if (isset($styleArray['wrapText'])) {
+ $this->setWrapText($styleArray['wrapText']);
+ }
+ if (isset($styleArray['shrinkToFit'])) {
+ $this->setShrinkToFit($styleArray['shrinkToFit']);
+ }
+ if (isset($styleArray['indent'])) {
+ $this->setIndent($styleArray['indent']);
+ }
+ if (isset($styleArray['readOrder'])) {
+ $this->setReadOrder($styleArray['readOrder']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Horizontal.
+ *
+ * @return null|string
+ */
+ public function getHorizontal()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHorizontal();
+ }
+
+ return $this->horizontal;
+ }
+
+ /**
+ * Set Horizontal.
+ *
+ * @param string $horizontalAlignment see self::HORIZONTAL_*
+ *
+ * @return $this
+ */
+ public function setHorizontal(string $horizontalAlignment): static
+ {
+ $horizontalAlignment = strtolower($horizontalAlignment);
+ if ($horizontalAlignment === self::HORIZONTAL_CENTER_CONTINUOUS_LC) {
+ $horizontalAlignment = self::HORIZONTAL_CENTER_CONTINUOUS;
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['horizontal' => $horizontalAlignment]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->horizontal = $horizontalAlignment;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Vertical.
+ *
+ * @return null|string
+ */
+ public function getVertical()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getVertical();
+ }
+
+ return $this->vertical;
+ }
+
+ /**
+ * Set Vertical.
+ *
+ * @param string $verticalAlignment see self::VERTICAL_*
+ *
+ * @return $this
+ */
+ public function setVertical($verticalAlignment): static
+ {
+ $verticalAlignment = strtolower($verticalAlignment);
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['vertical' => $verticalAlignment]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->vertical = $verticalAlignment;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get TextRotation.
+ *
+ * @return null|int
+ */
+ public function getTextRotation()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getTextRotation();
+ }
+
+ return $this->textRotation;
+ }
+
+ /**
+ * Set TextRotation.
+ *
+ * @param int $angleInDegrees
+ *
+ * @return $this
+ */
+ public function setTextRotation($angleInDegrees): static
+ {
+ // Excel2007 value 255 => PhpSpreadsheet value -165
+ if ($angleInDegrees == self::TEXTROTATION_STACK_EXCEL) {
+ $angleInDegrees = self::TEXTROTATION_STACK_PHPSPREADSHEET;
+ }
+
+ // Set rotation
+ if (($angleInDegrees >= -90 && $angleInDegrees <= 90) || $angleInDegrees == self::TEXTROTATION_STACK_PHPSPREADSHEET) {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['textRotation' => $angleInDegrees]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->textRotation = $angleInDegrees;
+ }
+ } else {
+ throw new PhpSpreadsheetException('Text rotation should be a value between -90 and 90.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Wrap Text.
+ *
+ * @return bool
+ */
+ public function getWrapText()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getWrapText();
+ }
+
+ return $this->wrapText;
+ }
+
+ /**
+ * Set Wrap Text.
+ *
+ * @param bool $wrapped
+ *
+ * @return $this
+ */
+ public function setWrapText($wrapped): static
+ {
+ if ($wrapped == '') {
+ $wrapped = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['wrapText' => $wrapped]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->wrapText = $wrapped;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Shrink to fit.
+ *
+ * @return bool
+ */
+ public function getShrinkToFit()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getShrinkToFit();
+ }
+
+ return $this->shrinkToFit;
+ }
+
+ /**
+ * Set Shrink to fit.
+ *
+ * @param bool $shrink
+ *
+ * @return $this
+ */
+ public function setShrinkToFit($shrink): static
+ {
+ if ($shrink == '') {
+ $shrink = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['shrinkToFit' => $shrink]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->shrinkToFit = $shrink;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get indent.
+ *
+ * @return int
+ */
+ public function getIndent()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getIndent();
+ }
+
+ return $this->indent;
+ }
+
+ /**
+ * Set indent.
+ *
+ * @param int $indent
+ *
+ * @return $this
+ */
+ public function setIndent($indent): static
+ {
+ if ($indent > 0) {
+ if (
+ $this->getHorizontal() != self::HORIZONTAL_GENERAL
+ && $this->getHorizontal() != self::HORIZONTAL_LEFT
+ && $this->getHorizontal() != self::HORIZONTAL_RIGHT
+ && $this->getHorizontal() != self::HORIZONTAL_DISTRIBUTED
+ ) {
+ $indent = 0; // indent not supported
+ }
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['indent' => $indent]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->indent = $indent;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get read order.
+ *
+ * @return int
+ */
+ public function getReadOrder()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getReadOrder();
+ }
+
+ return $this->readOrder;
+ }
+
+ /**
+ * Set read order.
+ *
+ * @param int $readOrder
+ *
+ * @return $this
+ */
+ public function setReadOrder($readOrder): static
+ {
+ if ($readOrder < 0 || $readOrder > 2) {
+ $readOrder = 0;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['readOrder' => $readOrder]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->readOrder = $readOrder;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+
+ return md5(
+ $this->horizontal
+ . $this->vertical
+ . $this->textRotation
+ . ($this->wrapText ? 't' : 'f')
+ . ($this->shrinkToFit ? 't' : 'f')
+ . $this->indent
+ . $this->readOrder
+ . __CLASS__
+ );
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'horizontal', $this->getHorizontal());
+ $this->exportArray2($exportedArray, 'indent', $this->getIndent());
+ $this->exportArray2($exportedArray, 'readOrder', $this->getReadOrder());
+ $this->exportArray2($exportedArray, 'shrinkToFit', $this->getShrinkToFit());
+ $this->exportArray2($exportedArray, 'textRotation', $this->getTextRotation());
+ $this->exportArray2($exportedArray, 'vertical', $this->getVertical());
+ $this->exportArray2($exportedArray, 'wrapText', $this->getWrapText());
+
+ return $exportedArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
new file mode 100644
index 0000000..006f64c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
@@ -0,0 +1,235 @@
+color = new Color(Color::COLOR_BLACK, $isSupervisor);
+
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->color->bindParent($this, 'color');
+ }
+ if ($isConditional) {
+ $this->borderStyle = self::BORDER_OMIT;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return Border
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ /** @var Borders $sharedComponent */
+ $sharedComponent = $parent->getSharedComponent();
+
+ return match ($this->parentPropertyName) {
+ 'bottom' => $sharedComponent->getBottom(),
+ 'diagonal' => $sharedComponent->getDiagonal(),
+ 'left' => $sharedComponent->getLeft(),
+ 'right' => $sharedComponent->getRight(),
+ 'top' => $sharedComponent->getTop(),
+ default => throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.'),
+ };
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getStyleArray([$this->parentPropertyName => $array]);
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->getTop()->applyFromArray(
+ * [
+ * 'borderStyle' => Border::BORDER_DASHDOT,
+ * 'color' => [
+ * 'rgb' => '808080'
+ * ]
+ * ]
+ * );
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['borderStyle'])) {
+ $this->setBorderStyle($styleArray['borderStyle']);
+ }
+ if (isset($styleArray['color'])) {
+ $this->getColor()->applyFromArray($styleArray['color']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Border style.
+ *
+ * @return string
+ */
+ public function getBorderStyle()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBorderStyle();
+ }
+
+ return $this->borderStyle;
+ }
+
+ /**
+ * Set Border style.
+ *
+ * @param bool|string $style
+ * When passing a boolean, FALSE equates Border::BORDER_NONE
+ * and TRUE to Border::BORDER_MEDIUM
+ *
+ * @return $this
+ */
+ public function setBorderStyle($style): static
+ {
+ if (empty($style)) {
+ $style = self::BORDER_NONE;
+ } elseif (is_bool($style)) {
+ $style = self::BORDER_MEDIUM;
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['borderStyle' => $style]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->borderStyle = $style;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Border Color.
+ *
+ * @return Color
+ */
+ public function getColor()
+ {
+ return $this->color;
+ }
+
+ /**
+ * Set Border Color.
+ *
+ * @return $this
+ */
+ public function setColor(Color $color): static
+ {
+ // make sure parameter is a real color and not a supervisor
+ $color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->color = $color;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+
+ return md5(
+ $this->borderStyle
+ . $this->color->getHashCode()
+ . __CLASS__
+ );
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'borderStyle', $this->getBorderStyle());
+ $this->exportArray2($exportedArray, 'color', $this->getColor());
+
+ return $exportedArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
new file mode 100644
index 0000000..b027029
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
@@ -0,0 +1,410 @@
+left = new Border($isSupervisor, $isConditional);
+ $this->right = new Border($isSupervisor, $isConditional);
+ $this->top = new Border($isSupervisor, $isConditional);
+ $this->bottom = new Border($isSupervisor, $isConditional);
+ $this->diagonal = new Border($isSupervisor, $isConditional);
+ $this->diagonalDirection = self::DIAGONAL_NONE;
+
+ // Specially for supervisor
+ if ($isSupervisor) {
+ // Initialize pseudo-borders
+ $this->allBorders = new Border(true, $isConditional);
+ $this->outline = new Border(true, $isConditional);
+ $this->inside = new Border(true, $isConditional);
+ $this->vertical = new Border(true, $isConditional);
+ $this->horizontal = new Border(true, $isConditional);
+
+ // bind parent if we are a supervisor
+ $this->left->bindParent($this, 'left');
+ $this->right->bindParent($this, 'right');
+ $this->top->bindParent($this, 'top');
+ $this->bottom->bindParent($this, 'bottom');
+ $this->diagonal->bindParent($this, 'diagonal');
+ $this->allBorders->bindParent($this, 'allBorders');
+ $this->outline->bindParent($this, 'outline');
+ $this->inside->bindParent($this, 'inside');
+ $this->vertical->bindParent($this, 'vertical');
+ $this->horizontal->bindParent($this, 'horizontal');
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return Borders
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getSharedComponent()->getBorders();
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ return ['borders' => $array];
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
+ * [
+ * 'bottom' => [
+ * 'borderStyle' => Border::BORDER_DASHDOT,
+ * 'color' => [
+ * 'rgb' => '808080'
+ * ]
+ * ],
+ * 'top' => [
+ * 'borderStyle' => Border::BORDER_DASHDOT,
+ * 'color' => [
+ * 'rgb' => '808080'
+ * ]
+ * ]
+ * ]
+ * );
+ *
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
+ * [
+ * 'allBorders' => [
+ * 'borderStyle' => Border::BORDER_DASHDOT,
+ * 'color' => [
+ * 'rgb' => '808080'
+ * ]
+ * ]
+ * ]
+ * );
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['left'])) {
+ $this->getLeft()->applyFromArray($styleArray['left']);
+ }
+ if (isset($styleArray['right'])) {
+ $this->getRight()->applyFromArray($styleArray['right']);
+ }
+ if (isset($styleArray['top'])) {
+ $this->getTop()->applyFromArray($styleArray['top']);
+ }
+ if (isset($styleArray['bottom'])) {
+ $this->getBottom()->applyFromArray($styleArray['bottom']);
+ }
+ if (isset($styleArray['diagonal'])) {
+ $this->getDiagonal()->applyFromArray($styleArray['diagonal']);
+ }
+ if (isset($styleArray['diagonalDirection'])) {
+ $this->setDiagonalDirection($styleArray['diagonalDirection']);
+ }
+ if (isset($styleArray['allBorders'])) {
+ $this->getLeft()->applyFromArray($styleArray['allBorders']);
+ $this->getRight()->applyFromArray($styleArray['allBorders']);
+ $this->getTop()->applyFromArray($styleArray['allBorders']);
+ $this->getBottom()->applyFromArray($styleArray['allBorders']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Left.
+ *
+ * @return Border
+ */
+ public function getLeft()
+ {
+ return $this->left;
+ }
+
+ /**
+ * Get Right.
+ *
+ * @return Border
+ */
+ public function getRight()
+ {
+ return $this->right;
+ }
+
+ /**
+ * Get Top.
+ *
+ * @return Border
+ */
+ public function getTop()
+ {
+ return $this->top;
+ }
+
+ /**
+ * Get Bottom.
+ *
+ * @return Border
+ */
+ public function getBottom()
+ {
+ return $this->bottom;
+ }
+
+ /**
+ * Get Diagonal.
+ *
+ * @return Border
+ */
+ public function getDiagonal()
+ {
+ return $this->diagonal;
+ }
+
+ /**
+ * Get AllBorders (pseudo-border). Only applies to supervisor.
+ *
+ * @return Border
+ */
+ public function getAllBorders()
+ {
+ if (!$this->isSupervisor) {
+ throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+ }
+
+ return $this->allBorders;
+ }
+
+ /**
+ * Get Outline (pseudo-border). Only applies to supervisor.
+ *
+ * @return Border
+ */
+ public function getOutline()
+ {
+ if (!$this->isSupervisor) {
+ throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+ }
+
+ return $this->outline;
+ }
+
+ /**
+ * Get Inside (pseudo-border). Only applies to supervisor.
+ *
+ * @return Border
+ */
+ public function getInside()
+ {
+ if (!$this->isSupervisor) {
+ throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+ }
+
+ return $this->inside;
+ }
+
+ /**
+ * Get Vertical (pseudo-border). Only applies to supervisor.
+ *
+ * @return Border
+ */
+ public function getVertical()
+ {
+ if (!$this->isSupervisor) {
+ throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+ }
+
+ return $this->vertical;
+ }
+
+ /**
+ * Get Horizontal (pseudo-border). Only applies to supervisor.
+ *
+ * @return Border
+ */
+ public function getHorizontal()
+ {
+ if (!$this->isSupervisor) {
+ throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
+ }
+
+ return $this->horizontal;
+ }
+
+ /**
+ * Get DiagonalDirection.
+ *
+ * @return int
+ */
+ public function getDiagonalDirection()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getDiagonalDirection();
+ }
+
+ return $this->diagonalDirection;
+ }
+
+ /**
+ * Set DiagonalDirection.
+ *
+ * @param int $direction see self::DIAGONAL_*
+ *
+ * @return $this
+ */
+ public function setDiagonalDirection($direction): static
+ {
+ if ($direction == '') {
+ $direction = self::DIAGONAL_NONE;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['diagonalDirection' => $direction]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->diagonalDirection = $direction;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashcode();
+ }
+
+ return md5(
+ $this->getLeft()->getHashCode()
+ . $this->getRight()->getHashCode()
+ . $this->getTop()->getHashCode()
+ . $this->getBottom()->getHashCode()
+ . $this->getDiagonal()->getHashCode()
+ . $this->getDiagonalDirection()
+ . __CLASS__
+ );
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'bottom', $this->getBottom());
+ $this->exportArray2($exportedArray, 'diagonal', $this->getDiagonal());
+ $this->exportArray2($exportedArray, 'diagonalDirection', $this->getDiagonalDirection());
+ $this->exportArray2($exportedArray, 'left', $this->getLeft());
+ $this->exportArray2($exportedArray, 'right', $this->getRight());
+ $this->exportArray2($exportedArray, 'top', $this->getTop());
+
+ return $exportedArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
new file mode 100644
index 0000000..fdc0a97
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
@@ -0,0 +1,425 @@
+ self::COLOR_BLACK,
+ 'White' => self::COLOR_WHITE,
+ 'Red' => self::COLOR_RED,
+ 'Green' => self::COLOR_GREEN,
+ 'Blue' => self::COLOR_BLUE,
+ 'Yellow' => self::COLOR_YELLOW,
+ 'Magenta' => self::COLOR_MAGENTA,
+ 'Cyan' => self::COLOR_CYAN,
+ ];
+
+ const VALIDATE_ARGB_SIZE = 8;
+ const VALIDATE_RGB_SIZE = 6;
+ const VALIDATE_COLOR_6 = '/^[A-F0-9]{6}$/i';
+ const VALIDATE_COLOR_8 = '/^[A-F0-9]{8}$/i';
+
+ private const INDEXED_COLORS = [
+ 1 => 'FF000000', // System Colour #1 - Black
+ 2 => 'FFFFFFFF', // System Colour #2 - White
+ 3 => 'FFFF0000', // System Colour #3 - Red
+ 4 => 'FF00FF00', // System Colour #4 - Green
+ 5 => 'FF0000FF', // System Colour #5 - Blue
+ 6 => 'FFFFFF00', // System Colour #6 - Yellow
+ 7 => 'FFFF00FF', // System Colour #7- Magenta
+ 8 => 'FF00FFFF', // System Colour #8- Cyan
+ 9 => 'FF800000', // Standard Colour #9
+ 10 => 'FF008000', // Standard Colour #10
+ 11 => 'FF000080', // Standard Colour #11
+ 12 => 'FF808000', // Standard Colour #12
+ 13 => 'FF800080', // Standard Colour #13
+ 14 => 'FF008080', // Standard Colour #14
+ 15 => 'FFC0C0C0', // Standard Colour #15
+ 16 => 'FF808080', // Standard Colour #16
+ 17 => 'FF9999FF', // Chart Fill Colour #17
+ 18 => 'FF993366', // Chart Fill Colour #18
+ 19 => 'FFFFFFCC', // Chart Fill Colour #19
+ 20 => 'FFCCFFFF', // Chart Fill Colour #20
+ 21 => 'FF660066', // Chart Fill Colour #21
+ 22 => 'FFFF8080', // Chart Fill Colour #22
+ 23 => 'FF0066CC', // Chart Fill Colour #23
+ 24 => 'FFCCCCFF', // Chart Fill Colour #24
+ 25 => 'FF000080', // Chart Line Colour #25
+ 26 => 'FFFF00FF', // Chart Line Colour #26
+ 27 => 'FFFFFF00', // Chart Line Colour #27
+ 28 => 'FF00FFFF', // Chart Line Colour #28
+ 29 => 'FF800080', // Chart Line Colour #29
+ 30 => 'FF800000', // Chart Line Colour #30
+ 31 => 'FF008080', // Chart Line Colour #31
+ 32 => 'FF0000FF', // Chart Line Colour #32
+ 33 => 'FF00CCFF', // Standard Colour #33
+ 34 => 'FFCCFFFF', // Standard Colour #34
+ 35 => 'FFCCFFCC', // Standard Colour #35
+ 36 => 'FFFFFF99', // Standard Colour #36
+ 37 => 'FF99CCFF', // Standard Colour #37
+ 38 => 'FFFF99CC', // Standard Colour #38
+ 39 => 'FFCC99FF', // Standard Colour #39
+ 40 => 'FFFFCC99', // Standard Colour #40
+ 41 => 'FF3366FF', // Standard Colour #41
+ 42 => 'FF33CCCC', // Standard Colour #42
+ 43 => 'FF99CC00', // Standard Colour #43
+ 44 => 'FFFFCC00', // Standard Colour #44
+ 45 => 'FFFF9900', // Standard Colour #45
+ 46 => 'FFFF6600', // Standard Colour #46
+ 47 => 'FF666699', // Standard Colour #47
+ 48 => 'FF969696', // Standard Colour #48
+ 49 => 'FF003366', // Standard Colour #49
+ 50 => 'FF339966', // Standard Colour #50
+ 51 => 'FF003300', // Standard Colour #51
+ 52 => 'FF333300', // Standard Colour #52
+ 53 => 'FF993300', // Standard Colour #53
+ 54 => 'FF993366', // Standard Colour #54
+ 55 => 'FF333399', // Standard Colour #55
+ 56 => 'FF333333', // Standard Colour #56
+ ];
+
+ /**
+ * ARGB - Alpha RGB.
+ *
+ * @var null|string
+ */
+ protected $argb;
+
+ /** @var bool */
+ private $hasChanged = false;
+
+ /**
+ * Create a new Color.
+ *
+ * @param string $colorValue ARGB value for the colour, or named colour
+ * @param bool $isSupervisor Flag indicating if this is a supervisor or not
+ * Leave this value at default unless you understand exactly what
+ * its ramifications are
+ * @param bool $isConditional Flag indicating if this is a conditional style or not
+ * Leave this value at default unless you understand exactly what
+ * its ramifications are
+ */
+ public function __construct($colorValue = self::COLOR_BLACK, $isSupervisor = false, $isConditional = false)
+ {
+ // Supervisor?
+ parent::__construct($isSupervisor);
+
+ // Initialise values
+ if (!$isConditional) {
+ $this->argb = $this->validateColor($colorValue) ?: self::COLOR_BLACK;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return Color
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+ /** @var Border|Fill $sharedComponent */
+ $sharedComponent = $parent->getSharedComponent();
+ if ($sharedComponent instanceof Fill) {
+ if ($this->parentPropertyName === 'endColor') {
+ return $sharedComponent->getEndColor();
+ }
+
+ return $sharedComponent->getStartColor();
+ }
+
+ return $sharedComponent->getColor();
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getStyleArray([$this->parentPropertyName => $array]);
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['rgb'])) {
+ $this->setRGB($styleArray['rgb']);
+ }
+ if (isset($styleArray['argb'])) {
+ $this->setARGB($styleArray['argb']);
+ }
+ }
+
+ return $this;
+ }
+
+ private function validateColor(?string $colorValue): string
+ {
+ if ($colorValue === null || $colorValue === '') {
+ return self::COLOR_BLACK;
+ }
+ $named = ucfirst(strtolower($colorValue));
+ if (array_key_exists($named, self::NAMED_COLOR_TRANSLATIONS)) {
+ return self::NAMED_COLOR_TRANSLATIONS[$named];
+ }
+ if (preg_match(self::VALIDATE_COLOR_8, $colorValue) === 1) {
+ return $colorValue;
+ }
+ if (preg_match(self::VALIDATE_COLOR_6, $colorValue) === 1) {
+ return 'FF' . $colorValue;
+ }
+
+ return '';
+ }
+
+ /**
+ * Get ARGB.
+ */
+ public function getARGB(): ?string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getARGB();
+ }
+
+ return $this->argb;
+ }
+
+ /**
+ * Set ARGB.
+ *
+ * @param string $colorValue ARGB value, or a named color
+ *
+ * @return $this
+ */
+ public function setARGB(?string $colorValue = self::COLOR_BLACK): static
+ {
+ $this->hasChanged = true;
+ $colorValue = $this->validateColor($colorValue);
+ if ($colorValue === '') {
+ return $this;
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['argb' => $colorValue]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->argb = $colorValue;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get RGB.
+ */
+ public function getRGB(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getRGB();
+ }
+
+ return substr($this->argb ?? '', 2);
+ }
+
+ /**
+ * Set RGB.
+ *
+ * @param string $colorValue RGB value, or a named color
+ *
+ * @return $this
+ */
+ public function setRGB(?string $colorValue = self::COLOR_BLACK): static
+ {
+ return $this->setARGB($colorValue);
+ }
+
+ /**
+ * Get a specified colour component of an RGB value.
+ *
+ * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param int $offset Position within the RGB value to extract
+ * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ *
+ * @return int|string The extracted colour component
+ */
+ private static function getColourComponent($rgbValue, $offset, $hex = true): string|int
+ {
+ $colour = substr($rgbValue, $offset, 2) ?: '';
+ if (preg_match('/^[0-9a-f]{2}$/i', $colour) !== 1) {
+ $colour = '00';
+ }
+
+ return ($hex) ? $colour : (int) hexdec($colour);
+ }
+
+ /**
+ * Get the red colour component of an RGB value.
+ *
+ * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ *
+ * @return int|string The red colour component
+ */
+ public static function getRed($rgbValue, $hex = true)
+ {
+ return self::getColourComponent($rgbValue, strlen($rgbValue) - 6, $hex);
+ }
+
+ /**
+ * Get the green colour component of an RGB value.
+ *
+ * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ *
+ * @return int|string The green colour component
+ */
+ public static function getGreen($rgbValue, $hex = true)
+ {
+ return self::getColourComponent($rgbValue, strlen($rgbValue) - 4, $hex);
+ }
+
+ /**
+ * Get the blue colour component of an RGB value.
+ *
+ * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param bool $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ *
+ * @return int|string The blue colour component
+ */
+ public static function getBlue($rgbValue, $hex = true)
+ {
+ return self::getColourComponent($rgbValue, strlen($rgbValue) - 2, $hex);
+ }
+
+ /**
+ * Adjust the brightness of a color.
+ *
+ * @param string $hexColourValue The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
+ * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
+ *
+ * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
+ */
+ public static function changeBrightness($hexColourValue, $adjustPercentage): string
+ {
+ $rgba = (strlen($hexColourValue) === 8);
+ $adjustPercentage = max(-1.0, min(1.0, $adjustPercentage));
+
+ /** @var int $red */
+ $red = self::getRed($hexColourValue, false);
+ /** @var int $green */
+ $green = self::getGreen($hexColourValue, false);
+ /** @var int $blue */
+ $blue = self::getBlue($hexColourValue, false);
+
+ return (($rgba) ? 'FF' : '') . RgbTint::rgbAndTintToRgb($red, $green, $blue, $adjustPercentage);
+ }
+
+ /**
+ * Get indexed color.
+ *
+ * @param int $colorIndex Index entry point into the colour array
+ * @param bool $background Flag to indicate whether default background or foreground colour
+ * should be returned if the indexed colour doesn't exist
+ */
+ public static function indexedColor($colorIndex, $background = false, ?array $palette = null): self
+ {
+ // Clean parameter
+ $colorIndex = (int) $colorIndex;
+
+ if (empty($palette)) {
+ if (isset(self::INDEXED_COLORS[$colorIndex])) {
+ return new self(self::INDEXED_COLORS[$colorIndex]);
+ }
+ } else {
+ if (isset($palette[$colorIndex])) {
+ return new self($palette[$colorIndex]);
+ }
+ }
+
+ return ($background) ? new self(self::COLOR_WHITE) : new self(self::COLOR_BLACK);
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+
+ return md5(
+ $this->argb
+ . __CLASS__
+ );
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'argb', $this->getARGB());
+
+ return $exportedArray;
+ }
+
+ public function getHasChanged(): bool
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->hasChanged;
+ }
+
+ return $this->hasChanged;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
new file mode 100644
index 0000000..9f6e8c7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
@@ -0,0 +1,357 @@
+style = new Style(false, true);
+ }
+
+ public function getNoFormatSet(): bool
+ {
+ return $this->noFormatSet;
+ }
+
+ public function setNoFormatSet(bool $noFormatSet): self
+ {
+ $this->noFormatSet = $noFormatSet;
+
+ return $this;
+ }
+
+ /**
+ * Get Condition type.
+ *
+ * @return string
+ */
+ public function getConditionType()
+ {
+ return $this->conditionType;
+ }
+
+ /**
+ * Set Condition type.
+ *
+ * @param string $type Condition type, see self::CONDITION_*
+ *
+ * @return $this
+ */
+ public function setConditionType($type): static
+ {
+ $this->conditionType = $type;
+
+ return $this;
+ }
+
+ /**
+ * Get Operator type.
+ *
+ * @return string
+ */
+ public function getOperatorType()
+ {
+ return $this->operatorType;
+ }
+
+ /**
+ * Set Operator type.
+ *
+ * @param string $type Conditional operator type, see self::OPERATOR_*
+ *
+ * @return $this
+ */
+ public function setOperatorType($type): static
+ {
+ $this->operatorType = $type;
+
+ return $this;
+ }
+
+ /**
+ * Get text.
+ *
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * Set text.
+ *
+ * @param string $text
+ *
+ * @return $this
+ */
+ public function setText($text): static
+ {
+ $this->text = $text;
+
+ return $this;
+ }
+
+ /**
+ * Get StopIfTrue.
+ *
+ * @return bool
+ */
+ public function getStopIfTrue()
+ {
+ return $this->stopIfTrue;
+ }
+
+ /**
+ * Set StopIfTrue.
+ *
+ * @param bool $stopIfTrue
+ *
+ * @return $this
+ */
+ public function setStopIfTrue($stopIfTrue): static
+ {
+ $this->stopIfTrue = $stopIfTrue;
+
+ return $this;
+ }
+
+ /**
+ * Get Conditions.
+ *
+ * @return (bool|float|int|string)[]
+ */
+ public function getConditions()
+ {
+ return $this->condition;
+ }
+
+ /**
+ * Set Conditions.
+ *
+ * @param (bool|float|int|string)[]|bool|float|int|string $conditions Condition
+ *
+ * @return $this
+ */
+ public function setConditions($conditions): static
+ {
+ if (!is_array($conditions)) {
+ $conditions = [$conditions];
+ }
+ $this->condition = $conditions;
+
+ return $this;
+ }
+
+ /**
+ * Add Condition.
+ *
+ * @param bool|float|int|string $condition Condition
+ *
+ * @return $this
+ */
+ public function addCondition($condition): static
+ {
+ $this->condition[] = $condition;
+
+ return $this;
+ }
+
+ /**
+ * Get Style.
+ *
+ * @return Style
+ */
+ public function getStyle()
+ {
+ return $this->style;
+ }
+
+ /**
+ * Set Style.
+ *
+ * @return $this
+ */
+ public function setStyle(Style $style): static
+ {
+ $this->style = $style;
+
+ return $this;
+ }
+
+ public function getDataBar(): ?ConditionalDataBar
+ {
+ return $this->dataBar;
+ }
+
+ public function setDataBar(ConditionalDataBar $dataBar): static
+ {
+ $this->dataBar = $dataBar;
+
+ return $this;
+ }
+
+ public function getColorScale(): ?ConditionalColorScale
+ {
+ return $this->colorScale;
+ }
+
+ public function setColorScale(ConditionalColorScale $colorScale): static
+ {
+ $this->colorScale = $colorScale;
+
+ return $this;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode(): string
+ {
+ return md5(
+ $this->conditionType
+ . $this->operatorType
+ . implode(';', $this->condition)
+ . $this->style->getHashCode()
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ /**
+ * Verify if param is valid condition type.
+ */
+ public static function isValidConditionType(string $type): bool
+ {
+ return in_array($type, self::CONDITION_TYPES);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
new file mode 100644
index 0000000..3c815e4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
@@ -0,0 +1,298 @@
+ '=',
+ Conditional::OPERATOR_GREATERTHAN => '>',
+ Conditional::OPERATOR_GREATERTHANOREQUAL => '>=',
+ Conditional::OPERATOR_LESSTHAN => '<',
+ Conditional::OPERATOR_LESSTHANOREQUAL => '<=',
+ Conditional::OPERATOR_NOTEQUAL => '<>',
+ ];
+
+ public const COMPARISON_RANGE_OPERATORS = [
+ Conditional::OPERATOR_BETWEEN => 'IF(AND(A1>=%s,A1<=%s),TRUE,FALSE)',
+ Conditional::OPERATOR_NOTBETWEEN => 'IF(AND(A1>=%s,A1<=%s),FALSE,TRUE)',
+ ];
+
+ public const COMPARISON_DUPLICATES_OPERATORS = [
+ Conditional::CONDITION_DUPLICATES => "COUNTIF('%s'!%s,%s)>1",
+ Conditional::CONDITION_UNIQUE => "COUNTIF('%s'!%s,%s)=1",
+ ];
+
+ protected Cell $cell;
+
+ /**
+ * @var int
+ */
+ protected $cellRow;
+
+ protected Worksheet $worksheet;
+
+ /**
+ * @var int
+ */
+ protected $cellColumn;
+
+ /**
+ * @var string
+ */
+ protected $conditionalRange;
+
+ /**
+ * @var string
+ */
+ protected $referenceCell;
+
+ /**
+ * @var int
+ */
+ protected $referenceRow;
+
+ /**
+ * @var int
+ */
+ protected $referenceColumn;
+
+ protected Calculation $engine;
+
+ public function __construct(Cell $cell, string $conditionalRange)
+ {
+ $this->cell = $cell;
+ $this->worksheet = $cell->getWorksheet();
+ [$this->cellColumn, $this->cellRow] = Coordinate::indexesFromString($this->cell->getCoordinate());
+ $this->setReferenceCellForExpressions($conditionalRange);
+
+ $this->engine = Calculation::getInstance($this->worksheet->getParent());
+ }
+
+ protected function setReferenceCellForExpressions(string $conditionalRange): void
+ {
+ $conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($conditionalRange)));
+ [$this->referenceCell] = $conditionalRange[0];
+
+ [$this->referenceColumn, $this->referenceRow] = Coordinate::indexesFromString($this->referenceCell);
+
+ // Convert our conditional range to an absolute conditional range, so it can be used "pinned" in formulae
+ $rangeSets = [];
+ foreach ($conditionalRange as $rangeSet) {
+ $absoluteRangeSet = array_map(
+ [Coordinate::class, 'absoluteCoordinate'],
+ $rangeSet
+ );
+ $rangeSets[] = implode(':', $absoluteRangeSet);
+ }
+ $this->conditionalRange = implode(',', $rangeSets);
+ }
+
+ public function evaluateConditional(Conditional $conditional): bool
+ {
+ // Some calculations may modify the stored cell; so reset it before every evaluation.
+ $cellColumn = Coordinate::stringFromColumnIndex($this->cellColumn);
+ $cellAddress = "{$cellColumn}{$this->cellRow}";
+ $this->cell = $this->worksheet->getCell($cellAddress);
+
+ return match ($conditional->getConditionType()) {
+ Conditional::CONDITION_CELLIS => $this->processOperatorComparison($conditional),
+ Conditional::CONDITION_DUPLICATES, Conditional::CONDITION_UNIQUE => $this->processDuplicatesComparison($conditional),
+ // Expression is NOT(ISERROR(SEARCH("",)))
+ Conditional::CONDITION_CONTAINSTEXT,
+ // Expression is ISERROR(SEARCH("",))
+ Conditional::CONDITION_NOTCONTAINSTEXT,
+ // Expression is LEFT(,LEN(""))=""
+ Conditional::CONDITION_BEGINSWITH,
+ // Expression is RIGHT(,LEN(""))=""
+ Conditional::CONDITION_ENDSWITH,
+ // Expression is LEN(TRIM())=0
+ Conditional::CONDITION_CONTAINSBLANKS,
+ // Expression is LEN(TRIM())>0
+ Conditional::CONDITION_NOTCONTAINSBLANKS,
+ // Expression is ISERROR()
+ Conditional::CONDITION_CONTAINSERRORS,
+ // Expression is NOT(ISERROR())
+ Conditional::CONDITION_NOTCONTAINSERRORS,
+ // Expression varies, depending on specified timePeriod value, e.g.
+ // Yesterday FLOOR(,1)=TODAY()-1
+ // Today FLOOR(,1)=TODAY()
+ // Tomorrow FLOOR(,1)=TODAY()+1
+ // Last 7 Days AND(TODAY()-FLOOR(,1)<=6,FLOOR(,1)<=TODAY())
+ Conditional::CONDITION_TIMEPERIOD,
+ Conditional::CONDITION_EXPRESSION => $this->processExpression($conditional),
+ default => false,
+ };
+ }
+
+ /**
+ * @return float|int|string
+ */
+ protected function wrapValue(mixed $value)
+ {
+ if (!is_numeric($value)) {
+ if (is_bool($value)) {
+ return $value ? 'TRUE' : 'FALSE';
+ } elseif ($value === null) {
+ return 'NULL';
+ }
+
+ return '"' . $value . '"';
+ }
+
+ return $value;
+ }
+
+ /**
+ * @return float|int|string
+ */
+ protected function wrapCellValue()
+ {
+ return $this->wrapValue($this->cell->getCalculatedValue());
+ }
+
+ /**
+ * @return float|int|string
+ */
+ protected function conditionCellAdjustment(array $matches)
+ {
+ $column = $matches[6];
+ $row = $matches[7];
+
+ if (!str_contains($column, '$')) {
+ $column = Coordinate::columnIndexFromString($column);
+ $column += $this->cellColumn - $this->referenceColumn;
+ $column = Coordinate::stringFromColumnIndex($column);
+ }
+
+ if (!str_contains($row, '$')) {
+ $row += $this->cellRow - $this->referenceRow;
+ }
+
+ if (!empty($matches[4])) {
+ $worksheet = $this->worksheet->getParentOrThrow()->getSheetByName(trim($matches[4], "'"));
+ if ($worksheet === null) {
+ return $this->wrapValue(null);
+ }
+
+ return $this->wrapValue(
+ $worksheet
+ ->getCell(str_replace('$', '', "{$column}{$row}"))
+ ->getCalculatedValue()
+ );
+ }
+
+ return $this->wrapValue(
+ $this->worksheet
+ ->getCell(str_replace('$', '', "{$column}{$row}"))
+ ->getCalculatedValue()
+ );
+ }
+
+ protected function cellConditionCheck(string $condition): string
+ {
+ $splitCondition = explode(Calculation::FORMULA_STRING_QUOTE, $condition);
+ $i = false;
+ foreach ($splitCondition as &$value) {
+ // Only count/replace in alternating array entries (ie. not in quoted strings)
+ $i = $i === false;
+ if ($i) {
+ $value = (string) preg_replace_callback(
+ '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
+ [$this, 'conditionCellAdjustment'],
+ $value
+ );
+ }
+ }
+ unset($value);
+
+ // Then rebuild the condition string to return it
+ return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
+ }
+
+ protected function adjustConditionsForCellReferences(array $conditions): array
+ {
+ return array_map(
+ [$this, 'cellConditionCheck'],
+ $conditions
+ );
+ }
+
+ protected function processOperatorComparison(Conditional $conditional): bool
+ {
+ if (array_key_exists($conditional->getOperatorType(), self::COMPARISON_RANGE_OPERATORS)) {
+ return $this->processRangeOperator($conditional);
+ }
+
+ $operator = self::COMPARISON_OPERATORS[$conditional->getOperatorType()];
+ $conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
+ $expression = sprintf('%s%s%s', (string) $this->wrapCellValue(), $operator, (string) array_pop($conditions));
+
+ return $this->evaluateExpression($expression);
+ }
+
+ protected function processRangeOperator(Conditional $conditional): bool
+ {
+ $conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
+ sort($conditions);
+ $expression = sprintf(
+ (string) preg_replace(
+ '/\bA1\b/i',
+ (string) $this->wrapCellValue(),
+ self::COMPARISON_RANGE_OPERATORS[$conditional->getOperatorType()]
+ ),
+ ...$conditions
+ );
+
+ return $this->evaluateExpression($expression);
+ }
+
+ protected function processDuplicatesComparison(Conditional $conditional): bool
+ {
+ $worksheetName = $this->cell->getWorksheet()->getTitle();
+
+ $expression = sprintf(
+ self::COMPARISON_DUPLICATES_OPERATORS[$conditional->getConditionType()],
+ $worksheetName,
+ $this->conditionalRange,
+ $this->cellConditionCheck($this->cell->getCalculatedValue())
+ );
+
+ return $this->evaluateExpression($expression);
+ }
+
+ protected function processExpression(Conditional $conditional): bool
+ {
+ $conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
+ $expression = array_pop($conditions);
+
+ $expression = (string) preg_replace(
+ '/\b' . $this->referenceCell . '\b/i',
+ (string) $this->wrapCellValue(),
+ $expression
+ );
+
+ return $this->evaluateExpression($expression);
+ }
+
+ protected function evaluateExpression(string $expression): bool
+ {
+ $expression = "={$expression}";
+
+ try {
+ $this->engine->flushInstance();
+ $result = (bool) $this->engine->calculateFormula($expression);
+ } catch (Exception) {
+ return false;
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php
new file mode 100644
index 0000000..30f5c97
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php
@@ -0,0 +1,39 @@
+cellMatcher = new CellMatcher($cell, $conditionalRange);
+ $this->styleMerger = new StyleMerger($cell->getStyle());
+ }
+
+ /**
+ * @param Conditional[] $conditionalStyles
+ */
+ public function matchConditions(array $conditionalStyles = []): Style
+ {
+ foreach ($conditionalStyles as $conditional) {
+ /** @var Conditional $conditional */
+ if ($this->cellMatcher->evaluateConditional($conditional) === true) {
+ // Merging the conditional style into the base style goes in here
+ $this->styleMerger->mergeStyle($conditional->getStyle());
+ if ($conditional->getStopIfTrue() === true) {
+ break;
+ }
+ }
+ }
+
+ return $this->styleMerger->getStyle();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php
new file mode 100644
index 0000000..7fcc080
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php
@@ -0,0 +1,92 @@
+minimumConditionalFormatValueObject;
+ }
+
+ public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
+ {
+ $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ public function getMidpointConditionalFormatValueObject(): ?ConditionalFormatValueObject
+ {
+ return $this->midpointConditionalFormatValueObject;
+ }
+
+ public function setMidpointConditionalFormatValueObject(ConditionalFormatValueObject $midpointConditionalFormatValueObject): self
+ {
+ $this->midpointConditionalFormatValueObject = $midpointConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ public function getMaximumConditionalFormatValueObject(): ?ConditionalFormatValueObject
+ {
+ return $this->maximumConditionalFormatValueObject;
+ }
+
+ public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
+ {
+ $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ public function getMinimumColor(): ?Color
+ {
+ return $this->minimumColor;
+ }
+
+ public function setMinimumColor(Color $minimumColor): self
+ {
+ $this->minimumColor = $minimumColor;
+
+ return $this;
+ }
+
+ public function getMidpointColor(): ?Color
+ {
+ return $this->midpointColor;
+ }
+
+ public function setMidpointColor(Color $midpointColor): self
+ {
+ $this->midpointColor = $midpointColor;
+
+ return $this;
+ }
+
+ public function getMaximumColor(): ?Color
+ {
+ return $this->maximumColor;
+ }
+
+ public function setMaximumColor(Color $maximumColor): self
+ {
+ $this->maximumColor = $maximumColor;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
new file mode 100644
index 0000000..370f102
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
@@ -0,0 +1,76 @@
+showValue;
+ }
+
+ public function setShowValue(bool $showValue): self
+ {
+ $this->showValue = $showValue;
+
+ return $this;
+ }
+
+ public function getMinimumConditionalFormatValueObject(): ?ConditionalFormatValueObject
+ {
+ return $this->minimumConditionalFormatValueObject;
+ }
+
+ public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
+ {
+ $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ public function getMaximumConditionalFormatValueObject(): ?ConditionalFormatValueObject
+ {
+ return $this->maximumConditionalFormatValueObject;
+ }
+
+ public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
+ {
+ $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ public function getColor(): string
+ {
+ return $this->color;
+ }
+
+ public function setColor(string $color): self
+ {
+ $this->color = $color;
+
+ return $this;
+ }
+
+ public function getConditionalFormattingRuleExt(): ?ConditionalFormattingRuleExtension
+ {
+ return $this->conditionalFormattingRuleExt;
+ }
+
+ public function setConditionalFormattingRuleExt(ConditionalFormattingRuleExtension $conditionalFormattingRuleExt): self
+ {
+ $this->conditionalFormattingRuleExt = $conditionalFormattingRuleExt;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
new file mode 100644
index 0000000..0260d73
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
@@ -0,0 +1,289 @@
+ attributes */
+
+ /** @var int */
+ private $minLength;
+
+ /** @var int */
+ private $maxLength;
+
+ /** @var null|bool */
+ private $border;
+
+ /** @var null|bool */
+ private $gradient;
+
+ /** @var string */
+ private $direction;
+
+ /** @var null|bool */
+ private $negativeBarBorderColorSameAsPositive;
+
+ /** @var string */
+ private $axisPosition;
+
+ // children
+
+ /** @var ConditionalFormatValueObject */
+ private $maximumConditionalFormatValueObject;
+
+ /** @var ConditionalFormatValueObject */
+ private $minimumConditionalFormatValueObject;
+
+ /** @var string */
+ private $borderColor;
+
+ /** @var string */
+ private $negativeFillColor;
+
+ /** @var string */
+ private $negativeBorderColor;
+
+ /** @var array */
+ private $axisColor = [
+ 'rgb' => null,
+ 'theme' => null,
+ 'tint' => null,
+ ];
+
+ public function getXmlAttributes(): array
+ {
+ $ret = [];
+ foreach (['minLength', 'maxLength', 'direction', 'axisPosition'] as $attrKey) {
+ if (null !== $this->{$attrKey}) {
+ $ret[$attrKey] = $this->{$attrKey};
+ }
+ }
+ foreach (['border', 'gradient', 'negativeBarBorderColorSameAsPositive'] as $attrKey) {
+ if (null !== $this->{$attrKey}) {
+ $ret[$attrKey] = $this->{$attrKey} ? '1' : '0';
+ }
+ }
+
+ return $ret;
+ }
+
+ public function getXmlElements(): array
+ {
+ $ret = [];
+ $elms = ['borderColor', 'negativeFillColor', 'negativeBorderColor'];
+ foreach ($elms as $elmKey) {
+ if (null !== $this->{$elmKey}) {
+ $ret[$elmKey] = ['rgb' => $this->{$elmKey}];
+ }
+ }
+ foreach (array_filter($this->axisColor) as $attrKey => $axisColorAttr) {
+ if (!isset($ret['axisColor'])) {
+ $ret['axisColor'] = [];
+ }
+ $ret['axisColor'][$attrKey] = $axisColorAttr;
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMinLength()
+ {
+ return $this->minLength;
+ }
+
+ public function setMinLength(int $minLength): self
+ {
+ $this->minLength = $minLength;
+
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMaxLength()
+ {
+ return $this->maxLength;
+ }
+
+ public function setMaxLength(int $maxLength): self
+ {
+ $this->maxLength = $maxLength;
+
+ return $this;
+ }
+
+ /**
+ * @return null|bool
+ */
+ public function getBorder()
+ {
+ return $this->border;
+ }
+
+ public function setBorder(bool $border): self
+ {
+ $this->border = $border;
+
+ return $this;
+ }
+
+ /**
+ * @return null|bool
+ */
+ public function getGradient()
+ {
+ return $this->gradient;
+ }
+
+ public function setGradient(bool $gradient): self
+ {
+ $this->gradient = $gradient;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDirection()
+ {
+ return $this->direction;
+ }
+
+ public function setDirection(string $direction): self
+ {
+ $this->direction = $direction;
+
+ return $this;
+ }
+
+ /**
+ * @return null|bool
+ */
+ public function getNegativeBarBorderColorSameAsPositive()
+ {
+ return $this->negativeBarBorderColorSameAsPositive;
+ }
+
+ public function setNegativeBarBorderColorSameAsPositive(bool $negativeBarBorderColorSameAsPositive): self
+ {
+ $this->negativeBarBorderColorSameAsPositive = $negativeBarBorderColorSameAsPositive;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAxisPosition()
+ {
+ return $this->axisPosition;
+ }
+
+ public function setAxisPosition(string $axisPosition): self
+ {
+ $this->axisPosition = $axisPosition;
+
+ return $this;
+ }
+
+ /**
+ * @return ConditionalFormatValueObject
+ */
+ public function getMaximumConditionalFormatValueObject()
+ {
+ return $this->maximumConditionalFormatValueObject;
+ }
+
+ public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
+ {
+ $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ /**
+ * @return ConditionalFormatValueObject
+ */
+ public function getMinimumConditionalFormatValueObject()
+ {
+ return $this->minimumConditionalFormatValueObject;
+ }
+
+ public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
+ {
+ $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBorderColor()
+ {
+ return $this->borderColor;
+ }
+
+ public function setBorderColor(string $borderColor): self
+ {
+ $this->borderColor = $borderColor;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNegativeFillColor()
+ {
+ return $this->negativeFillColor;
+ }
+
+ public function setNegativeFillColor(string $negativeFillColor): self
+ {
+ $this->negativeFillColor = $negativeFillColor;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNegativeBorderColor()
+ {
+ return $this->negativeBorderColor;
+ }
+
+ public function setNegativeBorderColor(string $negativeBorderColor): self
+ {
+ $this->negativeBorderColor = $negativeBorderColor;
+
+ return $this;
+ }
+
+ public function getAxisColor(): array
+ {
+ return $this->axisColor;
+ }
+
+ /**
+ * @param null|mixed $theme
+ * @param null|mixed $tint
+ */
+ public function setAxisColor(mixed $rgb, $theme = null, $tint = null): self
+ {
+ $this->axisColor = [
+ 'rgb' => $rgb,
+ 'theme' => $theme,
+ 'tint' => $tint,
+ ];
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
new file mode 100644
index 0000000..43f4808
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
@@ -0,0 +1,55 @@
+type = $type;
+ $this->value = $value;
+ $this->cellFormula = $cellFormula;
+ }
+
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ public function getValue(): null|float|int|string
+ {
+ return $this->value;
+ }
+
+ public function setValue(null|float|int|string $value): self
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ public function getCellFormula(): mixed
+ {
+ return $this->cellFormula;
+ }
+
+ public function setCellFormula(mixed $cellFormula): self
+ {
+ $this->cellFormula = $cellFormula;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
new file mode 100644
index 0000000..3ad69db
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
@@ -0,0 +1,215 @@
+id = '{' . $this->generateUuid() . '}';
+ } else {
+ $this->id = $id;
+ }
+ $this->cfRule = $cfRule;
+ }
+
+ private function generateUuid(): string
+ {
+ $chars = str_split('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx');
+
+ foreach ($chars as $i => $char) {
+ if ($char === 'x') {
+ $chars[$i] = dechex(random_int(0, 15));
+ } elseif ($char === 'y') {
+ $chars[$i] = dechex(random_int(8, 11));
+ }
+ }
+
+ return implode('', $chars);
+ }
+
+ public static function parseExtLstXml(?SimpleXMLElement $extLstXml): array
+ {
+ $conditionalFormattingRuleExtensions = [];
+ $conditionalFormattingRuleExtensionXml = null;
+ if ($extLstXml instanceof SimpleXMLElement) {
+ foreach ((count($extLstXml) > 0 ? $extLstXml : [$extLstXml]) as $extLst) {
+ //this uri is conditionalFormattings
+ //https://docs.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/07d607af-5618-4ca2-b683-6a78dc0d9627
+ if (isset($extLst->ext['uri']) && (string) $extLst->ext['uri'] === '{78C0D931-6437-407d-A8EE-F0AAD7539E65}') {
+ $conditionalFormattingRuleExtensionXml = $extLst->ext;
+ }
+ }
+
+ if ($conditionalFormattingRuleExtensionXml) {
+ $ns = $conditionalFormattingRuleExtensionXml->getNamespaces(true);
+ $extFormattingsXml = $conditionalFormattingRuleExtensionXml->children($ns['x14']);
+
+ foreach ($extFormattingsXml->children($ns['x14']) as $extFormattingXml) {
+ $extCfRuleXml = $extFormattingXml->cfRule;
+ $attributes = $extCfRuleXml->attributes();
+ if (!$attributes || ((string) $attributes->type) !== Conditional::CONDITION_DATABAR) {
+ continue;
+ }
+
+ $extFormattingRuleObj = new self((string) $attributes->id);
+ $extFormattingRuleObj->setSqref((string) $extFormattingXml->children($ns['xm'])->sqref);
+ $conditionalFormattingRuleExtensions[$extFormattingRuleObj->getId()] = $extFormattingRuleObj;
+
+ $extDataBarObj = new ConditionalDataBarExtension();
+ $extFormattingRuleObj->setDataBarExt($extDataBarObj);
+ $dataBarXml = $extCfRuleXml->dataBar;
+ self::parseExtDataBarAttributesFromXml($extDataBarObj, $dataBarXml);
+ self::parseExtDataBarElementChildrenFromXml($extDataBarObj, $dataBarXml, $ns);
+ }
+ }
+ }
+
+ return $conditionalFormattingRuleExtensions;
+ }
+
+ private static function parseExtDataBarAttributesFromXml(
+ ConditionalDataBarExtension $extDataBarObj,
+ SimpleXMLElement $dataBarXml
+ ): void {
+ $dataBarAttribute = $dataBarXml->attributes();
+ if ($dataBarAttribute === null) {
+ return;
+ }
+ if ($dataBarAttribute->minLength) {
+ $extDataBarObj->setMinLength((int) $dataBarAttribute->minLength);
+ }
+ if ($dataBarAttribute->maxLength) {
+ $extDataBarObj->setMaxLength((int) $dataBarAttribute->maxLength);
+ }
+ if ($dataBarAttribute->border) {
+ $extDataBarObj->setBorder((bool) (string) $dataBarAttribute->border);
+ }
+ if ($dataBarAttribute->gradient) {
+ $extDataBarObj->setGradient((bool) (string) $dataBarAttribute->gradient);
+ }
+ if ($dataBarAttribute->direction) {
+ $extDataBarObj->setDirection((string) $dataBarAttribute->direction);
+ }
+ if ($dataBarAttribute->negativeBarBorderColorSameAsPositive) {
+ $extDataBarObj->setNegativeBarBorderColorSameAsPositive((bool) (string) $dataBarAttribute->negativeBarBorderColorSameAsPositive);
+ }
+ if ($dataBarAttribute->axisPosition) {
+ $extDataBarObj->setAxisPosition((string) $dataBarAttribute->axisPosition);
+ }
+ }
+
+ private static function parseExtDataBarElementChildrenFromXml(ConditionalDataBarExtension $extDataBarObj, SimpleXMLElement $dataBarXml, array $ns): void
+ {
+ if ($dataBarXml->borderColor) {
+ $attributes = $dataBarXml->borderColor->attributes();
+ if ($attributes !== null) {
+ $extDataBarObj->setBorderColor((string) $attributes['rgb']);
+ }
+ }
+ if ($dataBarXml->negativeFillColor) {
+ $attributes = $dataBarXml->negativeFillColor->attributes();
+ if ($attributes !== null) {
+ $extDataBarObj->setNegativeFillColor((string) $attributes['rgb']);
+ }
+ }
+ if ($dataBarXml->negativeBorderColor) {
+ $attributes = $dataBarXml->negativeBorderColor->attributes();
+ if ($attributes !== null) {
+ $extDataBarObj->setNegativeBorderColor((string) $attributes['rgb']);
+ }
+ }
+ if ($dataBarXml->axisColor) {
+ $axisColorAttr = $dataBarXml->axisColor->attributes();
+ if ($axisColorAttr !== null) {
+ $extDataBarObj->setAxisColor((string) $axisColorAttr['rgb'], (string) $axisColorAttr['theme'], (string) $axisColorAttr['tint']);
+ }
+ }
+ $cfvoIndex = 0;
+ foreach ($dataBarXml->cfvo as $cfvo) {
+ $f = (string) $cfvo->children($ns['xm'])->f;
+ $attributes = $cfvo->attributes();
+ if (!($attributes)) {
+ continue;
+ }
+
+ if ($cfvoIndex === 0) {
+ $extDataBarObj->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $attributes['type'], null, (empty($f) ? null : $f)));
+ }
+ if ($cfvoIndex === 1) {
+ $extDataBarObj->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $attributes['type'], null, (empty($f) ? null : $f)));
+ }
+ ++$cfvoIndex;
+ }
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function setId(mixed $id): self
+ {
+ $this->id = $id;
+
+ return $this;
+ }
+
+ public function getCfRule(): string
+ {
+ return $this->cfRule;
+ }
+
+ public function setCfRule(string $cfRule): self
+ {
+ $this->cfRule = $cfRule;
+
+ return $this;
+ }
+
+ public function getDataBarExt(): ConditionalDataBarExtension
+ {
+ return $this->dataBar;
+ }
+
+ public function setDataBarExt(ConditionalDataBarExtension $dataBar): self
+ {
+ $this->dataBar = $dataBar;
+
+ return $this;
+ }
+
+ public function getSqref(): string
+ {
+ return $this->sqref;
+ }
+
+ public function setSqref(string $sqref): self
+ {
+ $this->sqref = $sqref;
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
new file mode 100644
index 0000000..9388848
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
@@ -0,0 +1,115 @@
+baseStyle = $baseStyle;
+ }
+
+ public function getStyle(): Style
+ {
+ return $this->baseStyle;
+ }
+
+ public function mergeStyle(Style $style): void
+ {
+ if ($style->getNumberFormat() !== null && $style->getNumberFormat()->getFormatCode() !== null) {
+ $this->baseStyle->getNumberFormat()->setFormatCode($style->getNumberFormat()->getFormatCode());
+ }
+
+ if ($style->getFont() !== null) {
+ $this->mergeFontStyle($this->baseStyle->getFont(), $style->getFont());
+ }
+
+ if ($style->getFill() !== null) {
+ $this->mergeFillStyle($this->baseStyle->getFill(), $style->getFill());
+ }
+
+ if ($style->getBorders() !== null) {
+ $this->mergeBordersStyle($this->baseStyle->getBorders(), $style->getBorders());
+ }
+ }
+
+ protected function mergeFontStyle(Font $baseFontStyle, Font $fontStyle): void
+ {
+ if ($fontStyle->getBold() !== null) {
+ $baseFontStyle->setBold($fontStyle->getBold());
+ }
+
+ if ($fontStyle->getItalic() !== null) {
+ $baseFontStyle->setItalic($fontStyle->getItalic());
+ }
+
+ if ($fontStyle->getStrikethrough() !== null) {
+ $baseFontStyle->setStrikethrough($fontStyle->getStrikethrough());
+ }
+
+ if ($fontStyle->getUnderline() !== null) {
+ $baseFontStyle->setUnderline($fontStyle->getUnderline());
+ }
+
+ if ($fontStyle->getColor() !== null && $fontStyle->getColor()->getARGB() !== null) {
+ $baseFontStyle->setColor($fontStyle->getColor());
+ }
+ }
+
+ protected function mergeFillStyle(Fill $baseFillStyle, Fill $fillStyle): void
+ {
+ if ($fillStyle->getFillType() !== null) {
+ $baseFillStyle->setFillType($fillStyle->getFillType());
+ }
+
+ //if ($fillStyle->getRotation() !== null) {
+ $baseFillStyle->setRotation($fillStyle->getRotation());
+ //}
+
+ if ($fillStyle->getStartColor() !== null && $fillStyle->getStartColor()->getARGB() !== null) {
+ $baseFillStyle->setStartColor($fillStyle->getStartColor());
+ }
+
+ if ($fillStyle->getEndColor() !== null && $fillStyle->getEndColor()->getARGB() !== null) {
+ $baseFillStyle->setEndColor($fillStyle->getEndColor());
+ }
+ }
+
+ protected function mergeBordersStyle(Borders $baseBordersStyle, Borders $bordersStyle): void
+ {
+ if ($bordersStyle->getTop() !== null) {
+ $this->mergeBorderStyle($baseBordersStyle->getTop(), $bordersStyle->getTop());
+ }
+
+ if ($bordersStyle->getBottom() !== null) {
+ $this->mergeBorderStyle($baseBordersStyle->getBottom(), $bordersStyle->getBottom());
+ }
+
+ if ($bordersStyle->getLeft() !== null) {
+ $this->mergeBorderStyle($baseBordersStyle->getLeft(), $bordersStyle->getLeft());
+ }
+
+ if ($bordersStyle->getRight() !== null) {
+ $this->mergeBorderStyle($baseBordersStyle->getRight(), $bordersStyle->getRight());
+ }
+ }
+
+ protected function mergeBorderStyle(Border $baseBorderStyle, Border $borderStyle): void
+ {
+ //if ($borderStyle->getBorderStyle() !== null) {
+ $baseBorderStyle->setBorderStyle($borderStyle->getBorderStyle());
+ //}
+
+ if ($borderStyle->getColor() !== null && $borderStyle->getColor()->getARGB() !== null) {
+ $baseBorderStyle->setColor($borderStyle->getColor());
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
new file mode 100644
index 0000000..2791a2b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
@@ -0,0 +1,66 @@
+cellRange = $cellRange;
+ }
+
+ public function newRule(string $ruleType): WizardInterface
+ {
+ return match ($ruleType) {
+ self::CELL_VALUE => new Wizard\CellValue($this->cellRange),
+ self::TEXT_VALUE => new Wizard\TextValue($this->cellRange),
+ self::BLANKS => new Wizard\Blanks($this->cellRange, true),
+ self::NOT_BLANKS => new Wizard\Blanks($this->cellRange, false),
+ self::ERRORS => new Wizard\Errors($this->cellRange, true),
+ self::NOT_ERRORS => new Wizard\Errors($this->cellRange, false),
+ self::EXPRESSION, self::FORMULA => new Wizard\Expression($this->cellRange),
+ self::DATES_OCCURRING => new Wizard\DateValue($this->cellRange),
+ self::DUPLICATES => new Wizard\Duplicates($this->cellRange, false),
+ self::UNIQUE => new Wizard\Duplicates($this->cellRange, true),
+ default => throw new Exception('No wizard exists for this CF rule type'),
+ };
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ $conditionalType = $conditional->getConditionType();
+
+ return match ($conditionalType) {
+ Conditional::CONDITION_CELLIS => Wizard\CellValue::fromConditional($conditional, $cellRange),
+ Conditional::CONDITION_CONTAINSTEXT, Conditional::CONDITION_NOTCONTAINSTEXT, Conditional::CONDITION_BEGINSWITH, Conditional::CONDITION_ENDSWITH => Wizard\TextValue::fromConditional($conditional, $cellRange),
+ Conditional::CONDITION_CONTAINSBLANKS, Conditional::CONDITION_NOTCONTAINSBLANKS => Wizard\Blanks::fromConditional($conditional, $cellRange),
+ Conditional::CONDITION_CONTAINSERRORS, Conditional::CONDITION_NOTCONTAINSERRORS => Wizard\Errors::fromConditional($conditional, $cellRange),
+ Conditional::CONDITION_TIMEPERIOD => Wizard\DateValue::fromConditional($conditional, $cellRange),
+ Conditional::CONDITION_EXPRESSION => Wizard\Expression::fromConditional($conditional, $cellRange),
+ Conditional::CONDITION_DUPLICATES, Conditional::CONDITION_UNIQUE => Wizard\Duplicates::fromConditional($conditional, $cellRange),
+ default => throw new Exception('No wizard exists for this CF rule type'),
+ };
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
new file mode 100644
index 0000000..dae96c8
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
@@ -0,0 +1,96 @@
+ false,
+ 'isBlank' => true,
+ 'notEmpty' => false,
+ 'empty' => true,
+ ];
+
+ protected const EXPRESSIONS = [
+ Wizard::NOT_BLANKS => 'LEN(TRIM(%s))>0',
+ Wizard::BLANKS => 'LEN(TRIM(%s))=0',
+ ];
+
+ protected bool $inverse;
+
+ public function __construct(string $cellRange, bool $inverse = false)
+ {
+ parent::__construct($cellRange);
+ $this->inverse = $inverse;
+ }
+
+ protected function inverse(bool $inverse): void
+ {
+ $this->inverse = $inverse;
+ }
+
+ protected function getExpression(): void
+ {
+ $this->expression = sprintf(
+ self::EXPRESSIONS[$this->inverse ? Wizard::BLANKS : Wizard::NOT_BLANKS],
+ $this->referenceCell
+ );
+ }
+
+ public function getConditional(): Conditional
+ {
+ $this->getExpression();
+
+ $conditional = new Conditional();
+ $conditional->setConditionType(
+ $this->inverse ? Conditional::CONDITION_CONTAINSBLANKS : Conditional::CONDITION_NOTCONTAINSBLANKS
+ );
+ $conditional->setConditions([$this->expression]);
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if (
+ $conditional->getConditionType() !== Conditional::CONDITION_CONTAINSBLANKS
+ && $conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSBLANKS
+ ) {
+ throw new Exception('Conditional is not a Blanks CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+ $wizard->inverse = $conditional->getConditionType() === Conditional::CONDITION_CONTAINSBLANKS;
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, $arguments): self
+ {
+ if (!array_key_exists($methodName, self::OPERATORS)) {
+ throw new Exception('Invalid Operation for Blanks CF Rule Wizard');
+ }
+
+ $this->inverse(self::OPERATORS[$methodName]);
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
new file mode 100644
index 0000000..ccdc081
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
@@ -0,0 +1,189 @@
+ Conditional::OPERATOR_EQUAL,
+ 'notEquals' => Conditional::OPERATOR_NOTEQUAL,
+ 'greaterThan' => Conditional::OPERATOR_GREATERTHAN,
+ 'greaterThanOrEqual' => Conditional::OPERATOR_GREATERTHANOREQUAL,
+ 'lessThan' => Conditional::OPERATOR_LESSTHAN,
+ 'lessThanOrEqual' => Conditional::OPERATOR_LESSTHANOREQUAL,
+ 'between' => Conditional::OPERATOR_BETWEEN,
+ 'notBetween' => Conditional::OPERATOR_NOTBETWEEN,
+ ];
+
+ protected const SINGLE_OPERATORS = CellMatcher::COMPARISON_OPERATORS;
+
+ protected const RANGE_OPERATORS = CellMatcher::COMPARISON_RANGE_OPERATORS;
+
+ /** @var string */
+ protected $operator = Conditional::OPERATOR_EQUAL;
+
+ /** @var array */
+ protected $operand = [0];
+
+ /**
+ * @var string[]
+ */
+ protected $operandValueType = [];
+
+ public function __construct(string $cellRange)
+ {
+ parent::__construct($cellRange);
+ }
+
+ protected function operator(string $operator): void
+ {
+ if ((!isset(self::SINGLE_OPERATORS[$operator])) && (!isset(self::RANGE_OPERATORS[$operator]))) {
+ throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
+ }
+
+ $this->operator = $operator;
+ }
+
+ protected function operand(int $index, mixed $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
+ {
+ if (is_string($operand)) {
+ $operand = $this->validateOperand($operand, $operandValueType);
+ }
+
+ $this->operand[$index] = $operand;
+ $this->operandValueType[$index] = $operandValueType;
+ }
+
+ /**
+ * @return float|int|string
+ */
+ protected function wrapValue(mixed $value, string $operandValueType)
+ {
+ if (!is_numeric($value) && !is_bool($value) && null !== $value) {
+ if ($operandValueType === Wizard::VALUE_TYPE_LITERAL) {
+ return '"' . str_replace('"', '""', $value) . '"';
+ }
+
+ return $this->cellConditionCheck($value);
+ }
+
+ if (null === $value) {
+ $value = 'NULL';
+ } elseif (is_bool($value)) {
+ $value = $value ? 'TRUE' : 'FALSE';
+ }
+
+ return $value;
+ }
+
+ public function getConditional(): Conditional
+ {
+ if (!isset(self::RANGE_OPERATORS[$this->operator])) {
+ unset($this->operand[1], $this->operandValueType[1]);
+ }
+ $values = array_map([$this, 'wrapValue'], $this->operand, $this->operandValueType);
+
+ $conditional = new Conditional();
+ $conditional->setConditionType(Conditional::CONDITION_CELLIS);
+ $conditional->setOperatorType($this->operator);
+ $conditional->setConditions($values);
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ protected static function unwrapString(string $condition): string
+ {
+ if ((str_starts_with($condition, '"')) && (str_starts_with(strrev($condition), '"'))) {
+ $condition = substr($condition, 1, -1);
+ }
+
+ return str_replace('""', '"', $condition);
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if ($conditional->getConditionType() !== Conditional::CONDITION_CELLIS) {
+ throw new Exception('Conditional is not a Cell Value CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+
+ $wizard->operator = $conditional->getOperatorType();
+ $conditions = $conditional->getConditions();
+ foreach ($conditions as $index => $condition) {
+ // Best-guess to try and identify if the text is a string literal, a cell reference or a formula?
+ $operandValueType = Wizard::VALUE_TYPE_LITERAL;
+ if (is_string($condition)) {
+ if (Calculation::keyInExcelConstants($condition)) {
+ $condition = Calculation::getExcelConstants($condition);
+ } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '$/i', $condition)) {
+ $operandValueType = Wizard::VALUE_TYPE_CELL;
+ $condition = self::reverseAdjustCellRef($condition, $cellRange);
+ } elseif (
+ preg_match('/\(\)/', $condition)
+ || preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
+ ) {
+ $operandValueType = Wizard::VALUE_TYPE_FORMULA;
+ $condition = self::reverseAdjustCellRef($condition, $cellRange);
+ } else {
+ $condition = self::unwrapString($condition);
+ }
+ }
+ $wizard->operand($index, $condition, $operandValueType);
+ }
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, array $arguments): self
+ {
+ if (!isset(self::MAGIC_OPERATIONS[$methodName]) && $methodName !== 'and') {
+ throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
+ }
+
+ if ($methodName === 'and') {
+ if (!isset(self::RANGE_OPERATORS[$this->operator])) {
+ throw new Exception('AND Value is only appropriate for range operators');
+ }
+
+ $this->operand(1, ...$arguments);
+
+ return $this;
+ }
+
+ $this->operator(self::MAGIC_OPERATIONS[$methodName]);
+ //$this->operand(0, ...$arguments);
+ if (count($arguments) < 2) {
+ $this->operand(0, $arguments[0]);
+ } else {
+ $this->operand(0, $arguments[0], $arguments[1]);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
new file mode 100644
index 0000000..453dcdf
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
@@ -0,0 +1,111 @@
+ Conditional::TIMEPERIOD_YESTERDAY,
+ 'today' => Conditional::TIMEPERIOD_TODAY,
+ 'tomorrow' => Conditional::TIMEPERIOD_TOMORROW,
+ 'lastSevenDays' => Conditional::TIMEPERIOD_LAST_7_DAYS,
+ 'last7Days' => Conditional::TIMEPERIOD_LAST_7_DAYS,
+ 'lastWeek' => Conditional::TIMEPERIOD_LAST_WEEK,
+ 'thisWeek' => Conditional::TIMEPERIOD_THIS_WEEK,
+ 'nextWeek' => Conditional::TIMEPERIOD_NEXT_WEEK,
+ 'lastMonth' => Conditional::TIMEPERIOD_LAST_MONTH,
+ 'thisMonth' => Conditional::TIMEPERIOD_THIS_MONTH,
+ 'nextMonth' => Conditional::TIMEPERIOD_NEXT_MONTH,
+ ];
+
+ protected const EXPRESSIONS = [
+ Conditional::TIMEPERIOD_YESTERDAY => 'FLOOR(%s,1)=TODAY()-1',
+ Conditional::TIMEPERIOD_TODAY => 'FLOOR(%s,1)=TODAY()',
+ Conditional::TIMEPERIOD_TOMORROW => 'FLOOR(%s,1)=TODAY()+1',
+ Conditional::TIMEPERIOD_LAST_7_DAYS => 'AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())',
+ Conditional::TIMEPERIOD_LAST_WEEK => 'AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))',
+ Conditional::TIMEPERIOD_THIS_WEEK => 'AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))',
+ Conditional::TIMEPERIOD_NEXT_WEEK => 'AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))',
+ Conditional::TIMEPERIOD_LAST_MONTH => 'AND(MONTH(%s)=MONTH(EDATE(TODAY(),0-1)),YEAR(%s)=YEAR(EDATE(TODAY(),0-1)))',
+ Conditional::TIMEPERIOD_THIS_MONTH => 'AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))',
+ Conditional::TIMEPERIOD_NEXT_MONTH => 'AND(MONTH(%s)=MONTH(EDATE(TODAY(),0+1)),YEAR(%s)=YEAR(EDATE(TODAY(),0+1)))',
+ ];
+
+ /** @var string */
+ protected $operator;
+
+ public function __construct(string $cellRange)
+ {
+ parent::__construct($cellRange);
+ }
+
+ protected function operator(string $operator): void
+ {
+ $this->operator = $operator;
+ }
+
+ protected function setExpression(): void
+ {
+ $referenceCount = substr_count(self::EXPRESSIONS[$this->operator], '%s');
+ $references = array_fill(0, $referenceCount, $this->referenceCell);
+ $this->expression = sprintf(self::EXPRESSIONS[$this->operator], ...$references);
+ }
+
+ public function getConditional(): Conditional
+ {
+ $this->setExpression();
+
+ $conditional = new Conditional();
+ $conditional->setConditionType(Conditional::CONDITION_TIMEPERIOD);
+ $conditional->setText($this->operator);
+ $conditional->setConditions([$this->expression]);
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if ($conditional->getConditionType() !== Conditional::CONDITION_TIMEPERIOD) {
+ throw new Exception('Conditional is not a Date Value CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+ $wizard->operator = $conditional->getText();
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, $arguments): self
+ {
+ if (!isset(self::MAGIC_OPERATIONS[$methodName])) {
+ throw new Exception('Invalid Operation for Date Value CF Rule Wizard');
+ }
+
+ $this->operator(self::MAGIC_OPERATIONS[$methodName]);
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php
new file mode 100644
index 0000000..57f4b5c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php
@@ -0,0 +1,75 @@
+ false,
+ 'unique' => true,
+ ];
+
+ protected bool $inverse;
+
+ public function __construct(string $cellRange, bool $inverse = false)
+ {
+ parent::__construct($cellRange);
+ $this->inverse = $inverse;
+ }
+
+ protected function inverse(bool $inverse): void
+ {
+ $this->inverse = $inverse;
+ }
+
+ public function getConditional(): Conditional
+ {
+ $conditional = new Conditional();
+ $conditional->setConditionType(
+ $this->inverse ? Conditional::CONDITION_UNIQUE : Conditional::CONDITION_DUPLICATES
+ );
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if (
+ $conditional->getConditionType() !== Conditional::CONDITION_DUPLICATES
+ && $conditional->getConditionType() !== Conditional::CONDITION_UNIQUE
+ ) {
+ throw new Exception('Conditional is not a Duplicates CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+ $wizard->inverse = $conditional->getConditionType() === Conditional::CONDITION_UNIQUE;
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, $arguments): self
+ {
+ if (!array_key_exists($methodName, self::OPERATORS)) {
+ throw new Exception('Invalid Operation for Errors CF Rule Wizard');
+ }
+
+ $this->inverse(self::OPERATORS[$methodName]);
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
new file mode 100644
index 0000000..4170b40
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
@@ -0,0 +1,92 @@
+ false,
+ 'isError' => true,
+ ];
+
+ protected const EXPRESSIONS = [
+ Wizard::NOT_ERRORS => 'NOT(ISERROR(%s))',
+ Wizard::ERRORS => 'ISERROR(%s)',
+ ];
+
+ protected bool $inverse;
+
+ public function __construct(string $cellRange, bool $inverse = false)
+ {
+ parent::__construct($cellRange);
+ $this->inverse = $inverse;
+ }
+
+ protected function inverse(bool $inverse): void
+ {
+ $this->inverse = $inverse;
+ }
+
+ protected function getExpression(): void
+ {
+ $this->expression = sprintf(
+ self::EXPRESSIONS[$this->inverse ? Wizard::ERRORS : Wizard::NOT_ERRORS],
+ $this->referenceCell
+ );
+ }
+
+ public function getConditional(): Conditional
+ {
+ $this->getExpression();
+
+ $conditional = new Conditional();
+ $conditional->setConditionType(
+ $this->inverse ? Conditional::CONDITION_CONTAINSERRORS : Conditional::CONDITION_NOTCONTAINSERRORS
+ );
+ $conditional->setConditions([$this->expression]);
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if (
+ $conditional->getConditionType() !== Conditional::CONDITION_CONTAINSERRORS
+ && $conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSERRORS
+ ) {
+ throw new Exception('Conditional is not an Errors CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+ $wizard->inverse = $conditional->getConditionType() === Conditional::CONDITION_CONTAINSERRORS;
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, $arguments): self
+ {
+ if (!array_key_exists($methodName, self::OPERATORS)) {
+ throw new Exception('Invalid Operation for Errors CF Rule Wizard');
+ }
+
+ $this->inverse(self::OPERATORS[$methodName]);
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php
new file mode 100644
index 0000000..01ef47a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php
@@ -0,0 +1,73 @@
+validateOperand($expression, Wizard::VALUE_TYPE_FORMULA);
+ $this->expression = $expression;
+
+ return $this;
+ }
+
+ public function getConditional(): Conditional
+ {
+ $expression = $this->adjustConditionsForCellReferences([$this->expression]);
+
+ $conditional = new Conditional();
+ $conditional->setConditionType(Conditional::CONDITION_EXPRESSION);
+ $conditional->setConditions($expression);
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if ($conditional->getConditionType() !== Conditional::CONDITION_EXPRESSION) {
+ throw new Exception('Conditional is not an Expression CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+ $wizard->expression = self::reverseAdjustCellRef((string) ($conditional->getConditions()[0]), $cellRange);
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, array $arguments): self
+ {
+ if ($methodName !== 'formula') {
+ throw new Exception('Invalid Operation for Expression CF Rule Wizard');
+ }
+
+ $this->expression(...$arguments);
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
new file mode 100644
index 0000000..0b845dd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
@@ -0,0 +1,164 @@
+ Conditional::OPERATOR_CONTAINSTEXT,
+ 'doesntContain' => Conditional::OPERATOR_NOTCONTAINS,
+ 'doesNotContain' => Conditional::OPERATOR_NOTCONTAINS,
+ 'beginsWith' => Conditional::OPERATOR_BEGINSWITH,
+ 'startsWith' => Conditional::OPERATOR_BEGINSWITH,
+ 'endsWith' => Conditional::OPERATOR_ENDSWITH,
+ ];
+
+ protected const OPERATORS = [
+ Conditional::OPERATOR_CONTAINSTEXT => Conditional::CONDITION_CONTAINSTEXT,
+ Conditional::OPERATOR_NOTCONTAINS => Conditional::CONDITION_NOTCONTAINSTEXT,
+ Conditional::OPERATOR_BEGINSWITH => Conditional::CONDITION_BEGINSWITH,
+ Conditional::OPERATOR_ENDSWITH => Conditional::CONDITION_ENDSWITH,
+ ];
+
+ protected const EXPRESSIONS = [
+ Conditional::OPERATOR_CONTAINSTEXT => 'NOT(ISERROR(SEARCH(%s,%s)))',
+ Conditional::OPERATOR_NOTCONTAINS => 'ISERROR(SEARCH(%s,%s))',
+ Conditional::OPERATOR_BEGINSWITH => 'LEFT(%s,LEN(%s))=%s',
+ Conditional::OPERATOR_ENDSWITH => 'RIGHT(%s,LEN(%s))=%s',
+ ];
+
+ /** @var string */
+ protected $operator;
+
+ /** @var string */
+ protected $operand;
+
+ /**
+ * @var string
+ */
+ protected $operandValueType;
+
+ public function __construct(string $cellRange)
+ {
+ parent::__construct($cellRange);
+ }
+
+ protected function operator(string $operator): void
+ {
+ if (!isset(self::OPERATORS[$operator])) {
+ throw new Exception('Invalid Operator for Text Value CF Rule Wizard');
+ }
+
+ $this->operator = $operator;
+ }
+
+ protected function operand(string $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
+ {
+ $operand = $this->validateOperand($operand, $operandValueType);
+
+ $this->operand = $operand;
+ $this->operandValueType = $operandValueType;
+ }
+
+ protected function wrapValue(string $value): string
+ {
+ return '"' . $value . '"';
+ }
+
+ protected function setExpression(): void
+ {
+ $operand = $this->operandValueType === Wizard::VALUE_TYPE_LITERAL
+ ? $this->wrapValue(str_replace('"', '""', $this->operand))
+ : $this->cellConditionCheck($this->operand);
+
+ if (
+ $this->operator === Conditional::OPERATOR_CONTAINSTEXT
+ || $this->operator === Conditional::OPERATOR_NOTCONTAINS
+ ) {
+ $this->expression = sprintf(self::EXPRESSIONS[$this->operator], $operand, $this->referenceCell);
+ } else {
+ $this->expression = sprintf(self::EXPRESSIONS[$this->operator], $this->referenceCell, $operand, $operand);
+ }
+ }
+
+ public function getConditional(): Conditional
+ {
+ $this->setExpression();
+
+ $conditional = new Conditional();
+ $conditional->setConditionType(self::OPERATORS[$this->operator]);
+ $conditional->setOperatorType($this->operator);
+ $conditional->setText(
+ $this->operandValueType !== Wizard::VALUE_TYPE_LITERAL
+ ? $this->cellConditionCheck($this->operand)
+ : $this->operand
+ );
+ $conditional->setConditions([$this->expression]);
+ $conditional->setStyle($this->getStyle());
+ $conditional->setStopIfTrue($this->getStopIfTrue());
+
+ return $conditional;
+ }
+
+ public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
+ {
+ if (!in_array($conditional->getConditionType(), self::OPERATORS, true)) {
+ throw new Exception('Conditional is not a Text Value CF Rule conditional');
+ }
+
+ $wizard = new self($cellRange);
+ $wizard->operator = (string) array_search($conditional->getConditionType(), self::OPERATORS, true);
+ $wizard->style = $conditional->getStyle();
+ $wizard->stopIfTrue = $conditional->getStopIfTrue();
+
+ // Best-guess to try and identify if the text is a string literal, a cell reference or a formula?
+ $wizard->operandValueType = Wizard::VALUE_TYPE_LITERAL;
+ $condition = $conditional->getText();
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '$/i', $condition)) {
+ $wizard->operandValueType = Wizard::VALUE_TYPE_CELL;
+ $condition = self::reverseAdjustCellRef($condition, $cellRange);
+ } elseif (
+ preg_match('/\(\)/', $condition)
+ || preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
+ ) {
+ $wizard->operandValueType = Wizard::VALUE_TYPE_FORMULA;
+ }
+ $wizard->operand = $condition;
+
+ return $wizard;
+ }
+
+ /**
+ * @param string $methodName
+ * @param mixed[] $arguments
+ */
+ public function __call($methodName, array $arguments): self
+ {
+ if (!isset(self::MAGIC_OPERATIONS[$methodName])) {
+ throw new Exception('Invalid Operation for Text Value CF Rule Wizard');
+ }
+
+ $this->operator(self::MAGIC_OPERATIONS[$methodName]);
+ //$this->operand(...$arguments);
+ if (count($arguments) < 2) {
+ $this->operand($arguments[0]);
+ } else {
+ $this->operand($arguments[0], $arguments[1]);
+ }
+
+ return $this;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
new file mode 100644
index 0000000..8f8848c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
@@ -0,0 +1,194 @@
+setCellRange($cellRange);
+ }
+
+ public function getCellRange(): string
+ {
+ return $this->cellRange;
+ }
+
+ public function setCellRange(string $cellRange): void
+ {
+ $this->cellRange = $cellRange;
+ $this->setReferenceCellForExpressions($cellRange);
+ }
+
+ protected function setReferenceCellForExpressions(string $conditionalRange): void
+ {
+ $conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($conditionalRange)));
+ [$this->referenceCell] = $conditionalRange[0];
+
+ [$this->referenceColumn, $this->referenceRow] = Coordinate::indexesFromString($this->referenceCell);
+ }
+
+ public function getStopIfTrue(): bool
+ {
+ return $this->stopIfTrue;
+ }
+
+ public function setStopIfTrue(bool $stopIfTrue): void
+ {
+ $this->stopIfTrue = $stopIfTrue;
+ }
+
+ public function getStyle(): Style
+ {
+ return $this->style ?? new Style(false, true);
+ }
+
+ public function setStyle(Style $style): void
+ {
+ $this->style = $style;
+ }
+
+ protected function validateOperand(string $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): string
+ {
+ if (
+ $operandValueType === Wizard::VALUE_TYPE_LITERAL
+ && str_starts_with($operand, '"')
+ && str_ends_with($operand, '"')
+ ) {
+ $operand = str_replace('""', '"', substr($operand, 1, -1));
+ } elseif ($operandValueType === Wizard::VALUE_TYPE_FORMULA && str_starts_with($operand, '=')) {
+ $operand = substr($operand, 1);
+ }
+
+ return $operand;
+ }
+
+ protected static function reverseCellAdjustment(array $matches, int $referenceColumn, int $referenceRow): string
+ {
+ $worksheet = $matches[1];
+ $column = $matches[6];
+ $row = $matches[7];
+
+ if (!str_contains($column, '$')) {
+ $column = Coordinate::columnIndexFromString($column);
+ $column -= $referenceColumn - 1;
+ $column = Coordinate::stringFromColumnIndex($column);
+ }
+
+ if (!str_contains($row, '$')) {
+ $row -= $referenceRow - 1;
+ }
+
+ return "{$worksheet}{$column}{$row}";
+ }
+
+ public static function reverseAdjustCellRef(string $condition, string $cellRange): string
+ {
+ $conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($cellRange)));
+ [$referenceCell] = $conditionalRange[0];
+ [$referenceColumnIndex, $referenceRow] = Coordinate::indexesFromString($referenceCell);
+
+ $splitCondition = explode(Calculation::FORMULA_STRING_QUOTE, $condition);
+ $i = false;
+ foreach ($splitCondition as &$value) {
+ // Only count/replace in alternating array entries (ie. not in quoted strings)
+ $i = $i === false;
+ if ($i) {
+ $value = (string) preg_replace_callback(
+ '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
+ fn ($matches): string => self::reverseCellAdjustment($matches, $referenceColumnIndex, $referenceRow),
+ $value
+ );
+ }
+ }
+ unset($value);
+
+ // Then rebuild the condition string to return it
+ return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
+ }
+
+ protected function conditionCellAdjustment(array $matches): string
+ {
+ $worksheet = $matches[1];
+ $column = $matches[6];
+ $row = $matches[7];
+
+ if (!str_contains($column, '$')) {
+ $column = Coordinate::columnIndexFromString($column);
+ $column += $this->referenceColumn - 1;
+ $column = Coordinate::stringFromColumnIndex($column);
+ }
+
+ if (!str_contains($row, '$')) {
+ $row += $this->referenceRow - 1;
+ }
+
+ return "{$worksheet}{$column}{$row}";
+ }
+
+ protected function cellConditionCheck(string $condition): string
+ {
+ $splitCondition = explode(Calculation::FORMULA_STRING_QUOTE, $condition);
+ $i = false;
+ foreach ($splitCondition as &$value) {
+ // Only count/replace in alternating array entries (ie. not in quoted strings)
+ $i = $i === false;
+ if ($i) {
+ $value = (string) preg_replace_callback(
+ '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
+ [$this, 'conditionCellAdjustment'],
+ $value
+ );
+ }
+ }
+ unset($value);
+
+ // Then rebuild the condition string to return it
+ return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
+ }
+
+ protected function adjustConditionsForCellReferences(array $conditions): array
+ {
+ return array_map(
+ [$this, 'cellConditionCheck'],
+ $conditions
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardInterface.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardInterface.php
new file mode 100644
index 0000000..10ad57b
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardInterface.php
@@ -0,0 +1,25 @@
+fillType = null;
+ }
+ $this->startColor = new Color(Color::COLOR_WHITE, $isSupervisor, $isConditional);
+ $this->endColor = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
+
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->startColor->bindParent($this, 'startColor');
+ $this->endColor->bindParent($this, 'endColor');
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return Fill
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getSharedComponent()->getFill();
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ return ['fill' => $array];
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getFill()->applyFromArray(
+ * [
+ * 'fillType' => Fill::FILL_GRADIENT_LINEAR,
+ * 'rotation' => 0.0,
+ * 'startColor' => [
+ * 'rgb' => '000000'
+ * ],
+ * 'endColor' => [
+ * 'argb' => 'FFFFFFFF'
+ * ]
+ * ]
+ * );
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['fillType'])) {
+ $this->setFillType($styleArray['fillType']);
+ }
+ if (isset($styleArray['rotation'])) {
+ $this->setRotation($styleArray['rotation']);
+ }
+ if (isset($styleArray['startColor'])) {
+ $this->getStartColor()->applyFromArray($styleArray['startColor']);
+ }
+ if (isset($styleArray['endColor'])) {
+ $this->getEndColor()->applyFromArray($styleArray['endColor']);
+ }
+ if (isset($styleArray['color'])) {
+ $this->getStartColor()->applyFromArray($styleArray['color']);
+ $this->getEndColor()->applyFromArray($styleArray['color']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Fill Type.
+ *
+ * @return null|string
+ */
+ public function getFillType()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getFillType();
+ }
+
+ return $this->fillType;
+ }
+
+ /**
+ * Set Fill Type.
+ *
+ * @param string $fillType Fill type, see self::FILL_*
+ *
+ * @return $this
+ */
+ public function setFillType($fillType): static
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['fillType' => $fillType]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->fillType = $fillType;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Rotation.
+ *
+ * @return float
+ */
+ public function getRotation()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getRotation();
+ }
+
+ return $this->rotation;
+ }
+
+ /**
+ * Set Rotation.
+ *
+ * @param float $angleInDegrees
+ *
+ * @return $this
+ */
+ public function setRotation($angleInDegrees): static
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['rotation' => $angleInDegrees]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->rotation = $angleInDegrees;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Start Color.
+ *
+ * @return Color
+ */
+ public function getStartColor()
+ {
+ return $this->startColor;
+ }
+
+ /**
+ * Set Start Color.
+ *
+ * @return $this
+ */
+ public function setStartColor(Color $color): static
+ {
+ $this->colorChanged = true;
+ // make sure parameter is a real color and not a supervisor
+ $color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStartColor()->getStyleArray(['argb' => $color->getARGB()]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->startColor = $color;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get End Color.
+ *
+ * @return Color
+ */
+ public function getEndColor()
+ {
+ return $this->endColor;
+ }
+
+ /**
+ * Set End Color.
+ *
+ * @return $this
+ */
+ public function setEndColor(Color $color): static
+ {
+ $this->colorChanged = true;
+ // make sure parameter is a real color and not a supervisor
+ $color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getEndColor()->getStyleArray(['argb' => $color->getARGB()]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->endColor = $color;
+ }
+
+ return $this;
+ }
+
+ public function getColorsChanged(): bool
+ {
+ if ($this->isSupervisor) {
+ $changed = $this->getSharedComponent()->colorChanged;
+ } else {
+ $changed = $this->colorChanged;
+ }
+
+ return $changed || $this->startColor->getHasChanged() || $this->endColor->getHasChanged();
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+
+ // Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
+ // different hashes if we don't explicitly prevent this
+ return md5(
+ $this->getFillType()
+ . $this->getRotation()
+ . ($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '')
+ . ($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '')
+ . ((string) $this->getColorsChanged())
+ . __CLASS__
+ );
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'fillType', $this->getFillType());
+ $this->exportArray2($exportedArray, 'rotation', $this->getRotation());
+ if ($this->getColorsChanged()) {
+ $this->exportArray2($exportedArray, 'endColor', $this->getEndColor());
+ $this->exportArray2($exportedArray, 'startColor', $this->getStartColor());
+ }
+
+ return $exportedArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
new file mode 100644
index 0000000..78b3f0d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
@@ -0,0 +1,891 @@
+name = null;
+ $this->size = null;
+ $this->bold = null;
+ $this->italic = null;
+ $this->superscript = null;
+ $this->subscript = null;
+ $this->underline = null;
+ $this->strikethrough = null;
+ $this->color = new Color(Color::COLOR_BLACK, $isSupervisor, $isConditional);
+ } else {
+ $this->color = new Color(Color::COLOR_BLACK, $isSupervisor);
+ }
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->color->bindParent($this, 'color');
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return Font
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getSharedComponent()->getFont();
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ return ['font' => $array];
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->applyFromArray(
+ * [
+ * 'name' => 'Arial',
+ * 'bold' => TRUE,
+ * 'italic' => FALSE,
+ * 'underline' => \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE,
+ * 'strikethrough' => FALSE,
+ * 'color' => [
+ * 'rgb' => '808080'
+ * ]
+ * ]
+ * );
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['name'])) {
+ $this->setName($styleArray['name']);
+ }
+ if (isset($styleArray['latin'])) {
+ $this->setLatin($styleArray['latin']);
+ }
+ if (isset($styleArray['eastAsian'])) {
+ $this->setEastAsian($styleArray['eastAsian']);
+ }
+ if (isset($styleArray['complexScript'])) {
+ $this->setComplexScript($styleArray['complexScript']);
+ }
+ if (isset($styleArray['bold'])) {
+ $this->setBold($styleArray['bold']);
+ }
+ if (isset($styleArray['italic'])) {
+ $this->setItalic($styleArray['italic']);
+ }
+ if (isset($styleArray['superscript'])) {
+ $this->setSuperscript($styleArray['superscript']);
+ }
+ if (isset($styleArray['subscript'])) {
+ $this->setSubscript($styleArray['subscript']);
+ }
+ if (isset($styleArray['underline'])) {
+ $this->setUnderline($styleArray['underline']);
+ }
+ if (isset($styleArray['strikethrough'])) {
+ $this->setStrikethrough($styleArray['strikethrough']);
+ }
+ if (isset($styleArray['color'])) {
+ $this->getColor()->applyFromArray($styleArray['color']);
+ }
+ if (isset($styleArray['size'])) {
+ $this->setSize($styleArray['size']);
+ }
+ if (isset($styleArray['chartColor'])) {
+ $this->chartColor = $styleArray['chartColor'];
+ }
+ if (isset($styleArray['scheme'])) {
+ $this->setScheme($styleArray['scheme']);
+ }
+ if (isset($styleArray['cap'])) {
+ $this->setCap($styleArray['cap']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Name.
+ *
+ * @return null|string
+ */
+ public function getName()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getName();
+ }
+
+ return $this->name;
+ }
+
+ public function getLatin(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getLatin();
+ }
+
+ return $this->latin;
+ }
+
+ public function getEastAsian(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getEastAsian();
+ }
+
+ return $this->eastAsian;
+ }
+
+ public function getComplexScript(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getComplexScript();
+ }
+
+ return $this->complexScript;
+ }
+
+ /**
+ * Set Name and turn off Scheme.
+ *
+ * @param string $fontname
+ */
+ public function setName($fontname): self
+ {
+ if ($fontname == '') {
+ $fontname = 'Calibri';
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['name' => $fontname]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->name = $fontname;
+ }
+
+ return $this->setScheme('');
+ }
+
+ public function setLatin(string $fontname): self
+ {
+ if ($fontname == '') {
+ $fontname = 'Calibri';
+ }
+ if (!$this->isSupervisor) {
+ $this->latin = $fontname;
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['latin' => $fontname]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ public function setEastAsian(string $fontname): self
+ {
+ if ($fontname == '') {
+ $fontname = 'Calibri';
+ }
+ if (!$this->isSupervisor) {
+ $this->eastAsian = $fontname;
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['eastAsian' => $fontname]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ public function setComplexScript(string $fontname): self
+ {
+ if ($fontname == '') {
+ $fontname = 'Calibri';
+ }
+ if (!$this->isSupervisor) {
+ $this->complexScript = $fontname;
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['complexScript' => $fontname]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Size.
+ *
+ * @return null|float
+ */
+ public function getSize()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getSize();
+ }
+
+ return $this->size;
+ }
+
+ /**
+ * Set Size.
+ *
+ * @param mixed $sizeInPoints A float representing the value of a positive measurement in points (1/72 of an inch)
+ *
+ * @return $this
+ */
+ public function setSize(mixed $sizeInPoints, bool $nullOk = false): static
+ {
+ if (is_string($sizeInPoints) || is_int($sizeInPoints)) {
+ $sizeInPoints = (float) $sizeInPoints; // $pValue = 0 if given string is not numeric
+ }
+
+ // Size must be a positive floating point number
+ // ECMA-376-1:2016, part 1, chapter 18.4.11 sz (Font Size), p. 1536
+ if (!is_float($sizeInPoints) || !($sizeInPoints > 0)) {
+ if (!$nullOk || $sizeInPoints !== null) {
+ $sizeInPoints = 10.0;
+ }
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['size' => $sizeInPoints]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->size = $sizeInPoints;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Bold.
+ *
+ * @return null|bool
+ */
+ public function getBold()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBold();
+ }
+
+ return $this->bold;
+ }
+
+ /**
+ * Set Bold.
+ *
+ * @param bool $bold
+ *
+ * @return $this
+ */
+ public function setBold($bold): static
+ {
+ if ($bold == '') {
+ $bold = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['bold' => $bold]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->bold = $bold;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Italic.
+ *
+ * @return null|bool
+ */
+ public function getItalic()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getItalic();
+ }
+
+ return $this->italic;
+ }
+
+ /**
+ * Set Italic.
+ *
+ * @param bool $italic
+ *
+ * @return $this
+ */
+ public function setItalic($italic): static
+ {
+ if ($italic == '') {
+ $italic = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['italic' => $italic]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->italic = $italic;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Superscript.
+ *
+ * @return null|bool
+ */
+ public function getSuperscript()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getSuperscript();
+ }
+
+ return $this->superscript;
+ }
+
+ /**
+ * Set Superscript.
+ *
+ * @return $this
+ */
+ public function setSuperscript(bool $superscript): static
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['superscript' => $superscript]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->superscript = $superscript;
+ if ($this->superscript) {
+ $this->subscript = false;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Subscript.
+ *
+ * @return null|bool
+ */
+ public function getSubscript()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getSubscript();
+ }
+
+ return $this->subscript;
+ }
+
+ /**
+ * Set Subscript.
+ *
+ * @return $this
+ */
+ public function setSubscript(bool $subscript): static
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['subscript' => $subscript]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->subscript = $subscript;
+ if ($this->subscript) {
+ $this->superscript = false;
+ }
+ }
+
+ return $this;
+ }
+
+ public function getBaseLine(): int
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBaseLine();
+ }
+
+ return $this->baseLine;
+ }
+
+ public function setBaseLine(int $baseLine): self
+ {
+ if (!$this->isSupervisor) {
+ $this->baseLine = $baseLine;
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['baseLine' => $baseLine]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ public function getStrikeType(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getStrikeType();
+ }
+
+ return $this->strikeType;
+ }
+
+ public function setStrikeType(string $strikeType): self
+ {
+ if (!$this->isSupervisor) {
+ $this->strikeType = $strikeType;
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['strikeType' => $strikeType]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ public function getUnderlineColor(): ?ChartColor
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getUnderlineColor();
+ }
+
+ return $this->underlineColor;
+ }
+
+ public function setUnderlineColor(array $colorArray): self
+ {
+ if (!$this->isSupervisor) {
+ $this->underlineColor = new ChartColor($colorArray);
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['underlineColor' => $colorArray]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ public function getChartColor(): ?ChartColor
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getChartColor();
+ }
+
+ return $this->chartColor;
+ }
+
+ public function setChartColor(array $colorArray): self
+ {
+ if (!$this->isSupervisor) {
+ $this->chartColor = new ChartColor($colorArray);
+ } else {
+ // should never be true
+ // @codeCoverageIgnoreStart
+ $styleArray = $this->getStyleArray(['chartColor' => $colorArray]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $this;
+ }
+
+ public function setChartColorFromObject(?ChartColor $chartColor): self
+ {
+ $this->chartColor = $chartColor;
+
+ return $this;
+ }
+
+ /**
+ * Get Underline.
+ *
+ * @return null|string
+ */
+ public function getUnderline()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getUnderline();
+ }
+
+ return $this->underline;
+ }
+
+ /**
+ * Set Underline.
+ *
+ * @param bool|string $underlineStyle \PhpOffice\PhpSpreadsheet\Style\Font underline type
+ * If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
+ * false equates to UNDERLINE_NONE
+ *
+ * @return $this
+ */
+ public function setUnderline($underlineStyle): static
+ {
+ if (is_bool($underlineStyle)) {
+ $underlineStyle = ($underlineStyle) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE;
+ } elseif ($underlineStyle == '') {
+ $underlineStyle = self::UNDERLINE_NONE;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['underline' => $underlineStyle]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->underline = $underlineStyle;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Strikethrough.
+ *
+ * @return null|bool
+ */
+ public function getStrikethrough()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getStrikethrough();
+ }
+
+ return $this->strikethrough;
+ }
+
+ /**
+ * Set Strikethrough.
+ *
+ * @param bool $strikethru
+ *
+ * @return $this
+ */
+ public function setStrikethrough($strikethru): static
+ {
+ if ($strikethru == '') {
+ $strikethru = false;
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['strikethrough' => $strikethru]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->strikethrough = $strikethru;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Color.
+ *
+ * @return Color
+ */
+ public function getColor()
+ {
+ return $this->color;
+ }
+
+ /**
+ * Set Color.
+ *
+ * @return $this
+ */
+ public function setColor(Color $color): static
+ {
+ // make sure parameter is a real color and not a supervisor
+ $color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getColor()->getStyleArray(['argb' => $color->getARGB()]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->color = $color;
+ }
+
+ return $this;
+ }
+
+ private function hashChartColor(?ChartColor $underlineColor): string
+ {
+ if ($underlineColor === null) {
+ return '';
+ }
+
+ return
+ $underlineColor->getValue()
+ . $underlineColor->getType()
+ . (string) $underlineColor->getAlpha();
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+
+ return md5(
+ $this->name
+ . $this->size
+ . ($this->bold ? 't' : 'f')
+ . ($this->italic ? 't' : 'f')
+ . ($this->superscript ? 't' : 'f')
+ . ($this->subscript ? 't' : 'f')
+ . $this->underline
+ . ($this->strikethrough ? 't' : 'f')
+ . $this->color->getHashCode()
+ . $this->scheme
+ . implode(
+ '*',
+ [
+ $this->latin,
+ $this->eastAsian,
+ $this->complexScript,
+ $this->strikeType,
+ $this->hashChartColor($this->chartColor),
+ $this->hashChartColor($this->underlineColor),
+ (string) $this->baseLine,
+ (string) $this->cap,
+ ]
+ )
+ . __CLASS__
+ );
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'baseLine', $this->getBaseLine());
+ $this->exportArray2($exportedArray, 'bold', $this->getBold());
+ $this->exportArray2($exportedArray, 'cap', $this->getCap());
+ $this->exportArray2($exportedArray, 'chartColor', $this->getChartColor());
+ $this->exportArray2($exportedArray, 'color', $this->getColor());
+ $this->exportArray2($exportedArray, 'complexScript', $this->getComplexScript());
+ $this->exportArray2($exportedArray, 'eastAsian', $this->getEastAsian());
+ $this->exportArray2($exportedArray, 'italic', $this->getItalic());
+ $this->exportArray2($exportedArray, 'latin', $this->getLatin());
+ $this->exportArray2($exportedArray, 'name', $this->getName());
+ $this->exportArray2($exportedArray, 'scheme', $this->getScheme());
+ $this->exportArray2($exportedArray, 'size', $this->getSize());
+ $this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough());
+ $this->exportArray2($exportedArray, 'strikeType', $this->getStrikeType());
+ $this->exportArray2($exportedArray, 'subscript', $this->getSubscript());
+ $this->exportArray2($exportedArray, 'superscript', $this->getSuperscript());
+ $this->exportArray2($exportedArray, 'underline', $this->getUnderline());
+ $this->exportArray2($exportedArray, 'underlineColor', $this->getUnderlineColor());
+
+ return $exportedArray;
+ }
+
+ public function getScheme(): string
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getScheme();
+ }
+
+ return $this->scheme;
+ }
+
+ public function setScheme(string $scheme): self
+ {
+ if ($scheme === '' || $scheme === 'major' || $scheme === 'minor') {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['scheme' => $scheme]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->scheme = $scheme;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set capitalization attribute. If not one of the permitted
+ * values (all, small, or none), set it to null.
+ * This will be honored only for the font for chart titles.
+ * None is distinguished from null because null will inherit
+ * the current value, whereas 'none' will override it.
+ */
+ public function setCap(string $cap): self
+ {
+ $this->cap = in_array($cap, self::VALID_CAPS, true) ? $cap : null;
+
+ return $this;
+ }
+
+ public function getCap(): ?string
+ {
+ return $this->cap;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $this->color = clone $this->color;
+ $this->chartColor = ($this->chartColor === null) ? null : clone $this->chartColor;
+ $this->underlineColor = ($this->underlineColor === null) ? null : clone $this->underlineColor;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
new file mode 100644
index 0000000..6e79106
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
@@ -0,0 +1,454 @@
+formatCode = null;
+ $this->builtInFormatCode = false;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor.
+ *
+ * @return NumberFormat
+ */
+ public function getSharedComponent()
+ {
+ /** @var Style */
+ $parent = $this->parent;
+
+ return $parent->getSharedComponent()->getNumberFormat();
+ }
+
+ /**
+ * Build style array from subcomponents.
+ *
+ * @param array $array
+ */
+ public function getStyleArray($array): array
+ {
+ return ['numberFormat' => $array];
+ }
+
+ /**
+ * Apply styles from array.
+ *
+ *
+ * $spreadsheet->getActiveSheet()->getStyle('B2')->getNumberFormat()->applyFromArray(
+ * [
+ * 'formatCode' => NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE
+ * ]
+ * );
+ *
+ *
+ * @param array $styleArray Array containing style information
+ *
+ * @return $this
+ */
+ public function applyFromArray(array $styleArray): static
+ {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
+ } else {
+ if (isset($styleArray['formatCode'])) {
+ $this->setFormatCode($styleArray['formatCode']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Format Code.
+ *
+ * @return null|string
+ */
+ public function getFormatCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getFormatCode();
+ }
+ if (is_int($this->builtInFormatCode)) {
+ return self::builtInFormatCode($this->builtInFormatCode);
+ }
+
+ return $this->formatCode;
+ }
+
+ /**
+ * Set Format Code.
+ *
+ * @param string $formatCode see self::FORMAT_*
+ *
+ * @return $this
+ */
+ public function setFormatCode(string $formatCode): static
+ {
+ if ($formatCode == '') {
+ $formatCode = self::FORMAT_GENERAL;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['formatCode' => $formatCode]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->formatCode = $formatCode;
+ $this->builtInFormatCode = self::builtInFormatCodeIndex($formatCode);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Built-In Format Code.
+ *
+ * @return false|int
+ */
+ public function getBuiltInFormatCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBuiltInFormatCode();
+ }
+
+ return $this->builtInFormatCode;
+ }
+
+ /**
+ * Set Built-In Format Code.
+ *
+ * @param int $formatCodeIndex Id of the built-in format code to use
+ *
+ * @return $this
+ */
+ public function setBuiltInFormatCode(int $formatCodeIndex): static
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(['formatCode' => self::builtInFormatCode($formatCodeIndex)]);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->builtInFormatCode = $formatCodeIndex;
+ $this->formatCode = self::builtInFormatCode($formatCodeIndex);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Fill built-in format codes.
+ */
+ private static function fillBuiltInFormatCodes(): void
+ {
+ // [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance]
+ // 18.8.30. numFmt (Number Format)
+ //
+ // The ECMA standard defines built-in format IDs
+ // 14: "mm-dd-yy"
+ // 22: "m/d/yy h:mm"
+ // 37: "#,##0 ;(#,##0)"
+ // 38: "#,##0 ;[Red](#,##0)"
+ // 39: "#,##0.00;(#,##0.00)"
+ // 40: "#,##0.00;[Red](#,##0.00)"
+ // 47: "mmss.0"
+ // KOR fmt 55: "yyyy-mm-dd"
+ // Excel defines built-in format IDs
+ // 14: "m/d/yyyy"
+ // 22: "m/d/yyyy h:mm"
+ // 37: "#,##0_);(#,##0)"
+ // 38: "#,##0_);[Red](#,##0)"
+ // 39: "#,##0.00_);(#,##0.00)"
+ // 40: "#,##0.00_);[Red](#,##0.00)"
+ // 47: "mm:ss.0"
+ // KOR fmt 55: "yyyy/mm/dd"
+
+ // Built-in format codes
+ if (empty(self::$builtInFormats)) {
+ self::$builtInFormats = [];
+
+ // General
+ self::$builtInFormats[0] = self::FORMAT_GENERAL;
+ self::$builtInFormats[1] = '0';
+ self::$builtInFormats[2] = '0.00';
+ self::$builtInFormats[3] = '#,##0';
+ self::$builtInFormats[4] = '#,##0.00';
+
+ self::$builtInFormats[9] = '0%';
+ self::$builtInFormats[10] = '0.00%';
+ self::$builtInFormats[11] = '0.00E+00';
+ self::$builtInFormats[12] = '# ?/?';
+ self::$builtInFormats[13] = '# ??/??';
+ self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
+ self::$builtInFormats[15] = 'd-mmm-yy';
+ self::$builtInFormats[16] = 'd-mmm';
+ self::$builtInFormats[17] = 'mmm-yy';
+ self::$builtInFormats[18] = 'h:mm AM/PM';
+ self::$builtInFormats[19] = 'h:mm:ss AM/PM';
+ self::$builtInFormats[20] = 'h:mm';
+ self::$builtInFormats[21] = 'h:mm:ss';
+ self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
+
+ self::$builtInFormats[37] = '#,##0_);(#,##0)'; // Despite ECMA '#,##0 ;(#,##0)';
+ self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; // Despite ECMA '#,##0 ;[Red](#,##0)';
+ self::$builtInFormats[39] = '#,##0.00_);(#,##0.00)'; // Despite ECMA '#,##0.00;(#,##0.00)';
+ self::$builtInFormats[40] = '#,##0.00_);[Red](#,##0.00)'; // Despite ECMA '#,##0.00;[Red](#,##0.00)';
+
+ self::$builtInFormats[44] = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
+ self::$builtInFormats[45] = 'mm:ss';
+ self::$builtInFormats[46] = '[h]:mm:ss';
+ self::$builtInFormats[47] = 'mm:ss.0'; // Despite ECMA 'mmss.0';
+ self::$builtInFormats[48] = '##0.0E+0';
+ self::$builtInFormats[49] = '@';
+
+ // CHT
+ self::$builtInFormats[27] = '[$-404]e/m/d';
+ self::$builtInFormats[30] = 'm/d/yy';
+ self::$builtInFormats[36] = '[$-404]e/m/d';
+ self::$builtInFormats[50] = '[$-404]e/m/d';
+ self::$builtInFormats[57] = '[$-404]e/m/d';
+
+ // THA
+ self::$builtInFormats[59] = 't0';
+ self::$builtInFormats[60] = 't0.00';
+ self::$builtInFormats[61] = 't#,##0';
+ self::$builtInFormats[62] = 't#,##0.00';
+ self::$builtInFormats[67] = 't0%';
+ self::$builtInFormats[68] = 't0.00%';
+ self::$builtInFormats[69] = 't# ?/?';
+ self::$builtInFormats[70] = 't# ??/??';
+
+ // JPN
+ self::$builtInFormats[28] = '[$-411]ggge"年"m"月"d"日"';
+ self::$builtInFormats[29] = '[$-411]ggge"年"m"月"d"日"';
+ self::$builtInFormats[31] = 'yyyy"年"m"月"d"日"';
+ self::$builtInFormats[32] = 'h"時"mm"分"';
+ self::$builtInFormats[33] = 'h"時"mm"分"ss"秒"';
+ self::$builtInFormats[34] = 'yyyy"年"m"月"';
+ self::$builtInFormats[35] = 'm"月"d"日"';
+ self::$builtInFormats[51] = '[$-411]ggge"年"m"月"d"日"';
+ self::$builtInFormats[52] = 'yyyy"年"m"月"';
+ self::$builtInFormats[53] = 'm"月"d"日"';
+ self::$builtInFormats[54] = '[$-411]ggge"年"m"月"d"日"';
+ self::$builtInFormats[55] = 'yyyy"年"m"月"';
+ self::$builtInFormats[56] = 'm"月"d"日"';
+ self::$builtInFormats[58] = '[$-411]ggge"年"m"月"d"日"';
+
+ // Flip array (for faster lookups)
+ self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
+ }
+ }
+
+ /**
+ * Get built-in format code.
+ *
+ * @param int $index
+ *
+ * @return string
+ */
+ public static function builtInFormatCode($index)
+ {
+ // Clean parameter
+ $index = (int) $index;
+
+ // Ensure built-in format codes are available
+ self::fillBuiltInFormatCodes();
+
+ // Lookup format code
+ if (isset(self::$builtInFormats[$index])) {
+ return self::$builtInFormats[$index];
+ }
+
+ return '';
+ }
+
+ /**
+ * Get built-in format code index.
+ *
+ * @param string $formatCodeIndex
+ *
+ * @return false|int
+ */
+ public static function builtInFormatCodeIndex($formatCodeIndex)
+ {
+ // Ensure built-in format codes are available
+ self::fillBuiltInFormatCodes();
+
+ // Lookup format code
+ if (array_key_exists($formatCodeIndex, self::$flippedBuiltInFormats)) {
+ return self::$flippedBuiltInFormats[$formatCodeIndex];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get hash code.
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+
+ return md5(
+ $this->formatCode
+ . $this->builtInFormatCode
+ . __CLASS__
+ );
+ }
+
+ /**
+ * Convert a value in a pre-defined format to a PHP string.
+ *
+ * @param mixed $value Value to format
+ * @param string $format Format code: see = self::FORMAT_* for predefined values;
+ * or can be any valid MS Excel custom format string
+ * @param array $callBack Callback function for additional formatting of string
+ *
+ * @return string Formatted string
+ */
+ public static function toFormattedString(mixed $value, $format, $callBack = null)
+ {
+ return NumberFormat\Formatter::toFormattedString($value, $format, $callBack);
+ }
+
+ protected function exportArray1(): array
+ {
+ $exportedArray = [];
+ $this->exportArray2($exportedArray, 'formatCode', $this->getFormatCode());
+
+ return $exportedArray;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
new file mode 100644
index 0000000..d6f373d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
@@ -0,0 +1,25 @@
+ '',
+ // 12-hour suffix
+ 'am/pm' => 'A',
+ // 4-digit year
+ 'e' => 'Y',
+ 'yyyy' => 'Y',
+ // 2-digit year
+ 'yy' => 'y',
+ // first letter of month - no php equivalent
+ 'mmmmm' => 'M',
+ // full month name
+ 'mmmm' => 'F',
+ // short month name
+ 'mmm' => 'M',
+ // mm is minutes if time, but can also be month w/leading zero
+ // so we try to identify times be the inclusion of a : separator in the mask
+ // It isn't perfect, but the best way I know how
+ ':mm' => ':i',
+ 'mm:' => 'i:',
+ // full day of week name
+ 'dddd' => 'l',
+ // short day of week name
+ 'ddd' => 'D',
+ // days leading zero
+ 'dd' => 'd',
+ // days no leading zero
+ 'd' => 'j',
+ // fractional seconds - no php equivalent
+ '.s' => '',
+ ];
+
+ /**
+ * Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock).
+ */
+ private const DATE_FORMAT_REPLACEMENTS24 = [
+ 'hh' => 'H',
+ 'h' => 'G',
+ // month leading zero
+ 'mm' => 'm',
+ // month no leading zero
+ 'm' => 'n',
+ // seconds
+ 'ss' => 's',
+ ];
+
+ /**
+ * Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock).
+ */
+ private const DATE_FORMAT_REPLACEMENTS12 = [
+ 'hh' => 'h',
+ 'h' => 'g',
+ // month leading zero
+ 'mm' => 'm',
+ // month no leading zero
+ 'm' => 'n',
+ // seconds
+ 'ss' => 's',
+ ];
+
+ private const HOURS_IN_DAY = 24;
+ private const MINUTES_IN_DAY = 60 * self::HOURS_IN_DAY;
+ private const SECONDS_IN_DAY = 60 * self::MINUTES_IN_DAY;
+ private const INTERVAL_PRECISION = 10;
+ private const INTERVAL_LEADING_ZERO = [
+ '[hh]',
+ '[mm]',
+ '[ss]',
+ ];
+ private const INTERVAL_ROUND_PRECISION = [
+ // hours and minutes truncate
+ '[h]' => self::INTERVAL_PRECISION,
+ '[hh]' => self::INTERVAL_PRECISION,
+ '[m]' => self::INTERVAL_PRECISION,
+ '[mm]' => self::INTERVAL_PRECISION,
+ // seconds round
+ '[s]' => 0,
+ '[ss]' => 0,
+ ];
+ private const INTERVAL_MULTIPLIER = [
+ '[h]' => self::HOURS_IN_DAY,
+ '[hh]' => self::HOURS_IN_DAY,
+ '[m]' => self::MINUTES_IN_DAY,
+ '[mm]' => self::MINUTES_IN_DAY,
+ '[s]' => self::SECONDS_IN_DAY,
+ '[ss]' => self::SECONDS_IN_DAY,
+ ];
+
+ private static function tryInterval(bool &$seekingBracket, string &$block, mixed $value, string $format): void
+ {
+ if ($seekingBracket) {
+ if (str_contains($block, $format)) {
+ $hours = (string) (int) round(
+ self::INTERVAL_MULTIPLIER[$format] * $value,
+ self::INTERVAL_ROUND_PRECISION[$format]
+ );
+ if (strlen($hours) === 1 && in_array($format, self::INTERVAL_LEADING_ZERO, true)) {
+ $hours = "0$hours";
+ }
+ $block = str_replace($format, $hours, $block);
+ $seekingBracket = false;
+ }
+ }
+ }
+
+ public static function format(mixed $value, string $format): string
+ {
+ // strip off first part containing e.g. [$-F800] or [$USD-409]
+ // general syntax: [$-]
+ // language info is in hexadecimal
+ // strip off chinese part like [DBNum1][$-804]
+ $format = (string) preg_replace('/^(\[DBNum\d\])*(\[\$[^\]]*\])/i', '', $format);
+
+ // OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
+ // but we don't want to change any quoted strings
+ /** @var callable */
+ $callable = [self::class, 'setLowercaseCallback'];
+ $format = (string) preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', $callable, $format);
+
+ // Only process the non-quoted blocks for date format characters
+
+ $blocks = explode('"', $format);
+ foreach ($blocks as $key => &$block) {
+ if ($key % 2 == 0) {
+ $block = strtr($block, self::DATE_FORMAT_REPLACEMENTS);
+ if (!strpos($block, 'A')) {
+ // 24-hour time format
+ // when [h]:mm format, the [h] should replace to the hours of the value * 24
+ $seekingBracket = true;
+ self::tryInterval($seekingBracket, $block, $value, '[h]');
+ self::tryInterval($seekingBracket, $block, $value, '[hh]');
+ self::tryInterval($seekingBracket, $block, $value, '[mm]');
+ self::tryInterval($seekingBracket, $block, $value, '[m]');
+ self::tryInterval($seekingBracket, $block, $value, '[s]');
+ self::tryInterval($seekingBracket, $block, $value, '[ss]');
+ $block = strtr($block, self::DATE_FORMAT_REPLACEMENTS24);
+ } else {
+ // 12-hour time format
+ $block = strtr($block, self::DATE_FORMAT_REPLACEMENTS12);
+ }
+ }
+ }
+ $format = implode('"', $blocks);
+
+ // escape any quoted characters so that DateTime format() will render them correctly
+ /** @var callable */
+ $callback = [self::class, 'escapeQuotesCallback'];
+ $format = (string) preg_replace_callback('/"(.*)"/U', $callback, $format);
+
+ $dateObj = Date::excelToDateTimeObject($value);
+ // If the colon preceding minute had been quoted, as happens in
+ // Excel 2003 XML formats, m will not have been changed to i above.
+ // Change it now.
+ $format = (string) \preg_replace('/\\\\:m/', ':i', $format);
+ $microseconds = (int) $dateObj->format('u');
+ if (str_contains($format, ':s.000')) {
+ $milliseconds = (int) round($microseconds / 1000.0);
+ if ($milliseconds === 1000) {
+ $milliseconds = 0;
+ $dateObj->modify('+1 second');
+ }
+ $dateObj->modify("-$microseconds microseconds");
+ $format = str_replace(':s.000', ':s.' . sprintf('%03d', $milliseconds), $format);
+ } elseif (str_contains($format, ':s.00')) {
+ $centiseconds = (int) round($microseconds / 10000.0);
+ if ($centiseconds === 100) {
+ $centiseconds = 0;
+ $dateObj->modify('+1 second');
+ }
+ $dateObj->modify("-$microseconds microseconds");
+ $format = str_replace(':s.00', ':s.' . sprintf('%02d', $centiseconds), $format);
+ } elseif (str_contains($format, ':s.0')) {
+ $deciseconds = (int) round($microseconds / 100000.0);
+ if ($deciseconds === 10) {
+ $deciseconds = 0;
+ $dateObj->modify('+1 second');
+ }
+ $dateObj->modify("-$microseconds microseconds");
+ $format = str_replace(':s.0', ':s.' . sprintf('%1d', $deciseconds), $format);
+ } else { // no fractional second
+ if ($microseconds >= 500000) {
+ $dateObj->modify('+1 second');
+ }
+ $dateObj->modify("-$microseconds microseconds");
+ }
+
+ return $dateObj->format($format);
+ }
+
+ private static function setLowercaseCallback(array $matches): string
+ {
+ return mb_strtolower($matches[0]);
+ }
+
+ private static function escapeQuotesCallback(array $matches): string
+ {
+ return '\\' . implode('\\', str_split($matches[1]));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
new file mode 100644
index 0000000..f7969a4
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
@@ -0,0 +1,188 @@
+' => $value > $comparisonValue,
+ '<' => $value < $comparisonValue,
+ '<=' => $value <= $comparisonValue,
+ '<>' => $value != $comparisonValue,
+ '=' => $value == $comparisonValue,
+ default => $value >= $comparisonValue,
+ };
+ }
+
+ private static function splitFormatForSectionSelection(array $sections, mixed $value): array
+ {
+ // Extract the relevant section depending on whether number is positive, negative, or zero?
+ // Text not supported yet.
+ // Here is how the sections apply to various values in Excel:
+ // 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT]
+ // 2 sections: [POSITIVE/ZERO/TEXT] [NEGATIVE]
+ // 3 sections: [POSITIVE/TEXT] [NEGATIVE] [ZERO]
+ // 4 sections: [POSITIVE] [NEGATIVE] [ZERO] [TEXT]
+ $sectionCount = count($sections);
+ // Colour could be a named colour, or a numeric index entry in the colour-palette
+ $color_regex = '/\\[(' . implode('|', Color::NAMED_COLORS) . '|color\\s*(\\d+))\\]/mui';
+ $cond_regex = '/\\[(>|>=|<|<=|=|<>)([+-]?\\d+([.]\\d+)?)\\]/';
+ $colors = ['', '', '', '', ''];
+ $conditionOperations = ['', '', '', '', ''];
+ $conditionComparisonValues = [0, 0, 0, 0, 0];
+ for ($idx = 0; $idx < $sectionCount; ++$idx) {
+ if (preg_match($color_regex, $sections[$idx], $matches)) {
+ if (isset($matches[2])) {
+ $colors[$idx] = '#' . BIFF8::lookup((int) $matches[2] + 7)['rgb'];
+ } else {
+ $colors[$idx] = $matches[0];
+ }
+ $sections[$idx] = (string) preg_replace($color_regex, '', $sections[$idx]);
+ }
+ if (preg_match($cond_regex, $sections[$idx], $matches)) {
+ $conditionOperations[$idx] = $matches[1];
+ $conditionComparisonValues[$idx] = $matches[2];
+ $sections[$idx] = (string) preg_replace($cond_regex, '', $sections[$idx]);
+ }
+ }
+ $color = $colors[0];
+ $format = $sections[0];
+ $absval = $value;
+ switch ($sectionCount) {
+ case 2:
+ $absval = abs($value);
+ if (!self::splitFormatComparison($value, $conditionOperations[0], $conditionComparisonValues[0], '>=', 0)) {
+ $color = $colors[1];
+ $format = $sections[1];
+ }
+
+ break;
+ case 3:
+ case 4:
+ $absval = abs($value);
+ if (!self::splitFormatComparison($value, $conditionOperations[0], $conditionComparisonValues[0], '>', 0)) {
+ if (self::splitFormatComparison($value, $conditionOperations[1], $conditionComparisonValues[1], '<', 0)) {
+ $color = $colors[1];
+ $format = $sections[1];
+ } else {
+ $color = $colors[2];
+ $format = $sections[2];
+ }
+ }
+
+ break;
+ }
+
+ return [$color, $format, $absval];
+ }
+
+ /**
+ * Convert a value in a pre-defined format to a PHP string.
+ *
+ * @param null|bool|float|int|RichText|string $value Value to format
+ * @param string $format Format code: see = self::FORMAT_* for predefined values;
+ * or can be any valid MS Excel custom format string
+ * @param array $callBack Callback function for additional formatting of string
+ *
+ * @return string Formatted string
+ */
+ public static function toFormattedString($value, $format, $callBack = null)
+ {
+ if (is_bool($value)) {
+ return $value ? Calculation::getTRUE() : Calculation::getFALSE();
+ }
+ // For now we do not treat strings in sections, although section 4 of a format code affects strings
+ // Process a single block format code containing @ for text substitution
+ if (preg_match(self::SECTION_SPLIT, $format) === 0 && preg_match(self::SYMBOL_AT, $format) === 1) {
+ return str_replace('"', '', preg_replace(self::SYMBOL_AT, (string) $value, $format) ?? '');
+ }
+
+ // If we have a text value, return it "as is"
+ if (!is_numeric($value)) {
+ return (string) $value;
+ }
+
+ // For 'General' format code, we just pass the value although this is not entirely the way Excel does it,
+ // it seems to round numbers to a total of 10 digits.
+ if (($format === NumberFormat::FORMAT_GENERAL) || ($format === NumberFormat::FORMAT_TEXT)) {
+ return self::adjustSeparators((string) $value);
+ }
+
+ // Ignore square-$-brackets prefix in format string, like "[$-411]ge.m.d", "[$-010419]0%", etc
+ $format = (string) preg_replace('/^\[\$-[^\]]*\]/', '', $format);
+
+ $format = (string) preg_replace_callback(
+ '/(["])(?:(?=(\\\\?))\\2.)*?\\1/u',
+ fn (array $matches): string => str_replace('.', chr(0x00), $matches[0]),
+ $format
+ );
+
+ // Convert any other escaped characters to quoted strings, e.g. (\T to "T")
+ $format = (string) preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/ui', '"${2}"', $format);
+
+ // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
+ $sections = preg_split(self::SECTION_SPLIT, $format) ?: [];
+
+ [$colors, $format, $value] = self::splitFormatForSectionSelection($sections, $value);
+
+ // In Excel formats, "_" is used to add spacing,
+ // The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space
+ $format = (string) preg_replace('/_.?/ui', ' ', $format);
+
+ // Let's begin inspecting the format and converting the value to a formatted string
+ if (
+ // Check for date/time characters (not inside quotes)
+ (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format))
+ // A date/time with a decimal time shouldn't have a digit placeholder before the decimal point
+ && (preg_match('/[0\?#]\.(?![^\[]*\])/miu', $format) === 0)
+ ) {
+ // datetime format
+ $value = DateFormatter::format($value, $format);
+ } else {
+ if (str_starts_with($format, '"') && str_ends_with($format, '"') && substr_count($format, '"') === 2) {
+ $value = substr($format, 1, -1);
+ } elseif (preg_match('/[0#, ]%/', $format)) {
+ // % number format - avoid weird '-0' problem
+ $value = PercentageFormatter::format(0 + (float) $value, $format);
+ } else {
+ $value = NumberFormatter::format($value, $format);
+ }
+ }
+
+ // Additional formatting provided by callback function
+ if ($callBack !== null) {
+ [$writerInstance, $function] = $callBack;
+ $value = $writerInstance->$function($value, $colors);
+ }
+
+ return str_replace(chr(0x00), '.', $value);
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
new file mode 100644
index 0000000..02944fb
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
@@ -0,0 +1,69 @@
+ 0);
+
+ return [
+ implode('.', $masks),
+ implode('.', array_reverse($postDecimalMasks)),
+ ];
+ }
+
+ private static function processComplexNumberFormatMask(mixed $number, string $mask): string
+ {
+ /** @var string */
+ $result = $number;
+ $maskingBlockCount = preg_match_all('/0+/', $mask, $maskingBlocks, PREG_OFFSET_CAPTURE);
+
+ if ($maskingBlockCount > 1) {
+ $maskingBlocks = array_reverse($maskingBlocks[0]);
+
+ $offset = 0;
+ foreach ($maskingBlocks as $block) {
+ $size = strlen($block[0]);
+ $divisor = 10 ** $size;
+ $offset = $block[1];
+
+ /** @var float */
+ $numberFloat = $number;
+ $blockValue = sprintf("%0{$size}d", fmod($numberFloat, $divisor));
+ $number = floor($numberFloat / $divisor);
+ $mask = substr_replace($mask, $blockValue, $offset, $size);
+ }
+ /** @var string */
+ $numberString = $number;
+ if ($number > 0) {
+ $mask = substr_replace($mask, $numberString, $offset, 0);
+ }
+ $result = $mask;
+ }
+
+ return self::makeString($result);
+ }
+
+ private static function complexNumberFormatMask(mixed $number, string $mask, bool $splitOnPoint = true): string
+ {
+ /** @var float */
+ $numberFloat = $number;
+ if ($splitOnPoint) {
+ $masks = explode('.', $mask);
+ if (count($masks) <= 2) {
+ $decmask = $masks[1] ?? '';
+ $decpos = substr_count($decmask, '0');
+ $numberFloat = round($numberFloat, $decpos);
+ }
+ }
+ $sign = ($numberFloat < 0.0) ? '-' : '';
+ $number = self::f2s(abs($numberFloat));
+
+ if ($splitOnPoint && str_contains($mask, '.') && str_contains($number, '.')) {
+ $numbers = explode('.', $number);
+ $masks = explode('.', $mask);
+ if (count($masks) > 2) {
+ $masks = self::mergeComplexNumberFormatMasks($numbers, $masks);
+ }
+ $integerPart = self::complexNumberFormatMask($numbers[0], $masks[0], false);
+ $numlen = strlen($numbers[1]);
+ $msklen = strlen($masks[1]);
+ if ($numlen < $msklen) {
+ $numbers[1] .= str_repeat('0', $msklen - $numlen);
+ }
+ $decimalPart = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1]), false));
+ $decimalPart = substr($decimalPart, 0, $msklen);
+
+ return "{$sign}{$integerPart}.{$decimalPart}";
+ }
+
+ if (strlen($number) < strlen($mask)) {
+ $number = str_repeat('0', strlen($mask) - strlen($number)) . $number;
+ }
+ $result = self::processComplexNumberFormatMask($number, $mask);
+
+ return "{$sign}{$result}";
+ }
+
+ public static function f2s(float $f): string
+ {
+ return self::floatStringConvertScientific((string) $f);
+ }
+
+ public static function floatStringConvertScientific(string $s): string
+ {
+ // convert only normalized form of scientific notation:
+ // optional sign, single digit 1-9,
+ // decimal point and digits (allowed to be omitted),
+ // E (e permitted), optional sign, one or more digits
+ if (preg_match('/^([+-])?([1-9])([.]([0-9]+))?[eE]([+-]?[0-9]+)$/', $s, $matches) === 1) {
+ $exponent = (int) $matches[5];
+ $sign = ($matches[1] === '-') ? '-' : '';
+ if ($exponent >= 0) {
+ $exponentPlus1 = $exponent + 1;
+ $out = $matches[2] . $matches[4];
+ $len = strlen($out);
+ if ($len < $exponentPlus1) {
+ $out .= str_repeat('0', $exponentPlus1 - $len);
+ }
+ $out = substr($out, 0, $exponentPlus1) . ((strlen($out) === $exponentPlus1) ? '' : ('.' . substr($out, $exponentPlus1)));
+ $s = "$sign$out";
+ } else {
+ $s = $sign . '0.' . str_repeat('0', -$exponent - 1) . $matches[2] . $matches[4];
+ }
+ }
+
+ return $s;
+ }
+
+ private static function formatStraightNumericValue(mixed $value, string $format, array $matches, bool $useThousands): string
+ {
+ /** @var float */
+ $valueFloat = $value;
+ $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(
+ $valueFloat,
+ strlen($right),
+ StringHelper::getDecimalSeparator(),
+ StringHelper::getThousandsSeparator()
+ );
+
+ return self::pregReplace(self::NUMBER_REGEX, $value, $format);
+ }
+
+ if (preg_match('/[0#]E[+-]0/i', $format)) {
+ // Scientific format
+ $decimals = strlen($right);
+ $size = $decimals + 3;
+
+ return sprintf("%{$size}.{$decimals}E", $valueFloat);
+ } elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) {
+ if ($valueFloat == floor($valueFloat) && substr_count($format, '.') === 1) {
+ $value *= 10 ** strlen(explode('.', $format)[1]);
+ }
+
+ $result = self::complexNumberFormatMask($value, $format);
+ if (str_contains($result, 'E')) {
+ // This is a hack and doesn't match Excel.
+ // It will, at least, be an accurate representation,
+ // even if formatted incorrectly.
+ // This is needed for absolute values >=1E18.
+ $result = self::f2s($valueFloat);
+ }
+
+ return $result;
+ }
+
+ $sprintf_pattern = "%0$minWidth." . strlen($right) . 'F';
+
+ /** @var float */
+ $valueFloat = $value;
+ $value = self::adjustSeparators(sprintf($sprintf_pattern, round($valueFloat, strlen($right))));
+
+ return self::pregReplace(self::NUMBER_REGEX, $value, $format);
+ }
+
+ public static function format(mixed $value, string $format): string
+ {
+ // The "_" in this string has already been stripped out,
+ // so this test is never true. Furthermore, testing
+ // on Excel shows this format uses Euro symbol, not "EUR".
+ // if ($format === NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE) {
+ // return 'EUR ' . sprintf('%1.2f', $value);
+ // }
+
+ $baseFormat = $format;
+
+ $useThousands = self::areThousandsRequired($format);
+ $scale = self::scaleThousandsMillions($format);
+
+ if (preg_match('/[#\?0]?.*[#\?0]\/(\?+|\d+|#)/', $format)) {
+ // It's a dirty hack; but replace # and 0 digit placeholders with ?
+ $format = (string) preg_replace('/[#0]+\//', '?/', $format);
+ $format = (string) preg_replace('/\/[#0]+/', '/?', $format);
+ $value = FractionFormatter::format($value, $format);
+ } else {
+ // Handle the number itself
+ // scale number
+ $value = $value / $scale;
+ $paddingPlaceholder = (str_contains($format, '?'));
+
+ // Replace # or ? with 0
+ $format = self::pregReplace('/[\\#\?](?=(?:[^"]*"[^"]*")*[^"]*\Z)/', '0', $format);
+ // Remove locale code [$-###] for an LCID
+ $format = self::pregReplace('/\[\$\-.*\]/', '', $format);
+
+ $n = '/\\[[^\\]]+\\]/';
+ $m = self::pregReplace($n, '', $format);
+
+ // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
+ $format = self::makeString(str_replace(['"', '*'], '', $format));
+ if (preg_match(self::NUMBER_REGEX, $m, $matches)) {
+ // There are placeholders for digits, so inject digits from the value into the mask
+ $value = self::formatStraightNumericValue($value, $format, $matches, $useThousands);
+ if ($paddingPlaceholder === true) {
+ $value = self::padValue($value, $baseFormat);
+ }
+ } elseif ($format !== NumberFormat::FORMAT_GENERAL) {
+ // Yes, I know that this is basically just a hack;
+ // if there's no placeholders for digits, just return the format mask "as is"
+ $value = self::makeString(str_replace('?', '', $format));
+ }
+ }
+
+ if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
+ // Currency or Accounting
+ $currencyCode = $m[1];
+ [$currencyCode] = explode('-', $currencyCode);
+ if ($currencyCode == '') {
+ $currencyCode = StringHelper::getCurrencyCode();
+ }
+ $value = self::pregReplace('/\[\$([^\]]*)\]/u', $currencyCode, (string) $value);
+ }
+
+ if (
+ (str_contains((string) $value, '0.'))
+ && ((str_contains($baseFormat, '#.')) || (str_contains($baseFormat, '?.')))
+ ) {
+ $value = preg_replace('/(\b)0\.|([^\d])0\./', '${2}.', (string) $value);
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * @param array|string $value
+ */
+ private static function makeString($value): string
+ {
+ return is_array($value) ? '' : "$value";
+ }
+
+ private static function pregReplace(string $pattern, string $replacement, string $subject): string
+ {
+ return self::makeString(preg_replace($pattern, $replacement, $subject) ?? '');
+ }
+
+ public static function padValue(string $value, string $baseFormat): string
+ {
+ /** @phpstan-ignore-next-line */
+ [$preDecimal, $postDecimal] = preg_split('/\.(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu', $baseFormat . '.?');
+
+ $length = strlen($value);
+ if (str_contains($postDecimal, '?')) {
+ $value = str_pad(rtrim($value, '0. '), $length, ' ', STR_PAD_RIGHT);
+ }
+ if (str_contains($preDecimal, '?')) {
+ $value = str_pad(ltrim($value, '0, '), $length, ' ', STR_PAD_LEFT);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Find out if we need thousands separator
+ * This is indicated by a comma enclosed by a digit placeholders: #, 0 or ?
+ */
+ public static function areThousandsRequired(string &$format): bool
+ {
+ $useThousands = (bool) preg_match('/([#\?0]),([#\?0])/', $format);
+ if ($useThousands) {
+ $format = self::pregReplace('/([#\?0]),([#\?0])/', '${1}${2}', $format);
+ }
+
+ return $useThousands;
+ }
+
+ /**
+ * Scale thousands, millions,...
+ * This is indicated by a number of commas after a digit placeholder: #, or 0.0,, or ?,.
+ */
+ public static function scaleThousandsMillions(string &$format): int
+ {
+ $scale = 1; // same as no scale
+ if (preg_match('/(#|0|\?)(,+)/', $format, $matches)) {
+ $scale = 1000 ** strlen($matches[2]);
+ // strip the commas
+ $format = self::pregReplace('/([#\?0]),+/', '${1}', $format);
+ }
+
+ return $scale;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
new file mode 100644
index 0000000..6d23f9d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
@@ -0,0 +1,48 @@
+ 0);
+ $replacement = "0{$wholePartSize}.{$decimalPartSize}";
+ $mask = (string) preg_replace('/[#0,]+\.?[?#0,]*/ui', "%{$replacement}F{$placeHolders}", $format);
+
+ /** @var float */
+ $valueFloat = $value;
+
+ return self::adjustSeparators(sprintf($mask, round($valueFloat, $decimalPartSize)));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php
new file mode 100644
index 0000000..43fd8ca
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php
@@ -0,0 +1,102 @@
+setCurrencyCode($currencyCode);
+ $this->setThousandsSeparator($thousandsSeparator);
+ $this->setDecimals($decimals);
+ $this->setCurrencySymbolPosition($currencySymbolPosition);
+ $this->setCurrencySymbolSpacing($currencySymbolSpacing);
+ $this->setLocale($locale);
+ $this->stripLeadingRLM = $stripLeadingRLM;
+ }
+
+ /**
+ * @throws Exception if the Intl extension and ICU version don't support Accounting formats
+ */
+ protected function getLocaleFormat(): string
+ {
+ if (self::icuVersion() < 53.0) {
+ // @codeCoverageIgnoreStart
+ throw new Exception('The Intl extension does not support Accounting Formats without ICU 53');
+ // @codeCoverageIgnoreEnd
+ }
+
+ // Scrutinizer does not recognize CURRENCY_ACCOUNTING
+ $formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY_ACCOUNTING);
+ $mask = $formatter->format($this->stripLeadingRLM);
+ if ($this->decimals === 0) {
+ $mask = (string) preg_replace('/\.0+/miu', '', $mask);
+ }
+
+ return str_replace('¤', $this->formatCurrencyCode(), $mask);
+ }
+
+ public static function icuVersion(): float
+ {
+ [$major, $minor] = explode('.', INTL_ICU_VERSION);
+
+ return (float) "{$major}.{$minor}";
+ }
+
+ private function formatCurrencyCode(): string
+ {
+ if ($this->locale === null) {
+ return $this->currencyCode . '*';
+ }
+
+ return "[\${$this->currencyCode}-{$this->locale}]";
+ }
+
+ public function format(): string
+ {
+ if ($this->localeFormat !== null) {
+ return $this->localeFormat;
+ }
+
+ return sprintf(
+ '_-%s%s%s0%s%s%s_-',
+ $this->currencySymbolPosition === self::LEADING_SYMBOL ? $this->formatCurrencyCode() : null,
+ (
+ $this->currencySymbolPosition === self::LEADING_SYMBOL
+ && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+ ) ? "\u{a0}" : '',
+ $this->thousandsSeparator ? '#,##' : null,
+ $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null,
+ (
+ $this->currencySymbolPosition === self::TRAILING_SYMBOL
+ && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+ ) ? "\u{a0}" : '',
+ $this->currencySymbolPosition === self::TRAILING_SYMBOL ? $this->formatCurrencyCode() : null
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php
new file mode 100644
index 0000000..bba4d25
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php
@@ -0,0 +1,125 @@
+setCurrencyCode($currencyCode);
+ $this->setThousandsSeparator($thousandsSeparator);
+ $this->setDecimals($decimals);
+ $this->setCurrencySymbolPosition($currencySymbolPosition);
+ $this->setCurrencySymbolSpacing($currencySymbolSpacing);
+ $this->setLocale($locale);
+ $this->stripLeadingRLM = $stripLeadingRLM;
+ }
+
+ public function setCurrencyCode(string $currencyCode): void
+ {
+ $this->currencyCode = $currencyCode;
+ }
+
+ public function setCurrencySymbolPosition(bool $currencySymbolPosition = self::LEADING_SYMBOL): void
+ {
+ $this->currencySymbolPosition = $currencySymbolPosition;
+ }
+
+ public function setCurrencySymbolSpacing(bool $currencySymbolSpacing = self::SYMBOL_WITHOUT_SPACING): void
+ {
+ $this->currencySymbolSpacing = $currencySymbolSpacing;
+ }
+
+ public function setStripLeadingRLM(bool $stripLeadingRLM): void
+ {
+ $this->stripLeadingRLM = $stripLeadingRLM;
+ }
+
+ protected function getLocaleFormat(): string
+ {
+ $formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY);
+ $mask = $formatter->format($this->stripLeadingRLM);
+ if ($this->decimals === 0) {
+ $mask = (string) preg_replace('/\.0+/miu', '', $mask);
+ }
+
+ return str_replace('¤', $this->formatCurrencyCode(), $mask);
+ }
+
+ private function formatCurrencyCode(): string
+ {
+ if ($this->locale === null) {
+ return $this->currencyCode;
+ }
+
+ return "[\${$this->currencyCode}-{$this->locale}]";
+ }
+
+ public function format(): string
+ {
+ if ($this->localeFormat !== null) {
+ return $this->localeFormat;
+ }
+
+ return sprintf(
+ '%s%s%s0%s%s%s',
+ $this->currencySymbolPosition === self::LEADING_SYMBOL ? $this->formatCurrencyCode() : null,
+ (
+ $this->currencySymbolPosition === self::LEADING_SYMBOL
+ && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+ ) ? "\u{a0}" : '',
+ $this->thousandsSeparator ? '#,##' : null,
+ $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null,
+ (
+ $this->currencySymbolPosition === self::TRAILING_SYMBOL
+ && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+ ) ? "\u{a0}" : '',
+ $this->currencySymbolPosition === self::TRAILING_SYMBOL ? $this->formatCurrencyCode() : null
+ );
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php
new file mode 100644
index 0000000..61ac117
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php
@@ -0,0 +1,125 @@
+separators = $this->padSeparatorArray(
+ is_array($separators) ? $separators : [$separators],
+ count($formatBlocks) - 1
+ );
+ $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+ }
+
+ private function mapFormatBlocks(string $value): string
+ {
+ // Any date masking codes are returned as lower case values
+ if (in_array(mb_strtolower($value), self::DATE_BLOCKS, true)) {
+ return mb_strtolower($value);
+ }
+
+ // Wrap any string literals in quotes, so that they're clearly defined as string literals
+ return $this->wrapLiteral($value);
+ }
+
+ public function format(): string
+ {
+ return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php
new file mode 100644
index 0000000..292c1ef
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php
@@ -0,0 +1,50 @@
+
+ */
+ protected array $formatBlocks;
+
+ /**
+ * @param null|string|string[] $separators
+ * If you want to use only a single format block, then pass a null as the separator argument
+ * @param DateTimeWizard|string ...$formatBlocks
+ */
+ public function __construct($separators, ...$formatBlocks)
+ {
+ $this->separators = $this->padSeparatorArray(
+ is_array($separators) ? $separators : [$separators],
+ count($formatBlocks) - 1
+ );
+ $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+ }
+
+ /**
+ * @param DateTimeWizard|string $value
+ */
+ private function mapFormatBlocks($value): string
+ {
+ // Any date masking codes are returned as lower case values
+ if (is_object($value)) {
+ // We can't explicitly test for Stringable until PHP >= 8.0
+ return $value;
+ }
+
+ // Wrap any string literals in quotes, so that they're clearly defined as string literals
+ return $this->wrapLiteral($value);
+ }
+
+ public function format(): string
+ {
+ return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php
new file mode 100644
index 0000000..8cd7da7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php
@@ -0,0 +1,46 @@
+= ";
+
+ protected function padSeparatorArray(array $separators, int $count): array
+ {
+ $lastSeparator = array_pop($separators);
+
+ return $separators + array_fill(0, $count, $lastSeparator);
+ }
+
+ protected function escapeSingleCharacter(string $value): string
+ {
+ if (str_contains(self::NO_ESCAPING_NEEDED, $value)) {
+ return $value;
+ }
+
+ return "\\{$value}";
+ }
+
+ protected function wrapLiteral(string $value): string
+ {
+ if (mb_strlen($value, 'UTF-8') === 1) {
+ return $this->escapeSingleCharacter($value);
+ }
+
+ // Wrap any other string literals in quotes, so that they're clearly defined as string literals
+ return '"' . str_replace('"', '""', $value) . '"';
+ }
+
+ protected function intersperse(string $formatBlock, ?string $separator): string
+ {
+ return "{$formatBlock}{$separator}";
+ }
+
+ public function __toString(): string
+ {
+ return $this->format();
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php
new file mode 100644
index 0000000..b81f77a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php
@@ -0,0 +1,153 @@
+ self::DAYS_DURATION,
+ self::HOURS_DURATION => self::HOURS_SHORT,
+ self::MINUTES_DURATION => self::MINUTES_LONG,
+ self::SECONDS_DURATION => self::SECONDS_LONG,
+ ];
+
+ protected const DURATION_DEFAULTS = [
+ self::HOURS_LONG => self::HOURS_DURATION,
+ self::HOURS_SHORT => self::HOURS_DURATION,
+ self::MINUTES_LONG => self::MINUTES_DURATION,
+ self::MINUTES_SHORT => self::MINUTES_DURATION,
+ self::SECONDS_LONG => self::SECONDS_DURATION,
+ self::SECONDS_SHORT => self::SECONDS_DURATION,
+ ];
+
+ public const SEPARATOR_COLON = ':';
+ public const SEPARATOR_SPACE_NONBREAKING = "\u{a0}";
+ public const SEPARATOR_SPACE = ' ';
+
+ public const DURATION_DEFAULT = [
+ self::HOURS_DURATION,
+ self::MINUTES_LONG,
+ self::SECONDS_LONG,
+ ];
+
+ /**
+ * @var string[]
+ */
+ protected array $separators;
+
+ /**
+ * @var string[]
+ */
+ protected array $formatBlocks;
+
+ protected bool $durationIsSet = false;
+
+ /**
+ * @param null|string|string[] $separators
+ * If you want to use the same separator for all format blocks, then it can be passed as a string literal;
+ * if you wish to use different separators, then they should be passed as an array.
+ * If you want to use only a single format block, then pass a null as the separator argument
+ */
+ public function __construct($separators = self::SEPARATOR_COLON, string ...$formatBlocks)
+ {
+ $separators ??= self::SEPARATOR_COLON;
+ $formatBlocks = (count($formatBlocks) === 0) ? self::DURATION_DEFAULT : $formatBlocks;
+
+ $this->separators = $this->padSeparatorArray(
+ is_array($separators) ? $separators : [$separators],
+ count($formatBlocks) - 1
+ );
+ $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+
+ if ($this->durationIsSet === false) {
+ // We need at least one duration mask, so if none has been set we change the first mask element
+ // to a duration.
+ $this->formatBlocks[0] = self::DURATION_DEFAULTS[mb_strtolower($this->formatBlocks[0])];
+ }
+ }
+
+ private function mapFormatBlocks(string $value): string
+ {
+ // Any duration masking codes are returned as lower case values
+ if (in_array(mb_strtolower($value), self::DURATION_BLOCKS, true)) {
+ if (array_key_exists(mb_strtolower($value), self::DURATION_MASKS)) {
+ if ($this->durationIsSet) {
+ // We should only have a single duration mask, the first defined in the mask set,
+ // so convert any additional duration masks to standard time masks.
+ $value = self::DURATION_MASKS[mb_strtolower($value)];
+ }
+ $this->durationIsSet = true;
+ }
+
+ return mb_strtolower($value);
+ }
+
+ // Wrap any string literals in quotes, so that they're clearly defined as string literals
+ return $this->wrapLiteral($value);
+ }
+
+ public function format(): string
+ {
+ return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php
new file mode 100644
index 0000000..0c02286
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php
@@ -0,0 +1,39 @@
+[a-z]{2})([-_](?P your site";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// Hello, i try to alert('Hack'); your site
+```
+
+Example 2: (Hexadecimal HTML Character)
+
+```php
+$harm_string = " ";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+```
+
+Example 3: (Unicode Hex Character)
+
+```php
+$harm_string = "CLICK ";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// CLICK
+```
+
+Example 4: (Unicode Character)
+
+```php
+$harm_string = "CLICK ";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// CLICK
+```
+
+Example 5.1: (non Inline CSS)
+
+```php
+$harm_string = '';
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+```
+
+Example 5.2: (with Inline CSS)
+
+```php
+$harm_string = ' ';
+$antiXss->removeEvilAttributes(array('style')); // allow style-attributes
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+```
+
+Example 6: (check if an string contains a XSS attack)
+
+```php
+$harm_string = "\x3cscript src=http://www.example.com/malicious-code.js\x3e\x3c/script\x3e";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+
+$antiXss->isXssFound();
+
+// true
+```
+
+Example 7: (allow e.g. iframes)
+
+```php
+$harm_string = "VIDEO ";
+
+$antiXss->removeEvilHtmlTags(array('iframe'));
+
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// VIDEO
+```
+
+
+### Unit Test:
+
+1) [Composer](https://getcomposer.org) is a prerequisite for running the tests.
+
+```
+composer install
+```
+
+2) The tests can be executed by running this command from the root directory:
+
+```bash
+./vendor/bin/phpunit
+```
+
+## AntiXss methods
+
+
+
+## addDoNotCloseHtmlTags(string[] $strings): $this
+↑
+Add some strings to the "_do_not_close_html_tags"-array.
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## addEvilAttributes(string[] $strings): $this
+↑
+Add some strings to the "_evil_attributes"-array.
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## addEvilHtmlTags(string[] $strings): $this
+↑
+Add some strings to the "_evil_html_tags"-array.
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## addNeverAllowedOnEventsAfterwards(string[] $strings): $this
+↑
+Add some strings to the "_never_allowed_on_events_afterwards"-array.
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## addNeverAllowedRegex(string[] $strings): $this
+↑
+Add some strings to the "_never_allowed_regex"-array.
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## addNeverAllowedStrAfterwards(string[] $strings): $this
+↑
+Add some strings to the "_never_allowed_str_afterwards"-array.
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## isXssFound(): bool|null
+↑
+Check if the "AntiXSS->xss_clean()"-method found an XSS attack in the last run.
+
+**Parameters:**
+__nothing__
+
+**Return:**
+- `bool|null Will return null if the "xss_clean()" wasn't running at all. `
+
+--------
+
+## removeDoNotCloseHtmlTags(string[] $strings): $this
+↑
+Remove some strings from the "_do_not_close_html_tags"-array.
+
+
+
+WARNING: Use this method only if you have a really good reason.
+
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## removeEvilAttributes(string[] $strings): $this
+↑
+Remove some strings from the "_evil_attributes"-array.
+
+
+
+WARNING: Use this method only if you have a really good reason.
+
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## removeEvilHtmlTags(string[] $strings): $this
+↑
+Remove some strings from the "_evil_html_tags"-array.
+
+
+
+WARNING: Use this method only if you have a really good reason.
+
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## removeNeverAllowedOnEventsAfterwards(string[] $strings): $this
+↑
+Remove some strings from the "_never_allowed_on_events_afterwards"-array.
+
+
+
+WARNING: Use this method only if you have a really good reason.
+
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## removeNeverAllowedRegex(string[] $strings): $this
+↑
+Remove some strings from the "_never_allowed_regex"-array.
+
+
+
+WARNING: Use this method only if you have a really good reason.
+
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## removeNeverAllowedStrAfterwards(string[] $strings): $this
+↑
+Remove some strings from the "_never_allowed_str_afterwards"-array.
+
+
+
+WARNING: Use this method only if you have a really good reason.
+
+
+**Parameters:**
+- `string[] $strings`
+
+**Return:**
+- `$this`
+
+--------
+
+## setReplacement(string $string): $this
+↑
+Set the replacement-string for not allowed strings.
+
+**Parameters:**
+- `string $string`
+
+**Return:**
+- `$this`
+
+--------
+
+## setStripe4byteChars(bool $bool): $this
+↑
+Set the option to stripe 4-Byte chars.
+
+
+
+INFO: use it if your DB (MySQL) can't use "utf8mb4" -> preventing stored XSS-attacks
+
+
+**Parameters:**
+- `bool $bool`
+
+**Return:**
+- `$this`
+
+--------
+
+## xss_clean(string|string[] $str): string|string[]
+↑
+XSS Clean
+
+
+
+Sanitizes data so that "Cross Site Scripting" hacks can be
+prevented. This method does a fair amount of work but
+it is extremely thorough, designed to prevent even the
+most obscure XSS attempts. But keep in mind that nothing
+is ever 100% foolproof...
+
+
+
+
+Note: Should only be used to deal with data upon submission.
+ It's not something that should be used for general
+ runtime processing.
+
+
+**Parameters:**
+- `TXssCleanInput $str input data e.g. string or array of strings `
+
+**Return:**
+- `string|string[]`
+
+--------
+
+
+### Support
+
+For support and donations please visit [Github](https://github.com/voku/anti-xss/) | [Issues](https://github.com/voku/anti-xss/issues) | [PayPal](https://paypal.me/moelleken) | [Patreon](https://www.patreon.com/voku).
+
+For status updates and release announcements please visit [Releases](https://github.com/voku/anti-xss/releases) | [Twitter](https://twitter.com/suckup_de) | [Patreon](https://www.patreon.com/voku/posts).
+
+For professional support please contact [me](https://about.me/voku).
+
+### Thanks
+
+- Thanks to [GitHub](https://github.com) (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc.
+- Thanks to [IntelliJ](https://www.jetbrains.com) as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
+- Thanks to [Travis CI](https://travis-ci.com/) for being the most awesome, easiest continous integration tool out there!
+- Thanks to [StyleCI](https://styleci.io/) for the simple but powerfull code style check.
+- Thanks to [PHPStan](https://github.com/phpstan/phpstan) && [Psalm](https://github.com/vimeo/psalm) for relly great Static analysis tools and for discover bugs in the code!
+
+### License
+[](https://app.fossa.io/projects/git%2Bgithub.com%2Fvoku%2Fanti-xss?ref=badge_large)
diff --git a/vendor/voku/anti-xss/build/composer.json b/vendor/voku/anti-xss/build/composer.json
new file mode 100644
index 0000000..30f30c3
--- /dev/null
+++ b/vendor/voku/anti-xss/build/composer.json
@@ -0,0 +1,5 @@
+{
+ "require-dev": {
+ "voku/php-readme-helper": "~0.6"
+ }
+}
diff --git a/vendor/voku/anti-xss/build/docs/base.md b/vendor/voku/anti-xss/build/docs/base.md
new file mode 100644
index 0000000..8485b00
--- /dev/null
+++ b/vendor/voku/anti-xss/build/docs/base.md
@@ -0,0 +1,167 @@
+[](https://github.com/voku/anti-xss/actions)
+[](http://codecov.io/github/voku/anti-xss?branch=master)
+[](https://www.codacy.com/app/voku/anti-xss)
+[](https://packagist.org/packages/voku/anti-xss)
+[](https://packagist.org/packages/voku/anti-xss)
+[](https://packagist.org/packages/voku/anti-xss)
+[](https://www.paypal.me/moelleken)
+[](https://www.patreon.com/voku)
+
+# :secret: AntiXSS
+
+"Cross-site scripting (XSS) is a type of computer security vulnerability typically found in Web applications. XSS enables
+attackers to inject client-side script into Web pages viewed by other users. A cross-site scripting vulnerability may be
+used by attackers to bypass access controls such as the same origin policy. Cross-site scripting carried out on websites
+accounted for roughly 84% of all security vulnerabilities documented by Symantec as of 2007." - http://en.wikipedia.org/wiki/Cross-site_scripting
+
+### DEMO:
+[http://anti-xss-demo.suckup.de/](http://anti-xss-demo.suckup.de/)
+
+### NOTES:
+1) Use [filter_input()](http://php.net/manual/de/function.filter-input.php) - don't use GLOBAL-Array (e.g. $_SESSION, $_GET, $_POST, $_SERVER) directly
+
+2) Use [html-sanitizer](https://github.com/tgalopin/html-sanitizer) or [HTML Purifier](http://htmlpurifier.org/) if you need a more configurable solution
+
+3) Add "Content Security Policy's" -> [Introduction to Content Security Policy](http://www.html5rocks.com/en/tutorials/security/content-security-policy/)
+
+4) DO NOT WRITE YOUR OWN REGEX TO PARSE HTML!
+
+5) READ THIS TEXT -> [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md)
+
+6) TEST THIS TOOL -> [Zed Attack Proxy (ZAP)](https://github.com/zaproxy/zaproxy)
+
+### Install via "composer require"
+```shell
+composer require voku/anti-xss
+```
+
+### Usage:
+
+```php
+
+use voku\helper\AntiXSS;
+
+require_once __DIR__ . '/vendor/autoload.php'; // example path
+
+$antiXss = new AntiXSS();
+```
+
+Example 1: (HTML Character)
+
+```php
+$harm_string = "Hello, i try to your site";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// Hello, i try to alert('Hack'); your site
+```
+
+Example 2: (Hexadecimal HTML Character)
+
+```php
+$harm_string = " ";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+```
+
+Example 3: (Unicode Hex Character)
+
+```php
+$harm_string = "CLICK ";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// CLICK
+```
+
+Example 4: (Unicode Character)
+
+```php
+$harm_string = "CLICK ";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// CLICK
+```
+
+Example 5.1: (non Inline CSS)
+
+```php
+$harm_string = ' ';
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+```
+
+Example 5.2: (with Inline CSS)
+
+```php
+$harm_string = ' ';
+$antiXss->removeEvilAttributes(array('style')); // allow style-attributes
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+```
+
+Example 6: (check if an string contains a XSS attack)
+
+```php
+$harm_string = "\x3cscript src=http://www.example.com/malicious-code.js\x3e\x3c/script\x3e";
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+//
+
+$antiXss->isXssFound();
+
+// true
+```
+
+Example 7: (allow e.g. iframes)
+
+```php
+$harm_string = "VIDEO ";
+
+$antiXss->removeEvilHtmlTags(array('iframe'));
+
+$harmless_string = $antiXss->xss_clean($harm_string);
+
+// VIDEO
+```
+
+
+### Unit Test:
+
+1) [Composer](https://getcomposer.org) is a prerequisite for running the tests.
+
+```
+composer install
+```
+
+2) The tests can be executed by running this command from the root directory:
+
+```bash
+./vendor/bin/phpunit
+```
+
+## AntiXss methods
+
+%__functions_index__voku\helper\AntiXSS__%
+
+%__functions_list__voku\helper\AntiXSS__%
+
+### Support
+
+For support and donations please visit [Github](https://github.com/voku/anti-xss/) | [Issues](https://github.com/voku/anti-xss/issues) | [PayPal](https://paypal.me/moelleken) | [Patreon](https://www.patreon.com/voku).
+
+For status updates and release announcements please visit [Releases](https://github.com/voku/anti-xss/releases) | [Twitter](https://twitter.com/suckup_de) | [Patreon](https://www.patreon.com/voku/posts).
+
+For professional support please contact [me](https://about.me/voku).
+
+### Thanks
+
+- Thanks to [GitHub](https://github.com) (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc.
+- Thanks to [IntelliJ](https://www.jetbrains.com) as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
+- Thanks to [Travis CI](https://travis-ci.com/) for being the most awesome, easiest continous integration tool out there!
+- Thanks to [StyleCI](https://styleci.io/) for the simple but powerfull code style check.
+- Thanks to [PHPStan](https://github.com/phpstan/phpstan) && [Psalm](https://github.com/vimeo/psalm) for relly great Static analysis tools and for discover bugs in the code!
+
+### License
+[](https://app.fossa.io/projects/git%2Bgithub.com%2Fvoku%2Fanti-xss?ref=badge_large)
diff --git a/vendor/voku/anti-xss/build/generate_docs.php b/vendor/voku/anti-xss/build/generate_docs.php
new file mode 100644
index 0000000..2bb3f7f
--- /dev/null
+++ b/vendor/voku/anti-xss/build/generate_docs.php
@@ -0,0 +1,11 @@
+generate(
+ __DIR__ . '/../src/voku/helper/AntiXSS.php',
+ __DIR__ . '/docs/base.md'
+);
+
+file_put_contents(__DIR__ . '/../README.md', $readmeText);
diff --git a/vendor/voku/anti-xss/composer.json b/vendor/voku/anti-xss/composer.json
new file mode 100644
index 0000000..1821727
--- /dev/null
+++ b/vendor/voku/anti-xss/composer.json
@@ -0,0 +1,41 @@
+{
+ "name": "voku/anti-xss",
+ "description": "anti xss-library",
+ "type": "library",
+ "keywords": [
+ "anti-xss",
+ "clean",
+ "security",
+ "xss"
+ ],
+ "homepage": "https://github.com/voku/anti-xss",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "EllisLab Dev Team",
+ "homepage": "http://ellislab.com/"
+ },
+ {
+ "name": "Lars Moelleken",
+ "email": "lars@moelleken.org",
+ "homepage": "https://www.moelleken.org/"
+ }
+ ],
+ "require": {
+ "php": ">=7.0.0",
+ "voku/portable-utf8": "~6.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "voku\\helper\\": "src/voku/helper/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1.x-dev"
+ }
+ }
+}
diff --git a/vendor/voku/anti-xss/src/voku/helper/AntiXSS.php b/vendor/voku/anti-xss/src/voku/helper/AntiXSS.php
new file mode 100644
index 0000000..5036961
--- /dev/null
+++ b/vendor/voku/anti-xss/src/voku/helper/AntiXSS.php
@@ -0,0 +1,2143 @@
+_initNeverAllowedStr();
+ $this->_initNeverAllowedRegex();
+ }
+
+ /**
+ * Compact any exploded words.
+ *
+ *
+ *
+ * INFO: This corrects words like: j a v a s c r i p t
+ *
+ * These words are compacted back to their correct state.
+ *
+ *
+ * @param string $str
+ *
+ * @return string
+ */
+ private function _compact_exploded_javascript($str)
+ {
+ static $WORDS_CACHE;
+ $WORDS_CACHE['chunk'] = [];
+ $WORDS_CACHE['split'] = [];
+
+ $words = [
+ 'javascript',
+ ' | | | | | | | | | | | | |