Merge branch 'master' into PHP8-Sane-Property-Names

# Conflicts:
#	CHANGELOG.md
#	src/PhpSpreadsheet/Shared/Drawing.php
#	src/PhpSpreadsheet/Spreadsheet.php
#	src/PhpSpreadsheet/Style/Conditional.php
This commit is contained in:
MarkBaker 2020-12-27 15:07:50 +01:00
commit 11522afee0
68 changed files with 1495 additions and 696 deletions

1
.gitattributes vendored
View File

@ -5,7 +5,6 @@
/.php_cs.dist export-ignore /.php_cs.dist export-ignore
/.sami.php export-ignore /.sami.php export-ignore
/.scrutinizer.yml export-ignore /.scrutinizer.yml export-ignore
/.travis.yml export-ignore
/CHANGELOG.PHPExcel.md export-ignore /CHANGELOG.PHPExcel.md export-ignore
/bin export-ignore /bin export-ignore
/composer.lock export-ignore /composer.lock export-ignore

View File

@ -1,4 +1,4 @@
name: Build name: main
on: [ push, pull_request ] on: [ push, pull_request ]
jobs: jobs:
test: test:
@ -10,6 +10,7 @@ jobs:
- '7.3' - '7.3'
- '7.4' - '7.4'
- '8.0' - '8.0'
- '8.1'
name: PHP ${{ matrix.php-version }} name: PHP ${{ matrix.php-version }}
@ -37,7 +38,7 @@ jobs:
- name: Delete composer lock file - name: Delete composer lock file
id: composer-lock id: composer-lock
if: ${{ matrix.php-version == '8.0' }} if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' }}
run: | run: |
rm composer.lock rm composer.lock
echo "::set-output name=flags::--ignore-platform-reqs" echo "::set-output name=flags::--ignore-platform-reqs"
@ -148,3 +149,25 @@ jobs:
./vendor/bin/phpunit --coverage-clover coverage-clover.xml ./vendor/bin/phpunit --coverage-clover coverage-clover.xml
curl -LO https://scrutinizer-ci.com/ocular.phar curl -LO https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage-clover.xml php ocular.phar code-coverage:upload --format=php-clover coverage-clover.xml
release:
runs-on: ubuntu-latest
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.ref }} # Otherwise our annotated tag is not fetched and we cannot get correct version
# Create release
- name: Get release info
id: release-info
run: |
echo "::set-output name=subject::$(git tag --format '%(contents:subject)' --points-at)"
git tag --format '%(contents:body)' --points-at > release-body.txt
- uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.ref }}
release_name: ${{ steps.release-info.outputs.subject }}
body_path: release-body.txt

View File

@ -1,34 +0,0 @@
language: php
dist: bionic
php:
- 7.2
- 7.3
- 7.4
- nightly
cache:
directories:
- vendor
- $HOME/.composer/cache
before_script:
# Deactivate xdebug
- if [[ $TRAVIS_PHP_VERSION != nightly ]]; then phpenv config-rm xdebug.ini; fi
- if [[ $TRAVIS_PHP_VERSION == nightly ]]; then rm composer.lock; fi
- composer install --ignore-platform-reqs
script:
- ./vendor/bin/phpunit --color=always --coverage-text
allow_failures:
- php: nightly
jobs:
include:
- stage: Code style
php: 7.4
script:
- ./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run
- ./vendor/bin/phpcs

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added ### Added
- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647)
- Provided a Size Helper class to validate size values (pt, px, em) - Provided a Size Helper class to validate size values (pt, px, em)
### Changed ### Changed
@ -25,8 +26,17 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed ### Fixed
- 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). - 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 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) ### Security Fix (CVE-2020-7776)

View File

@ -1,6 +1,6 @@
# PhpSpreadsheet # PhpSpreadsheet
[![Build Status](https://travis-ci.org/PHPOffice/PhpSpreadsheet.svg?branch=master)](https://travis-ci.org/PHPOffice/PhpSpreadsheet) [![Build Status](https://github.com/PHPOffice/PhpSpreadsheet/workflows/main/badge.svg)](https://github.com/PHPOffice/PhpSpreadsheet/actions)
[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master) [![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
[![Total Downloads](https://img.shields.io/packagist/dt/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) [![Total Downloads](https://img.shields.io/packagist/dt/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet)

View File

@ -59,7 +59,7 @@
"psr/simple-cache": "^1.0", "psr/simple-cache": "^1.0",
"psr/http-client": "^1.0", "psr/http-client": "^1.0",
"psr/http-factory": "^1.0", "psr/http-factory": "^1.0",
"voku/anti-xss": "^4.1" "ezyang/htmlpurifier": "^4.13"
}, },
"require-dev": { "require-dev": {
"dompdf/dompdf": "^0.8.5", "dompdf/dompdf": "^0.8.5",

845
composer.lock generated
View File

@ -4,8 +4,62 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "209605c0b9329968170279f40db65d22", "content-hash": "458fe4e974b469230da589a8781d1e0e",
"packages": [ "packages": [
{
"name": "ezyang/htmlpurifier",
"version": "v4.13.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"require-dev": {
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
},
"type": "library",
"autoload": {
"psr-0": {
"HTMLPurifier": "library/"
},
"files": [
"library/HTMLPurifier.composer.php"
],
"exclude-from-classmap": [
"/library/HTMLPurifier/Language/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/ezyang/htmlpurifier/issues",
"source": "https://github.com/ezyang/htmlpurifier/tree/master"
},
"time": "2020-06-29T00:56:53+00:00"
},
{ {
"name": "maennchen/zipstream-php", "name": "maennchen/zipstream-php",
"version": "2.1.0", "version": "2.1.0",
@ -477,242 +531,6 @@
], ],
"time": "2017-10-23T01:57:42+00:00" "time": "2017-10-23T01:57:42+00:00"
}, },
{
"name": "symfony/polyfill-iconv",
"version": "v1.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-iconv.git",
"reference": "c536646fdb4f29104dd26effc2fdcb9a5b085024"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c536646fdb4f29104dd26effc2fdcb9a5b085024",
"reference": "c536646fdb4f29104dd26effc2fdcb9a5b085024",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"suggest": {
"ext-iconv": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Iconv\\": ""
},
"files": [
"bootstrap.php"
]
},
"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"
],
"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": "2020-10-23T14:02:19+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
},
"files": [
"bootstrap.php"
]
},
"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"
],
"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": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"files": [
"bootstrap.php"
],
"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"
],
"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": "2020-07-14T12:35:20+00:00"
},
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.18.1", "version": "v1.18.1",
@ -789,323 +607,6 @@
} }
], ],
"time": "2020-07-14T12:35:20+00:00" "time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "639447d008615574653fb3bc60d1986d7172eaae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
"reference": "639447d008615574653fb3bc60d1986d7172eaae",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"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"
],
"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": "2020-07-14T12:35:20+00:00"
},
{
"name": "voku/anti-xss",
"version": "4.1.30",
"source": {
"type": "git",
"url": "https://github.com/voku/anti-xss.git",
"reference": "ff6e54f4a98ad1cd28f8b4a0f3c3f92f3c421f0a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/anti-xss/zipball/ff6e54f4a98ad1cd28f8b4a0f3c3f92f3c421f0a",
"reference": "ff6e54f4a98ad1cd28f8b4a0f3c3f92f3c421f0a",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"voku/portable-utf8": "~5.4.50"
},
"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": "http://www.moelleken.org/"
}
],
"description": "anti xss-library",
"homepage": "https://github.com/voku/anti-xss",
"keywords": [
"anti-xss",
"clean",
"security",
"xss"
],
"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": "2020-11-12T00:30:57+00:00"
},
{
"name": "voku/portable-ascii",
"version": "1.5.6",
"source": {
"type": "git",
"url": "https://github.com/voku/portable-ascii.git",
"reference": "80953678b19901e5165c56752d087fc11526017c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c",
"reference": "80953678b19901e5165c56752d087fc11526017c",
"shasum": ""
},
"require": {
"php": ">=7.0.0"
},
"require-dev": {
"phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
},
"suggest": {
"ext-intl": "Use Intl for transliterator_transliterate() support"
},
"type": "library",
"autoload": {
"psr-4": {
"voku\\": "src/voku/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Lars Moelleken",
"homepage": "http://www.moelleken.org/"
}
],
"description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
"homepage": "https://github.com/voku/portable-ascii",
"keywords": [
"ascii",
"clean",
"php"
],
"funding": [
{
"url": "https://www.paypal.me/moelleken",
"type": "custom"
},
{
"url": "https://github.com/voku",
"type": "github"
},
{
"url": "https://opencollective.com/portable-ascii",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/voku",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
"type": "tidelift"
}
],
"time": "2020-11-12T00:07:28+00:00"
},
{
"name": "voku/portable-utf8",
"version": "5.4.50",
"source": {
"type": "git",
"url": "https://github.com/voku/portable-utf8.git",
"reference": "f14ed68ea9ced6639e71ca989c6d907892115ba0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/portable-utf8/zipball/f14ed68ea9ced6639e71ca989c6d907892115ba0",
"reference": "f14ed68ea9ced6639e71ca989c6d907892115ba0",
"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.5.6"
},
"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": {
"psr-4": {
"voku\\": "src/voku/"
},
"files": [
"bootstrap.php"
]
},
"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"
],
"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": "2020-11-12T00:17:47+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -4150,6 +3651,165 @@
], ],
"time": "2020-07-14T12:35:20+00:00" "time": "2020-07-14T12:35:20+00:00"
}, },
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
},
"files": [
"bootstrap.php"
]
},
"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"
],
"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": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"files": [
"bootstrap.php"
],
"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"
],
"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": "2020-07-14T12:35:20+00:00"
},
{ {
"name": "symfony/polyfill-php70", "name": "symfony/polyfill-php70",
"version": "v1.18.1", "version": "v1.18.1",
@ -4227,6 +3887,79 @@
], ],
"time": "2020-07-14T12:35:20+00:00" "time": "2020-07-14T12:35:20+00:00"
}, },
{
"name": "symfony/polyfill-php72",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "639447d008615574653fb3bc60d1986d7172eaae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
"reference": "639447d008615574653fb3bc60d1986d7172eaae",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"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"
],
"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": "2020-07-14T12:35:20+00:00"
},
{ {
"name": "symfony/polyfill-php73", "name": "symfony/polyfill-php73",
"version": "v1.18.1", "version": "v1.18.1",
@ -4836,7 +4569,7 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^7.2|^8.0", "php": "^7.2||^8.0",
"ext-ctype": "*", "ext-ctype": "*",
"ext-dom": "*", "ext-dom": "*",
"ext-gd": "*", "ext-gd": "*",
@ -4852,5 +4585,5 @@
"ext-zlib": "*" "ext-zlib": "*"
}, },
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "1.1.0" "plugin-api-version": "2.0.0"
} }

View File

@ -422,8 +422,10 @@ foreach ($worksheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator(); $cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(FALSE); // This loops through all cells, $cellIterator->setIterateOnlyExistingCells(FALSE); // This loops through all cells,
// even if a cell value is not set. // even if a cell value is not set.
// By default, only cells that have a value // For 'TRUE', we loop through cells
// set will be iterated. // only when their value is set.
// If this method is not called,
// the default value is 'false'.
foreach ($cellIterator as $cell) { foreach ($cellIterator as $cell) {
echo '<td>' . echo '<td>' .
$cell->getValue() . $cell->getValue() .

View File

@ -458,6 +458,24 @@ $reader->setSheetIndex(0);
$spreadsheet = $reader->load("sample.csv"); $spreadsheet = $reader->load("sample.csv");
``` ```
You may also let PhpSpreadsheet attempt to guess the input encoding.
It will do so based on a test for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE,
or UTF-32LE),
or by doing heuristic tests for those encodings, falling back to a
specifiable encoding (default is CP1252) if all of those tests fail.
```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
$encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding('sample.csv');
// or, e.g. $encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding(
// 'sample.csv', 'ISO-8859-2');
$reader->setInputEncoding($encoding);
$reader->setDelimiter(';');
$reader->setEnclosure('');
$reader->setSheetIndex(0);
$spreadsheet = $reader->load('sample.csv');
```
#### Read a specific worksheet #### Read a specific worksheet

Binary file not shown.

View File

@ -1108,6 +1108,10 @@ class MathTrig
$digits = Functions::flattenSingleValue($digits); $digits = Functions::flattenSingleValue($digits);
if ((is_numeric($number)) && (is_numeric($digits))) { if ((is_numeric($number)) && (is_numeric($digits))) {
if ($number == 0.0) {
return 0.0;
}
if ($number < 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);
} }
@ -1134,6 +1138,10 @@ class MathTrig
$digits = Functions::flattenSingleValue($digits); $digits = Functions::flattenSingleValue($digits);
if ((is_numeric($number)) && (is_numeric($digits))) { if ((is_numeric($number)) && (is_numeric($digits))) {
if ($number == 0.0) {
return 0.0;
}
if ($number < 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);
} }

View File

@ -27,15 +27,15 @@ class TextData
{ {
$character = Functions::flattenSingleValue($character); $character = Functions::flattenSingleValue($character);
if ((!is_numeric($character)) || ($character < 0)) { if (!is_numeric($character)) {
return Functions::VALUE();
}
$character = (int) $character;
if ($character < 1 || $character > 255) {
return Functions::VALUE(); return Functions::VALUE();
} }
if (function_exists('iconv')) { return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
}
return mb_convert_encoding('&#' . (int) $character . ';', 'UTF-8', 'HTML-ENTITIES');
} }
/** /**
@ -160,7 +160,7 @@ class TextData
// Validate parameters // Validate parameters
if (!is_numeric($value) || !is_numeric($decimals)) { if (!is_numeric($value) || !is_numeric($decimals)) {
return Functions::NAN(); return Functions::VALUE();
} }
$decimals = floor($decimals); $decimals = floor($decimals);
@ -174,6 +174,7 @@ class TextData
} }
$value = MathTrig::MROUND($value, $round); $value = MathTrig::MROUND($value, $round);
} }
$mask = "$mask;($mask)";
return NumberFormat::toFormattedString($value, $mask); return NumberFormat::toFormattedString($value, $mask);
} }
@ -265,7 +266,7 @@ class TextData
// Validate parameters // Validate parameters
if (!is_numeric($value) || !is_numeric($decimals)) { if (!is_numeric($value) || !is_numeric($decimals)) {
return Functions::NAN(); return Functions::VALUE();
} }
$decimals = (int) floor($decimals); $decimals = (int) floor($decimals);

View File

@ -18,7 +18,7 @@ class NamedFormula extends DefinedName
) { ) {
// Validate data // Validate data
if (empty($formula)) { if (empty($formula)) {
throw new Exception('Tou must specify a Formula value for a Named Formula'); throw new Exception('You must specify a Formula value for a Named Formula');
} }
parent::__construct($name, $worksheet, $formula, $localOnly, $scope); parent::__construct($name, $worksheet, $formula, $localOnly, $scope);
} }

View File

@ -9,6 +9,21 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Csv extends BaseReader class Csv extends BaseReader
{ {
const UTF8_BOM = "\xEF\xBB\xBF";
const UTF8_BOM_LEN = 3;
const UTF16BE_BOM = "\xfe\xff";
const UTF16BE_BOM_LEN = 2;
const UTF16BE_LF = "\x00\x0a";
const UTF16LE_BOM = "\xff\xfe";
const UTF16LE_BOM_LEN = 2;
const UTF16LE_LF = "\x0a\x00";
const UTF32BE_BOM = "\x00\x00\xfe\xff";
const UTF32BE_BOM_LEN = 4;
const UTF32BE_LF = "\x00\x00\x00\x0a";
const UTF32LE_BOM = "\xff\xfe\x00\x00";
const UTF32LE_BOM_LEN = 4;
const UTF32LE_LF = "\x0a\x00\x00\x00";
/** /**
* Input encoding. * Input encoding.
* *
@ -90,12 +105,8 @@ class Csv extends BaseReader
{ {
rewind($this->fileHandle); rewind($this->fileHandle);
switch ($this->inputEncoding) { if (fgets($this->fileHandle, self::UTF8_BOM_LEN + 1) !== self::UTF8_BOM) {
case 'UTF-8': rewind($this->fileHandle);
fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ?
fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0);
break;
} }
} }
@ -213,7 +224,9 @@ class Csv extends BaseReader
private function getNextLine() private function getNextLine()
{ {
$line = ''; $line = '';
$enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')' . preg_quote($this->enclosure, '/'); $enclosure = ($this->escapeCharacter === '' ? ''
: ('(?<!' . preg_quote($this->escapeCharacter, '/') . ')'))
. preg_quote($this->enclosure, '/');
do { do {
// Get the next line in the file // Get the next line in the file
@ -307,7 +320,7 @@ class Csv extends BaseReader
$this->fileHandle = fopen('php://memory', 'r+b'); $this->fileHandle = fopen('php://memory', 'r+b');
$data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding); $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
fwrite($this->fileHandle, $data); fwrite($this->fileHandle, $data);
rewind($this->fileHandle); $this->skipBOM();
} }
} }
@ -531,4 +544,63 @@ class Csv extends BaseReader
return in_array($type, $supportedTypes, true); 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 ($compare === substr($first4, 0, strlen($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 = 'CP1252'): string
{
$encoding = self::guessEncodingBom($filename);
if ($encoding === '') {
$encoding = self::guessEncodingNoBom($filename);
}
return ($encoding === '') ? $dflt : $encoding;
}
} }

View File

@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Slk extends BaseReader class Slk extends BaseReader
{ {
@ -516,7 +517,7 @@ class Slk extends BaseReader
$spreadsheet->createSheet(); $spreadsheet->createSheet();
} }
$spreadsheet->setActiveSheetIndex($this->sheetIndex); $spreadsheet->setActiveSheetIndex($this->sheetIndex);
$spreadsheet->getActiveSheet()->setTitle(basename($pFilename, '.slk')); $spreadsheet->getActiveSheet()->setTitle(substr(basename($pFilename, '.slk'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
// Loop through file // Loop through file
$column = $row = ''; $column = $row = '';

View File

@ -2973,6 +2973,9 @@ class Xls extends BaseReader
// offset within (spliced) record data // offset within (spliced) record data
$pos = 0; $pos = 0;
// Limit global SST position, further control for bad SST Length in BIFF8 data
$limitposSST = 0;
// get spliced record data // get spliced record data
$splicedRecordData = $this->getSplicedRecordData(); $splicedRecordData = $this->getSplicedRecordData();
@ -2986,8 +2989,17 @@ class Xls extends BaseReader
$nm = self::getInt4d($recordData, 4); $nm = self::getInt4d($recordData, 4);
$pos += 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) // loop through the Unicode strings (16-bit length)
for ($i = 0; $i < $nm; ++$i) { for ($i = 0; $i < $nm && $pos < $limitposSST; ++$i) {
// number of characters in the Unicode string // number of characters in the Unicode string
$numChars = self::getUInt2d($recordData, $pos); $numChars = self::getUInt2d($recordData, $pos);
$pos += 2; $pos += 2;
@ -3020,7 +3032,7 @@ class Xls extends BaseReader
// expected byte length of character array if not split // expected byte length of character array if not split
$len = ($isCompressed) ? $numChars : $numChars * 2; $len = ($isCompressed) ? $numChars : $numChars * 2;
// look up limit position // look up limit position - Check it again to be sure that no error occurs when parsing SST structure
foreach ($spliceOffsets as $spliceOffset) { foreach ($spliceOffsets as $spliceOffset) {
// it can happen that the string is empty, therefore we need // it can happen that the string is empty, therefore we need
// <= and not just < // <= and not just <

View File

@ -1280,7 +1280,7 @@ class Xlsx extends BaseReader
} }
// Valid range? // Valid range?
if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') { if ($extractedRange == '') {
continue; continue;
} }
@ -1350,7 +1350,7 @@ class Xlsx extends BaseReader
$extractedRange = (string) $definedName; $extractedRange = (string) $definedName;
// Valid range? // Valid range?
if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') { if ($extractedRange == '') {
continue; continue;
} }
@ -1398,6 +1398,9 @@ class Xlsx extends BaseReader
$locatedSheet = $excel->getSheetByName($extractedSheetName); $locatedSheet = $excel->getSheetByName($extractedSheetName);
} }
if ($locatedSheet === null && !DefinedName::testIfFormula($definedRange)) {
$definedRange = '#REF!';
}
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $definedRange, false)); $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $definedRange, false));
} }
} }
@ -1968,7 +1971,7 @@ class Xlsx extends BaseReader
$unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings']; $unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
foreach ($sheetPrinterSettings as $rId => $printerSettings) { foreach ($sheetPrinterSettings as $rId => $printerSettings) {
$rId = substr($rId, 3); // rIdXXX $rId = substr($rId, 3) . 'ps'; // rIdXXX, add 'ps' suffix to avoid identical resource identifier collision with unparsed vmlDrawing
$unparsedPrinterSettings[$rId] = []; $unparsedPrinterSettings[$rId] = [];
$unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']); $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
$unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target']; $unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];

View File

@ -41,7 +41,7 @@ class Hyperlinks
foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) { foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
$cell = $worksheet->getCell($cellReference); $cell = $worksheet->getCell($cellReference);
if (isset($linkRel['id'])) { if (isset($linkRel['id'])) {
$hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']]; $hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']] ?? null;
if (isset($hyperlink['location'])) { if (isset($hyperlink['location'])) {
$hyperlinkUrl .= '#' . (string) $hyperlink['location']; $hyperlinkUrl .= '#' . (string) $hyperlink['location'];
} }

View File

@ -617,9 +617,11 @@ class Xml extends BaseReader
++$rowID; ++$rowID;
} }
$xmlX = $worksheet->children($namespaces['x']); if (isset($namespaces['x'])) {
if (isset($xmlX->WorksheetOptions)) { $xmlX = $worksheet->children($namespaces['x']);
(new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet); if (isset($xmlX->WorksheetOptions)) {
(new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet);
}
} }
} }
++$worksheetID; ++$worksheetID;

View File

@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Shared; namespace PhpOffice\PhpSpreadsheet\Shared;
use GdImage;
class Drawing class Drawing
{ {
/** /**
@ -98,7 +100,7 @@ class Drawing
*/ */
public static function pixelsToPoints($pxValue) public static function pixelsToPoints($pxValue)
{ {
return $pxValue * 0.67777777; return $pxValue * 0.75;
} }
/** /**
@ -111,7 +113,7 @@ class Drawing
public static function pointsToPixels($ptValue) public static function pointsToPixels($ptValue)
{ {
if ($ptValue != 0) { if ($ptValue != 0) {
return (int) ceil($ptValue * 1.333333333); return (int) ceil($ptValue / 0.75);
} }
return 0; return 0;
@ -152,7 +154,7 @@ class Drawing
* *
* @param string $bmpFilename Path to Windows DIB (BMP) image * @param string $bmpFilename Path to Windows DIB (BMP) image
* *
* @return resource * @return GdImage|resource
*/ */
public static function imagecreatefrombmp($bmpFilename) public static function imagecreatefrombmp($bmpFilename)
{ {

View File

@ -666,7 +666,7 @@ class Spreadsheet
// Adjust active sheet index if necessary // Adjust active sheet index if necessary
if ( if (
($this->activeSheetIndex >= $sheetIndex) && ($this->activeSheetIndex >= $sheetIndex) &&
($sheetIndex > count($this->workSheetCollection) - 1) ($this->activeSheetIndex > 0 || $numSheets <= 1)
) { ) {
--$this->activeSheetIndex; --$this->activeSheetIndex;
} }

View File

@ -189,7 +189,7 @@ class Conditional implements IComparable
/** /**
* Set Conditions. * Set Conditions.
* *
* @param string[] $conditions Condition * @param bool|float|int|string|string[] $conditions Condition
* *
* @return $this * @return $this
*/ */

View File

@ -42,13 +42,6 @@ class Style extends Supervisor
*/ */
protected $numberFormat; protected $numberFormat;
/**
* Conditional styles.
*
* @var Conditional[]
*/
protected $conditionalStyles;
/** /**
* Protection. * Protection.
* *
@ -85,7 +78,6 @@ class Style extends Supervisor
parent::__construct($isSupervisor); parent::__construct($isSupervisor);
// Initialise values // Initialise values
$this->conditionalStyles = [];
$this->font = new Font($isSupervisor, $isConditional); $this->font = new Font($isSupervisor, $isConditional);
$this->fill = new Fill($isSupervisor, $isConditional); $this->fill = new Fill($isSupervisor, $isConditional);
$this->borders = new Borders($isSupervisor, $isConditional); $this->borders = new Borders($isSupervisor, $isConditional);
@ -210,6 +202,8 @@ class Style extends Supervisor
$rangeEnd = Coordinate::coordinateFromString($rangeB); $rangeEnd = Coordinate::coordinateFromString($rangeB);
// Translate column into index // Translate column into index
$rangeStart0 = $rangeStart[0];
$rangeEnd0 = $rangeEnd[0];
$rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]); $rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]);
$rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]); $rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]);
@ -359,6 +353,13 @@ class Style extends Supervisor
for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
$oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true; $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
} }
foreach ($this->getActiveSheet()->getColumnIterator($rangeStart0, $rangeEnd0) as $columnIterator) {
$cellIterator = $columnIterator->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $columnCell) {
$columnCell->getStyle()->applyFromArray($pStyles);
}
}
break; break;
case 'ROW': case 'ROW':
@ -370,6 +371,13 @@ class Style extends Supervisor
$oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true; $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
} }
} }
foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) {
$cellIterator = $rowIterator->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $rowCell) {
$rowCell->getStyle()->applyFromArray($pStyles);
}
}
break; break;
case 'CELL': case 'CELL':
@ -597,18 +605,12 @@ class Style extends Supervisor
*/ */
public function getHashCode() public function getHashCode()
{ {
$hashConditionals = '';
foreach ($this->conditionalStyles as $conditional) {
$hashConditionals .= $conditional->getHashCode();
}
return md5( return md5(
$this->fill->getHashCode() . $this->fill->getHashCode() .
$this->font->getHashCode() . $this->font->getHashCode() .
$this->borders->getHashCode() . $this->borders->getHashCode() .
$this->alignment->getHashCode() . $this->alignment->getHashCode() .
$this->numberFormat->getHashCode() . $this->numberFormat->getHashCode() .
$hashConditionals .
$this->protection->getHashCode() . $this->protection->getHashCode() .
($this->quotePrefix ? 't' : 'f') . ($this->quotePrefix ? 't' : 'f') .
__CLASS__ __CLASS__

View File

@ -92,10 +92,11 @@ class ColumnCellIterator extends CellIterator
*/ */
public function seek($row = 1) public function seek($row = 1)
{ {
if ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) {
throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
}
if (($row < $this->startRow) || ($row > $this->endRow)) { if (($row < $this->startRow) || ($row > $this->endRow)) {
throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})"); throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
} elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) {
throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
} }
$this->currentRow = $row; $this->currentRow = $row;
@ -113,7 +114,7 @@ class ColumnCellIterator extends CellIterator
/** /**
* Return the current cell in this worksheet column. * Return the current cell in this worksheet column.
* *
* @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell * @return \PhpOffice\PhpSpreadsheet\Cell\Cell
*/ */
public function current() public function current()
{ {
@ -180,18 +181,12 @@ class ColumnCellIterator extends CellIterator
) { ) {
++$this->startRow; ++$this->startRow;
} }
if ($this->startRow > $this->endRow) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
while ( while (
(!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) && (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) &&
($this->endRow >= $this->startRow) ($this->endRow >= $this->startRow)
) { ) {
--$this->endRow; --$this->endRow;
} }
if ($this->endRow < $this->startRow) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
} }
} }
} }

View File

@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet; namespace PhpOffice\PhpSpreadsheet\Worksheet;
use GdImage;
class MemoryDrawing extends BaseDrawing class MemoryDrawing extends BaseDrawing
{ {
// Rendering functions // Rendering functions
@ -19,7 +21,7 @@ class MemoryDrawing extends BaseDrawing
/** /**
* Image resource. * Image resource.
* *
* @var resource * @var GdImage|resource
*/ */
private $imageResource; private $imageResource;
@ -62,7 +64,7 @@ class MemoryDrawing extends BaseDrawing
/** /**
* Get image resource. * Get image resource.
* *
* @return resource * @return GdImage|resource
*/ */
public function getImageResource() public function getImageResource()
{ {
@ -72,7 +74,7 @@ class MemoryDrawing extends BaseDrawing
/** /**
* Set image resource. * Set image resource.
* *
* @param resource $value * @param GdImage|resource $value
* *
* @return $this * @return $this
*/ */

View File

@ -93,12 +93,14 @@ class RowCellIterator extends CellIterator
*/ */
public function seek($column = 'A') public function seek($column = 'A')
{ {
$columnx = $column;
$column = Coordinate::columnIndexFromString($column); $column = Coordinate::columnIndexFromString($column);
if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) { if ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) {
throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
} elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) {
throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist'); throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
} }
if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
throw new PhpSpreadsheetException("Column $columnx is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
}
$this->currentColumnIndex = $column; $this->currentColumnIndex = $column;
return $this; return $this;
@ -181,15 +183,9 @@ class RowCellIterator extends CellIterator
while ((!$this->worksheet->cellExistsByColumnAndRow($this->startColumnIndex, $this->rowIndex)) && ($this->startColumnIndex <= $this->endColumnIndex)) { while ((!$this->worksheet->cellExistsByColumnAndRow($this->startColumnIndex, $this->rowIndex)) && ($this->startColumnIndex <= $this->endColumnIndex)) {
++$this->startColumnIndex; ++$this->startColumnIndex;
} }
if ($this->startColumnIndex > $this->endColumnIndex) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
while ((!$this->worksheet->cellExistsByColumnAndRow($this->endColumnIndex, $this->rowIndex)) && ($this->endColumnIndex >= $this->startColumnIndex)) { while ((!$this->worksheet->cellExistsByColumnAndRow($this->endColumnIndex, $this->rowIndex)) && ($this->endColumnIndex >= $this->startColumnIndex)) {
--$this->endColumnIndex; --$this->endColumnIndex;
} }
if ($this->endColumnIndex < $this->startColumnIndex) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
} }
} }
} }

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer; namespace PhpOffice\PhpSpreadsheet\Writer;
use HTMLPurifier;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
@ -23,7 +24,6 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing; use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use voku\helper\AntiXSS;
class Html extends BaseWriter class Html extends BaseWriter
{ {
@ -1789,9 +1789,9 @@ class Html extends BaseWriter
{ {
$result = ''; $result = '';
if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) { if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) {
$sanitizer = new AntiXSS(); $sanitizer = new HTMLPurifier();
$sanitizedString = $sanitizer->xss_clean($pSheet->getComment($coordinate)->getText()->getPlainText()); $sanitizedString = $sanitizer->purify($pSheet->getComment($coordinate)->getText()->getPlainText());
if (!$sanitizer->isXssFound()) { if ($sanitizedString !== '') {
$result .= '<a class="comment-indicator"></a>'; $result .= '<a class="comment-indicator"></a>';
$result .= '<div class="comment">' . nl2br($sanitizedString) . '</div>'; $result .= '<div class="comment">' . nl2br($sanitizedString) . '</div>';
$result .= PHP_EOL; $result .= PHP_EOL;

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xls; namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
use GdImage;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation; use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
@ -2254,7 +2255,7 @@ class Worksheet extends BIFFwriter
*/ */
public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void
{ {
$bitmap_array = (is_resource($bitmap) ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap)); $bitmap_array = (is_resource($bitmap) || $bitmap instanceof GdImage ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap));
[$width, $height, $size, $data] = $bitmap_array; [$width, $height, $size, $data] = $bitmap_array;
// Scale the frame of the image. // Scale the frame of the image.
@ -2460,7 +2461,7 @@ class Worksheet extends BIFFwriter
/** /**
* Convert a GD-image into the internal format. * Convert a GD-image into the internal format.
* *
* @param resource $image The image to process * @param GdImage|resource $image The image to process
* *
* @return array Array with data and properties of the bitmap * @return array Array with data and properties of the bitmap
*/ */

View File

@ -1079,7 +1079,7 @@ class Worksheet extends WriterPart
{ {
$objWriter->writeAttribute('t', $mappedType); $objWriter->writeAttribute('t', $mappedType);
if (!$cellValue instanceof RichText) { if (!$cellValue instanceof RichText) {
self::writeElementIf($objWriter, isset($pFlippedStringTable[$cellValue]), 'v', $pFlippedStringTable[$cellValue]); self::writeElementIf($objWriter, isset($pFlippedStringTable[$cellValue]), 'v', $pFlippedStringTable[$cellValue] ?? '');
} else { } else {
$objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]); $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]);
} }

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests; namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\DefinedName;
use PhpOffice\PhpSpreadsheet\NamedFormula;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -164,4 +165,24 @@ class DefinedNameFormulaTest extends TestCase
'utf-8 named ranges in a formula' => ['Здравствуй+мир', true], 'utf-8 named ranges in a formula' => ['Здравствуй+мир', true],
]; ];
} }
public function testEmptyNamedFormula(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$spreadSheet = new Spreadsheet();
$workSheet1 = $spreadSheet->getActiveSheet();
new NamedFormula('namedformula', $workSheet1);
}
public function testChangeFormula(): void
{
$spreadSheet = new Spreadsheet();
$workSheet1 = $spreadSheet->getActiveSheet();
$namedFormula = new NamedFormula('namedformula', $workSheet1, '=1');
self::assertEquals('=1', $namedFormula->getFormula());
$namedFormula->setFormula('=2');
self::assertEquals('=2', $namedFormula->getFormula());
$namedFormula->setFormula('');
self::assertEquals('=2', $namedFormula->getFormula());
}
} }

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests; namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\DefinedName;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -121,4 +122,83 @@ class DefinedNameTest extends TestCase
$this->spreadsheet->getDefinedName('foo')->getValue() $this->spreadsheet->getDefinedName('foo')->getValue()
); );
} }
public function testDefinedNameNoWorksheetNoScope(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
new NamedRange('xyz');
}
public function testSetAndGetRange(): void
{
$this->spreadsheet->addDefinedName(
DefinedName::createInstance('xyz', $this->spreadsheet->getActiveSheet(), 'A1')
);
$namedRange = $this->spreadsheet->getDefinedName('XYZ');
self::assertInstanceOf(NamedRange::class, $namedRange);
self::assertEquals('A1', $namedRange->getRange());
self::assertEquals('A1', $namedRange->getValue());
$namedRange->setRange('A2');
self::assertEquals('A2', $namedRange->getValue());
}
public function testChangeWorksheet(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('xyz', $sheet2, '$A$1');
$namedRange->setWorksheet($sheet1);
$this->spreadsheet->addNamedRange($namedRange);
$sheet1->getCell('B2')->setValue('=XYZ');
self::assertEquals(1, $sheet1->getCell('B2')->getCalculatedValue());
$sheet2->getCell('B2')->setValue('=XYZ');
self::assertEquals(1, $sheet2->getCell('B2')->getCalculatedValue());
}
public function testLocalOnly(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('abc', $sheet2, '$A$1');
$namedRange->setWorksheet($sheet1)->setLocalOnly(true);
$this->spreadsheet->addNamedRange($namedRange);
$sheet1->getCell('C2')->setValue('=ABC');
self::assertEquals(1, $sheet1->getCell('C2')->getCalculatedValue());
$sheet2->getCell('C2')->setValue('=ABC');
self::assertEquals('#NAME?', $sheet2->getCell('C2')->getCalculatedValue());
}
public function testScope(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('abc', $sheet2, '$A$1');
$namedRange->setScope($sheet1);
$this->spreadsheet->addNamedRange($namedRange);
$sheet1->getCell('C2')->setValue('=ABC');
self::assertEquals(2, $sheet1->getCell('C2')->getCalculatedValue());
$sheet2->getCell('C2')->setValue('=ABC');
self::assertEquals('#NAME?', $sheet2->getCell('C2')->getCalculatedValue());
}
public function testClone(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('abc', $sheet2, '$A$1');
$namedRangeClone = clone $namedRange;
$ss1 = $namedRange->getWorksheet();
$ss2 = $namedRangeClone->getWorksheet();
self::assertNotSame($ss1, $ss2);
self::assertEquals($ss1->getTitle(), $ss2->getTitle());
}
} }

View File

@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Logical; use PhpOffice\PhpSpreadsheet\Calculation\Logical;
use PhpOffice\PhpSpreadsheet\DocumentGenerator; use PhpOffice\PhpSpreadsheet\DocumentGenerator;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use UnexpectedValueException;
class DocumentGeneratorTest extends TestCase class DocumentGeneratorTest extends TestCase
{ {
@ -137,4 +138,13 @@ EXPECTED
], ],
]; ];
} }
public function testGenerateFunctionBadArray(): void
{
$this->expectException(UnexpectedValueException::class);
$phpSpreadsheetFunctions = [
'ABS' => ['category' => Cat::CATEGORY_MATH_AND_TRIG, 'functionCall' => 1],
];
DocumentGenerator::generateFunctionListByName($phpSpreadsheetFunctions);
}
} }

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Functional; namespace PhpOffice\PhpSpreadsheetTests\Functional;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
class CommentsTest extends AbstractFunctional class CommentsTest extends AbstractFunctional
{ {
@ -35,10 +36,22 @@ class CommentsTest extends AbstractFunctional
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $format); $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $format);
$commentsLoaded = $reloadedSpreadsheet->getSheet(0)->getComments(); $sheet = $reloadedSpreadsheet->getSheet(0);
$commentsLoaded = $sheet->getComments();
self::assertCount(1, $commentsLoaded); self::assertCount(1, $commentsLoaded);
$commentCoordinate = key($commentsLoaded); $commentCoordinate = key($commentsLoaded);
self::assertSame('E10', $commentCoordinate); self::assertSame('E10', $commentCoordinate);
$comment = $commentsLoaded[$commentCoordinate];
self::assertEquals('Comment to test', (string) $comment);
$commentClone = clone $comment;
self::assertEquals($comment, $commentClone);
self::assertNotSame($comment, $commentClone);
if ($format === 'Xlsx') {
self::assertEquals('feb0c24b880a8130262dadf801f85e94', $comment->getHashCode());
self::assertEquals(Alignment::HORIZONTAL_GENERAL, $comment->getAlignment());
$comment->setAlignment(Alignment::HORIZONTAL_RIGHT);
self::assertEquals(Alignment::HORIZONTAL_RIGHT, $comment->getAlignment());
}
} }
} }

View File

@ -43,7 +43,7 @@ class SampleTest extends TestCase
} }
// Unfortunately some tests are too long be ran with code-coverage // Unfortunately some tests are too long be ran with code-coverage
// analysis on Travis, so we need to exclude them // analysis on GitHub Actions, so we need to exclude them
global $argv; global $argv;
if (in_array('--coverage-clover', $argv)) { if (in_array('--coverage-clover', $argv)) {
$tooLongToBeCovered = [ $tooLongToBeCovered = [

View File

@ -161,4 +161,45 @@ class IOFactoryTest extends TestCase
IOFactory::registerReader('foo', 'bar'); IOFactory::registerReader('foo', 'bar');
} }
public function testCreateInvalidWriter(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Writer\Exception::class);
$spreadsheet = new Spreadsheet();
IOFactory::createWriter($spreadsheet, 'bad');
}
public function testCreateInvalidReader(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Reader\Exception::class);
IOFactory::createReader('bad');
}
public function testCreateReaderUnknownExtension(): void
{
$filename = 'samples/Reader/sampleData/example1.tsv';
$reader = IOFactory::createReaderForFile($filename);
self::assertEquals('PhpOffice\\PhpSpreadsheet\\Reader\\Csv', get_class($reader));
}
public function testCreateReaderCsvExtension(): void
{
$filename = 'samples/Reader/sampleData/example1.csv';
$reader = IOFactory::createReaderForFile($filename);
self::assertEquals('PhpOffice\\PhpSpreadsheet\\Reader\\Csv', get_class($reader));
}
public function testCreateReaderNoExtension(): void
{
$filename = 'samples/Reader/sampleData/example1xls';
$reader = IOFactory::createReaderForFile($filename);
self::assertEquals('PhpOffice\\PhpSpreadsheet\\Reader\\Xls', get_class($reader));
}
public function testCreateReaderNotSpreadsheet(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Reader\Exception::class);
$filename = __FILE__;
IOFactory::createReaderForFile($filename);
}
} }

View File

@ -275,4 +275,66 @@ EOF;
$reader = new Csv(); $reader = new Csv();
$reader->load('tests/data/Reader/CSV/encoding.utf8.csvxxx'); $reader->load('tests/data/Reader/CSV/encoding.utf8.csvxxx');
} }
/**
* @dataProvider providerEscapes
*/
public function testInferSeparator(string $escape, string $delimiter): void
{
$reader = new Csv();
$reader->setEscapeCharacter($escape);
$filename = 'tests/data/Reader/CSV/escape.csv';
$reader->listWorksheetInfo($filename);
self::assertEquals($delimiter, $reader->getDelimiter());
}
public function providerEscapes()
{
return [
['\\', ';'],
["\x0", ','],
[(version_compare(PHP_VERSION, '7.4') < 0) ? "\x0" : '', ','],
];
}
/**
* @dataProvider providerGuessEncoding
*/
public function testGuessEncoding(string $filename): void
{
$reader = new Csv();
$reader->setInputEncoding(Csv::guessEncoding($filename));
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('première', $sheet->getCell('A1')->getValue());
self::assertEquals('sixième', $sheet->getCell('C2')->getValue());
}
public function providerGuessEncoding()
{
return [
['tests/data/Reader/CSV/premiere.utf8.csv'],
['tests/data/Reader/CSV/premiere.utf8bom.csv'],
['tests/data/Reader/CSV/premiere.utf16be.csv'],
['tests/data/Reader/CSV/premiere.utf16bebom.csv'],
['tests/data/Reader/CSV/premiere.utf16le.csv'],
['tests/data/Reader/CSV/premiere.utf16lebom.csv'],
['tests/data/Reader/CSV/premiere.utf32be.csv'],
['tests/data/Reader/CSV/premiere.utf32bebom.csv'],
['tests/data/Reader/CSV/premiere.utf32le.csv'],
['tests/data/Reader/CSV/premiere.utf32lebom.csv'],
['tests/data/Reader/CSV/premiere.win1252.csv'],
];
}
public function testGuessEncodingDefltIso2(): void
{
$filename = 'tests/data/Reader/CSV/premiere.win1252.csv';
$reader = new Csv();
$reader->setInputEncoding(Csv::guessEncoding($filename, 'ISO-8859-2'));
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('premičre', $sheet->getCell('A1')->getValue());
self::assertEquals('sixičme', $sheet->getCell('C2')->getValue());
}
} }

View File

@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Slk; use PhpOffice\PhpSpreadsheet\Reader\Slk;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\Font;
@ -12,6 +13,16 @@ class SlkTest extends \PHPUnit\Framework\TestCase
{ {
private static $testbook = __DIR__ . '/../../../samples/templates/SylkTest.slk'; private static $testbook = __DIR__ . '/../../../samples/templates/SylkTest.slk';
private $filename = '';
protected function teardown(): void
{
if ($this->filename) {
unlink($this->filename);
$this->filename = '';
}
}
public function testInfo(): void public function testInfo(): void
{ {
$reader = new Slk(); $reader = new Slk();
@ -131,4 +142,17 @@ class SlkTest extends \PHPUnit\Framework\TestCase
self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB()); self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB());
} }
public function testLongName(): void
{
$contents = file_get_contents(self::$testbook);
$this->filename = File::sysGetTempDir()
. '/123456789a123456789b123456789c12345.slk';
file_put_contents($this->filename, $contents);
$reader = new Slk();
$spreadsheet = $reader->load($this->filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('123456789a123456789b123456789c1', $sheet->getTitle());
self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB());
}
} }

View File

@ -43,4 +43,39 @@ class XlsTest extends AbstractFunctional
self::assertEquals($sheet->getCell('A1')->getFormattedValue(), $newsheet->getCell('A1')->getFormattedValue()); self::assertEquals($sheet->getCell('A1')->getFormattedValue(), $newsheet->getCell('A1')->getFormattedValue());
self::assertEquals($sheet->getCell("$col$row")->getFormattedValue(), $newsheet->getCell("$col$row")->getFormattedValue()); self::assertEquals($sheet->getCell("$col$row")->getFormattedValue(), $newsheet->getCell("$col$row")->getFormattedValue());
} }
/**
* Test load Xls file with invalid length in SST map.
*/
public function testLoadXlsBug1592(): void
{
$filename = 'tests/data/Reader/XLS/bug1592.xls';
$reader = new Xls();
// When no fix applied, spreadsheet is not loaded
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
$col = $sheet->getHighestColumn();
$row = $sheet->getHighestRow();
$newspreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$newsheet = $newspreadsheet->getActiveSheet();
$newcol = $newsheet->getHighestColumn();
$newrow = $newsheet->getHighestRow();
self::assertEquals($spreadsheet->getSheetCount(), $newspreadsheet->getSheetCount());
self::assertEquals($sheet->getTitle(), $newsheet->getTitle());
self::assertEquals($sheet->getColumnDimensions(), $newsheet->getColumnDimensions());
self::assertEquals($col, $newcol);
self::assertEquals($row, $newrow);
$rowIterator = $sheet->getRowIterator();
foreach ($rowIterator as $row) {
foreach ($row->getCellIterator() as $cell) {
$valOld = $cell->getFormattedValue();
$valNew = $newsheet->getCell($cell->getCoordinate())->getFormattedValue();
self::assertEquals($valOld, $valNew);
}
}
}
} }

View File

@ -0,0 +1,22 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PHPUnit\Framework\TestCase;
class NamedRangeTest extends TestCase
{
public static function testBug1686b(): void
{
$xlsxFile = 'tests/data/Reader/XLSX/bug1686b.xlsx';
$reader = new Xlsx();
$spreadsheet = $reader->load($xlsxFile);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals(2.1, $sheet->getCell('A1')->getCalculatedValue());
self::assertEquals('#REF!', $sheet->getCell('A2')->getCalculatedValue());
self::assertEquals('#REF!', $sheet->getCell('A3')->getCalculatedValue());
self::assertEquals('#NAME?', $sheet->getCell('A4')->getCalculatedValue());
self::assertEquals('#REF!', $sheet->getCell('A5')->getCalculatedValue());
}
}

View File

@ -82,14 +82,9 @@ class XmlLoadTest extends TestCase
public function testLoadUnusableSample(): void public function testLoadUnusableSample(): void
{ {
// Sample spreadsheet is not readable by Excel. // Sample spreadsheet is not readable by Excel.
// But PhpSpreadsheet can load it except for coverage test. // But PhpSpreadsheet can load it.
//global $argv;
//if (in_array('--coverage-clover', $argv)) {
// self::markTestSkipped('Mysterious Travis coverage failure IOFactoryTest');
//}
$filename = __DIR__ $filename = __DIR__
. '/../../../..' . '/../../../..'
//. '/samples/templates/Excel2003XMLTest.xml';
. '/samples/templates/excel2003.short.bad.xml'; . '/samples/templates/excel2003.short.bad.xml';
$reader = new Xml(); $reader = new Xml();
$spreadsheet = $reader->load($filename); $spreadsheet = $reader->load($filename);

View File

@ -0,0 +1,70 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PHPUnit\Framework\TestCase;
class XmlOddTest extends TestCase
{
private $filename = '';
protected function teardown(): void
{
if ($this->filename) {
unlink($this->filename);
$this->filename = '';
}
}
public function testWriteThenRead(): void
{
$xmldata = <<< 'EOT'
<?xml version="1.0" encoding="UTF-8"?><?mso-application progid="Excel.Sheet"?>
<Workbook xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
>
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Title>Xml2003 Short Workbook</Title>
</DocumentProperties>
<CustomDocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<my_x05d0_Int dt:dt="integer">2</my_x05d0_Int>
</CustomDocumentProperties>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>9000</WindowHeight>
<WindowWidth>13860</WindowWidth>
<WindowTopX>240</WindowTopX>
<WindowTopY>75</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<ss:Worksheet ss:Name="Sample Data">
<Table>
<Column ss:Width="96.4913"/>
<Row ss:Index="8" ss:AutoFitHeight="0" ss:Height="14.9953">
<Cell>
<ss:Data ss:Type="String">Test String 1</ss:Data>
</Cell>
</Row>
</Table>
</ss:Worksheet>
</Workbook>
EOT;
$this->filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
file_put_contents($this->filename, $xmldata);
$reader = new Xml();
$spreadsheet = $reader->load($this->filename);
self::assertEquals(1, $spreadsheet->getSheetCount());
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('Sample Data', $sheet->getTitle());
self::assertEquals('Test String 1', $sheet->getCell('A8')->getValue());
$props = $spreadsheet->getProperties();
self::assertEquals('Xml2003 Short Workbook', $props->getTitle());
self::assertEquals('2', $props->getCustomPropertyValue('myאInt'));
}
}

View File

@ -51,6 +51,147 @@ class SpreadsheetTest extends TestCase
*/ */
public function testGetSheetByName($index, $sheetName): void public function testGetSheetByName($index, $sheetName): void
{ {
self::assertEquals($this->object->getSheet($index), $this->object->getSheetByName($sheetName)); self::assertSame($this->object->getSheet($index), $this->object->getSheetByName($sheetName));
}
public function testAddSheetDuplicateTitle(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$sheet = new Worksheet();
$sheet->setTitle('someSheet2');
$this->object->addSheet($sheet);
}
public function testAddSheetNoAdjustActive(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$sheet = new Worksheet();
$sheet->setTitle('someSheet4');
$this->object->addSheet($sheet);
self::assertEquals(2, $this->object->getActiveSheetIndex());
}
public function testAddSheetAdjustActive(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$sheet = new Worksheet();
$sheet->setTitle('someSheet0');
$this->object->addSheet($sheet, 0);
self::assertEquals(3, $this->object->getActiveSheetIndex());
}
public function testRemoveSheetIndexTooHigh(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->removeSheetByIndex(4);
}
public function testRemoveSheetNoAdjustActive(): void
{
$this->object->setActiveSheetIndex(1);
self::assertEquals(1, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(2);
self::assertEquals(1, $this->object->getActiveSheetIndex());
}
public function testRemoveSheetAdjustActive(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(1);
self::assertEquals(1, $this->object->getActiveSheetIndex());
}
public function testGetSheetIndexTooHigh(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->getSheet(4);
}
public function testGetIndexNonExistent(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$sheet = new Worksheet();
$sheet->setTitle('someSheet4');
$this->object->getIndex($sheet);
}
public function testSetIndexByName(): void
{
$this->object->setIndexByName('someSheet1', 1);
self::assertEquals('someSheet2', $this->object->getSheet(0)->getTitle());
self::assertEquals('someSheet1', $this->object->getSheet(1)->getTitle());
self::assertEquals('someSheet 3', $this->object->getSheet(2)->getTitle());
}
public function testRemoveAllSheets(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(0);
self::assertEquals(1, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(0);
self::assertEquals(0, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(0);
self::assertEquals(-1, $this->object->getActiveSheetIndex());
$sheet = new Worksheet();
$sheet->setTitle('someSheet4');
$this->object->addSheet($sheet);
self::assertEquals(0, $this->object->getActiveSheetIndex());
}
public function testBug1735(): void
{
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$spreadsheet->createSheet()->setTitle('addedsheet');
$spreadsheet->setActiveSheetIndex(1);
$spreadsheet->removeSheetByIndex(0);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('addedsheet', $sheet->getTitle());
}
public function testSetActiveSheetIndexTooHigh(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->setActiveSheetIndex(4);
}
public function testSetActiveSheetNoSuchName(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->setActiveSheetIndexByName('unknown');
}
public function testAddExternal(): void
{
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->createSheet()->setTitle('someSheet19');
$sheet->getCell('A1')->setValue(1);
$sheet->getCell('A1')->getStyle()->getFont()->setBold(true);
$sheet->getCell('B1')->getStyle()->getFont()->setSuperscript(true);
$sheet->getCell('C1')->getStyle()->getFont()->setSubscript(true);
self::assertCount(4, $spreadsheet->getCellXfCollection());
self::assertEquals(1, $sheet->getCell('A1')->getXfIndex());
$this->object->getActiveSheet()->getCell('A1')->getStyle()->getFont()->setBold(true);
self::assertCount(2, $this->object->getCellXfCollection());
$sheet3 = $this->object->addExternalSheet($sheet);
self::assertCount(6, $this->object->getCellXfCollection());
self::assertEquals('someSheet19', $sheet3->getTitle());
self::assertEquals(1, $sheet3->getCell('A1')->getValue());
self::assertTrue($sheet3->getCell('A1')->getStyle()->getFont()->getBold());
// Prove Xf index changed although style is same.
self::assertEquals(3, $sheet3->getCell('A1')->getXfIndex());
}
public function testAddExternalDuplicateName(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->createSheet()->setTitle('someSheet1');
$sheet->getCell('A1')->setValue(1);
$sheet->getCell('A1')->getStyle()->getFont()->setBold(true);
$this->object->addExternalSheet($sheet);
} }
} }

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Style; namespace PhpOffice\PhpSpreadsheetTests\Style;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class StyleTest extends TestCase class StyleTest extends TestCase
@ -19,4 +20,141 @@ class StyleTest extends TestCase
$outArray = $cell1style->getStyleArray($styleArray); $outArray = $cell1style->getStyleArray($styleArray);
self::assertEquals($styleArray, $outArray['quotePrefix']); self::assertEquals($styleArray, $outArray['quotePrefix']);
} }
public function testStyleColumn(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cellCoordinates = 'A:B';
$styleArray = [
'font' => [
'bold' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
$sheet->setCellValue('A1', 'xxxa1');
$sheet->setCellValue('A2', 'xxxa2');
$sheet->setCellValue('A3', 'xxxa3');
$sheet->setCellValue('B1', 'xxxa1');
$sheet->setCellValue('B2', 'xxxa2');
$sheet->setCellValue('B3', 'xxxa3');
$sheet->setCellValue('C1', 'xxxc1');
$sheet->setCellValue('C2', 'xxxc2');
$sheet->setCellValue('C3', 'xxxc3');
$styleArray = [
'font' => [
'italic' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
self::assertTrue($sheet->getStyle('A1')->getFont()->getBold());
self::assertTrue($sheet->getStyle('B2')->getFont()->getBold());
self::assertFalse($sheet->getStyle('C3')->getFont()->getBold());
self::assertTrue($sheet->getStyle('A1')->getFont()->getItalic());
self::assertTrue($sheet->getStyle('B2')->getFont()->getItalic());
self::assertFalse($sheet->getStyle('C3')->getFont()->getItalic());
}
public function testStyleRow(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cellCoordinates = '2:3';
$styleArray = [
'font' => [
'bold' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
$sheet->setCellValue('A1', 'xxxa1');
$sheet->setCellValue('A2', 'xxxa2');
$sheet->setCellValue('A3', 'xxxa3');
$sheet->setCellValue('B1', 'xxxa1');
$sheet->setCellValue('B2', 'xxxa2');
$sheet->setCellValue('B3', 'xxxa3');
$sheet->setCellValue('C1', 'xxxc1');
$sheet->setCellValue('C2', 'xxxc2');
$sheet->setCellValue('C3', 'xxxc3');
$styleArray = [
'font' => [
'italic' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
self::assertFalse($sheet->getStyle('A1')->getFont()->getBold());
self::assertTrue($sheet->getStyle('B2')->getFont()->getBold());
self::assertTrue($sheet->getStyle('C3')->getFont()->getBold());
self::assertFalse($sheet->getStyle('A1')->getFont()->getItalic());
self::assertTrue($sheet->getStyle('B2')->getFont()->getItalic());
self::assertTrue($sheet->getStyle('C3')->getFont()->getItalic());
}
public function testIssue1712A(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$rgb = '4467b8';
$sheet->fromArray(['OK', 'KO']);
$spreadsheet->getActiveSheet()
->getStyle('A1')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
$spreadsheet->getActiveSheet()
->getStyle('B')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
self::assertEquals($rgb, $sheet->getCell('A1')->getStyle()->getFill()->getStartColor()->getRGB());
self::assertEquals($rgb, $sheet->getCell('B1')->getStyle()->getFill()->getStartColor()->getRGB());
}
public function testIssue1712B(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$rgb = '4467b8';
$spreadsheet->getActiveSheet()
->getStyle('A1')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
$spreadsheet->getActiveSheet()
->getStyle('B')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
$sheet->fromArray(['OK', 'KO']);
self::assertEquals($rgb, $sheet->getCell('A1')->getStyle()->getFill()->getStartColor()->getRGB());
self::assertEquals($rgb, $sheet->getCell('B1')->getStyle()->getFill()->getStartColor()->getRGB());
}
public function testStyleLoopUpwards(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cellCoordinates = 'C5:A3';
$styleArray = [
'font' => [
'bold' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
$sheet->setCellValue('A1', 'xxxa1');
$sheet->setCellValue('A2', 'xxxa2');
$sheet->setCellValue('A3', 'xxxa3');
$sheet->setCellValue('B1', 'xxxa1');
$sheet->setCellValue('B2', 'xxxa2');
$sheet->setCellValue('B3', 'xxxa3');
$sheet->setCellValue('C1', 'xxxc1');
$sheet->setCellValue('C2', 'xxxc2');
$sheet->setCellValue('C3', 'xxxc3');
self::assertFalse($sheet->getStyle('A1')->getFont()->getBold());
self::assertFalse($sheet->getStyle('B2')->getFont()->getBold());
self::assertTrue($sheet->getStyle('C3')->getFont()->getBold());
}
} }

View File

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\ColumnCellIterator;
use PHPUnit\Framework\TestCase;
class ColumnCellIterator2Test extends TestCase
{
/**
* @dataProvider providerExistingCell
*/
public function testEndRange(?bool $existing, string $expectedResultFirst, string $expectedResultLast): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B2')->setValue('cellb2');
$sheet->getCell('B6')->setValue('cellb6');
$iterator = new ColumnCellIterator($sheet, 'B', 1, 8);
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$lastCoordinate = '';
$firstCoordinate = '';
foreach ($iterator as $cell) {
$lastCoordinate = $cell->getCoordinate();
if (!$firstCoordinate) {
$firstCoordinate = $lastCoordinate;
}
}
self::assertEquals($expectedResultFirst, $firstCoordinate);
self::assertEquals($expectedResultLast, $lastCoordinate);
}
public function providerExistingCell(): array
{
return [
[null, 'B1', 'B8'],
[false, 'B1', 'B8'],
[true, 'B2', 'B6'],
];
}
/**
* @dataProvider providerEmptyColumn
*/
public function testEmptyColumn(?bool $existing, int $expectedResult): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B2')->setValue('cellb2');
$sheet->getCell('B6')->setValue('cellb6');
$iterator = new ColumnCellIterator($sheet, 'C');
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$numCells = 0;
foreach ($iterator as $cell) {
++$numCells;
}
self::assertEquals($expectedResult, $numCells);
}
public function providerEmptyColumn(): array
{
return [
[null, 6],
[false, 6],
[true, 0],
];
}
}

View File

@ -71,11 +71,21 @@ class ColumnCellIteratorTest extends TestCase
public function testSeekOutOfRange(): void public function testSeekOutOfRange(): void
{ {
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Row 1 is out of range');
$iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4); $iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4);
$iterator->seek(1); $iterator->seek(1);
} }
public function testSeekNotExisting(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Cell does not exist');
$iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4);
$iterator->setIterateOnlyExistingCells(true);
$iterator->seek(2);
}
public function testPrevOutOfRange(): void public function testPrevOutOfRange(): void
{ {
$iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4); $iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4);

View File

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
use PHPUnit\Framework\TestCase;
class RowCellIterator2Test extends TestCase
{
/**
* @dataProvider providerExistingCell
*/
public function testEndRangeTrue(?bool $existing, string $expectedResultFirst, string $expectedResultLast): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('C2')->setValue('cellb2');
$sheet->getCell('F2')->setValue('cellf2');
$iterator = new RowCellIterator($sheet, 2, 'B', 'H');
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$lastCoordinate = '';
$firstCoordinate = '';
foreach ($iterator as $cell) {
$lastCoordinate = $cell->getCoordinate();
if (!$firstCoordinate) {
$firstCoordinate = $lastCoordinate;
}
}
self::assertEquals($expectedResultFirst, $firstCoordinate);
self::assertEquals($expectedResultLast, $lastCoordinate);
}
public function providerExistingCell(): array
{
return [
[null, 'B2', 'H2'],
[false, 'B2', 'H2'],
[true, 'C2', 'F2'],
];
}
/**
* @dataProvider providerEmptyRow
*/
public function testEmptyRow(?bool $existing, int $expectedResult): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B2')->setValue('cellb2');
$sheet->getCell('F2')->setValue('cellf2');
$iterator = new RowCellIterator($sheet, '3');
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$numCells = 0;
foreach ($iterator as $cell) {
++$numCells;
}
self::assertEquals($expectedResult, $numCells);
}
public function providerEmptyRow(): array
{
return [
[null, 6],
[false, 6],
[true, 0],
];
}
}

View File

@ -73,9 +73,22 @@ class RowCellIteratorTest extends TestCase
public function testSeekOutOfRange(): void public function testSeekOutOfRange(): void
{ {
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class); $this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Column A is out of range');
$iterator = new RowCellIterator($this->mockWorksheet, 2, 'B', 'D'); $iterator = new RowCellIterator($this->mockWorksheet, 2, 'B', 'D');
$iterator->seek(1); self::assertFalse($iterator->getIterateOnlyExistingCells());
self::assertEquals(2, $iterator->getCurrentColumnIndex());
$iterator->seek('A');
}
public function testSeekNotExisting(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Cell does not exist');
$iterator = new RowCellIterator($this->mockWorksheet, 2, 'B', 'D');
$iterator->setIterateOnlyExistingCells(true);
$iterator->seek('B');
} }
public function testPrevOutOfRange(): void public function testPrevOutOfRange(): void

View File

@ -10,15 +10,56 @@ use PhpOffice\PhpSpreadsheetTests\Functional;
class XssVulnerabilityTest extends Functional\AbstractFunctional class XssVulnerabilityTest extends Functional\AbstractFunctional
{ {
public function providerAcceptableMarkupRichText()
{
return [
'basic text' => ['Hello, I am safely viewing your site', 'Hello, I am safely viewing your site'],
'link' => ["<a href='Visit Google'>Google is here</a>", '<a href="Visit%20Google">Google is here</a>'],
];
}
/**
* @dataProvider providerAcceptableMarkupRichText
*
* @param string $safeTextString
* @param string $adjustedTextString
*/
public function testMarkupInComment($safeTextString, $adjustedTextString): void
{
$spreadsheet = new Spreadsheet();
$richText = new RichText();
$richText->createText($safeTextString);
$spreadsheet->getActiveSheet()->getCell('A1')->setValue('XSS Test');
$spreadsheet->getActiveSheet()
->getComment('A1')
->setText($richText);
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
$writer = IOFactory::createWriter($spreadsheet, 'Html');
$writer->save($filename);
$verify = file_get_contents($filename);
// Ensure that executable js has been stripped from the comments
self::assertStringContainsString($adjustedTextString, $verify);
}
public function providerXssRichText() public function providerXssRichText()
{ {
return [ return [
'script tag' => ['<script>alert(1)</script>'], 'script tag' => ["Hello, I am trying to <script>alert('Hack');</script> your site"],
'javascript tag' => ['javascript:alert(1)'], 'javascript tag' => ["<a href='&#x2000;javascript:alert(1)'>CLICK</a>"],
'with unicode' => ['java\u0003script:alert(1)'], 'with unicode' => ['<a href="\\u0001java\\u0003script:alert(1)">CLICK<a>'],
'inline css' => ['<li style="list-style-image: url(javascript:alert(0))">'],
'char value chevron' => ["\x3cscript src=http://www.example.com/malicious-code.js\x3e\x3c/script\x3e"],
]; ];
} }
private static $counter = 0;
/** /**
* @dataProvider providerXssRichText * @dataProvider providerXssRichText
* *
@ -43,6 +84,8 @@ class XssVulnerabilityTest extends Functional\AbstractFunctional
$writer->save($filename); $writer->save($filename);
$verify = file_get_contents($filename); $verify = file_get_contents($filename);
$counter = self::$counter++;
file_put_contents("verify{$counter}.html", $verify);
// Ensure that executable js has been stripped from the comments // Ensure that executable js has been stripped from the comments
self::assertStringNotContainsString($xssTextString, $verify); self::assertStringNotContainsString($xssTextString, $verify);
} }

View File

@ -1,6 +1,11 @@
<?php <?php
return [ return [
[
0,
0,
2,
],
[ [
662, 662,
662.78999999999996, 662.78999999999996,

View File

@ -1,6 +1,11 @@
<?php <?php
return [ return [
[
0,
0,
2,
],
[ [
663, 663,
662.78999999999996, 662.78999999999996,

View File

@ -9,6 +9,10 @@ return [
'#VALUE!', '#VALUE!',
-5, -5,
], ],
[
'#VALUE!',
0,
],
[ [
'A', 'A',
65, 65,
@ -22,27 +26,39 @@ return [
126, 126,
], ],
[ [
'⽇', 'Á',
193,
],
[
'ÿ',
255,
],
[
'#VALUE!',
256,
],
[
'#VALUE!', // '⽇',
12103, 12103,
], ],
[ [
'œ', '#VALUE!', // 'œ',
0x153, 0x153,
], ],
[ [
'ƒ', '#VALUE!', // 'ƒ',
0x192, 0x192,
], ],
[ [
'℅', '#VALUE!', // '℅',
0x2105, 0x2105,
], ],
[ [
'∑', '#VALUE!', // '∑',
0x2211, 0x2211,
], ],
[ [
'†', '#VALUE!', // '†',
0x2020, 0x2020,
], ],
]; ];

View File

@ -6,11 +6,20 @@ return [
123.456, 123.456,
2, 2,
], ],
[
'$123.46',
123.456,
],
[ [
'$123.32', '$123.32',
123.321, 123.321,
2, 2,
], ],
[
'($123.32)',
-123.321,
2,
],
[ [
'$1,235,000', '$1,235,000',
1234567, 1234567,
@ -22,12 +31,17 @@ return [
-5, -5,
], ],
[ [
'#NUM!', '($1,200,000)',
-1234567,
-5,
],
[
'#VALUE!',
'ABC', 'ABC',
2, 2,
], ],
[ [
'#NUM!', '#VALUE!',
123.456, 123.456,
'ABC', 'ABC',
], ],

View File

@ -20,13 +20,41 @@ return [
true, true,
], ],
[ [
'#NUM!', '-123456.79',
-123456.789,
2,
true,
],
[
'123500',
123456.789,
-2,
true,
],
[
'123,500',
123456.789,
-2,
],
[
'-123500',
-123456.789,
-2,
true,
],
[
'-123,500',
-123456.789,
-2,
],
[
'#VALUE!',
'ABC', 'ABC',
2, 2,
null, null,
], ],
[ [
'#NUM!', '#VALUE!',
123.456, 123.456,
'ABC', 'ABC',
null, null,

View File

@ -54,6 +54,11 @@ return [
'Mark Baker', 'Mark Baker',
2, 2,
], ],
[
1,
'',
'Mark Baker',
],
[ [
'#VALUE!', '#VALUE!',
'BITE', 'BITE',

View File

@ -0,0 +1,4 @@
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\"
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\",d
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\"
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\"
Can't render this file because it contains an unexpected character in line 1 and column 3.

Binary file not shown.
1 �p�r�e�m�i�è�r�e� �s�e�c�o�n�d� �t�r�o�i�s�i�è�m�e� �
2 �Q�u�a�t�r�i�è�m�e� �c�i�n�q�u�i�è�m�e� �s�i�x�i�è�m�e� �

Binary file not shown.
1 première second troisième
2 Quatrième cinquième sixième

Binary file not shown.
1 p�r�e�m�i�è�r�e�,�s�e�c�o�n�d�,�t�r�o�i�s�i�è�m�e� �
2 �Q�u�a�t�r�i�è�m�e�,�c�i�n�q�u�i�è�m�e�,�s�i�x�i�è�m�e� �
3

Binary file not shown.
1 première second troisième
2 Quatrième cinquième sixième

Binary file not shown.
1 ���p���r���e���m���i������r���e��� ���s���e���c���o���n���d��� ���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e��� ���c���i���n���q���u���i������m���e��� ���s���i���x���i������m���e��� ���

Binary file not shown.
1 �����p���r���e���m���i������r���e��� ���s���e���c���o���n���d��� ���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e��� ���c���i���n���q���u���i������m���e��� ���s���i���x���i������m���e��� ���

Binary file not shown.
1 p���r���e���m���i������r���e���,���s���e���c���o���n���d���,���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e���,���c���i���n���q���u���i������m���e���,���s���i���x���i������m���e��� ���
3 ���

Binary file not shown.
1 ��p���r���e���m���i������r���e���,���s���e���c���o���n���d���,���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e���,���c���i���n���q���u���i������m���e���,���s���i���x���i������m���e��� ���
3 ���

View File

@ -0,0 +1,2 @@
première,second,troisième
Quatrième,cinquième,sixième
1 première second troisième
2 Quatrième cinquième sixième

View File

@ -0,0 +1,2 @@
première,second,troisième
Quatrième,cinquième,sixième
1 première second troisième
2 Quatrième cinquième sixième

View File

@ -0,0 +1,2 @@
première,second,troisième
Quatrième,cinquième,sixième
1 première second troisième
2 Quatrième cinquième sixième

Binary file not shown.

Binary file not shown.