diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..571d8af96 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,263 @@ +name: Spout CI + +on: push + +jobs: + tests-on-php-latest: + strategy: + matrix: + operating-system: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.operating-system }} + name: Tests - PHP 8.0 on ${{ matrix.operating-system }} + env: + extensions: zip, xmlreader, dom + cache-key: cache-matrix-v1 # can be any string, change to clear the extension cache. + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: '8.0' + extensions: ${{ env.extensions }} + key: ${{ env.cache-key }} + + - name: Cache extensions + uses: actions/cache@v2 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php + with: + php-version: '8.0' + extensions: ${{ env.extensions }} + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + # Use composer.json for key, if composer.lock is not committed. + # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Test with phpunit + run: vendor/bin/phpunit --no-coverage + + tests-on-older-php: + strategy: + matrix: + php-versions: ['7.2', '7.3', '7.4'] + runs-on: ubuntu-latest + name: Tests - PHP ${{ matrix.php-versions }} + env: + extensions: zip, xmlreader, dom + cache-key: cache-matrix-v1 # can be any string, change to clear the extension cache. + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ matrix.php-versions }} + extensions: ${{ env.extensions }} + key: ${{ env.cache-key }} + + - name: Cache extensions + uses: actions/cache@v2 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php + with: + php-version: ${{ matrix.php-versions }} + extensions: ${{ env.extensions }} + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + # Use composer.json for key, if composer.lock is not committed. + # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Test with phpunit + run: vendor/bin/phpunit --no-coverage + + code-coverage: + name: Code coverage + runs-on: ubuntu-latest + env: + extensions: zip, xmlreader, dom + cache-key: cache-single-v1 # can be any string, change to clear the extension cache. + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: '7.4' + extensions: ${{ env.extensions }} + key: ${{ env.cache-key }} + + - name: Cache extensions + uses: actions/cache@v2 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: ${{ env.extensions }} + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + # Use composer.json for key, if composer.lock is not committed. + # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Run Tests with Code Coverage + run: vendor/bin/phpunit --coverage-clover=coverage.clover + + # TODO: Send coverage data to a new provider + + coding-style: + name: Coding Style + runs-on: ubuntu-latest + env: + extensions: zip, xmlreader, dom + cache-key: cache-single-v1 # can be any string, change to clear the extension cache. + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: '8.0' + extensions: ${{ env.extensions }} + key: ${{ env.cache-key }} + + - name: Cache extensions + uses: actions/cache@v2 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + extensions: ${{ env.extensions }} + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + # Use composer.json for key, if composer.lock is not committed. + # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Run PHP-CS-Fixer + run: vendor/bin/php-cs-fixer fix --verbose --diff --dry-run --diff-format=udiff + + static-analysis: + name: Static Analysis + runs-on: ubuntu-latest + env: + extensions: zip, xmlreader, dom + cache-key: cache-single-v1 # can be any string, change to clear the extension cache. + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: '8.0' + extensions: ${{ env.extensions }} + key: ${{ env.cache-key }} + + - name: Cache extensions + uses: actions/cache@v2 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + extensions: ${{ env.extensions }} + tools: phpstan + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + # Use composer.json for key, if composer.lock is not committed. + # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Static Analysis using PHPStan + run: phpstan analyse --no-progress src/ diff --git a/.php_cs.dist b/.php-cs-fixer.dist.php similarity index 97% rename from .php_cs.dist rename to .php-cs-fixer.dist.php index 9879a529c..c7e7f81df 100644 --- a/.php_cs.dist +++ b/.php-cs-fixer.dist.php @@ -19,7 +19,7 @@ 'no_break_comment' => ['comment_text' => 'do nothing'], 'no_empty_phpdoc' => false, 'no_null_property_initialization' => true, - 'no_short_echo_tag' => true, + 'echo_tag_syntax' => false, 'no_superfluous_elseif' => true, 'no_superfluous_phpdoc_tags' => false, 'no_unneeded_control_parentheses' => ['statements' => ['break', 'clone', 'continue', 'echo_print', 'switch_case', 'yield']], @@ -37,7 +37,7 @@ 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], 'phpdoc_separation' => false, 'protected_to_private' => true, - 'psr4' => true, + 'psr_autoloading' => true, 'return_type_declaration' => ['space_before' => 'one'], 'semicolon_after_instruction' => true, 'simplified_null_return' => false, diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 365ee595f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,61 +0,0 @@ -dist: bionic -sudo: false - -language: php - -matrix: - include: - - php: 7.2 - env: WITH_PHPUNIT=true - - php: 7.3 - env: WITH_PHPUNIT=true - - php: 7.3 - env: WITH_PHPUNIT=true WITH_COVERAGE=true - - php: 7.4 - env: WITH_PHPUNIT=true - - php: 8.0 - env: WITH_PHPUNIT=true - - -cache: - directories: - - $HOME/.composer/cache - - $HOME/.php-cs-fixer - -before_install: - - | - if [[ "$WITH_COVERAGE" != "true" ]]; then - phpenv config-rm xdebug.ini - fi - if [[ "$WITH_CS" != "true" ]]; then - composer remove friendsofphp/php-cs-fixer --dev --no-update - fi - - composer validate - -install: - - composer install --no-interaction --prefer-source - -before_script: - - mkdir -p "$HOME/.php-cs-fixer" - - mkdir -p build/logs - -script: - - | - if [[ "$WITH_CS" == "true" ]]; then - vendor/bin/php-cs-fixer fix --config=.php_cs.dist --verbose --diff --dry-run --diff-format=udiff - fi - - | - if [[ "$WITH_PHPUNIT" == "true" ]]; then - if [[ "$WITH_COVERAGE" == "true" ]]; then - vendor/bin/phpunit --coverage-clover=build/logs/coverage.clover - else - vendor/bin/phpunit --no-coverage - fi - fi - -after_script: - - | - if [[ "$WITH_COVERAGE" == "true" ]]; then - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover build/logs/coverage.clover - fi diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index c5fe2ec75..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,35 +0,0 @@ -build: false -clone_depth: 1 - -environment: - PHP_CHOCO_VERSION: 7.2.0 - PHP_CACHE_DIR: C:\tools\php - -cache: - - '%PHP_CACHE_DIR% -> appveyor.yml' - -init: - - SET PATH=%PHP_CACHE_DIR%;%PATH% - - SET COMPOSER_CACHE_DIR=%PHP_CACHE_DIR% - - SET COMPOSER_NO_INTERACTION=1 - - SET PHP=0 - - SET ANSICON=121x90 (121x90) - -install: - - IF EXIST %PHP_CACHE_DIR% (SET PHP=1) - - IF %PHP%==0 cinst php -y --version %PHP_CHOCO_VERSION% --params "/InstallDir:%PHP_CACHE_DIR%" - - IF %PHP%==0 cinst composer -y --ia "/DEV=%PHP_CACHE_DIR%" - - cd %PHP_CACHE_DIR% - - IF %PHP%==0 copy php.ini-production php.ini - - IF %PHP%==0 echo extension_dir=ext >> php.ini - - IF %PHP%==0 echo extension=php_fileinfo.dll >> php.ini - - IF %PHP%==0 echo extension=php_mbstring.dll >> php.ini - - IF %PHP%==0 echo extension=php_openssl.dll >> php.ini - - php -v - - IF %PHP%==0 (composer --version) ELSE (composer self-update) - - cd %APPVEYOR_BUILD_FOLDER% - - composer install --prefer-dist --no-progress - -test_script: - - cd %APPVEYOR_BUILD_FOLDER% - - vendor\bin\phpunit --colors=always diff --git a/src/Spout/Reader/Common/Creator/ReaderEntityFactory.php b/src/Spout/Reader/Common/Creator/ReaderEntityFactory.php index 06a9d9fbe..3b51102aa 100644 --- a/src/Spout/Reader/Common/Creator/ReaderEntityFactory.php +++ b/src/Spout/Reader/Common/Creator/ReaderEntityFactory.php @@ -35,6 +35,7 @@ public static function createCSVReader() return ReaderFactory::createFromType(Type::CSV); } catch (UnsupportedTypeException $e) { // should never happen + return null; } } @@ -49,6 +50,7 @@ public static function createXLSXReader() return ReaderFactory::createFromType(Type::XLSX); } catch (UnsupportedTypeException $e) { // should never happen + return null; } } @@ -63,6 +65,7 @@ public static function createODSReader() return ReaderFactory::createFromType(Type::ODS); } catch (UnsupportedTypeException $e) { // should never happen + return null; } } } diff --git a/src/Spout/Writer/Common/Creator/WriterEntityFactory.php b/src/Spout/Writer/Common/Creator/WriterEntityFactory.php index 8e43c31e3..f07e5f013 100644 --- a/src/Spout/Writer/Common/Creator/WriterEntityFactory.php +++ b/src/Spout/Writer/Common/Creator/WriterEntityFactory.php @@ -50,6 +50,7 @@ public static function createCSVWriter() return WriterFactory::createFromType(Type::CSV); } catch (UnsupportedTypeException $e) { // should never happen + return null; } } @@ -64,6 +65,7 @@ public static function createXLSXWriter() return WriterFactory::createFromType(Type::XLSX); } catch (UnsupportedTypeException $e) { // should never happen + return null; } } @@ -78,6 +80,7 @@ public static function createODSWriter() return WriterFactory::createFromType(Type::ODS); } catch (UnsupportedTypeException $e) { // should never happen + return null; } } diff --git a/tests/Spout/Reader/CSV/ReaderTest.php b/tests/Spout/Reader/CSV/ReaderTest.php index 684b04542..1d092f062 100644 --- a/tests/Spout/Reader/CSV/ReaderTest.php +++ b/tests/Spout/Reader/CSV/ReaderTest.php @@ -237,7 +237,9 @@ public function testReadShouldSupportEscapedCharacters() public function testReadShouldNotTruncateLineBreak() { $allRows = $this->getAllRowsForFile('csv_with_line_breaks.csv'); - $this->assertEquals("This is,\na comma", $allRows[0][0]); + + $newLine = PHP_EOL; // to support both Unix and Windows + $this->assertEquals("This is,{$newLine}a comma", $allRows[0][0]); } /** @@ -419,10 +421,11 @@ public function testReaderShouldNotTrimCellValues() { $allRows = $this->getAllRowsForFile('sheet_with_untrimmed_strings.csv'); + $newLine = PHP_EOL; // to support both Unix and Windows $expectedRows = [ ['A'], [' A '], - ["\n\tA\n\t"], + ["$newLine\tA$newLine\t"], ]; $this->assertEquals($expectedRows, $allRows, 'Cell values should not be trimmed'); diff --git a/tests/Spout/Writer/ODS/WriterTest.php b/tests/Spout/Writer/ODS/WriterTest.php index a9aaa0e39..d9295bb61 100644 --- a/tests/Spout/Writer/ODS/WriterTest.php +++ b/tests/Spout/Writer/ODS/WriterTest.php @@ -291,12 +291,19 @@ public function testAddRowShouldSupportFloatValuesInDifferentLocale() // Installed locales differ from one system to another, so we can't pick // a given locale. $supportedLocales = explode("\n", shell_exec('locale -a')); + $foundCommaLocale = false; foreach ($supportedLocales as $supportedLocale) { \setlocale(LC_ALL, $supportedLocale); if (\localeconv()['decimal_point'] === ',') { + $foundCommaLocale = true; break; } } + + if (!$foundCommaLocale) { + $this->markTestSkipped('No locale with comma decimal separator'); + } + $this->assertEquals(',', \localeconv()['decimal_point']); $fileName = 'test_add_row_should_support_float_values_in_different_locale.xlsx'; @@ -306,8 +313,8 @@ public function testAddRowShouldSupportFloatValuesInDifferentLocale() $this->writeToODSFile($dataRows, $fileName); - $this->assertValueWasNotWrittenToSheet($fileName, 1, "1234,5"); - $this->assertValueWasWrittenToSheet($fileName, 1, "1234.5"); + $this->assertValueWasNotWrittenToSheet($fileName, 1, '1234,5'); + $this->assertValueWasWrittenToSheet($fileName, 1, '1234.5'); } finally { // reset locale \setlocale(LC_ALL, $previousLocale); @@ -511,6 +518,10 @@ public function testAddRowShouldKeepNewLines() */ public function testGeneratedFileShouldHaveTheCorrectMimeType() { + if (!function_exists('finfo')) { + $this->markTestSkipped('finfo is not available on this system (possibly running on Windows where the DLL needs to be added explicitly to the php.ini)'); + } + $fileName = 'test_mime_type.ods'; $resourcePath = $this->getGeneratedResourcePath($fileName); $dataRow = ['foo']; diff --git a/tests/Spout/Writer/XLSX/WriterTest.php b/tests/Spout/Writer/XLSX/WriterTest.php index 26b379257..246950681 100644 --- a/tests/Spout/Writer/XLSX/WriterTest.php +++ b/tests/Spout/Writer/XLSX/WriterTest.php @@ -406,12 +406,19 @@ public function testAddRowShouldSupportFloatValuesInDifferentLocale() // Installed locales differ from one system to another, so we can't pick // a given locale. $supportedLocales = explode("\n", shell_exec('locale -a')); + $foundCommaLocale = false; foreach ($supportedLocales as $supportedLocale) { \setlocale(LC_ALL, $supportedLocale); if (\localeconv()['decimal_point'] === ',') { + $foundCommaLocale = true; break; } } + + if (!$foundCommaLocale) { + $this->markTestSkipped('No locale with comma decimal separator'); + } + $this->assertEquals(',', \localeconv()['decimal_point']); $fileName = 'test_add_row_should_support_float_values_in_different_locale.xlsx'; @@ -421,8 +428,8 @@ public function testAddRowShouldSupportFloatValuesInDifferentLocale() $this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = false); - $this->assertInlineDataWasNotWrittenToSheet($fileName, 1, "1234,5"); - $this->assertInlineDataWasWrittenToSheet($fileName, 1, "1234.5"); + $this->assertInlineDataWasNotWrittenToSheet($fileName, 1, '1234,5'); + $this->assertInlineDataWasWrittenToSheet($fileName, 1, '1234.5'); } finally { // reset locale \setlocale(LC_ALL, $previousLocale); @@ -568,6 +575,10 @@ public function testAddRowShouldEscapeControlCharacters() */ public function testGeneratedFileShouldHaveTheCorrectMimeType() { + if (!function_exists('finfo')) { + $this->markTestSkipped('finfo is not available on this system (possibly running on Windows where the DLL needs to be added explicitly to the php.ini)'); + } + $fileName = 'test_mime_type.xlsx'; $resourcePath = $this->getGeneratedResourcePath($fileName); $dataRows = $this->createRowsFromValues([['foo']]);