diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2cc8ff4 --- /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.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..0b3ffdc 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.2 or later with the `mbstring` extension. + +## Usage + +### Strip accents -## Usage ```php -=7.1" + "php": ">=8.2", + "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..9d73bf9 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'; /** - * @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..e3471fc 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 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('')); + } +}