From 2c7008a2401e2332c37a4fe5340ff6cacbabc3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Fabr=C3=A9gat?= Date: Wed, 15 Apr 2026 11:33:19 -0700 Subject: [PATCH 1/4] Modernize for PHP 8.1+ and PHPUnit 11 Drop PHP 7.x support, require PHP 8.1+. Update PHPUnit from ^7 to ^11 (fixes CVE-2026-24765 Dependabot alert). Modernize source with typed constants and trailing commas. Rewrite tests with data providers and proper assertions. Add phpunit.xml.dist, CI workflow for PRs, and Packagist badges to README. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 30 +++++++++ .gitignore | 1 + README.md | 50 ++++++++++----- composer.json | 12 ++-- phpunit.xml.dist | 17 +++++ src/StripAccents.php | 67 ++++++-------------- tests/StripAccentsTest.php | 124 +++++++++++++++++++++++-------------- 7 files changed, 185 insertions(+), 116 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 phpunit.xml.dist diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a98926e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: ['8.1', '8.2', '8.3', '8.4'] + + steps: + - uses: actions/checkout@v4 + + - name: Set up PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run tests + run: vendor/bin/phpunit --testdox diff --git a/.gitignore b/.gitignore index dccb98d..61fb2a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.idea /composer.lock /vendor +/.phpunit.cache .DS_Store diff --git a/README.md b/README.md index 083d4ca..5810062 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,54 @@ -# Strip accents -This PHP 7 library removes accent from strings. It is based on the sample code [published here](http://www.infowebmaster.fr/tutoriel/php-enlever-accents). +# Strip Accents +[![Packagist Version](https://img.shields.io/packagist/v/codeinc/strip-accents)](https://packagist.org/packages/codeinc/strip-accents) +[![Packagist Downloads](https://img.shields.io/packagist/dt/codeinc/strip-accents)](https://packagist.org/packages/codeinc/strip-accents) +[![PHP Version](https://img.shields.io/packagist/dependency-v/codeinc/strip-accents/php)](https://packagist.org/packages/codeinc/strip-accents) +[![License](https://img.shields.io/packagist/l/codeinc/strip-accents)](LICENSE) + +A PHP library to remove accents and diacritics from strings. + +## Installation + +```bash +composer require codeinc/strip-accents +``` + +Requires PHP 8.1 or later. + +## Usage + +### Strip accents -## Usage ```php -=7.1" + "php": ">=8.1", + "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^7" + "phpunit/phpunit": "^11.0" }, "autoload": { "psr-4": { @@ -34,4 +34,4 @@ "*": "dist" } } -} \ No newline at end of file +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..68b052b --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + + tests + + + + + src + + + diff --git a/src/StripAccents.php b/src/StripAccents.php index 220a023..6b78715 100644 --- a/src/StripAccents.php +++ b/src/StripAccents.php @@ -1,77 +1,48 @@ -// Date: 15/03/2018 -// Time: 10:52 -// Project: StipAccents -// -declare(strict_types = 1); -namespace CodeInc\StripAccents; +declare(strict_types=1); + +namespace CodeInc\StripAccents; /** - * Class StripAccents - * - * @link http://www.infowebmaster.fr/tutoriel/php-enlever-accents - * @package CodeInc\StripAccents - * @author Joan Fabrégat + * Removes accents and diacritics from strings. */ class StripAccents { - public const DEFAULT_ENCODING = 'utf-8'; + private const string DEFAULT_ENCODING = 'utf-8'; /** - * @param string $string - * @param string $encoding - * @return string + * Strips accents and diacritics from a string. */ public static function strip(string $string, string $encoding = self::DEFAULT_ENCODING): string { - // converting accents in HTML entities $string = htmlentities($string, ENT_NOQUOTES, $encoding); - // replacing the HTML entities to extract the first letter - // examples: "&ecute;" => "e", "&Ecute;" => "E", "à" => "a" ... + // replace accented HTML entities with their base letter + // e.g. "é" => "e", "É" => "E", "à" => "a" $string = preg_replace( - '#&([A-za-z])(?:acute|grave|cedil|circ|orn|ring|slash|th|tilde|uml);#', + '#&([A-Za-z])(?:acute|grave|cedil|circ|orn|ring|slash|th|tilde|uml);#', '\1', $string ); - // replacing ligatures - // Exemple "œ" => "oe", "Æ" => "AE" - $string = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $string); + // replace ligatures e.g. "œ" => "oe", "Æ" => "AE" + $string = preg_replace('#&([A-Za-z]{2})(?:lig);#', '\1', $string); - // removing the remaining bits + // remove any remaining HTML entities $string = preg_replace('#&[^;]+;#', '', $string); return $string; } /** - * @param string $string - * @param string $encoding - * @param string $replaceWith - * @return string + * Strips accents and non-printable characters from a string. */ - public static function stripNonPrint(string $string, - string $replaceWith = '', - string $encoding = self::DEFAULT_ENCODING): string - { + public static function stripNonPrint( + string $string, + string $replaceWith = '', + string $encoding = self::DEFAULT_ENCODING, + ): string { return preg_replace('/[[:^print:]]/', $replaceWith, self::strip($string, $encoding)); } -} \ No newline at end of file +} diff --git a/tests/StripAccentsTest.php b/tests/StripAccentsTest.php index 59650ef..4bbf90d 100644 --- a/tests/StripAccentsTest.php +++ b/tests/StripAccentsTest.php @@ -1,63 +1,93 @@ -// Date: 15/03/2018 -// Time: 10:57 -// Project: StripAccents -// -declare(strict_types = 1); + +declare(strict_types=1); + namespace CodeInc\StripAccents\Tests; + use CodeInc\StripAccents\StripAccents; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; - -/** - * Class StripAccentsTest - * - * @package CodeInc\StripAccents\Tests - * @author Joan Fabrégat - */ class StripAccentsTest extends TestCase { - public const ACCENTS = [ - 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', - 'Ï', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'à', 'á', 'â', - 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ð', 'ò', - 'ó', 'ô', 'õ', 'ö', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ']; - - public const LETTERS = [ - 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', - 'I', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'a', 'a', 'a', - 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'e', 'o', - 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y' + private const array ACCENT_MAP = [ + 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', + 'Ç' => 'C', + 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', + 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', + 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', + 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', + 'Ý' => 'Y', + 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', + 'ç' => 'c', + 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', + 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', + 'ð' => 'e', + 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', + 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', + 'ý' => 'y', 'ÿ' => 'y', ]; - public function testOneByOne():void + public static function accentProvider(): iterable { - foreach (self::ACCENTS as $key => $accent) { - self::assertTrue(StripAccents::strip($accent) == self::LETTERS[$key]); + foreach (self::ACCENT_MAP as $accented => $expected) { + yield $accented => [$accented, $expected]; } } - public function testAll():void + #[DataProvider('accentProvider')] + public function testIndividualAccent(string $accented, string $expected): void + { + $this->assertSame($expected, StripAccents::strip($accented)); + } + + public function testAllAccentsConcatenated(): void { - self::assertTrue( - StripAccents::strip(implode("", self::ACCENTS)) - == implode(self::LETTERS) + $input = implode('', array_keys(self::ACCENT_MAP)); + $expected = implode('', array_values(self::ACCENT_MAP)); + $this->assertSame($expected, StripAccents::strip($input)); + } + + public function testSentence(): void + { + $this->assertSame( + "C'est une super chaine de caracteres avec beaucoup d'accents", + StripAccents::strip("C'est une super chaîne de caractères avec beaucoup d'accents"), ); } -} \ No newline at end of file + + public function testAsciiPassthrough(): void + { + $ascii = 'Hello World 123 !@#'; + $this->assertSame($ascii, StripAccents::strip($ascii)); + } + + public function testEmptyString(): void + { + $this->assertSame('', StripAccents::strip('')); + } + + public function testLigatures(): void + { + $this->assertSame('oe', StripAccents::strip('œ')); + $this->assertSame('AE', StripAccents::strip('Æ')); + } + + public function testStripNonPrint(): void + { + $this->assertSame('ABC AEY ', StripAccents::stripNonPrint("ABC ÀÈÝ 是我这")); + } + + public function testStripNonPrintWithReplacement(): void + { + $result = StripAccents::stripNonPrint("ABC ÀÈÝ 是我这", '-'); + $this->assertStringStartsWith('ABC AEY ', $result); + // each CJK character is 3 bytes in UTF-8, each non-printable byte is replaced individually + $this->assertSame(9, substr_count($result, '-')); + } + + public function testStripNonPrintEmptyString(): void + { + $this->assertSame('', StripAccents::stripNonPrint('')); + } +} From 93e2f17759b451467fbec6f349d0e28d124341f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Fabr=C3=A9gat?= Date: Wed, 15 Apr 2026 11:38:16 -0700 Subject: [PATCH 2/4] Fix Copilot review: drop typed constants, fix README Remove typed class constants (PHP 8.3+ only) to maintain PHP 8.1+ compatibility. Document mbstring requirement and correct stripNonPrint example output in README. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 4 ++-- src/StripAccents.php | 2 +- tests/StripAccentsTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5810062..29b9fe1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A PHP library to remove accents and diacritics from strings. composer require codeinc/strip-accents ``` -Requires PHP 8.1 or later. +Requires PHP 8.1 or later with the `mbstring` extension. ## Usage @@ -38,7 +38,7 @@ echo StripAccents::stripNonPrint("ABC ÀÈÝ 是我这"); // "ABC AEY " echo StripAccents::stripNonPrint("ABC ÀÈÝ 是我这", replaceWith: "-"); -// "ABC AEY ---" +// "ABC AEY ---------" (each multibyte character is replaced per-byte) ``` ### Custom encoding diff --git a/src/StripAccents.php b/src/StripAccents.php index 6b78715..5092daa 100644 --- a/src/StripAccents.php +++ b/src/StripAccents.php @@ -9,7 +9,7 @@ */ class StripAccents { - private const string DEFAULT_ENCODING = 'utf-8'; + private const DEFAULT_ENCODING = 'utf-8'; /** * Strips accents and diacritics from a string. diff --git a/tests/StripAccentsTest.php b/tests/StripAccentsTest.php index 4bbf90d..e3471fc 100644 --- a/tests/StripAccentsTest.php +++ b/tests/StripAccentsTest.php @@ -10,7 +10,7 @@ class StripAccentsTest extends TestCase { - private const array ACCENT_MAP = [ + private const ACCENT_MAP = [ 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', From 9558a6edf073a84a3beb14b69cdf6dc676575a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Fabr=C3=A9gat?= Date: Wed, 15 Apr 2026 11:42:34 -0700 Subject: [PATCH 3/4] Bump minimum PHP to 8.2 (PHPUnit 11 requirement) PHPUnit 11 requires PHP 8.2+, so drop 8.1 from the matrix and bump the composer constraint accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 2 +- README.md | 2 +- composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a98926e..2cc8ff4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - php-version: ['8.1', '8.2', '8.3', '8.4'] + php-version: ['8.2', '8.3', '8.4'] steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index 29b9fe1..0b3ffdc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A PHP library to remove accents and diacritics from strings. composer require codeinc/strip-accents ``` -Requires PHP 8.1 or later with the `mbstring` extension. +Requires PHP 8.2 or later with the `mbstring` extension. ## Usage diff --git a/composer.json b/composer.json index f131714..6f7f08e 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "type": "library", "license": "MIT", "require": { - "php": ">=8.1", + "php": ">=8.2", "ext-mbstring": "*" }, "require-dev": { From ea09e7f200e695eb3c8de661ef7545ae326b25a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Fabr=C3=A9gat?= Date: Wed, 15 Apr 2026 11:50:02 -0700 Subject: [PATCH 4/4] Restore public visibility of DEFAULT_ENCODING constant Keep backward compatibility with existing consumers that may reference StripAccents::DEFAULT_ENCODING directly. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/StripAccents.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StripAccents.php b/src/StripAccents.php index 5092daa..9d73bf9 100644 --- a/src/StripAccents.php +++ b/src/StripAccents.php @@ -9,7 +9,7 @@ */ class StripAccents { - private const DEFAULT_ENCODING = 'utf-8'; + public const DEFAULT_ENCODING = 'utf-8'; /** * Strips accents and diacritics from a string.