diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 522cff4..b9c2547 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,267 +13,275 @@ permissions: jobs: -# vlucas-phpdotenv-php83-82-81-80: -# -# name: "PHP${{ matrix.php }} vlucas/phpdotenv ${{ matrix.vlucas-phpdotenv }} ${{ matrix.os-title }} ${{ matrix.dependency-prefer-title }}" -# runs-on: "${{ matrix.os }}" -# strategy: -# fail-fast: false -# matrix: -# os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] -# php: [ "8.3", "8.2", "8.1", "8.0" ] -# vlucas-phpdotenv: [ "^5.0", "^4.1.5", "^3.6.4", "^2.0", "^1.0" ] -# dependency-prefer: [ "prefer-stable", "prefer-lowest" ] -# include: -# - php: "8.3" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.2" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.2" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.1" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.0" -# phpunit: "^9.3" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - os: "ubuntu-latest" -# os-title: "ubuntu" -# - os: "macos-latest" -# os-title: "macos" -# - os: "windows-latest" -# os-title: "win" -# - dependency-prefer: "prefer-stable" -# dependency-prefer-title: "stable" -# - dependency-prefer: "prefer-lowest" -# dependency-prefer-title: "lowest" -# -# steps: -# -# - name: "Checkout code" -# uses: "actions/checkout@v4" -# -# - name: "Validate composer.json and composer.lock" -# run: "composer validate --strict" -# -# - name: "Setup PHP" -# uses: "shivammathur/setup-php@v2" -# with: -# php-version: "${{ matrix.php }}" -# extensions: fileinfo, mbstring -# ini-values: error_reporting=E_ALL -# coverage: none -# env: -# COMPOSER_TOKEN: "${{ secrets.GITHUB_TOKEN }}" -# -# # find composer's cache directory - so we know which directory to cache in the next step -# - name: "Find composer's cache directory" -# id: "composer-cache" -# shell: bash # make sure this step works on Windows - see https://github.com/actions/runner/issues/2224#issuecomment-1289533957 -# run: | -# echo "composer_cache_dir=$(composer config cache-files-dir)">> "$GITHUB_OUTPUT" -# -# - name: "Cache composer's cache directory" -# uses: "actions/cache@v3" -# with: -# path: "${{ steps.composer-cache.outputs.composer_cache_dir }}" -# key: "[${{ matrix.os }}][php-${{ matrix.php }}][vlucas/phpdotenv-${{ matrix.vlucas-phpdotenv }}][${{ matrix.dependency-prefer }}][composer.json-${{ hashFiles('composer.json') }}]" -# -# - name: "Install dependencies" -# uses: "nick-fields/retry@v2" -# with: -# timeout_minutes: 5 -# max_attempts: 5 -# command: | -# composer remove "infection/infection" --dev --no-interaction --no-update -# composer remove "phpstan/phpstan" --dev --no-interaction --no-update -# composer remove "squizlabs/php_codesniffer" --dev --no-interaction --no-update -# composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer require "vlucas/phpdotenv:${{ matrix.vlucas-phpdotenv }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress -# -# - name: "Execute tests" -# run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure -# -# -# -# vlucas-phpdotenv-php74-73-72-71-70: -# -# name: "PHP${{ matrix.php }} vlucas/phpdotenv ${{ matrix.vlucas-phpdotenv }} ${{ matrix.os-title }} ${{ matrix.dependency-prefer-title }}" -# runs-on: "${{ matrix.os }}" -# strategy: -# fail-fast: false -# matrix: -# os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] -# php: [ "7.4", "7.3", "7.2", "7.1", "7.0" ] -# vlucas-phpdotenv: [ "^5.0", "^4.1.5", "^3.6.4", "^2.0", "^1.0" ] -# dependency-prefer: [ "prefer-stable", "prefer-lowest" ] -# include: -# - php: "7.4" -# phpunit: "^9.3" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - php: "7.3" -# phpunit: "^9.3" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - php: "7.2" -# phpunit: "^8.0" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - php: "7.1" -# phpunit: "^7.0" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - php: "7.0" -# phpunit: "^6.0" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - os: "ubuntu-latest" -# os-title: "ubuntu" -# - os: "macos-latest" -# os-title: "macos" -# - os: "windows-latest" -# os-title: "win" -# - dependency-prefer: "prefer-stable" -# dependency-prefer-title: "stable" -# - dependency-prefer: "prefer-lowest" -# dependency-prefer-title: "lowest" -# exclude: -# - php: "7.0" -# vlucas-phpdotenv: "^5.0" -# -# steps: -# -# - name: "Checkout code" -# uses: "actions/checkout@v4" -# -# - name: "Validate composer.json and composer.lock" -# run: "composer validate --strict" -# -# - name: "Setup PHP" -# uses: "shivammathur/setup-php@v2" -# with: -# php-version: "${{ matrix.php }}" -# extensions: fileinfo, mbstring -# ini-values: error_reporting=E_ALL -# coverage: none -# env: -# COMPOSER_TOKEN: "${{ secrets.GITHUB_TOKEN }}" -# -# # find composer's cache directory - so we know which directory to cache in the next step -# - name: "Find composer's cache directory" -# id: "composer-cache" -# shell: bash # make sure this step works on Windows - see https://github.com/actions/runner/issues/2224#issuecomment-1289533957 -# run: | -# echo "composer_cache_dir=$(composer config cache-files-dir)">> "$GITHUB_OUTPUT" -# -# - name: "Cache composer's cache directory" -# uses: "actions/cache@v3" -# with: -# path: "${{ steps.composer-cache.outputs.composer_cache_dir }}" -# key: "[${{ matrix.os }}][php-${{ matrix.php }}][vlucas/phpdotenv-${{ matrix.vlucas-phpdotenv }}][${{ matrix.dependency-prefer }}][composer.json-${{ hashFiles('composer.json') }}]" -# -# - name: "Install dependencies" -# uses: "nick-fields/retry@v2" -# with: -# timeout_minutes: 5 -# max_attempts: 5 -# command: | -# composer remove "infection/infection" --dev --no-interaction --no-update -# composer remove "phpstan/phpstan" --dev --no-interaction --no-update -# composer remove "squizlabs/php_codesniffer" --dev --no-interaction --no-update -# composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer require "vlucas/phpdotenv:${{ matrix.vlucas-phpdotenv }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress -# -# - name: "Execute tests" -# run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure -# -# -# -# symfony-dotenv-php83-82-81-80: -# -# name: "PHP${{ matrix.php }} symfony/dotenv ${{ matrix.symfony-dotenv }} ${{ matrix.os-title }} ${{ matrix.dependency-prefer-title }}" -# runs-on: "${{ matrix.os }}" -# strategy: -# fail-fast: false -# matrix: -# os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] -# php: [ "8.3", "8.2", "8.1", "8.0" ] -# symfony-dotenv: [ "^6.0", "^5.0", "^4.0", "^3.3" ] -# dependency-prefer: [ "prefer-stable", "prefer-lowest" ] -# include: -# - php: "8.3" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.2" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.2" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.1" -# phpunit: "^10.1.0" -# phpunit-config-file: "phpunit.github-actions.xml.dist" -# - php: "8.0" -# phpunit: "^9.3" -# phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" -# - os: "ubuntu-latest" -# os-title: "ubuntu" -# - os: "macos-latest" -# os-title: "macos" -# - os: "windows-latest" -# os-title: "win" -# - dependency-prefer: "prefer-stable" -# dependency-prefer-title: "stable" -# - dependency-prefer: "prefer-lowest" -# dependency-prefer-title: "lowest" -# -# steps: -# -# - name: "Checkout code" -# uses: "actions/checkout@v4" -# -# - name: "Validate composer.json and composer.lock" -# run: "composer validate --strict" -# -# - name: "Setup PHP" -# uses: "shivammathur/setup-php@v2" -# with: -# php-version: "${{ matrix.php }}" -# extensions: fileinfo, mbstring -# ini-values: error_reporting=E_ALL -# coverage: none -# env: -# COMPOSER_TOKEN: "${{ secrets.GITHUB_TOKEN }}" -# -# # find composer's cache directory - so we know which directory to cache in the next step -# - name: "Find composer's cache directory" -# id: "composer-cache" -# shell: bash # make sure this step works on Windows - see https://github.com/actions/runner/issues/2224#issuecomment-1289533957 -# run: | -# echo "composer_cache_dir=$(composer config cache-files-dir)">> "$GITHUB_OUTPUT" -# -# - name: "Cache composer's cache directory" -# uses: "actions/cache@v3" -# with: -# path: "${{ steps.composer-cache.outputs.composer_cache_dir }}" -# key: "[${{ matrix.os }}][php-${{ matrix.php }}][symfony/dotenv-${{ matrix.symfony-dotenv }}][${{ matrix.dependency-prefer }}][composer.json-${{ hashFiles('composer.json') }}]" -# -# - name: "Install dependencies" -# uses: "nick-fields/retry@v2" -# with: -# timeout_minutes: 5 -# max_attempts: 5 -# command: | -# composer remove "infection/infection" --dev --no-interaction --no-update -# composer remove "phpstan/phpstan" --dev --no-interaction --no-update -# composer remove "squizlabs/php_codesniffer" --dev --no-interaction --no-update -# composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress -# -# - name: "Execute tests" -# run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure + vlucas-phpdotenv-php83-82-81-80: + + name: "PHP${{ matrix.php }} vlucas/phpdotenv ${{ matrix.vlucas-phpdotenv }} ${{ matrix.os-title }} ${{ matrix.dependency-prefer-title }}" + runs-on: "${{ matrix.os }}" + strategy: + fail-fast: false + matrix: + os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] + php: [ "8.3", "8.2", "8.1", "8.0" ] + vlucas-phpdotenv: [ "^5.0", "^4.1.5", "^3.6.4", "^2.0", "^1.1.0" ] + dependency-prefer: [ "prefer-stable", "prefer-lowest" ] + include: + - php: "8.3" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.2" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.2" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.1" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.0" + phpunit: "^9.3" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - os: "ubuntu-latest" + os-title: "ubuntu" + - os: "macos-latest" + os-title: "macos" + - os: "windows-latest" + os-title: "win" + - dependency-prefer: "prefer-stable" + dependency-prefer-title: "stable" + - dependency-prefer: "prefer-lowest" + dependency-prefer-title: "lowest" + + steps: + + - name: "Checkout code" + uses: "actions/checkout@v4" + + - name: "Validate composer.json and composer.lock" + run: "composer validate --strict" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + extensions: fileinfo, mbstring + ini-values: error_reporting=E_ALL + coverage: none + env: + COMPOSER_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + # find composer's cache directory - so we know which directory to cache in the next step + - name: "Find composer's cache directory" + id: "composer-cache" + shell: bash # make sure this step works on Windows - see https://github.com/actions/runner/issues/2224#issuecomment-1289533957 + run: | + echo "composer_cache_dir=$(composer config cache-files-dir)">> "$GITHUB_OUTPUT" + + - name: "Cache composer's cache directory" + uses: "actions/cache@v3" + with: + path: "${{ steps.composer-cache.outputs.composer_cache_dir }}" + key: "[${{ matrix.os }}][php-${{ matrix.php }}][vlucas/phpdotenv-${{ matrix.vlucas-phpdotenv }}][${{ matrix.dependency-prefer }}][composer.json-${{ hashFiles('composer.json') }}]" + + - name: "Install dependencies" + uses: "nick-fields/retry@v2" + with: + timeout_minutes: 5 + max_attempts: 5 + shell: bash # make sure "^" characters are interpreted properly on Windows (e.g. in "^5.0") + command: | + composer remove "infection/infection" --dev --no-interaction --no-update + composer remove "phpcsstandards/php_codesniffer" --dev --no-interaction --no-update + composer remove "phpstan/phpstan" --dev --no-interaction --no-update + composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update + composer require "vlucas/phpdotenv:${{ matrix.vlucas-phpdotenv }}" --dev --no-interaction --no-update + composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress + + - name: "Execute tests" + run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure + + + + vlucas-phpdotenv-php74-73-72-71-70: + + name: "PHP${{ matrix.php }} vlucas/phpdotenv ${{ matrix.vlucas-phpdotenv }} ${{ matrix.os-title }} ${{ matrix.dependency-prefer-title }}" + runs-on: "${{ matrix.os }}" + strategy: + fail-fast: false + matrix: + os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] + php: [ "7.4", "7.3", "7.2", "7.1", "7.0" ] + vlucas-phpdotenv: [ "^5.0", "^4.1.5", "^3.6.4", "^2.0", "^1.1.0" ] + dependency-prefer: [ "prefer-stable", "prefer-lowest" ] + include: + - php: "7.4" + phpunit: "^9.3" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - php: "7.3" + phpunit: "^9.3" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - php: "7.2" + phpunit: "^8.0" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - php: "7.1" + phpunit: "^7.0" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - php: "7.0" + phpunit: "^6.0" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - os: "ubuntu-latest" + os-title: "ubuntu" + - os: "macos-latest" + os-title: "macos" + - os: "windows-latest" + os-title: "win" + - dependency-prefer: "prefer-stable" + dependency-prefer-title: "stable" + - dependency-prefer: "prefer-lowest" + dependency-prefer-title: "lowest" + exclude: + - php: "7.0" + vlucas-phpdotenv: "^5.0" + + steps: + + - name: "Checkout code" + uses: "actions/checkout@v4" + + - name: "Validate composer.json and composer.lock" + run: "composer validate --strict" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + extensions: fileinfo, mbstring + ini-values: error_reporting=E_ALL + coverage: none + env: + COMPOSER_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + # find composer's cache directory - so we know which directory to cache in the next step + - name: "Find composer's cache directory" + id: "composer-cache" + shell: bash # make sure this step works on Windows - see https://github.com/actions/runner/issues/2224#issuecomment-1289533957 + run: | + echo "composer_cache_dir=$(composer config cache-files-dir)">> "$GITHUB_OUTPUT" + + - name: "Cache composer's cache directory" + uses: "actions/cache@v3" + with: + path: "${{ steps.composer-cache.outputs.composer_cache_dir }}" + key: "[${{ matrix.os }}][php-${{ matrix.php }}][vlucas/phpdotenv-${{ matrix.vlucas-phpdotenv }}][${{ matrix.dependency-prefer }}][composer.json-${{ hashFiles('composer.json') }}]" + + - name: "Install dependencies" + uses: "nick-fields/retry@v2" + with: + timeout_minutes: 5 + max_attempts: 5 + shell: bash # make sure "^" characters are interpreted properly on Windows (e.g. in "^5.0") + command: | + composer remove "infection/infection" --dev --no-interaction --no-update + composer remove "phpcsstandards/php_codesniffer" --dev --no-interaction --no-update + composer remove "phpstan/phpstan" --dev --no-interaction --no-update + composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update + composer require "vlucas/phpdotenv:${{ matrix.vlucas-phpdotenv }}" --dev --no-interaction --no-update + composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress + + - name: "Execute tests" + run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure + + + + symfony-dotenv-php83-82-81-80: + + name: "PHP${{ matrix.php }} symfony/dotenv ${{ matrix.symfony-dotenv }} ${{ matrix.os-title }} ${{ matrix.dependency-prefer-title }}" + runs-on: "${{ matrix.os }}" + strategy: + fail-fast: false + matrix: + os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] + php: [ "8.3", "8.2", "8.1", "8.0" ] + symfony-dotenv: [ "^7.0", "^6.0", "^5.0", "^4.0", "^3.3" ] + dependency-prefer: [ "prefer-stable", "prefer-lowest" ] + include: + - php: "8.3" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.2" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.2" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.1" + phpunit: "^10.1.0" + phpunit-config-file: "phpunit.github-actions.xml.dist" + - php: "8.0" + phpunit: "^9.3" + phpunit-config-file: "phpunit.github-actions.up-to-9.xml.dist" + - os: "ubuntu-latest" + os-title: "ubuntu" + - os: "macos-latest" + os-title: "macos" + - os: "windows-latest" + os-title: "win" + - dependency-prefer: "prefer-stable" + dependency-prefer-title: "stable" + - dependency-prefer: "prefer-lowest" + dependency-prefer-title: "lowest" + exclude: + - php: "8.1" + symfony-dotenv: "^7.0" + - php: "8.0" + symfony-dotenv: "^7.0" + + steps: + + - name: "Checkout code" + uses: "actions/checkout@v4" + + - name: "Validate composer.json and composer.lock" + run: "composer validate --strict" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + extensions: fileinfo, mbstring + ini-values: error_reporting=E_ALL + coverage: none + env: + COMPOSER_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + # find composer's cache directory - so we know which directory to cache in the next step + - name: "Find composer's cache directory" + id: "composer-cache" + shell: bash # make sure this step works on Windows - see https://github.com/actions/runner/issues/2224#issuecomment-1289533957 + run: | + echo "composer_cache_dir=$(composer config cache-files-dir)">> "$GITHUB_OUTPUT" + + - name: "Cache composer's cache directory" + uses: "actions/cache@v3" + with: + path: "${{ steps.composer-cache.outputs.composer_cache_dir }}" + key: "[${{ matrix.os }}][php-${{ matrix.php }}][symfony/dotenv-${{ matrix.symfony-dotenv }}][${{ matrix.dependency-prefer }}][composer.json-${{ hashFiles('composer.json') }}]" + + - name: "Install dependencies" + uses: "nick-fields/retry@v2" + with: + timeout_minutes: 5 + max_attempts: 5 + shell: bash # make sure "^" characters are interpreted properly on Windows (e.g. in "^5.0") + command: | + composer remove "infection/infection" --dev --no-interaction --no-update + composer remove "phpcsstandards/php_codesniffer" --dev --no-interaction --no-update + composer remove "phpstan/phpstan" --dev --no-interaction --no-update + composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update + composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" --dev --no-interaction --no-update + composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress + + - name: "Execute tests" + run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure @@ -286,7 +294,7 @@ jobs: matrix: os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] php: [ "7.4", "7.3", "7.2", "7.1", "7.0" ] - symfony-dotenv: [ "^6.0", "^5.0", "^4.0", "^3.3" ] + symfony-dotenv: [ "^7.0", "^6.0", "^5.0", "^4.0", "^3.3" ] dependency-prefer: [ "prefer-stable", "prefer-lowest" ] include: - php: "7.4" @@ -315,16 +323,26 @@ jobs: - dependency-prefer: "prefer-lowest" dependency-prefer-title: "lowest" exclude: + - php: "7.4" + symfony-dotenv: "^7.0" - php: "7.4" symfony-dotenv: "^6.0" + - php: "7.3" + symfony-dotenv: "^7.0" - php: "7.3" symfony-dotenv: "^6.0" + - php: "7.2" + symfony-dotenv: "^7.0" - php: "7.2" symfony-dotenv: "^6.0" + - php: "7.1" + symfony-dotenv: "^7.0" - php: "7.1" symfony-dotenv: "^6.0" - php: "7.1" symfony-dotenv: "^5.0" + - php: "7.0" + symfony-dotenv: "^7.0" - php: "7.0" symfony-dotenv: "^6.0" - php: "7.0" @@ -368,246 +386,14 @@ jobs: with: timeout_minutes: 5 max_attempts: 5 + shell: bash # make sure "^" characters are interpreted properly on Windows (e.g. in "^5.0") command: | composer remove "infection/infection" --dev --no-interaction --no-update + composer remove "phpcsstandards/php_codesniffer" --dev --no-interaction --no-update composer remove "phpstan/phpstan" --dev --no-interaction --no-update - composer remove "squizlabs/php_codesniffer" --dev --no-interaction --no-update composer require "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" --dev --no-interaction --no-update composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction --optimize-autoloader --no-progress - name: "Execute tests" run: vendor/bin/phpunit "--configuration=${{ matrix.phpunit-config-file }}" --no-coverage --stop-on-error --stop-on-failure - - - -# symfony-dotenv-6: -# -# name: PHP ${{ matrix.php }} - symfony/dotenv ${{ matrix.symfony-dotenv }} - ${{ matrix.os }} - ${{ matrix.dependency-prefer }} -# runs-on: ${{ matrix.os }} -# strategy: -# fail-fast: true -# matrix: -# os: [ubuntu-latest, macos-latest, windows-latest] -# php: [8.2, 8.1, "8.0"] -# symfony-dotenv: [^6.0] -# dependency-prefer: [prefer-stable, prefer-lowest] -# phpunit-config-file: ["phpunit.up-to-9.xml.dist"] -# include: -# - php: 8.2 -# phpunit: ^9.3 -# - php: 8.1 -# phpunit: ^9.3 -# - php: "8.0" -# phpunit: ^9.3 -# -# steps: -# - name: Checkout code -# uses: actions/checkout@v2 -# -# - name: Setup PHP -# uses: shivammathur/setup-php@v2 -# with: -# php-version: ${{ matrix.php }} -# extensions: fileinfo, mbstring -# coverage: none -# -## # find out composer's cache directory on the current os - for the "Cache dependencies (composer)" step below -## - name: Determine composer's cache directory -## id: composer-cache -## run: | -## echo "::set-output name=dir::$(composer config cache-files-dir)" -## -## - name: Cache dependencies (composer) -## uses: actions/cache@v2 -## with: -## path: ${{ steps.composer-cache.outputs.dir }} -## key: php-${{ matrix.php }}-symfony-dotenv-${{ matrix.symfony-dotenv }}-os-${{ matrix.os }}-dependency-prefer-${{ matrix.dependency-prefer }}-composer-${{ hashFiles('composer.json') }} -# -# - name: Install dependencies (composer) -# run: | -# composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction -# -# - name: Execute tests -# run: vendor/bin/phpunit --configuration=${{ matrix.phpunit-config-file }} --no-coverage --stop-on-error --stop-on-failure -# -# symfony-dotenv-5: -# -# name: PHP ${{ matrix.php }} - symfony/dotenv ${{ matrix.symfony-dotenv }} - ${{ matrix.os }} - ${{ matrix.dependency-prefer }} -# runs-on: ${{ matrix.os }} -# strategy: -# fail-fast: true -# matrix: -# os: [ubuntu-latest, macos-latest, windows-latest] -# php: [7.4, 7.3, 7.2] -# symfony-dotenv: [^5.0] -# dependency-prefer: [prefer-stable, prefer-lowest] -# phpunit-config-file: ["phpunit.up-to-9.xml.dist"] -# include: -# - php: 7.4 -# phpunit: ^9.0 -# - php: 7.3 -# phpunit: ^9.0 -# - php: 7.2 -# phpunit: ^8.0 -# -# steps: -# - name: Checkout code -# uses: actions/checkout@v2 -# -# - name: Setup PHP -# uses: shivammathur/setup-php@v2 -# with: -# php-version: ${{ matrix.php }} -# extensions: fileinfo, mbstring -# coverage: none -# -## # find out composer's cache directory on the current os - for the "Cache dependencies (composer)" step below -## - name: Determine composer's cache directory -## id: composer-cache -## run: | -## echo "::set-output name=dir::$(composer config cache-files-dir)" -## -## - name: Cache dependencies (composer) -## uses: actions/cache@v2 -## with: -## path: ${{ steps.composer-cache.outputs.dir }} -## key: php-${{ matrix.php }}-symfony-dotenv-${{ matrix.symfony-dotenv }}-os-${{ matrix.os }}-dependency-prefer-${{ matrix.dependency-prefer }}-composer-${{ hashFiles('composer.json') }} -# -# - name: Install dependencies (composer) -# run: | -# composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction -# -# - name: Execute tests -# run: vendor/bin/phpunit --configuration=${{ matrix.phpunit-config-file }} --no-coverage --stop-on-error --stop-on-failure -# -# symfony-dotenv-4: -# -# name: PHP ${{ matrix.php }} - symfony/dotenv ${{ matrix.symfony-dotenv }} - ${{ matrix.os }} - ${{ matrix.dependency-prefer }} -# runs-on: ${{ matrix.os }} -# strategy: -# fail-fast: true -# matrix: -# os: [ubuntu-latest, macos-latest, windows-latest] -# php: [7.4, 7.3, 7.2, 7.1] -# symfony-dotenv: [^4.0] -# dependency-prefer: [prefer-stable, prefer-lowest] -# phpunit-config-file: ["phpunit.up-to-9.xml.dist"] -# include: -# - php: 7.4 -# phpunit: ^9.0 -# - php: 7.3 -# phpunit: ^9.0 -# - php: 7.2 -# phpunit: ^8.0 -# - php: 7.1 -# phpunit: ^7.0 -# -# steps: -# - name: Checkout code -# uses: actions/checkout@v2 -# -# - name: Setup PHP -# uses: shivammathur/setup-php@v2 -# with: -# php-version: ${{ matrix.php }} -# extensions: fileinfo, mbstring -# coverage: none -# -## # find out composer's cache directory on the current os - for the "Cache dependencies (composer)" step below -## - name: Determine composer's cache directory -## id: composer-cache -## run: | -## echo "::set-output name=dir::$(composer config cache-files-dir)" -## -## - name: Cache dependencies (composer) -## uses: actions/cache@v2 -## with: -## path: ${{ steps.composer-cache.outputs.dir }} -## key: php-${{ matrix.php }}-symfony-dotenv-${{ matrix.symfony-dotenv }}-os-${{ matrix.os }}-dependency-prefer-${{ matrix.dependency-prefer }}-composer-${{ hashFiles('composer.json') }} -# -# - name: Install dependencies (composer) -# run: | -# composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction -# -# - name: Execute tests -# run: vendor/bin/phpunit --configuration=${{ matrix.phpunit-config-file }} --no-coverage --stop-on-error --stop-on-failure -# -# symfony-dotenv-33: -# -# name: PHP ${{ matrix.php }} - symfony/dotenv ${{ matrix.symfony-dotenv }} - ${{ matrix.os }} - ${{ matrix.dependency-prefer }} -# runs-on: ${{ matrix.os }} -# strategy: -# fail-fast: true -# matrix: -# os: [ubuntu-latest, macos-latest, windows-latest] -# php: [8.3, 8.2, 8.1, "8.0", 7.4, 7.3, 7.2, 7.1, "7.0"] -# symfony-dotenv: [^3.3] -# dependency-prefer: [prefer-stable, prefer-lowest] -# phpunit-config-file: ["phpunit.xml.dist"] -# include: -# - php: 8.3 -# phpunit: "^10.1.0" -# - php: 8.2 -# phpunit: "^10.1.0" -# - php: 8.1 -# phpunit: ^9.3 -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# - php: "8.0" -# phpunit: ^9.3 -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# - php: 7.4 -# phpunit: "^9.0" -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# - php: 7.3 -# phpunit: "^9.0" -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# - php: 7.2 -# phpunit: "^8.0" -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# - php: 7.1 -# phpunit: "^7.0" -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# - php: "7.0" -# phpunit: "^6.0" -# phpunit-config-file: "phpunit.up-to-9.xml.dist" -# exclude: -# - os: windows-latest -# symfony-dotenv: ^3.3 # symfony-dotenv on Windows can't read multiple values with different capitalisations at once (e.g. "On" and "on") -# - php: 8.1 -# dependency-prefer: prefer-lowest -# symfony-dotenv: ^3.3 # "Deprecated: preg_match(): Passing null to parameter #4 ($flags) of type int is deprecated" exists in symfony-dotenv before 3.3.14 -# -# steps: -# - name: Checkout code -# uses: actions/checkout@v2 -# -# - name: Setup PHP -# uses: shivammathur/setup-php@v2 -# with: -# php-version: ${{ matrix.php }} -# extensions: fileinfo, mbstring -# coverage: none -# -## # find out composer's cache directory on the current os - for the "Cache dependencies (composer)" step below -## - name: Determine composer's cache directory -## id: composer-cache -## run: | -## echo "::set-output name=dir::$(composer config cache-files-dir)" -## -## - name: Cache dependencies (composer) -## uses: actions/cache@v2 -## with: -## path: ${{ steps.composer-cache.outputs.dir }} -## key: php-${{ matrix.php }}-symfony-dotenv-${{ matrix.symfony-dotenv }}-os-${{ matrix.os }}-dependency-prefer-${{ matrix.dependency-prefer }}-composer-${{ hashFiles('composer.json') }} -# -# - name: Install dependencies (composer) -# run: | -# composer require "symfony/dotenv:${{ matrix.symfony-dotenv }}" "phpunit/phpunit:${{ matrix.phpunit }}" --dev --no-interaction --no-update -# composer update --${{ matrix.dependency-prefer }} --prefer-dist --no-interaction -# -# - name: Execute tests -# run: vendor/bin/phpunit --configuration=${{ matrix.phpunit-config-file }} --no-coverage --stop-on-error --stop-on-failure diff --git a/composer.json b/composer.json index 5621182..0a9865a 100644 --- a/composer.json +++ b/composer.json @@ -26,13 +26,14 @@ "require": { "php": "7.0.* | 7.1.* | 7.2.* | 7.3.* | 7.4.* | 8.0.* | 8.1.* | 8.2.* | 8.3.*", "ext-mbstring": "*", - "vlucas/phpdotenv": "^1.0 | ^2.0 | ^3.0 | ^4.0 | ^5.0" + "vlucas/phpdotenv": "^1.1.0 | ^2.0 | ^3.0 | ^4.0 | ^5.0" }, "require-dev": { + "infection/infection": "^0.10 | ^0.11 | ^0.12 | ^0.13 | ^0.14 | ^0.15 | ^0.16 | ^0.17 | ^0.18 | ^0.19 | ^0.20 | ^0.21 | ^0.22 | ^0.23 | ^0.24 | ^0.25 | ^0.26 | ^0.27", "jchook/phpunit-assert-throws": "^1.0", + "phpcsstandards/php_codesniffer": "^3.7.2", "phpstan/phpstan": "^0.9 | ^0.10 | ^0.11 | ^0.12 | ^1.0", - "phpunit/phpunit": "~4.8 | ^5.0 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", - "squizlabs/php_codesniffer": "^3.5" + "phpunit/phpunit": "~4.8 | ^5.0 | ^6.0 | ^7.0 | ^8.4 | ^9.0 | ^10.0" }, "autoload": { "psr-4": { @@ -45,14 +46,24 @@ } }, "scripts": { - "test": "vendor/bin/phpunit", - "phpstan": "vendor/bin/phpstan analyse -c phpstan.neon --level=max", - "phpcs": "vendor/bin/phpcs" + "infection": "vendor/bin/infection --threads=max --show-mutations", + "phpcbf": "vendor/bin/phpcbf", + "phpcs": "vendor/bin/phpcs", + "phpstan": "vendor/bin/phpstan.phar analyse --level=max", + "test": "vendor/bin/phpunit" + }, + "scripts-descriptions": { + "infection": "Run Infection tests", + "phpcbf": "Run PHP Code Beautifier and Fixer against your application", + "phpcs": "Run PHP CodeSniffer against your application", + "phpstan": "Run PHPStan static analysis against your application", + "test": "Run PHPUnit tests" }, "config": { "sort-packages": true, "allow-plugins": { - "ocramius/package-versions": true + "ocramius/package-versions": true, + "infection/extension-installer": true } } } diff --git a/phpunit.github-actions.up-to-9.xml.dist b/phpunit.github-actions.up-to-9.xml.dist index 6f1215a..871f7f3 100644 --- a/phpunit.github-actions.up-to-9.xml.dist +++ b/phpunit.github-actions.up-to-9.xml.dist @@ -4,7 +4,7 @@ xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd" backupGlobals="true" backupStaticAttributes="true" - beStrictAboutOutputDuringTests="true" + beStrictAboutOutputDuringTests="false" bootstrap="vendor/autoload.php" colors="false" convertDeprecationsToExceptions="false" diff --git a/src/DotEnvAdapters/AbstractDotEnvAdapter.php b/src/DotEnvAdapters/AbstractDotEnvAdapter.php new file mode 100644 index 0000000..4c8e8d7 --- /dev/null +++ b/src/DotEnvAdapters/AbstractDotEnvAdapter.php @@ -0,0 +1,214 @@ + The original set of getenv() values. */ + private $origGetEnv = []; + + /** @var array The original set of $_ENV values. */ + private $origEnv = []; + + /** @var array The original set of $_SERVER values. */ + private $origServer = []; + + + + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + abstract protected function importWillUpdateGetenvValues(): bool; + + /** + * When going through the process of backing up and clearing the getenv() values, work out if the code should touch + * only the variables defined in the .env file (which requires it to be loaded an extra time beforehand). + * + * PHP 7.0 and below can't get a list of the current env vars using getenv() (with no arguments). + * + * So getting the keys from the .env file allows us to override those values and replace them after without needing + * to know the full list. + * + * @return boolean + */ + protected function shouldOnlyWorkWithVariablesDefinedInEnvFile(): bool + { + // look for PHP 7.0 or below + return (bool) version_compare(PHP_VERSION, '7.1.0', '<'); + } + + + + /** + * Load the values from the given .env file, and return them. + * + * NOTE: This MUST leave the getenv(), $_ENV, $_SERVER etc values as they were to begin with. + * + * @param string $path The path to the .env file. + * @return ValueStore + * @throws InvalidPathException When the directory or file could not be used. + * @throws Throwable Rethrows any other Throwable exception. + */ + public function import(string $path): ValueStore + { + try { + + $this->recordCurrentEnvValues($path); + $this->removeCurrentEnvValues(); + $valueStore = $this->importValuesFromEnvFile($path); + + } catch (Throwable $e) { + + throw $this->exceptionIsBecauseFileCantBeOpened($e) + ? InvalidPathException::invalidPath($path, $e) + : $e; + + } finally { + + $valueStore = $valueStore ?? new ValueStore(); + + $keysJustOverridden = array_keys($valueStore->all()); + $this->restoreOriginalEnvValues($keysJustOverridden); + } + + return $valueStore; + } + + + + /** + * Record the current environment values, to be restored later. + * + * @param string $path The path to the .env file. + * @return void + */ + private function recordCurrentEnvValues(string $path) + { + $this->origEnv = $_ENV; + $this->origServer = $_SERVER; + + if (!$this->importWillUpdateGetenvValues()) { + return; + } + + $this->origGetEnv = $this->shouldOnlyWorkWithVariablesDefinedInEnvFile() + ? $this->resolveCurrentEnvVarsBasedOnKeysDefinedInEnvFile($path) + : GetenvSupport::getAllGetenvVariables(); + } + + /** + * Generate an array of the current env variables, based on the keys defined in the .env file. + * + * @param string $path The path to the .env file. + * @return array + */ + private function resolveCurrentEnvVarsBasedOnKeysDefinedInEnvFile(string $path): array + { + $keys = $this->determineKeysDefinedInEnvFile($path); + return GetenvSupport::getParticularGetenvVariables($keys); + } + + /** + * Look into a dotenv file, and find out which keys it defines. + * + * @param string $path The path to the .env file. + * @return string[] + */ + private function determineKeysDefinedInEnvFile(string $path): array + { + $envFileValues = $this->parseEnvFileForValues($path); + + return array_keys($envFileValues); + } + + /** + * Parse the contents of a .env file for key value pairs. + * + * Used when using PHP 7.0 or below, to determine which keys are in the .env file. + * + * @param string $path The path to the .env file. + * @return array + */ + protected function parseEnvFileForValues(string $path): array + { + throw GeneralException::pleaseOverrideMethodInChildClass(static::class, __FUNCTION__); + } + + + + /** + * Remove all current environment values. + * + * @return void + */ + private function removeCurrentEnvValues() + { + $_ENV = $_SERVER = []; + + if (!$this->importWillUpdateGetenvValues()) { + return; + } + + $origGetEnvKeys = array_keys($this->origGetEnv); + GetenvSupport::removeGetenvVariables($origGetEnvKeys); + } + + + + /** + * Read the data from the given .env path. + * + * @param string $path The path to the .env file. + * @return ValueStore + */ + abstract protected function importValuesFromEnvFile(string $path): ValueStore; + + + + /** + * Check if the given exception is because the .env file could not be opened. + * + * @param Throwable $e The exception to check. + * @return boolean + */ + abstract protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool; + + + + /** + * Restore the original environment values. + * + * @param string[] $keysJustOverridden The keys that were just overridden. + * @return void + */ + private function restoreOriginalEnvValues(array $keysJustOverridden) + { + $_ENV = $this->origEnv; + $_SERVER = $this->origServer; + + if (!$this->importWillUpdateGetenvValues()) { + return; + } + + // PHP 7.1 and 7.2 on Windows don't pick up keys with empty values + // so explicitly remove the values here in case any were empty + GetenvSupport::removeGetenvVariables($keysJustOverridden); + + $this->shouldOnlyWorkWithVariablesDefinedInEnvFile() + ? GetenvSupport::addGetenvVariables($this->origGetEnv) + : GetenvSupport::replaceAllGetenvVariables($this->origGetEnv); + } +} diff --git a/src/DotEnvAdapters/DotEnvAdapterPicker.php b/src/DotEnvAdapters/DotEnvAdapterPicker.php index 3823461..1679177 100644 --- a/src/DotEnvAdapters/DotEnvAdapterPicker.php +++ b/src/DotEnvAdapters/DotEnvAdapterPicker.php @@ -2,8 +2,7 @@ namespace CodeDistortion\FluentDotEnv\DotEnvAdapters; -use CodeDistortion\FluentDotEnv\DotEnvAdapters\Symfony\SymfonyAdapter3; -use CodeDistortion\FluentDotEnv\DotEnvAdapters\Symfony\SymfonyAdapter4Plus; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\Symfony\SymfonyAdapter; use CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas\VLucasAdapterV1; use CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas\VLucasAdapterV2; use CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas\VLucasAdapterV3; @@ -15,7 +14,6 @@ use Dotenv\Dotenv as DotenvV4; use Dotenv\Dotenv as DotenvV5; use Dotenv\Environment\DotenvFactory as DotenvFactoryV3; -use ReflectionMethod; use Symfony\Component\Dotenv\Dotenv as SymfonyDotenv; /** @@ -56,7 +54,6 @@ public static function pickAdapter(array $order = ['vlucas', 'symfony']): DotEnv * Detect the version of vlucas/phpdotenv installed. * * @return DotEnvAdapterInterface|null - * @throws DependencyException When the vlucas/phpdotenv package cannot be found. */ public static function detectVLucasPhpDotEnv() { @@ -78,25 +75,11 @@ public static function detectVLucasPhpDotEnv() * Detect the version of symfony/dotenv installed. * * @return DotEnvAdapterInterface|null - * @throws DependencyException When the symfony/dotenv package cannot be found. */ public static function detectSymfonyDotEnv() { - if (class_exists(SymfonyDotenv::class)) { - - // before version 3.3.7, symfony/dotenv's Dotenv::populate(..) method checked to see if each value is - // present in getenv(..) first before importing it. An extra step needs to be taken to compensate for this. - - // to try and detect a change after this so the extra work can be removed, this code looks for the 'void' - // return type in the Dotenv::load(..) method which was added in version 4.0.0 - $reflectionMethod = new ReflectionMethod(SymfonyDotenv::class, 'load'); - $returnType = $reflectionMethod->getReturnType(); - $returnTypeName = !is_null($returnType) ? $returnType->getName() : null; - if ($returnTypeName == 'void') { - return new SymfonyAdapter4Plus(); - } - return new SymfonyAdapter3(); - } - return null; + return class_exists(SymfonyDotenv::class) + ? new SymfonyAdapter() + : null; } } diff --git a/src/DotEnvAdapters/DotEnvAdapterTrait.php b/src/DotEnvAdapters/DotEnvAdapterTrait.php new file mode 100644 index 0000000..997e0d6 --- /dev/null +++ b/src/DotEnvAdapters/DotEnvAdapterTrait.php @@ -0,0 +1,57 @@ +usePutenv(false); + } + $dotenv->load($path); + + unset($_ENV['SYMFONY_DOTENV_VARS']); // symfony/dotenv sets this key + + return new ValueStore($_ENV); + } + + + + /** + * Check if the given exception is because the .env file could not be opened. + * + * @param Throwable $e The exception to check. + * @return boolean + */ + protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool + { + return $e instanceof PathException; + } + + + + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't, then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + protected function importWillUpdateGetenvValues(): bool + { + // updating of getenv() values will be turned off when this method is available + return !method_exists(Dotenv::class, 'usePutenv'); + } + + /** + * Parse the contents of a .env file for key value pairs. + * + * Used when using PHP 7.0 or below, to determine which keys are in the .env file. + * + * @param string $path The path to the .env file. + * @return array + */ + protected function parseEnvFileForValues(string $path): array + { + $content = $this->getFileContent($path); + + // luckily Symfony Dotenv's parse() method is public + return (new Dotenv())->parse($content, 'doesnt-matter'); + } +} diff --git a/src/DotEnvAdapters/Symfony/SymfonyAdapter3.php b/src/DotEnvAdapters/Symfony/SymfonyAdapter3.php deleted file mode 100644 index a24cd8e..0000000 --- a/src/DotEnvAdapters/Symfony/SymfonyAdapter3.php +++ /dev/null @@ -1,69 +0,0 @@ -runImport($path); - } catch (Throwable $e) { - - throw ($e instanceof PathException - ? InvalidPathException::invalidPath($path, $e) - : $e); - - } finally { - GetenvSupport::replaceGetenv($origGetEnv); - $_ENV = $origEnv; - $_SERVER = $origServer; - } - - return $values; - } - - /** - * Actually import the data from the given .env path - * - * @param string $path The path to the .env file. - * @return ValueStore - */ - private function runImport(string $path): ValueStore - { - foreach (GetenvSupport::getenvValues() as $key => $value) { - putenv($key); - } - $_SERVER = $_ENV = []; - - (new Dotenv())->load($path); - unset($_SERVER['SYMFONY_DOTENV_VARS']); - - return new ValueStore($_SERVER); - } -} diff --git a/src/DotEnvAdapters/Symfony/SymfonyAdapter4Plus.php b/src/DotEnvAdapters/Symfony/SymfonyAdapter4Plus.php deleted file mode 100644 index 5cb66bf..0000000 --- a/src/DotEnvAdapters/Symfony/SymfonyAdapter4Plus.php +++ /dev/null @@ -1,62 +0,0 @@ -runImport($path); - } catch (Throwable $e) { - - throw ($e instanceof PathException - ? InvalidPathException::invalidPath($path, $e) - : $e); - - } finally { - $_ENV = $origEnv; - $_SERVER = $origServer; - } - - return $values; - } - - /** - * Actually import the data from the given .env path - * - * @param string $path The path to the .env file. - * @return ValueStore - */ - private function runImport(string $path): ValueStore - { - $_SERVER = $_ENV = []; - - (new Dotenv())->load($path); - unset($_SERVER['SYMFONY_DOTENV_VARS']); - return new ValueStore($_SERVER); - } -} diff --git a/src/DotEnvAdapters/VLucas/AbstractVLucasAdapter.php b/src/DotEnvAdapters/VLucas/AbstractVLucasAdapter.php deleted file mode 100644 index 9454240..0000000 --- a/src/DotEnvAdapters/VLucas/AbstractVLucasAdapter.php +++ /dev/null @@ -1,25 +0,0 @@ -loader = new VLucasV2Loader($this->filePath, $immutable = true); + return $this->loader->load(); + } + + /** + * Load `.env` file in given directory + */ + public function overload() + { + $this->loader = new VLucasV2Loader($this->filePath, $immutable = false); + return $this->loader->load(); + } +} diff --git a/src/DotEnvAdapters/VLucas/Support/VLucasV2Loader.php b/src/DotEnvAdapters/VLucas/Support/VLucasV2Loader.php new file mode 100644 index 0000000..dd7c574 --- /dev/null +++ b/src/DotEnvAdapters/VLucas/Support/VLucasV2Loader.php @@ -0,0 +1,39 @@ +normaliseEnvironmentVariable($name, $value); + + // Don't overwrite existing environment variables if we're immutable + // Ruby's dotenv does this with `ENV[key] ||= value`. + if ($this->immutable === true && !is_null($this->getEnvironmentVariable($name))) { + return; + } + +// putenv("$name=$value"); +// $_ENV[$name] = $value; + $_SERVER[$name] = $value; + } +} diff --git a/src/DotEnvAdapters/VLucas/VLucasAdapterV1.php b/src/DotEnvAdapters/VLucas/VLucasAdapterV1.php index b22ca9b..e54a0ff 100644 --- a/src/DotEnvAdapters/VLucas/VLucasAdapterV1.php +++ b/src/DotEnvAdapters/VLucas/VLucasAdapterV1.php @@ -2,69 +2,77 @@ namespace CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas; -use CodeDistortion\FluentDotEnv\Exceptions\InvalidPathException; -use CodeDistortion\FluentDotEnv\Misc\GetenvSupport; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\AbstractDotEnvAdapter; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\DotEnvAdapterTrait; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas\Support\VLucasV1Dotenv; use CodeDistortion\FluentDotEnv\Misc\ValueStore; -use Dotenv; use InvalidArgumentException; use Throwable; /** * Adapter for vlucas/phpdotenv v1. */ -class VLucasAdapterV1 extends AbstractVLucasAdapter +class VLucasAdapterV1 extends AbstractDotEnvAdapter { + use DotEnvAdapterTrait; + + + /** - * Load the values from the given .env file, and return them. - * - * NOTE: This MUST leave the getenv(), $_ENV, $_SERVER etc values as they were to begin with. + * Read the data from the given .env path. * * @param string $path The path to the .env file. * @return ValueStore - * @throws InvalidPathException When the directory or file could not be used. - * @throws Throwable Rethrows any Throwable exception. */ - public function import(string $path): ValueStore + protected function importValuesFromEnvFile(string $path): ValueStore { - $origGetEnv = GetenvSupport::getenvValues(); - $origEnv = $_ENV; - $origServer = $_SERVER; - - try { - $values = $this->runImport($path); - } catch (Throwable $e) { - - throw ($e instanceof InvalidArgumentException - ? InvalidPathException::invalidPath($path, $e) - : $e); - - } finally { - GetenvSupport::replaceGetenv($origGetEnv); - $_ENV = $origEnv; - $_SERVER = $origServer; + $directory = $this->getDir($path); + $filename = $this->getFilename($path); + + $dotEnv = new VLucasV1Dotenv(); + if (method_exists($dotEnv, 'makeMutable')) { + $dotEnv->makeMutable(); } - return $values; + $dotEnv->load($directory, $filename); + + return new ValueStore($_SERVER); } + + /** - * Actually import the data from the given .env path + * Check if the given exception is because the .env file could not be opened. * - * @param string $path The path to the .env file. - * @return ValueStore + * @param Throwable $e The exception to check. + * @return boolean */ - private function runImport(string $path): ValueStore + protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool { - $_SERVER = []; + return $e instanceof InvalidArgumentException; + } - $dotEnv = (new Dotenv()); - if (method_exists($dotEnv, 'makeMutable')) { - $dotEnv->makeMutable(); - } - list($directory, $filename) = $this->splitPath($path); - $dotEnv->load($directory, $filename); - return new ValueStore($_SERVER); + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't, then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + protected function importWillUpdateGetenvValues(): bool + { + // custom class VLucasV1Dotenv overrides Dotenv's setEnvironmentVariable() method + // to remove the putenv() and $_ENV lines. HOWEVER: + // version <= 1.0.6 don't have the setEnvironmentVariable() method + // version <= 1.0.9 some issues in overriding setEnvironmentVariable() + // version ^1.1.0 is required in composer.json to avoid problems with these old versions + + // otherwise… + // not needed because vlucas/phpdotenv v1 is being used in a way where + // it doesn't update getenv() values, it only populates $_SERVER + // - see use of overloaded VLucasV1Dotenv class above + return false; } } diff --git a/src/DotEnvAdapters/VLucas/VLucasAdapterV2.php b/src/DotEnvAdapters/VLucas/VLucasAdapterV2.php index 92e2a17..36baaff 100644 --- a/src/DotEnvAdapters/VLucas/VLucasAdapterV2.php +++ b/src/DotEnvAdapters/VLucas/VLucasAdapterV2.php @@ -2,65 +2,65 @@ namespace CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas; -use CodeDistortion\FluentDotEnv\Exceptions\InvalidPathException; -use CodeDistortion\FluentDotEnv\Misc\GetenvSupport; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\AbstractDotEnvAdapter; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\DotEnvAdapterTrait; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas\Support\VLucasV2Dotenv; use CodeDistortion\FluentDotEnv\Misc\ValueStore; -use Dotenv\Dotenv; -use Dotenv\Exception\InvalidPathException as DotEnvInvalidPathException; use InvalidArgumentException; use Throwable; /** * Adapter for vlucas/phpdotenv v2. */ -class VLucasAdapterV2 extends AbstractVLucasAdapter +class VLucasAdapterV2 extends AbstractDotEnvAdapter { + use DotEnvAdapterTrait; + + + /** - * Load the values from the given .env file, and return them. - * - * NOTE: This MUST leave the getenv(), $_ENV, $_SERVER etc values as they were to begin with. + * Read the data from the given .env path. * * @param string $path The path to the .env file. * @return ValueStore - * @throws InvalidPathException When the directory or file could not be used. - * @throws Throwable Rethrows any Throwable exception. */ - public function import(string $path): ValueStore + protected function importValuesFromEnvFile(string $path): ValueStore { - $origGetEnv = GetenvSupport::getenvValues(); - $origEnv = $_ENV; - $origServer = $_SERVER; + $directory = $this->getDir($path); + $filename = $this->getFilename($path); - try { - $values = $this->runImport($path); - } catch (Throwable $e) { + (new VLucasV2Dotenv($directory, $filename))->overload(); - throw (($e instanceof DotEnvInvalidPathException) || ($e instanceof InvalidArgumentException) - ? InvalidPathException::invalidPath($path, $e) - : $e); + return new ValueStore($_SERVER); + } - } finally { - GetenvSupport::replaceGetenv($origGetEnv); - $_ENV = $origEnv; - $_SERVER = $origServer; - } - return $values; - } /** - * Actually import the data from the given .env path + * Check if the given exception is because the .env file could not be opened. * - * @param string $path The path to the .env file. - * @return ValueStore + * @param Throwable $e The exception to check. + * @return boolean */ - private function runImport(string $path): ValueStore + protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool { - $_SERVER = []; + return $e instanceof InvalidArgumentException; + } - list($directory, $filename) = $this->splitPath($path); - (new Dotenv($directory, $filename))->overload(); - return new ValueStore($_SERVER); + + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't, then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + protected function importWillUpdateGetenvValues(): bool + { + // not needed because vlucas/phpdotenv v2 is being used in a way where + // it doesn't update getenv() values, it only populates $_SERVER + // - see use of overloaded VLucasV2Dotenv class above + return false; } } diff --git a/src/DotEnvAdapters/VLucas/VLucasAdapterV3.php b/src/DotEnvAdapters/VLucas/VLucasAdapterV3.php index 7f144c7..5d00e13 100644 --- a/src/DotEnvAdapters/VLucas/VLucasAdapterV3.php +++ b/src/DotEnvAdapters/VLucas/VLucasAdapterV3.php @@ -2,7 +2,8 @@ namespace CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas; -use CodeDistortion\FluentDotEnv\Exceptions\InvalidPathException; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\AbstractDotEnvAdapter; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\DotEnvAdapterTrait; use CodeDistortion\FluentDotEnv\Misc\ValueStore; use Dotenv\Dotenv; use Dotenv\Environment\Adapter\ServerConstAdapter; @@ -13,55 +14,59 @@ /** * Adapter for vlucas/phpdotenv v3. */ -class VLucasAdapterV3 extends AbstractVLucasAdapter +class VLucasAdapterV3 extends AbstractDotEnvAdapter { + use DotEnvAdapterTrait; + + + /** - * Load the values from the given .env file, and return them. - * - * NOTE: This MUST leave the getenv(), $_ENV, $_SERVER etc values as they were to begin with. + * Read the data from the given .env path. * * @param string $path The path to the .env file. * @return ValueStore - * @throws InvalidPathException When the directory or file could not be used. - * @throws Throwable Rethrows any Throwable exception. */ - public function import(string $path): ValueStore + protected function importValuesFromEnvFile(string $path): ValueStore { - $origServer = $_SERVER; + $directory = $this->getDir($path); + $filename = $this->getFilename($path); - try { - $values = $this->runImport($path); - } catch (Throwable $e) { + // ImportAndPopulate determines what was imported based on $_SERVER + // and chooses what to update based on that + $factory = new DotenvFactory([new ServerConstAdapter()]); + $dotenv = Dotenv::create($directory, $filename, $factory); + $dotenv->load(); - throw ($e instanceof DotEnvInvalidPathException - ? InvalidPathException::invalidPath($path, $e) - : $e); + return new ValueStore($_SERVER); + } - } finally { - $_SERVER = $origServer; - } - return $values; - } /** - * Actually import the data from the given .env path + * Check if the given exception is because the .env file could not be opened. * - * @param string $path The path to the .env file. - * @return ValueStore + * @param Throwable $e The exception to check. + * @return boolean */ - private function runImport(string $path): ValueStore + protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool { - $_SERVER = []; + return $e instanceof DotEnvInvalidPathException; + } - list($directory, $filename) = $this->splitPath($path); - // ImportAndPopulate determines what was imported based on $_SERVER - // and chooses what to update based on that - $factory = new DotenvFactory([new ServerConstAdapter()]); - $dotenv = Dotenv::create($directory, $filename, $factory); - $dotenv->load(); - return new ValueStore($_SERVER); + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't, then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + protected function importWillUpdateGetenvValues(): bool + { + // not needed because vlucas/phpdotenv v3 is being used in a way where + // it doesn't update getenv() values, it only populates $_SERVER + // - see use of ServerConstAdapter() above + return false; } } diff --git a/src/DotEnvAdapters/VLucas/VLucasAdapterV4.php b/src/DotEnvAdapters/VLucas/VLucasAdapterV4.php index bff5049..e439262 100644 --- a/src/DotEnvAdapters/VLucas/VLucasAdapterV4.php +++ b/src/DotEnvAdapters/VLucas/VLucasAdapterV4.php @@ -2,7 +2,8 @@ namespace CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas; -use CodeDistortion\FluentDotEnv\Exceptions\InvalidPathException; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\AbstractDotEnvAdapter; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\DotEnvAdapterTrait; use CodeDistortion\FluentDotEnv\Misc\ValueStore; use Dotenv\Dotenv; use Dotenv\Exception\InvalidPathException as DotEnvInvalidPathException; @@ -13,47 +14,22 @@ /** * Adapter for vlucas/phpdotenv v4. */ -class VLucasAdapterV4 extends AbstractVLucasAdapter +class VLucasAdapterV4 extends AbstractDotEnvAdapter { - /** - * Load the values from the given .env file, and return them. - * - * NOTE: This MUST leave the getenv(), $_ENV, $_SERVER etc values as they were to begin with. - * - * @param string $path The path to the .env file. - * @return ValueStore - * @throws InvalidPathException When the directory or file could not be used. - * @throws Throwable Rethrows any Throwable exception. - */ - public function import(string $path): ValueStore - { - $origServer = $_SERVER; - try { - $values = $this->runImport($path); - } catch (Throwable $e) { + use DotEnvAdapterTrait; - throw ($e instanceof DotEnvInvalidPathException - ? InvalidPathException::invalidPath($path, $e) - : $e); - } finally { - $_SERVER = $origServer; - } - - return $values; - } /** - * Actually import the data from the given .env path + * Read the data from the given .env path. * * @param string $path The path to the .env file. * @return ValueStore */ - private function runImport(string $path): ValueStore + protected function importValuesFromEnvFile(string $path): ValueStore { - $_SERVER = []; - - list($directory, $filename) = $this->splitPath($path); + $directory = $this->getDir($path); + $filename = $this->getFilename($path); // ImportAndPopulate determines what was imported based on $_SERVER // and chooses what to update based on that @@ -63,4 +39,34 @@ private function runImport(string $path): ValueStore return new ValueStore($_SERVER); } + + + + /** + * Check if the given exception is because the .env file could not be opened. + * + * @param Throwable $e The exception to check. + * @return boolean + */ + protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool + { + return $e instanceof DotEnvInvalidPathException; + } + + + + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't, then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + protected function importWillUpdateGetenvValues(): bool + { + // not needed because vlucas/phpdotenv v4 is being used in a way where + // it doesn't update getenv() values, it only populates $_SERVER + // - see use of ServerConstAdapter() above + return false; + } } diff --git a/src/DotEnvAdapters/VLucas/VLucasAdapterV5.php b/src/DotEnvAdapters/VLucas/VLucasAdapterV5.php index 377b651..32014f1 100644 --- a/src/DotEnvAdapters/VLucas/VLucasAdapterV5.php +++ b/src/DotEnvAdapters/VLucas/VLucasAdapterV5.php @@ -2,7 +2,8 @@ namespace CodeDistortion\FluentDotEnv\DotEnvAdapters\VLucas; -use CodeDistortion\FluentDotEnv\Exceptions\InvalidPathException; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\AbstractDotEnvAdapter; +use CodeDistortion\FluentDotEnv\DotEnvAdapters\DotEnvAdapterTrait; use CodeDistortion\FluentDotEnv\Misc\ValueStore; use Dotenv\Dotenv; use Dotenv\Exception\InvalidPathException as DotEnvInvalidPathException; @@ -13,47 +14,22 @@ /** * Adapter for vlucas/phpdotenv v5. */ -class VLucasAdapterV5 extends AbstractVLucasAdapter +class VLucasAdapterV5 extends AbstractDotEnvAdapter { - /** - * Load the values from the given .env file, and return them. - * - * NOTE: This MUST leave the getenv(), $_ENV, $_SERVER etc values as they were to begin with. - * - * @param string $path The path to the .env file. - * @return ValueStore - * @throws InvalidPathException When the directory or file could not be used. - * @throws Throwable Rethrows any Throwable exception. - */ - public function import(string $path): ValueStore - { - $origServer = $_SERVER; - try { - $values = $this->runImport($path); - } catch (Throwable $e) { + use DotEnvAdapterTrait; - throw ($e instanceof DotEnvInvalidPathException - ? InvalidPathException::invalidPath($path, $e) - : $e); - } finally { - $_SERVER = $origServer; - } - - return $values; - } /** - * Actually import the data from the given .env path + * Read the data from the given .env path. * * @param string $path The path to the .env file. * @return ValueStore */ - private function runImport(string $path): ValueStore + protected function importValuesFromEnvFile(string $path): ValueStore { - $_SERVER = []; - - list($directory, $filename) = $this->splitPath($path); + $directory = $this->getDir($path); + $filename = $this->getFilename($path); // ImportAndPopulate determines what was imported based on $_SERVER // and chooses what to update based on that @@ -65,4 +41,35 @@ private function runImport(string $path): ValueStore return new ValueStore($_SERVER); } + + + + /** + * Check if the given exception is because the .env file could not be opened. + * + * @param Throwable $e The exception to check. + * @return boolean + */ + protected function exceptionIsBecauseFileCantBeOpened(Throwable $e): bool + { + return $e instanceof DotEnvInvalidPathException; + } + + + + /** + * Work out if the import process will update the getenv() values. + * + * If it doesn't, then the process of backing up and clearing the getenv() values can be skipped. + * + * @return boolean + */ + protected function importWillUpdateGetenvValues(): bool + { + // not needed because vlucas/phpdotenv v5 is being used in a way where + // it doesn't update getenv() values, it only populates $_SERVER + // - see use of ServerConstAdapter() above + // and it requires PHP 7.1+ anyway (which means that we can get the full list of current getenv() values anyway) + return false; + } } diff --git a/src/Exceptions/DependencyException.php b/src/Exceptions/DependencyException.php index b99767c..09c5335 100644 --- a/src/Exceptions/DependencyException.php +++ b/src/Exceptions/DependencyException.php @@ -8,7 +8,7 @@ class DependencyException extends FluentDotEnvException { /** - * A dot-env package could not be found. + * A dot-env package could not be found. * * @return self */ diff --git a/src/Exceptions/GeneralException.php b/src/Exceptions/GeneralException.php new file mode 100644 index 0000000..61d4fb2 --- /dev/null +++ b/src/Exceptions/GeneralException.php @@ -0,0 +1,21 @@ +valueStore->get($key); + $value = is_string($value) + ? $value + : ''; + $values[$key] = ($castMethod ? $this->$castMethod($value) : $value ); } + return ($needsArray ? $values : reset($values)); } diff --git a/src/Misc/GetenvSupport.php b/src/Misc/GetenvSupport.php index 91d975f..4aa8b89 100644 --- a/src/Misc/GetenvSupport.php +++ b/src/Misc/GetenvSupport.php @@ -12,54 +12,111 @@ class GetenvSupport /** * Get the full list of current getenv() values. * - * @return mixed[] + * @return array */ - public static function getenvValues(): array + public static function getAllGetenvVariables(): array { if ((!function_exists('getenv')) || (!function_exists('putenv'))) { return []; } - // getenv requires a $key to be passed to it before PHP 7.1 + // getenv() requires a $key to be passed to it before PHP 7.1 try { - return (array) getenv(); + + return @(array) getenv(); + } catch (Throwable $e) { + + // if needed, get the getenv() values individually + $values = []; + foreach (array_keys($_ENV) as $key) { + $values[$key] = getenv($key); + } + return $values; + } + } + + /** + * Get the current getenv() values for the given keys. + * + * @param string[] $keys The keys to return values for. + * @return array + */ + public static function getParticularGetenvVariables(array $keys): array + { + if ((!function_exists('getenv')) || (!function_exists('putenv'))) { + return []; } - // if needed, get the getenv() values individually $values = []; - foreach (array_keys($_ENV) as $key) { - $values[$key] = getenv($key); + foreach ($keys as $key) { + $value = getenv($key); + if ($value !== false) { + $values[$key] = $value; + } } return $values; } /** - * Set getenv() to contain the given values. + * Remove particular getenv() values. + * + * @param string[] $keys The keys to remove. + * @return void + */ + public static function removeGetenvVariables(array $keys) + { + foreach ($keys as $key) { + putenv($key); // clear existing + } + } + + /** + * Set new getenv() values. + * + * @param array $values The values to add to getenv(). + * @return void + */ + public static function addGetenvVariables(array $values) + { + foreach ($values as $key => $value) { + putenv("$key=$value"); + } + } + + /** + * Set getenv() to contain the given values. This replaces the current set of values. * - * @param string[] $values The values to replace getenv() values with. + * @param array $newValues The values to replace getenv() values with. * @return void */ - public static function replaceGetenv(array $values) + public static function replaceAllGetenvVariables(array $newValues) { if ((!function_exists('getenv')) || (!function_exists('putenv'))) { return; } - $currentValues = static::getenvValues(); - $allValues = array_merge($currentValues, $values); + $currentValues = self::getAllGetenvVariables(); - foreach (array_keys($allValues) as $key) { + $allKeys = array_unique( + array_merge( + array_keys($currentValues), + array_keys($newValues) + ) + ); + + foreach ($allKeys as $key) { // add or keep - if (array_key_exists($key, $values)) { + if (array_key_exists($key, $newValues)) { // update if different - $value = $values[$key]; - if ((!isset($currentValues[$key])) || ($currentValues[$key] != $value)) { + $value = $newValues[$key]; + if ((!isset($currentValues[$key])) || ($currentValues[$key] !== $value)) { putenv("$key=$value"); } } else { + // remove putenv((string) $key); } diff --git a/src/Misc/ValueStore.php b/src/Misc/ValueStore.php index 8a15275..ee96f47 100644 --- a/src/Misc/ValueStore.php +++ b/src/Misc/ValueStore.php @@ -10,14 +10,14 @@ class ValueStore /** * The imported values. * - * @var mixed[] + * @var array */ private $values; /** * The imported values - before any filtering. * - * @var mixed[] + * @var array */ private $original; @@ -32,7 +32,7 @@ class ValueStore /** * Constructor. * - * @param mixed[] $values The imported values. + * @param array $values The imported values. */ public function __construct(array $values = []) { @@ -48,6 +48,7 @@ public function __construct(array $values = []) public function merge(ValueStore $valueStore) { foreach (func_get_args() as $valueStore) { + /** @var ValueStore $valueStore */ $this->original = array_merge( $this->original, $valueStore->original @@ -92,7 +93,7 @@ public function recalculateValues() /** * Return all of the stored values. * - * @return mixed[] + * @return array */ public function all() { diff --git a/src/PipelineActions/Populate/PopulateGetenvAction.php b/src/PipelineActions/Populate/PopulateGetenvAction.php index a7aac9e..a5fd23b 100644 --- a/src/PipelineActions/Populate/PopulateGetenvAction.php +++ b/src/PipelineActions/Populate/PopulateGetenvAction.php @@ -20,7 +20,7 @@ class PopulateGetenvAction extends AbstractPopulationAction */ protected function applyFromValueStore(ValueStore $values) { - $getenvValues = GetenvSupport::getenvValues(); + $getenvValues = GetenvSupport::getAllGetenvVariables(); foreach ($values->all() as $key => $value) { if (($this->overrideExisting) || (!array_key_exists($key, $getenvValues))) { @@ -28,7 +28,7 @@ protected function applyFromValueStore(ValueStore $values) } } - GetenvSupport::replaceGetenv($getenvValues); + GetenvSupport::replaceAllGetenvVariables($getenvValues); return $this; } diff --git a/tests/Unit/FluentDotEnvTest.php b/tests/Unit/FluentDotEnvTest.php index ca96a0f..17611cb 100644 --- a/tests/Unit/FluentDotEnvTest.php +++ b/tests/Unit/FluentDotEnvTest.php @@ -5,8 +5,8 @@ use CodeDistortion\FluentDotEnv\Exceptions\AlreadyLoadedException; use CodeDistortion\FluentDotEnv\Exceptions\InvalidPathException; use CodeDistortion\FluentDotEnv\Exceptions\ValidationException; -use CodeDistortion\FluentDotEnv\Tests\PHPUnitTestCase; use CodeDistortion\FluentDotEnv\FluentDotEnv; +use CodeDistortion\FluentDotEnv\Tests\PHPUnitTestCase; /** * Test the ConfigDTO class @@ -30,10 +30,10 @@ protected function customSetUp() ]; putenv('UNTOUCHED_KEY=untouched-value'); putenv('INITIAL_KEY=initial-value'); - putenv('NEW_KEY'); - putenv('EMPTY_KEY'); - putenv('INTEGER_KEY'); - putenv('BOOLEAN_KEY'); + putenv('NEW_KEY'); // i.e. remove + putenv('EMPTY_KEY'); // i.e. remove + putenv('INTEGER_KEY'); // i.e. remove + putenv('BOOLEAN_KEY'); // i.e. remove } /** @@ -42,7 +42,7 @@ protected function customSetUp() * * @return FluentDotEnv */ - protected function newFluentDotEnv() + protected static function newFluentDotEnv() { $fDotEnv = FluentDotEnv::new(); if (file_exists(__DIR__ . '/../../vendor/symfony/dotenv')) { @@ -56,7 +56,7 @@ protected function newFluentDotEnv() * * @return array[] */ - public static function CanImportProperlyDataProvider() + public static function CanImportProperlyDataProvider(): array { $notImported = [ 'UNTOUCHED_KEY' => 'untouched-value', @@ -383,6 +383,7 @@ public static function CanImportProperlyDataProvider() foreach ($combinations as $index => $combination) { $combinations[$index] = array_merge($default, $combination); } +$combinations = ['filename 2' => $combinations['filename 2']]; return $combinations; } @@ -442,9 +443,10 @@ public function can_import_properly( array $expectedServer, string $expectedException = null ) { + $this->customSetUp(); - $fDotEnv = $this->newFluentDotEnv(); + $fDotEnv = self::newFluentDotEnv(); // pre-import actions if (!is_null($pick)) { @@ -507,15 +509,15 @@ public function can_import_properly( $expectedException, function () use (&$fDotEnv, $useSafeLoad, $envFilename) { - ($useSafeLoad + $useSafeLoad ? $fDotEnv->safeLoad(__DIR__ . '/input/' . $envFilename) - : $fDotEnv->load(__DIR__ . '/input/' . $envFilename)); + : $fDotEnv->load(__DIR__ . '/input/' . $envFilename); } ); } else { - $values = ($useSafeLoad + $values = $useSafeLoad ? $fDotEnv->safeLoad(__DIR__ . '/input/' . $envFilename)->all() - : $fDotEnv->load(__DIR__ . '/input/' . $envFilename)->all()); + : $fDotEnv->load(__DIR__ . '/input/' . $envFilename)->all(); $this->assertSame($expectedValues, $values); // foreach ($expectedGetenv as $key => $value) { @@ -531,7 +533,7 @@ function () use (&$fDotEnv, $useSafeLoad, $envFilename) { * * @return array[] */ - public static function CanCallMethodsInDifferentWaysDataProvider() + public static function CanCallMethodsInDifferentWaysDataProvider(): array { $imported = [ 'INITIAL_KEY' => 'override-value', @@ -905,7 +907,7 @@ public function can_call_methods_in_different_ways( ) { $this->customSetUp(); - $fDotEnv = $this->newFluentDotEnv(); + $fDotEnv = self::newFluentDotEnv(); foreach ($methodsAndParams as $oneMethodAndParams) { $method = $oneMethodAndParams['method']; @@ -932,7 +934,7 @@ function () use (&$fDotEnv) { * * @return array[] */ - public static function canCallValidationAfterLoadDataProvider() + public static function canCallValidationAfterLoadDataProvider(): array { $validCallback = function (string $key, $value) { return true; @@ -1095,17 +1097,21 @@ public function can_call_validation_after_load( ) { $this->customSetUp(); - $fDotEnv = $this->newFluentDotEnv()->load(__DIR__ . '/input/.env'); + $fDotEnv = self::newFluentDotEnv()->load(__DIR__ . '/input/.env'); + $callable = [$fDotEnv, $method]; + if (!is_callable($callable)) { + self::fail(); + } if ($expectedException) { $this->assertThrows( $expectedException, - function () use (&$fDotEnv, $method, $params) { - call_user_func_array([$fDotEnv, $method], $params); + function () use ($callable, $params) { + call_user_func_array($callable, $params); } ); } else { - call_user_func_array([$fDotEnv, $method], $params); + call_user_func_array($callable, $params); $this->assertTrue(true); } @@ -1122,7 +1128,7 @@ public function pick_after_load_works() { $this->customSetUp(); - $fDotEnv = $this->newFluentDotEnv()->load(__DIR__ . '/input/.env')->pick('NEW_KEY')->pick('EMPTY_KEY'); + $fDotEnv = self::newFluentDotEnv()->load(__DIR__ . '/input/.env')->pick('NEW_KEY')->pick('EMPTY_KEY'); $this->assertSame( [ 'NEW_KEY' => 'new-value1', @@ -1131,7 +1137,7 @@ public function pick_after_load_works() $fDotEnv->all() ); - $fDotEnv = $this->newFluentDotEnv()->load(__DIR__ . '/input/.env'); + $fDotEnv = self::newFluentDotEnv()->load(__DIR__ . '/input/.env'); $this->assertSame( [ 'INITIAL_KEY' => 'override-value', @@ -1155,28 +1161,28 @@ public function loading_multiple_env_files_works() $this->customSetUp(); // passed in an array - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->load([__DIR__ . '/input/1.env']); $this->assertSame(['MY_VALUE1' => 'a'], $fDotEnv->all()); - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->load([__DIR__ . '/input/1.env', __DIR__ . '/input/2.env']); $this->assertSame(['MY_VALUE1' => 'b'], $fDotEnv->all()); - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->load([__DIR__ . '/input/2.env', __DIR__ . '/input/1.env']); $this->assertSame(['MY_VALUE1' => 'a'], $fDotEnv->all()); // passed as separate parameters - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->load(__DIR__ . '/input/1.env'); $this->assertSame(['MY_VALUE1' => 'a'], $fDotEnv->all()); - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->load(__DIR__ . '/input/1.env', __DIR__ . '/input/2.env'); $this->assertSame(['MY_VALUE1' => 'b'], $fDotEnv->all()); - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->load(__DIR__ . '/input/2.env', __DIR__ . '/input/1.env'); $this->assertSame(['MY_VALUE1' => 'a'], $fDotEnv->all()); @@ -1184,7 +1190,7 @@ public function loading_multiple_env_files_works() $this->assertThrows( AlreadyLoadedException::class, function () { - $this->newFluentDotEnv() + self::newFluentDotEnv() ->load(__DIR__ . '/input/1.env') ->load(__DIR__ . '/input/2.env'); } @@ -1193,7 +1199,7 @@ function () { $this->assertThrows( AlreadyLoadedException::class, function () { - $this->newFluentDotEnv() + self::newFluentDotEnv() ->safeLoad(__DIR__ . '/input/1.env') ->safeLoad(__DIR__ . '/input/2.env'); } @@ -1203,24 +1209,24 @@ function () { $this->assertThrows( InvalidPathException::class, function () { - $this->newFluentDotEnv() + self::newFluentDotEnv() ->load([__DIR__ . '/input/missing.env']); } ); $this->assertThrows( InvalidPathException::class, function () { - $this->newFluentDotEnv() + self::newFluentDotEnv() ->load([__DIR__ . '/input/1.env', __DIR__ . '/input/missing.env', __DIR__ . '/input/2.env']); } ); // missing file - but with safeLoad() - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->safeLoad(__DIR__ . '/input/missing.env'); $this->assertSame([], $fDotEnv->all()); - $fDotEnv = $this->newFluentDotEnv() + $fDotEnv = self::newFluentDotEnv() ->safeLoad(__DIR__ . '/input/1.env', __DIR__ . '/input/missing.env', __DIR__ . '/input/2.env'); $this->assertSame(['MY_VALUE1' => 'b'], $fDotEnv->all()); } @@ -1230,8 +1236,37 @@ function () { * * @return array[] */ - public static function canCastProperlyDataProvider() + public static function canCastProperlyDataProvider(): array { + // symfony/dotenv ^3.3 on Windows does not support uppercase values + // perform an initial test to work out if such values should be excluded from the test + $fDotEnv = self::newFluentDotEnv()->safeLoad(__DIR__ . '/input/cast.env'); + $supportsUppercaseValues = $fDotEnv->get('TrUe') === 'TrUe'; + + $keysWithUppercaseValues = [ + 'TrUe', + 'TRUE', + 'FaLsE', + 'FALSE', + 'On', + 'ON', + 'Off', + 'OFF', + 'YeS', + 'YES', + 'No', + 'NO', + ]; + $hasUpperCaseChar = function ($value) use ($keysWithUppercaseValues): bool { + return in_array($value, $keysWithUppercaseValues, true); + }; + + $includeKey = function ($value) use ($supportsUppercaseValues, $hasUpperCaseChar): bool { + return $supportsUppercaseValues || !$hasUpperCaseChar($value); + }; + + + $boolean = [ 'true' => true, 'TrUe' => true, @@ -1267,6 +1302,11 @@ public static function canCastProperlyDataProvider() $return = []; foreach (['castBoolean' => $boolean, 'castInteger' => $integer] as $castMethod => $tests) { foreach ($tests as $key => $expected) { + + if (!$includeKey($key)) { + continue; + } + $return[] = [ 'castMethod' => $castMethod, 'key' => (string) $key, @@ -1290,7 +1330,7 @@ public static function canCastProperlyDataProvider() */ public function can_cast_properly(string $castMethod, string $key, $expected) { - $fDotEnv = $this->newFluentDotEnv()->safeLoad(__DIR__ . '/input/cast.env'); + $fDotEnv = self::newFluentDotEnv()->safeLoad(__DIR__ . '/input/cast.env'); $this->assertSame($expected, $fDotEnv->$castMethod($key)); } @@ -1299,7 +1339,7 @@ public function can_cast_properly(string $castMethod, string $key, $expected) * * @return array[] */ - public static function canGetMultipleValuesDataProvider() + public static function canGetMultipleValuesDataProvider(): array { return [ // GET @@ -1473,7 +1513,7 @@ public static function canGetMultipleValuesDataProvider() */ public function can_get_multiple_values(string $method, array $params, $expected) { - $fDotEnv = $this->newFluentDotEnv()->safeLoad(__DIR__ . '/input/many_values.env'); + $fDotEnv = self::newFluentDotEnv()->safeLoad(__DIR__ . '/input/many_values.env'); $callable = [$fDotEnv, $method]; if (is_callable($callable)) { @@ -1481,4 +1521,37 @@ public function can_get_multiple_values(string $method, array $params, $expected $this->assertSame($expected, $output); } } + + /** + * Test that the ->load() doesn't change the existing getenv(), $_SERVER and $_ENV values. + * + * @test + * @return void + */ + public function current_env_values_arent_changed_by_loading_process() + { + $this->customSetUp(); + + $fDotEnv = self::newFluentDotEnv()->load(__DIR__ . '/input/.env'); + + // the loaded .env values + $this->assertSame('', $fDotEnv->get('UNTOUCHED_KEY')); + $this->assertSame('override-value', $fDotEnv->get('INITIAL_KEY')); + $this->assertSame('new-value1', $fDotEnv->get('NEW_KEY')); + + // the getenv() values + $this->assertSame('untouched-value', getenv('UNTOUCHED_KEY')); + $this->assertSame('initial-value', getenv('INITIAL_KEY')); + $this->assertFalse(getenv('NEW_KEY')); + + // the $_SERVER values + $this->assertSame('untouched-value', $_SERVER['UNTOUCHED_KEY']); + $this->assertSame('initial-value', $_SERVER['INITIAL_KEY']); + $this->assertFalse(array_key_exists('NEW_KEY', $_SERVER)); + + // the $_ENV values + $this->assertSame('untouched-value', $_ENV['UNTOUCHED_KEY']); + $this->assertSame('initial-value', $_ENV['INITIAL_KEY']); + $this->assertFalse(array_key_exists('NEW_KEY', $_ENV)); + } }