From 5cba8ca9ac60b28ae5b59803c973ac2a2749e8c3 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 25 Jun 2026 14:49:14 +0900 Subject: [PATCH] Fixes from phpstan checks, add PHP CI/CD github action --- .github/workflows-disabled/ci.yml | 33 -- .../call-php-static-checks-and-test.yml | 21 ++ .../workflows/php-static-checks-and-tests.yml | 309 ++++++++++++++++++ .phive/phars.xml | 4 +- src/Convert/Byte.php | 123 ++++--- src/DB/IO.php | 6 +- src/Language/L10n.php | 2 +- src/Logging/Logger/Level.php | 4 +- src/Output/Form/Elements.php | 33 +- src/UrlRequests/Curl.php | 19 +- test/phpunit/Get/dotenv/.env | 1 + 11 files changed, 431 insertions(+), 124 deletions(-) delete mode 100644 .github/workflows-disabled/ci.yml create mode 100644 .github/workflows/call-php-static-checks-and-test.yml create mode 100644 .github/workflows/php-static-checks-and-tests.yml create mode 120000 test/phpunit/Get/dotenv/.env diff --git a/.github/workflows-disabled/ci.yml b/.github/workflows-disabled/ci.yml deleted file mode 100644 index 9791d21..0000000 --- a/.github/workflows-disabled/ci.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: CI -run-name: ${{ github.actor}} runs CI - -on: [push] - -jobs: - phpstan: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # - uses: php-actions/composer@v6 - # env: - # COMPOSER_ROOT_VERSION: dev-master - - name: "Restore result cache" - uses: actions/cache/restore@v4 - with: - path: ./tmp - key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}" - restore-keys: | - result-cache-v1-${{ matrix.php-version }}- - - name: PHPStan Static Analysis - uses: php-actions/phpstan@v3 - with: - path: src/ - - name: "Save result cache" - uses: actions/cache/save@v4 - if: always() - with: - path: ./tmp - key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}" - # - name: PHPunit Tests - # run: | - # vendor/bin/phpunit diff --git a/.github/workflows/call-php-static-checks-and-test.yml b/.github/workflows/call-php-static-checks-and-test.yml new file mode 100644 index 0000000..fac80d7 --- /dev/null +++ b/.github/workflows/call-php-static-checks-and-test.yml @@ -0,0 +1,21 @@ +name: Call PHP Static Checks and Tests +run-name: ${{ github.actor }} calls Content.github-actions/php-static-checks-and-tests + +on: + workflow_dispatch: + push: + branches: [master, staging, development] + +jobs: + call-check-and-build: + # call local if repository name is Composer/CoreLibs-Composer-All.git + uses: ${{ github.repository == 'Composer/CoreLibs-Composer-All' && './.github/workflows/php-static-checks-and-tests.yml' || 'OmnicomProduction-Japan/Content.github-actions/.github/workflows/php-static-checks-and-tests.yml@v1' }} + with: + php-version: '8.4,8.5' + composer-directory: './www' + source-directory: './www' + disable-phan: ${{ vars.DISABLE_PHAN_CHECK == 'true' || false }} + secrets: + opj-package-user: ${{ secrets.OPJ_PACKAGE_USER }} + opj-package-api-key: ${{ secrets.OPJ_PACKAGE_API_KEY }} + diff --git a/.github/workflows/php-static-checks-and-tests.yml b/.github/workflows/php-static-checks-and-tests.yml new file mode 100644 index 0000000..4217ed5 --- /dev/null +++ b/.github/workflows/php-static-checks-and-tests.yml @@ -0,0 +1,309 @@ +# external call not yet setup, only copy to repo at the moment +name: PHP Static Checks and Tests Matrix Workflow +run-name: ${{ github.actor}} runs PHP-MATRIX-CI + +on: + workflow_call: + inputs: + php-version: + type: string + required: false + default: '8.5' + description: 'PHP versions to use for the checks and tests, default is 8.4 and 8.5, can be a comma-separated list of versions' + phpunit-version: + type: string + required: false + default: '^13' + description: 'PHPUnit versions to use for the tests, default is ^13, can be a comma-separated list of versions' + operating-system: + type: string + required: false + default: 'ubuntu-latest' + description: 'Operating systems to run the tests on, default is ubuntu-latest, can be a comma-separated list of operating systems' + disable-phan: + type: boolean + default: false + required: false + description: 'Set to "true" to skip running phan' + composer-directory: + type: string + required: false + default: './' + description: 'Directory where composer.json is located, relative to the repository root' + source-directory: + type: string + required: false + default: './src' + description: 'Directory where the PHP source code is located, relative to the repository root' + secrets: + opj-package-user: + required: true + description: 'Username for private package repository' + opj-package-api-key: + required: true + description: 'API key for private package repository' + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + php-version: ${{ steps.set-matrix.outputs.php-version }} + phpunit-version: ${{ steps.set-matrix.outputs.phpunit-version }} + operating-system: ${{ steps.set-matrix.outputs.operating-system }} + steps: + - name: Set matrix values + id: set-matrix + run: | + echo "php-version=$(echo '${{ inputs.php-version }}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT + echo "phpunit-version=$(echo '${{ inputs.phpunit-version }}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT + echo "operating-system=$(echo '${{ inputs.operating-system }}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT + + php-check-and-test-matrix: + needs: setup + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: ${{fromJSON(needs.setup.outputs.operating-system)}} + php-version: ${{fromJSON(needs.setup.outputs.php-version)}} + phpunit-version: ${{fromJSON(needs.setup.outputs.phpunit-version)}} + # include: + # - operating-system: ubuntu-latest + # php-version: '8.4' + # phpunit-version: '^13' + steps: + - name: Checkout code + uses: actions/checkout@v6 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, intl, sqlite, pdo_sqlite + # ini-values: post_max_size=256M, max_execution_time=180 + tools: composer, ${{ inputs.disable-phan == false && 'phan, ' || '' }}phpstan, phpcs, phpunit:${{ matrix.phpunit-version }}, cs2pr + # coverage: pcov + coverage: xdebug + # env: + # # Does not work, hast to be set in dependencies step, see below in "install dependencies" + # COMPOSER_AUTH: | + # { + # "http-basic": { + # "packages.omnicomproduction.jp": { + # "username": "${{ secrets.OPJ_PACKAGE_USER }}", + # "password": "${{ secrets.OPJ_PACKAGE_API_KEY }}" + # } + # } + # } + - name: Check Tools setup + id: check-tools + run: | + REQUIRED_TOOLS=("composer" "phpunit" "phpstan" ${{ inputs.disable-phan == false && '"phan" ' || '' }}"phpcs" "cs2pr") + SKIP_IF_FAILED=("phan") + for tool in "${REQUIRED_TOOLS[@]}"; do + if ! command -v "$tool" &> /dev/null; then + if [[ " ${SKIP_IF_FAILED[@]} " =~ " ${tool} " ]]; then + echo "⚠️ Warning: $tool is not installed, but it's optional. Skipping..." + echo "${tool}=skip" >> $GITHUB_OUTPUT + continue + fi + echo "❌ Error: $tool failed to load!" + exit 1 + else + # the tool path does not have "setup-php/tools/ inside + tool_path=$(which "$tool") + if [[ "$tool_path" != */setup-php/tools/* ]]; then + if [[ " ${SKIP_IF_FAILED[@]} " =~ " ${tool} " ]]; then + echo "⚠️ Warning: $tool installation check failed, but it's optional. Skipping..." + echo "${tool}=skip" >> $GITHUB_OUTPUT + continue + fi + echo "❌ Error: $tool path lookup failed!" + exit 1 + fi + tool_version=$("$tool" --version 2>&1) + echo "✅ $tool loaded successfully: $tool_version ($(which $tool))" + echo "${tool}=ok" >> $GITHUB_OUTPUT + fi + done + - name: Check source folder and composer.json setup + run: | + if [ ! -d "${{ inputs.source-directory }}" ]; then + echo "❌ Error: Source directory '${{ inputs.source-directory }}' does not exist!" + exit 1 + fi + if [ ! -f "${{ inputs.composer-directory }}/composer.json" ]; then + echo "❌ Error: composer.json not found in '${{ inputs.composer-directory }}'!" + exit 1 + fi + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + working-directory: ${{ inputs.composer-directory }} + + - name: Cache dependencies + uses: actions/cache@v5 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: | + composer config http-basic.packages.omnicomproduction.jp "${{ secrets.opj-package-user }}" "${{ secrets.opj-package-api-key }}" + composer install --prefer-dist + working-directory: ${{ inputs.composer-directory }} + + - name: Run composer audit + id: run-composer-audit + run: composer audit --format=json --no-scripts --no-plugins --no-interaction > composer-audit-output.json + # some post action with the composer output format file + working-directory: ${{ inputs.composer-directory }} + + - name: Run phpcs + id: run-phpcs + run: phpcs -q --report=checkstyle --standard=phpcs.xml --ignore=vendor/ ${{ inputs.source-directory }} | cs2pr + + - name: Run phan + id: run-phan + if: ${{ ! inputs.disable-phan && steps.check-tools.outputs.phan == 'ok' }} + run: phan --analyze-twice --no-progress-bar --config-file=./.phan/config.php + + # Must have "./tmp" directory set in configuration + # if we have "./tmp/phpstan" or anything different adjust the path below + - name: "Restore result cache" + uses: actions/cache/restore@v5 + with: + path: ./tmp + key: "result-cache-${{ github.run_id }}" + restore-keys: | + result-cache-v1- + - name: Run PHPStan + id: run-phpstan + run: phpstan analyse --no-progress --configuration=phpstan.neon + - name: "Save result cache" + uses: actions/cache/save@v5 + if: always() + with: + path: ./tmp + key: "result-cache-${{ github.run_id }}" + + - name: Test with phpunit + id: run-phpunit + run: | + phpunit --colors=never \ + --no-coverage \ + --coverage-text=phpunit-coverage.txt \ + --only-summary-for-coverage-text \ + --coverage-openclover=phpunit-openclover.xml + + - name: Code Coverage Summary Report + uses: saschanowak/CloverCodeCoverageSummary@1.1.1 + with: + filename: phpunit-openclover.xml + + - name: Summary + if: always() + env: + COMPOSER_AUDIT_RESULT: ${{ steps.run-composer-audit.outcome }} + COMPOSER_AUDIT_FILE: ${{ inputs.composer-directory }}/composer-audit-output.json + PHPCS_RESULT: ${{ steps.run-phpcs.outcome }} + PHAN_RESULT: ${{ steps.run-phan.outcome }} + PHPSTAN_RESULT: ${{ steps.run-phpstan.outcome }} + PHPUNIT_RESULT: ${{ steps.run-phpunit.outcome }} + run: | + icon() { if [[ "$1" == "success" ]]; then echo "✅"; elif [[ "$1" == "skipped" ]]; then echo "⏭️"; else echo "❌"; fi; } + row() { echo "$1$(icon $2) $2"; } + + cat >> $GITHUB_STEP_SUMMARY << EOF +

PHP CI Results

+ + + + + + $(row "composer audit" "$COMPOSER_AUDIT_RESULT") + $(row "phpcs" "$PHPCS_RESULT") + $(row "phan" "$PHAN_RESULT") + $(row "phpstan" "$PHPSTAN_RESULT") + $(row "phpunit" "$PHPUNIT_RESULT") + +
CheckStatus
+ +
+ Run details + + | Input | Value | + | --- | --- | + | PHP version | \`${{ inputs.php-version }}\` | + | PHPUnit version | \`${{ inputs.phpunit-version }}\` | + | Operating system | \`${{ inputs.operating-system }}\` | + | Phan disabled | \`${{ inputs.disable-phan }}\` | + | Composer directory | \`${{ inputs.composer-directory }}\` | + | Source directory | \`${{ inputs.source-directory }}\` | + +
+ EOF + + if [ -f phpunit-coverage.txt ]; then + { + echo "

Code Coverage

" + echo "" + echo "| Metric | Coverage | Status |" + echo "| --- | --- | --- |" + grep -E '^\s+(Classes|Methods|Lines):' phpunit-coverage.txt | head -3 | while IFS= read -r line; do + name=$(echo "$line" | sed 's/^[[:space:]]*//' | cut -d: -f1) + value=$(echo "$line" | cut -d: -f2- | sed 's/^[[:space:]]*//') + pct=$(echo "$value" | grep -oE '[0-9]+\.[0-9]+' | head -1) + circle=$(awk -v p="$pct" 'BEGIN { if (p+0 == 100) print "🟢"; else if (p+0 >= 60) print "🟠"; else print "🔴" }') + echo "| $name | $value | $circle |" + done + echo "" + echo "
Full coverage report" + echo "" + echo '```' + cat phpunit-coverage.txt + echo '```' + echo "" + echo "
" + } >> $GITHUB_STEP_SUMMARY + fi + + cat code-coverage-summary.md >> $GITHUB_STEP_SUMMARY + cat code-coverage-details.md >> $GITHUB_STEP_SUMMARY + + if [ -f "$COMPOSER_AUDIT_FILE" ] && jq empty "$COMPOSER_AUDIT_FILE" 2>/dev/null; then + { + echo "

Composer Audit

" + echo "" + PACKAGES_AUDITED=$(jq -r '.metadata["packages-audited"] // "?"' "$COMPOSER_AUDIT_FILE") + ADVISORIES_COUNT=$(jq -r '.metadata["packages-with-advisories"] // 0' "$COMPOSER_AUDIT_FILE") + echo "| Packages audited | With advisories |" + echo "| --- | --- |" + echo "| $PACKAGES_AUDITED | $ADVISORIES_COUNT |" + echo "" + ADVISORY_ROWS=$(jq -r ' + .packages[] + | select(.advisories and (.advisories | length) > 0) + | .name as $name | .version as $ver + | .advisories[] + | "| \($name) | \($ver) | \(.cve // "N/A") | \(.title) |" + ' "$COMPOSER_AUDIT_FILE" 2>/dev/null || true) + if [ -n "$ADVISORY_ROWS" ]; then + echo "| Package | Version | CVE | Title |" + echo "| --- | --- | --- | --- |" + echo "$ADVISORY_ROWS" + echo "" + fi + echo "
Full audit output" + echo "" + echo '```json' + cat "$COMPOSER_AUDIT_FILE" + echo '```' + echo "" + echo "
" + } >> $GITHUB_STEP_SUMMARY + fi diff --git a/.phive/phars.xml b/.phive/phars.xml index f2178dd..d3b868e 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,9 +1,9 @@ - + - + diff --git a/src/Convert/Byte.php b/src/Convert/Byte.php index 651d70f..a54412c 100644 --- a/src/Convert/Byte.php +++ b/src/Convert/Byte.php @@ -43,71 +43,70 @@ class Byte public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string { // if not numeric, return as is - if (is_numeric($bytes)) { - // flags bit wise check - // remove space between number and suffix - if ($flags & self::BYTE_FORMAT_NOSPACE) { - $space = false; - } else { - $space = true; - } - // use sprintf instead of round - if ($flags & self::BYTE_FORMAT_ADJUST) { - $adjust = true; - } else { - $adjust = false; - } - // use SI 1000 mod and not 1024 mod - if ($flags & self::BYTE_FORMAT_SI) { - $si = true; - } else { - $si = false; - } - if ($flags > 7) { - throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1); - } - - // si or normal - $unit = $si ? 1000 : 1024; - // always positive - $abs_bytes = $bytes == PHP_INT_MIN ? PHP_INT_MAX : abs((float)$bytes); - // smaller than unit is always B - if ($abs_bytes < $unit) { - return $bytes . 'B'; - } - // labels in order of size [Y, Z] - $labels = ['', 'K', 'M', 'G', 'T', 'P', 'E']; - // exp position calculation - $exp = (int)floor(log($abs_bytes, $unit)); - // avoid printing out anything larger than max labels - if ($exp >= count($labels)) { - $exp = count($labels) - 1; - } - // deviation calculation - $dev = pow($unit, $exp) * ($unit - 0.05); - // shift the exp +1 for on the border units - if ( - $exp < 6 && - $abs_bytes > ($dev - (((int)$dev & 0xfff) == 0xd00 ? 52 : 0)) - ) { - $exp++; - } - // label name, including leading space if flagged - $pre = ($space ? ' ' : '') . ($labels[$exp] ?? '>E') . ($si ? 'i' : '') . 'B'; - $bytes_calc = $abs_bytes / pow($unit, $exp); - // if original is negative, reverse - if ($bytes < 0) { - $bytes_calc *= -1; - } - if ($adjust) { - return sprintf("%.2f%s", $bytes_calc, $pre); - } else { - return round($bytes_calc, 2) . $pre; - } - } else { + if (!is_numeric($bytes)) { // if anything other return as string return (string)$bytes; } + // flags bit wise check + // remove space between number and suffix + if ($flags & self::BYTE_FORMAT_NOSPACE) { + $space = false; + } else { + $space = true; + } + // use sprintf instead of round + if ($flags & self::BYTE_FORMAT_ADJUST) { + $adjust = true; + } else { + $adjust = false; + } + // use SI 1000 mod and not 1024 mod + if ($flags & self::BYTE_FORMAT_SI) { + $si = true; + } else { + $si = false; + } + if ($flags > 7) { + throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1); + } + + // si or normal + $unit = $si ? 1000 : 1024; + // always positive + $abs_bytes = $bytes == PHP_INT_MIN ? PHP_INT_MAX : abs((float)$bytes); + // smaller than unit is always B + if ($abs_bytes < $unit) { + return $bytes . 'B'; + } + // labels in order of size [Y, Z] + $labels = ['', 'K', 'M', 'G', 'T', 'P', 'E']; + // exp position calculation + $exp = (int)floor(log($abs_bytes, $unit)); + // avoid printing out anything larger than max labels + if ($exp >= count($labels)) { + $exp = count($labels) - 1; + } + // deviation calculation + $dev = pow($unit, $exp) * ($unit - 0.05); + // shift the exp +1 for on the border units + if ( + $exp < 6 && + $abs_bytes > ($dev - (((int)$dev & 0xfff) == 0xd00 ? 52 : 0)) + ) { + $exp++; + } + // label name, including leading space if flagged + $pre = ($space ? ' ' : '') . ($labels[$exp] ?? '>E') . ($si ? 'i' : '') . 'B'; + $bytes_calc = $abs_bytes / pow($unit, $exp); + // if original is negative, reverse + if ($bytes < 0) { + $bytes_calc *= -1; + } + if ($adjust) { + return sprintf("%.2f%s", $bytes_calc, $pre); + } else { + return round($bytes_calc, 2) . $pre; + } } /** diff --git a/src/DB/IO.php b/src/DB/IO.php index 12673fc..5c083dc 100644 --- a/src/DB/IO.php +++ b/src/DB/IO.php @@ -2232,9 +2232,9 @@ class IO $milliseconds = $matches[13] ?? ''; // clean up, hide entries that have 00 in the time group - $hour = $hour != '00' ? preg_replace('/^0/', '', $hour) : ''; - $minutes = $minutes != '00' ? preg_replace('/^0/', '', $minutes) : ''; - $seconds = $seconds != '00' ? preg_replace('/^0/', '', $seconds) : ''; + $hour = !empty($hour) ? preg_replace('/^0/', '', $hour) : ''; + $minutes = !empty($minutes) ? preg_replace('/^0/', '', $minutes) : ''; + $seconds = !empty($seconds) ? preg_replace('/^0/', '', $seconds) : ''; // strip any leading or trailing spaces $time_string = trim( diff --git a/src/Language/L10n.php b/src/Language/L10n.php index 14dad30..f0200e6 100644 --- a/src/Language/L10n.php +++ b/src/Language/L10n.php @@ -146,7 +146,7 @@ class L10n * @param string $locale language name, if not set, try previous set * @param string $domain set name for mo file, if not set, try previous set * @param string $path path, if not set try to get from paths array, else self - * @param string $override_encoding if locale does not env encoding set, use this one + * @param string $override_encoding if locale does not have encoding set, use this one * @return GetTextReader the main gettext reader object */ public function getTranslator( diff --git a/src/Logging/Logger/Level.php b/src/Logging/Logger/Level.php index dd59bfa..7e0edfc 100644 --- a/src/Logging/Logger/Level.php +++ b/src/Logging/Logger/Level.php @@ -86,7 +86,7 @@ enum Level: int /** * @param value-of|LogLevel::*|'Debug'|'Info'|'Notice'|'Warning'|'Error'|'Critical'|'Alert'|'Emergency' $name - * @return static + * @return self */ public static function fromName(string $name): self { @@ -104,7 +104,7 @@ enum Level: int /** * @param value-of $value - * @return static + * @return self */ public static function fromValue(int $value): self { diff --git a/src/Output/Form/Elements.php b/src/Output/Form/Elements.php index 7f25e12..6327e9e 100644 --- a/src/Output/Form/Elements.php +++ b/src/Output/Form/Elements.php @@ -232,8 +232,15 @@ class Elements * @param string $class style sheet * @return string correct string for url href process */ - private static function createUrl($href, $atag, $_1, $_2, $_3, $name, $class): string - { + private static function createUrl( + string $href, + string $atag, + string $_1, + string $_2, + string $_3, + string $name, + string $class + ): string { // $this->debug('URL', "1: $_1 - 2: $_2 - $_3 - atag: $atag - name: $name - class: $class"); // if $_1 ends with //, then we strip $_1 complete & target is also blanked (its an internal link) if (preg_match("/\/\/$/", $_1) && preg_match("/^\//", $_2)) { @@ -250,11 +257,8 @@ class Elements . "##GT##" . ($name ? $name : $_2 . $_3) . "##LT##/a##GT##"; } elseif ($href && !$atag) { return "href=##QUOT##$_1$_2$_3##QUOT##"; - } elseif ($atag) { - return $atag . $_2 . $_3; - } else { - return $href; } + return $atag . $_2 . $_3; } /** @@ -269,8 +273,15 @@ class Elements * @param string $class style sheet * @return string created html email a href string */ - private static function createEmail($mailto, $atag, $_1, $_2, $_3, $title, $class) - { + private static function createEmail( + string $mailto, + string $atag, + string $_1, + string $_2, + string $_3, + string $title, + string $class + ): string { $email = $_1 . "@" . $_2 . "." . $_3; if (!$mailto && !$atag) { return "##LT##a href=##QUOT##mailto:" . $email . "##QUOT##" @@ -278,12 +289,8 @@ class Elements . "##GT##" . ($title ? $title : $email) . "##LT##/a##GT##"; } elseif ($mailto && !$atag) { return "mailto:" . $email; - } elseif ($atag) { - return $atag . $email; - } else { - // else just return email as is - return $email; } + return $atag . $email; } } diff --git a/src/UrlRequests/Curl.php b/src/UrlRequests/Curl.php index 1af6318..30368d3 100644 --- a/src/UrlRequests/Curl.php +++ b/src/UrlRequests/Curl.php @@ -954,11 +954,10 @@ class Curl implements Interface\RequestsInterface ) ) { unset($this->config['headers'][$header_key]); - unset($this->headers_named[$header_key]); + unset($this->headers_named[strtolower($header_key)]); } elseif ( - // string value, array keys = in - // or both array and not a full match in the one before - (is_string($value) || is_array($value)) && + // right side is array, and value is anything string or array + // if right side is string, and we give value array, we do nothing is_array($this->config['headers'][$header_key]) ) { // part remove of key, value must be array @@ -966,10 +965,14 @@ class Curl implements Interface\RequestsInterface $value = [$value]; } // array values so we rewrite the key pos - $this->config['headers'][$header_key] = array_values(array_diff( - $this->config['headers'][$header_key], - $value - )); + /** @var array $headers */ + $headers = $this->config['headers'][$header_key]; + $this->config['headers'][$header_key] = array_values( + array_diff( + $headers, + $value + ) + ); } } } diff --git a/test/phpunit/Get/dotenv/.env b/test/phpunit/Get/dotenv/.env new file mode 120000 index 0000000..a67af1f --- /dev/null +++ b/test/phpunit/Get/dotenv/.env @@ -0,0 +1 @@ +test.env \ No newline at end of file