各国の文字列を扱う、"String"

[Symfony Component Advent Calendar 2022](https://qiita.com/advent-calendar/2022/symfony-component)の22日目の記事です。

## 最初に
[Symfony](https://symfony.com)はPHPのフレームワークのひとつです。しかし、公式サイトの説明文には

> Symfony is a set of PHP Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony.
  （SymfonyはPHPコンポーネントのセットで、Webアプリケーションフレームワークで、哲学、そしてコミュニティです。それらがハーモニーを奏でながら動作しています。）

と書かれている通り、PHPコンポーネントのセットで、たくさんのコンポーネントを提供しており、それらを組み合わせてひとつのフレームワークとして動作しています。Symfonyのコンポーネントは、Symfony上だけで動作するのではなく、他のPHPフレームワークやアプリケーションでも動作している強力なものが揃っています。

今回はそれらの中から、役立ちそうなもの・お薦めしたいものを紹介していきたいと思います。

※記事内ではautoloadのインポートは省略します。


## 各国の文字列を扱う、"String"

[String](https://symfony.com/doc/current/components/string.html)は、文字列を扱うコンポーネントです。各国の文字列を適切に扱います。


### インストール

```shell
composer require symfony/string
```

### バイト、コードポイント、書記素クラスタ

`String`コンポーネントを使うと、文字列をバイト、コードポイント、書記素クラスタごとに扱えるようになります。
それぞれは、ざっくり言うと以下のような扱いになります。

| 種類 | 内容 |
|---|---|
| バイト | 各文字コードのバイト |
| コードポイント | Unicode
| 書記素クラスタ | 実際の文字としてのUnicodeの組み合わせ |

ぱっと見、ちょっとわからないですが、例えば`नमस्ते`（ナマステ）という文字の場合、UTF-8でバイト数は18バイトあります。
これをUnicodeにすると、6つのコードで構成されています。
しかしながら、文字数（書記素クラスタ）的には4文字になります。以下の図だとよりわかりやすいと思います。

![文字列比較](https://symfony.com/doc/6.2/_images/bytes-points-graphemes.png)

このバイト、コードポイント、書記素クラスタごとにオブジェクトを作って、それぞれを扱えるようにできます。

```php

use Symfony\Component\String\ByteString;
use Symfony\Component\String\CodePointString;
use Symfony\Component\String\UnicodeString;

$string = 'नमस्ते';

// バイト
$byteString = new ByteString($string);
echo $byteString->length(); // 18

// コードポイント
$codePointString = new CodePointString($string);
echo $codePointString->length(); // 6

// 書記素クラスタ
$unicodeString = new UnicodeString($string);
echo $unicodeString->length(); // 4
```

このように、それぞれの種類で文字列データを扱うことができます。もちろん日本語もOKです。
ただ、実際によく使うのは書記素クラスタになると思いますし、`UnicodeString`にはたくさんの機能がついています。
その一部を紹介します。


```php

use function Symfony\Component\String\u;

$unicodeString = u('文字列'); // new UnicodeString()と同一

echo u('ABCDE')->lower(); // 文字列を小文字にする 'abcde'

echo u('abcde')->upper(); // 大文字にする 'ABCDE'

echo u('my favorite framework')->title(); // タイトル文字列にする 'My favorite framework'
echo u('my favorite framework')->title(true); // 各スペース直後の文字を大文字に 'My Favorite Framework'

echo u('my favorite framework')->camel(); // キャメルケースに 'myFavoriteFramework'
echo u('my favorite framework')->snake(); // スネークケースに 'my_favorite_framework'

echo u('abc')->indexOf('B'); // 該当の文字があるかチェック（大文字小文字判断） null
echo u('abc')->ignoreCase()->indexOf('B') 該当の文字があるかチェック（大文字小文字判断せず） // 1

echo u('Item')->ensureStart('get'); // 指定した文字列でスタートする 'getItem'
echo u('getItem')->ensureStart('get'); // 指定した文字列がスタートしてたら何もしない 'getItem'
echo u('Item')->ensureEnd('Controller'); // 指定した文字列でおわる 'ItemController'
echo u('ItemController')->ensureEnd('Controller'); // 指定した文字列でおわってたら何もしない 'ItemController'

echo u(' hoge ')->trim(' '); // 指定の文字をトリム 'hoge'
echo u('my-hoge.png')->trimPrefix('my-'); // 前から指定の文字をトリム 'hoge.png'
echo u('my-hoge.png')->trimSuffix('.png'); // 後ろから指定の文字をトリム 'my-hoge'
echo u('my favorite framework')->truncate(5, '...'); // 指定した文字数で切る 'my fa...'

echo u('てんきがいいですね')->reverse(); // 反対にする 'ねすでいいがきんて'

echo u('abcde')->equalsTo('abcde'); // 文字列比較 true
echo u('abcde')->match('/b(.+)e$/'); // 正規表現で検索 ['bcde', 'cd', null]
echo u('abcde')->replace('bc', 'hi'); // 文字列置換 'ahide'
echo u('abcde')->replaceMatches('/bc/', 'hi'); // 正規表現で文字列置換 'ahide'
```

などなどあります。`ensure***`あたり、なんか役立ちそうな気がします。
ぜひ[公式ドキュメント](https://symfony.com/doc/current/components/string.html)をご覧ください。


### スラッグ

WordPressなどのCMSは、タイトルをそのままURLにすることがあります。`String`では`SluggerInterface`を使って、スラッグを作成することができます。

```php

use Symfony\Component\String\Slugger\AsciiSlugger;

$title = 'My favorite framework';

$slugger = new AsciiSlugger();
$slug = $slugger->slug($title);

echo $slug; // my-favorite-framework

$title = 'すきな　フレームワーク';
$slug = $slugger->slug($title, 'ja'); // 言語指定可能（ただ、指定しなくても自動判別するっぽい）

echo $slug; // sukina-furemuwaku
```

#### 絵文字置換

Symfony 6.2から、`AsciiSlugger::withEmoji()`を使うことで、タイトルに使われた絵文字を文字列に置換してスラッグをつくります。

```php

$title = 'My favorite 🐈';

$slugger = new AsciiSlugger();
$slug = $slugger->withEmoji()->slug($title);

echo $slug; // my-favorite-neko

$slug = $slugger->withEmoji()->slug($title, 'en'); // 言語指定可能
echo $slug; // my-favorite-cat
```

### Inflector

`Inflector`を使うことで、文字列の単数系・複数形を取得できます。

```php

use Symfony\Component\String\Inflector\EnglishInflector;

$inflector = new EnglishInflector();

// 単数系→複数形
print_r($inflector->pluralize('knife'); // ['knives']
print_r($inflector->pluralize('person'); // ['persons', 'people'] 複数ある場合は、それぞれを返す

// 複数形→単数系
print_r($inflector->singularize('geese'); // ['goose']
print_r($inflector->singularize('leaves') // ['leaf', 'leave', 'leaff'] 複数ある場合は、それぞれを返す
```

## まとめ

今回は`String`を紹介しました。特に`UnicodeString`は普通にPHPの文字列関数を使うよりも便利という印象を受けました。
また、`Slugger`, `Inflector`なども含め、何かしらメソッド名やクラス名、ファイル名などを生成するのに便利だと思います。