From 7b14215ddef49abc1301c5dd0ae5eb5f77121361 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 16 Sep 2026 11:47:19 +0200 Subject: [PATCH] Replace iconv hard dependency with polyfill --- .github/workflows/ci.yml | 46 +++++++++++++++++++++++++++++- .github/workflows/pull-request.yml | 44 ++++++++++++++++++++++++++++ composer.json | 5 ++-- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 106d3d2..a0afc33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,8 +67,52 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: ./clover.xml + # Runs the whole suite with ext-iconv removed, proving the library works with + # neither ext-iconv nor ext-mbstring by falling back to the pure-PHP + # symfony/polyfill-iconv. The polyfill only activates when iconv is genuinely + # absent (not merely disabled via disable_functions), so the extension is + # dropped with setup-php's ":iconv". If a runner has iconv compiled into core + # and can't drop it, the suite is skipped with a warning rather than giving a + # misleading pass. + test-without-iconv: + name: Tests without ext-iconv (PHP ${{ matrix.php-versions }}) + strategy: + fail-fast: false + matrix: + php-versions: ['8.1', '8.5'] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Setup PHP without iconv + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: :iconv, imagick, gd + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Install PHP dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Install Node dependencies + run: npm install + + - name: Test with phpunit (polyfill-iconv path) + run: | + if php -r 'exit(extension_loaded("iconv") ? 1 : 0);'; then + echo "ext-iconv is absent; conversions use the pure-PHP polyfill." + php -r 'require "vendor/autoload.php"; $r = new ReflectionFunction("iconv"); if ($r->isInternal()) { fwrite(STDERR, "Expected the polyfill iconv(), got a native one\n"); exit(1); } echo "iconv() is provided by symfony/polyfill-iconv\n";' + vendor/bin/phpunit + else + echo "::warning::ext-iconv is compiled into core on this runner and could not be removed; skipping the no-iconv suite." + fi + release: - needs: [lint, test] + needs: [lint, test, test-without-iconv] runs-on: ubuntu-latest steps: - uses: googleapis/release-please-action@v4 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 5459fa5..f1d94c3 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -68,6 +68,50 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: ./clover.xml + # Runs the whole suite with ext-iconv removed, proving the library works with + # neither ext-iconv nor ext-mbstring by falling back to the pure-PHP + # symfony/polyfill-iconv. The polyfill only activates when iconv is genuinely + # absent (not merely disabled via disable_functions), so the extension is + # dropped with setup-php's ":iconv". If a runner has iconv compiled into core + # and can't drop it, the suite is skipped with a warning rather than giving a + # misleading pass. + test-without-iconv: + name: Tests without ext-iconv (PHP ${{ matrix.php-versions }}) + strategy: + fail-fast: false + matrix: + php-versions: ['8.1', '8.5'] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Setup PHP without iconv + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: :iconv, imagick, gd + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Install PHP dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Install Node dependencies + run: npm install + + - name: Test with phpunit (polyfill-iconv path) + run: | + if php -r 'exit(extension_loaded("iconv") ? 1 : 0);'; then + echo "ext-iconv is absent; conversions use the pure-PHP polyfill." + php -r 'require "vendor/autoload.php"; $r = new ReflectionFunction("iconv"); if ($r->isInternal()) { fwrite(STDERR, "Expected the polyfill iconv(), got a native one\n"); exit(1); } echo "iconv() is provided by symfony/polyfill-iconv\n";' + vendor/bin/phpunit + else + echo "::warning::ext-iconv is compiled into core on this runner and could not be removed; skipping the no-iconv suite." + fi + pr-title: runs-on: ubuntu-latest steps: diff --git a/composer.json b/composer.json index 9df090d..83c4822 100644 --- a/composer.json +++ b/composer.json @@ -5,10 +5,11 @@ "homepage": "https://github.com/Bacon/BaconQrCode", "require": { "php": "^8.1", - "ext-iconv": "*", - "dasprid/enum": "^1.0.3" + "dasprid/enum": "^1.0.3", + "symfony/polyfill-iconv": "*" }, "suggest": { + "ext-iconv": "for faster byte-mode and kanji encoding (a pure-PHP polyfill is used otherwise)", "ext-imagick": "to generate QR code images" }, "authors": [