Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
push:
branches: [master]
pull_request:
branches: [master]
Comment on lines +5 to +7

Copilot AI Apr 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow is restricted to branches: [master]. If the default branch is main (as suggested by the PR diff base), CI won’t run for pushes/PRs to the default branch. Consider switching this filter to the actual default branch name or removing the branch filter to ensure CI runs on all PRs.

Suggested change
branches: [master]
pull_request:
branches: [master]
branches: [main]
pull_request:
branches: [main]

Copilot uses AI. Check for mistakes.

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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/.idea
/composer.lock
/vendor
/.phpunit.cache
.DS_Store
50 changes: 35 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
<?php
use CodeInc\StripAccents\StripAccents;

echo StripAccents::strip("C'est une super chaîne de caractères avec beaucoup d'accents");
// echoes: C'est une super chaine de caracteres avec beaucoup d'accents
// "C'est une super chaine de caracteres avec beaucoup d'accents"

echo StripAccents::strip("ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ");
// echoes: AAAAAACEEEEIIIIOOOOOUUUUYaaaaaaceeeeiiiioooooouuuuyy
// "AAAAAACEEEEIIIIOOOOOUUUUYaaaaaaceeeeiiiieooooouuuuyy"
```

### Strip accents and non-printable characters

```php
use CodeInc\StripAccents\StripAccents;

echo StripAccents::stripNonPrint("ABC ÀÈÝ 是我这");
// echoes: ABC AEY ---
// "ABC AEY "

// You can specify any encoding supported by htmlentities() as a second parameter
echo StripAccents::strip("A strïng with àccénts", "iso-8859-1");
echo StripAccents::stripNonPrint("ABC ÀÈÝ 是我这", replaceWith: "-");
// "ABC AEY ---------" (each multibyte character is replaced per-byte)
```

### Custom encoding

## Installation
This library is available through [Packagist](https://packagist.org/packages/codeinc/strip-accents) and can be installed using [Composer](https://getcomposer.org/):
Both methods accept an encoding parameter (any encoding supported by `htmlentities()`):

```bash
composer require codeinc/strip-accents
```php
echo StripAccents::strip("A strïng with àccénts", encoding: "iso-8859-1");
```


## License
This library is published under the MIT license (see the [LICENSE](LICENSE) file).

This library is published under the MIT license (see the [LICENSE](LICENSE) file).
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "codeinc/strip-accents",
"version": "1.1.0",
"description": "Removes accents from strings",
"homepage": "https://github.com/CodeIncHQ/StripAccents",
"description": "A PHP library to remove accents and diacritics from strings",
"homepage": "https://github.com/codeinchq/strip-accents",
"type": "library",
"license": "MIT",
"require": {
"php": ">=7.1"
"php": ">=8.2",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^7"
"phpunit/phpunit": "^11.0"
},
"autoload": {
"psr-4": {
Expand All @@ -34,4 +34,4 @@
"*": "dist"
}
}
}
}
17 changes: 17 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
65 changes: 18 additions & 47 deletions src/StripAccents.php
Original file line number Diff line number Diff line change
@@ -1,77 +1,48 @@
<?php
//
// +---------------------------------------------------------------------+
// | CODE INC. SOURCE CODE |
// +---------------------------------------------------------------------+
// | Copyright (c) 2017 - Code Inc. SAS - All Rights Reserved. |
// | Visit https://www.codeinc.fr for more information about licensing. |
// +---------------------------------------------------------------------+
// | NOTICE: All information contained herein is, and remains the |
// | property of Code Inc. SAS. The intellectual and technical concepts |
// | contained herein are proprietary to Code Inc. SAS are protected by |
// | trade secret or copyright law. Dissemination of this information or |
// | reproduction of this material is strictly forbidden unless prior |
// | written permission is obtained from Code Inc. SAS. |
// +---------------------------------------------------------------------+
//
// Author: Joan Fabrégat <joan@codeinc.fr>
// 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 <joan@codeinc.fr>
* 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. "&eacute;" => "e", "&Eacute;" => "E", "&agrave;" => "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. "&oelig;" => "oe", "&AElig;" => "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));
}
}
}
124 changes: 77 additions & 47 deletions tests/StripAccentsTest.php
Original file line number Diff line number Diff line change
@@ -1,63 +1,93 @@
<?php
//
// +---------------------------------------------------------------------+
// | CODE INC. SOURCE CODE |
// +---------------------------------------------------------------------+
// | Copyright (c) 2017 - Code Inc. SAS - All Rights Reserved. |
// | Visit https://www.codeinc.fr for more information about licensing. |
// +---------------------------------------------------------------------+
// | NOTICE: All information contained herein is, and remains the |
// | property of Code Inc. SAS. The intellectual and technical concepts |
// | contained herein are proprietary to Code Inc. SAS are protected by |
// | trade secret or copyright law. Dissemination of this information or |
// | reproduction of this material is strictly forbidden unless prior |
// | written permission is obtained from Code Inc. SAS. |
// +---------------------------------------------------------------------+
//
// Author: Joan Fabrégat <joan@codeinc.fr>
// 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 <joan@codeinc.fr>
*/
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"),
);
}
}

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(''));
}
}
Loading