Создание и управление строками

Дата обновления перевода 2025-09-22

Создание и управление строками

Symfony предоставляет объектно-ориентированный API для работы со строками Юникода (в виде байтов,
кодовых точек и кластеров графем). Этот API доступен через компонент String, который вы должны предварительно установить в своем приложении:

1
$ composer require symfony/string

Note

Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно подключить файл vendor/autoload.php в вашем коде для включения механизма автозагрузки классов, предоставляемых Composer. Детальнее читайте в этой статье.

Что такое String?

Вы можете пропустить этот раздел, если уже знаете, что такое "кодовая точка" или
"кластер графем" в контексте работы со строками. В противном случае прочитайте этот раздел, чтобы узнать о терминологии, используемой в этом компоненте.

Такие языки, как английский, требуют очень ограниченного набора знаков и символов для для отображения любого содержания. Каждая строка представляет собой ряд знаков (букв или символов) и их можно закодировать даже с помощью самых ограниченных стандартов (например, ASCII).

Однако другие языки требуют тысячи символов для отображения своего содержания. Для них нужны сложные стандарты кодирования, такие как Юникод, и такие понятия, как "знак" больше не имеют смысла. Вместо этого приходится иметь дело с такими терминами:

  • Кодовые точки: это атомарные единицы информации. Строка - это серия кодовых точек. Каждая кодовая точка - это число, значение которого определено стандартом Юникода. Например, английская буква A - это U+0041, а японская кана - кодовая точка U+306E.
  • Кластеры графем: это последовательность из одной или нескольких кодовых точек, которые отображаются как единая графическая единица. Например, испанская буква ñ является кластером графем, содержащим две кодовые точки: U+006E = n ("латинская малая буква N") + U+0303 = ◌̃ ("комбинированная тильда").
  • Байты: это фактическая информация, хранящаяся в содержании строки. Каждая кодовая точка может занимать один или несколько байтов в зависимости от используемого стандарта

(UTF-8, UTF-16 и т. д.).

На следующем рисунке показаны байты, кодовые точки и кластеры графем для одного и того же слова, написанного на английском (hello) и на хинди (नमस्ते):

Каждая буква в слове «hello» состоит из одного байта, одной кодовой точки и одного кластера графем. В переводе на хинди первые две буквы («नम») занимают три байта, одну кодовую точку и один кластер графем. Последние буквы («स्ते») занимают шесть байт, две точки кода и один кластер графем.

Использование

Создайте новый объект типа ByteString, CodePointString или UnicodeString, передайте содержание строки в качестве их аргументов, а затем используйте объектно-ориентированный API для работы с этими строками:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\String\UnicodeString;

$text = (new UnicodeString('This is a déjà-vu situation.'))
    ->trimEnd('.')
    ->replace('déjà-vu', 'jamais-vu')
    ->append('!');
// $text = 'This is a jamais-vu situation!'

$content = new UnicodeString('नमस्ते दुनिया');
if ($content->ignoreCase()->startsWith('नमस्ते')) {
    // ...
}

Справочник методов

Методы для создания объектов строки

Для начала вы можете создать объекты, подготовленные для хранения строк в виде байтов, кодовых точек
и кластеров графем с помощью следующих классов:

1
2
3
4
5
6
7
8
use Symfony\Component\String\ByteString;
use Symfony\Component\String\CodePointString;
use Symfony\Component\String\UnicodeString;

$foo = new ByteString('hello');
$bar = new CodePointString('hello');
// UnicodeString - самый широко используемый класс
$baz = new UnicodeString('hello');

Используйте статический метод wrap() для инстанцирования более чем одного объекта строки:

1
2
3
4
5
6
7
$contents = ByteString::wrap(['hello', 'world']);        // $contents = ByteString[]
$contents = UnicodeString::wrap(['I', '❤️', 'Symfony']); // $contents = UnicodeString[]

// использовать метод unwrap, чтобы выполнить обратное преобразование
$contents = UnicodeString::unwrap([
    new UnicodeString('hello'), new UnicodeString('world'),
]); // $contents = ['hello', 'world']

Если вы работаете с большим количеством объектов String, воспользуйтесь функциями быстрого доступа, чтобы сделать ваш код более лаконичным:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// функция b() создает байтовые строки
use function Symfony\Component\String\b;

// обе строки эквивалентны
$foo = new ByteString('hello');
$foo = b('hello');

// функция u() создает строки Юникода
use function Symfony\Component\String\u;

// обе строки эквивалентны
$foo = new UnicodeString('hello');
$foo = u('hello');

// функция s() создает байтовые строки или строки Юникода,
// в зависимости от заданного содержания
use function Symfony\Component\String\s;

// создает объект ByteString
$foo = s("\xfe\xff");
// создает объект UnicodeString
$foo = s('अनुच्छेद');

Есть также несколько специализированных конструкторов:

1
2
3
4
5
6
7
8
9
10
// ByteString может создать рандомную строку заданной длины
$foo = ByteString::fromRandom(12);
// по умолчанию рандомные строки используют знаки A-Za-z0-9; вы можете ограничить
// знаки для использования с помощью второго необязательного аргумента
$foo = ByteString::fromRandom(6, 'AEIOU0123456789');
$foo = ByteString::fromRandom(10, 'qwertyuiop');

// CodePointString и UnicodeString могут создать строку из кодовых точек
$foo = UnicodeString::fromCodePoints(0x928, 0x92E, 0x938, 0x94D, 0x924, 0x947);
// эквивалентно: $foo = new UnicodeString('नमस्ते');

Методы для преобразования объектов строки

Каждый объект строки может быть преобразован в два других типа объектов:

1
2
3
4
5
6
7
8
$foo = ByteString::fromRandom(12)->toCodePointString();
$foo = (new CodePointString('hello'))->toUnicodeString();
$foo = UnicodeString::fromCodePoints(0x68, 0x65, 0x6C, 0x6C, 0x6F)->toByteString();

// необязательный аргумент $toEncoding определяет кодировку целевой строки
$foo = (new CodePointString('hello'))->toByteString('Windows-1252');
// необязательный аргумент $fromEncoding определяет кодировку изначальной строки
$foo = (new ByteString('さよなら'))->toCodePointString('ISO-2022-JP');

Если преобразование по какой-либо причине невозможно, вы получите
InvalidArgumentException.

Существует также метод, позволяющий получить байты, хранящиеся в некоторой позиции:

1
2
3
4
5
6
7
// ('नमस्ते' bytes = [224, 164, 168, 224, 164, 174, 224, 164, 184,
//                  224, 165, 141, 224, 164, 164, 224, 165, 135])
b('नमस्ते')->bytesAt(0);   // [224]
u('नमस्ते')->bytesAt(0);   // [224, 164, 168]

b('नमस्ते')->bytesAt(1);   // [164]
u('नमस्ते')->bytesAt(1);   // [224, 164, 174]

Методы, связанные с длиной и знаками пробела

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// возвращает количество графем, кодовых точек или байтов в заданной строке
$word = 'नमस्ते';
(new ByteString($word))->length();      // 18 (bytes)
(new CodePointString($word))->length(); // 6 (code points)
(new UnicodeString($word))->length();   // 4 (graphemes)

// некоторые символы требуют вдвое большей ширины, чем другие, для их представления при использовании
// моноширинного шрифта (например, в консоли). Этот метод возвращает общую ширину,
// необходимую для представления всего слова
$word = 'नमस्ते';
(new ByteString($word))->width();      // 18
(new CodePointString($word))->width(); // 4
(new UnicodeString($word))->width();   // 4
// если текст содержит несколько строк, возвращается максимальная ширина всех строк
$text = "<<<END
This is a
multiline text
END";
u($text)->width(); // 14

// возвращает TRUE, только если строка точно пустая (даже без пробелов)
u('hello world')->isEmpty();  // false
u('     ')->isEmpty();        // false
u('')->isEmpty();             // true

// удаляет все пробелы ('\n\r\t\x0C') из начала и конца строки и
// заменяет два или более последовательных пробела  одним пробелом (' ')
u("  \n\n   hello \t   \n\r   world \n    \n")->collapseWhitespace(); // 'hello world'

Методы для изменения регистра

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// изменяет все графемы/кодовые точки на нижний регистр
u('FOO Bar Brİan')->lower();  // 'foo bar bri̇an'
// изменяет все графемы/кодовые точки на нижний регистр в соответствии с локально-специфическими отображениями регистра
u('FOO Bar Brİan')->localeLower('en');  // 'foo bar bri̇an'
u('FOO Bar Brİan')->localeLower('lt');  // 'foo bar bri̇̇an'

// когда речь идет о разных языках, прописных/строчных букв недостаточно
// существует три регистра (нижний, верхний, заглавный), некоторые знаки не имеют регистра,
// регистр зависит от контекста и локали и т. д.
// этот метод возвращает строку, которую можно использовать при сравнении без учета регистра
u('FOO Bar')->folded();             // 'foo bar'
u('Die O\'Brian Straße')->folded(); // "die o'brian strasse"

// изменяет все графемы/кодовые точки на верхний регистр
u('foo BAR bάz')->upper(); // 'FOO BAR BΆZ'
// изменяет все графемы/кодовые точки на верхний регистр в соответствии с локально-специфическими отображениями регистра
u('foo BAR bάz')->localeUpper('en'); // 'FOO BAR BΆZ'
u('foo BAR bάz')->localeUpper('el'); // 'FOO BAR BAZ'

// изменяет все графемы/кодовые точки на "заглавный регистр"
u('foo ijssel')->title();     // 'Foo ijssel'
u('foo ijssel')->title(true); // 'Foo Ijssel'
// изменяет все графемы/кодовые точки на "заглавный регистр" в соответствии с локально-специфическими отображениями регистра
u('foo ijssel')->localeTitle('en'); // 'Foo ijssel'
u('foo ijssel')->localeTitle('nl'); // 'Foo IJssel'

// изменяет все графемы/кодовые точки на camelCase
u('Foo: Bar-baz.')->camel(); // 'fooBarBaz'
// изменяет все графемы/кодовые точки на snake_case
u('Foo: Bar-baz.')->snake(); // 'foo_bar_baz'
// изменяет все графемы/кодовые точки на kebab-case
u('Foo: Bar-baz.')->kebab(); // 'foo-bar-baz'
// изменяет все графемы/кодовые точки на PascalCase
u('Foo: Bar-baz.')->pascal(); // 'FooBarBaz'
// других регистров можно достичь путем изменения методов. Например, PascalCase:
u('Foo: Bar-baz.')->camel()->title(); // 'FooBarBaz'

7.1

Методы localeLower(), localeUpper() и localeTitle() были представлены в Symfony 7.1.

7.2

Метод kebab() был представлен в Symfony 7.2.

7.3

Метод pascal() был представлен в Symfony 7.3.

Методы всех классов строки по умолчанию чувствительны к регистру. Вы можете выполнять операции без учета регистра с помощью метода ignoreCase():

1
2
u('abc')->indexOf('B');               // null
u('abc')->ignoreCase()->indexOf('B'); // 1

Методы для добавления в начале и в конце

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// добавляет заданное содержание (одну или несколько строк) в начало/конец строки
u('world')->prepend('hello');      // 'helloworld'
u('world')->prepend('hello', ' '); // 'hello world'

u('hello')->append('world');      // 'helloworld'
u('hello')->append(' ', 'world'); // 'hello world'

// добавляет заданное содержание в начало строки (или удаляет его), чтобы
// убедиться, что содержание начинается именно с него
u('Name')->ensureStart('get');       // 'getName'
u('getName')->ensureStart('get');    // 'getName'
u('getgetName')->ensureStart('get'); // 'getName'
// этот метод похож, но работает в конце содержания, а не в начале
u('User')->ensureEnd('Controller');           // 'UserController'
u('UserController')->ensureEnd('Controller'); // 'UserController'
u('UserControllerController')->ensureEnd('Controller'); // 'UserController'

// возвращает содержание, найденное до/после первого появления заданной строки
u('hello world')->before('world');   // 'hello '
u('hello world')->before('o');       // 'hell'
u('hello world')->before('o', true); // 'hello'

u('hello world')->after('hello');   // ' world'
u('hello world')->after('o');       // ' world'
u('hello world')->after('o', true); // 'o world'

// возвращает содержание, найденное до/после последнего появления заданной строки
u('hello world')->beforeLast('o');       // 'hello w'
u('hello world')->beforeLast('o', true); // 'hello wo'

u('hello world')->afterLast('o');       // 'rld'
u('hello world')->afterLast('o', true); // 'orld'

Методы для добавления и обрезания

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// создает строку с длиной как у первого аргумента, добавляя заданную
// строку в начало, конец или с обеих сторон строки
u(' Lorem Ipsum ')->padBoth(20, '-'); // '--- Lorem Ipsum ----'
u(' Lorem Ipsum')->padStart(20, '-'); // '-------- Lorem Ipsum'
u('Lorem Ipsum ')->padEnd(20, '-');   // 'Lorem Ipsum --------'

// повторяет заданную строку столько раз, сколько было передано в качестве аргумента
u('_.')->repeat(10); // '_._._._._._._._._._.'

// удаляет заданные символы (по умолчанию: пробелы) из начала и конца строки
u('   Lorem Ipsum   ')->trim(); // 'Lorem Ipsum'
u('Lorem Ipsum   ')->trim('m'); // 'Lorem Ipsum   '
u('Lorem Ipsum')->trim('m');    // 'Lorem Ipsu'

u('   Lorem Ipsum   ')->trimStart(); // 'Lorem Ipsum   '
u('   Lorem Ipsum   ')->trimEnd();   // '   Lorem Ipsum'

// удаляет заданное содержание из начала/конца строки  
u('file-image-0001.png')->trimPrefix('file-');           // 'image-0001.png'
u('file-image-0001.png')->trimPrefix('image-');          // 'file-image-0001.png'
u('file-image-0001.png')->trimPrefix('file-image-');     // '0001.png'
u('template.html.twig')->trimSuffix('.html');            // 'template.html.twig'
u('template.html.twig')->trimSuffix('.twig');            // 'template.html'
u('template.html.twig')->trimSuffix('.html.twig');       // 'template'
// when passing an array of prefix/suffix, only the first one found is trimmed
u('file-image-0001.png')->trimPrefix(['file-', 'image-']); // 'image-0001.png'
u('template.html.twig')->trimSuffix(['.twig', '.html']);   // 'template.html'

Методы для поиска и замены

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// проверяет, начинается/заканчивается ли строка с заданной строки
u('https://symfony.com')->startsWith('https'); // true
u('report-1234.pdf')->endsWith('.pdf');        // true

// проверяет, совпадает ли содержание строки с заданным содержанием точно
u('foo')->equalsTo('foo'); // true

// проверяет, соответствует ли содержание строки заданному регулярному выражению
u('avatar-73647.png')->match('/avatar-(\d+)\.png/');
// result = ['avatar-73647.png', '73647', null]

// Вы можете передать флажки для preg_match() в качестве второго аргумента. Если переданы флажки PREG_PATTERN_ORDER
// или PREG_SET_ORDER, то будет использоваться preg_match_all().
u('206-555-0100 and 800-555-1212')->match('/\d{3}-\d{3}-\d{4}/', \PREG_PATTERN_ORDER);
// result = [['206-555-0100', '800-555-1212']]

// проверяет, содержит ли строка какую-либо из других заданных строк 
u('aeiou')->containsAny('a');                 // true
u('aeiou')->containsAny(['ab', 'efg']);       // false
u('aeiou')->containsAny(['eio', 'foo', 'z']); // true

// находит позицию первого появления заданной строки
// (второй аргумент - это позиция начала поиска, а отрицательные
// значения имеют тот же смысл, что и в функциях PHP)
u('abcdeabcde')->indexOf('c');     // 2
u('abcdeabcde')->indexOf('c', 2);  // 2
u('abcdeabcde')->indexOf('c', -4); // 7
u('abcdeabcde')->indexOf('eab');   // 4
u('abcdeabcde')->indexOf('k');     // null

// находит позицию последнего появления заданной строки
// (второй аргумент - это позиция начала поиска, а отрицательные
// значения имеют тот же смысл, что и в функциях PHP)
u('abcdeabcde')->indexOfLast('c');     // 7
u('abcdeabcde')->indexOfLast('c', 2);  // 7
u('abcdeabcde')->indexOfLast('c', -4); // 2
u('abcdeabcde')->indexOfLast('eab');   // 4
u('abcdeabcde')->indexOfLast('k');     // null

// заменяет все появления заданной строки
u('http://symfony.com')->replace('http://', 'https://'); // 'https://symfony.com'
// заменяет все появления заданного регулярного выражения
u('(+1) 206-555-0100')->replaceMatches('/[^A-Za-z0-9]++/', ''); // '12065550100'
// вы можете передать вызываемое в качестве второго аргумента, чтобы выполнить продвинутую замену
u('123')->replaceMatches('/\d/', function (string $match): string {
    return '['.$match[0].']';
}); // result = '[1][2][3]'

Методы для объединения, разделения, усечения и перевертывания

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// использует строку в качестве "клея" для объединения всех заданных строк
u(', ')->join(['foo', 'bar']); // 'foo, bar'

// разбивает строку на части с помощью заданного разделителя
u('template_name.html.twig')->split('.');    // ['template_name', 'html', 'twig']
// вы можете задать максимальное количество частей в качестве второго аргумента 
u('template_name.html.twig')->split('.', 2); // ['template_name', 'html.twig']

// возвращает подстроку, начинающуюся с первого аргумента и имеющую длину
// второго необязательного аргумента (отрицательные значения имеют то же значение, что и в функциях PHP)
u('Symfony is great')->slice(0, 7);  // 'Symfony'
u('Symfony is great')->slice(0, -6); // 'Symfony is'
u('Symfony is great')->slice(11);    // 'great'
u('Symfony is great')->slice(-5);    // 'great'

// уменьшает строку до длины, заданной в качестве аргумента (если она больше) 
u('Lorem Ipsum')->truncate(3);             // 'Lor'
u('Lorem Ipsum')->truncate(80);            // 'Lorem Ipsum'
// второй аргумент - знак(и), добавляемый(еся) при разрезании строки
// (общая длина включает длину этого знака(ов))
u('Lorem Ipsum')->truncate(8, '…');        // 'Lorem I…'
// если третий аргумент - false, то последнее слово перед отсечением сохраняется,
// даже если при этом строка получается длиннее желаемой длины
u('Lorem Ipsum')->truncate(8, '…', false); // 'Lorem Ipsum'
// возвращает до последнего полного слова, которое укладывается в заданную длину, не превышая ее  
u('Lorem ipsum dolor sit amet')->truncate(8, cut: TruncateMode::WordBefore); // 'Lorem'
// возвращает до последнего полного слова, которое укладывается в заданную длину, при необходимости превышая ее  
u('Lorem ipsum dolor sit amet')->truncate(8, cut: TruncateMode::WordAfter);   // 'Lorem ipsum'

7.2

Параметр TruncateMode для функции обрезания был представлен в Symfony 7.2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// разбивает строку на строки заданной длины 
u('Lorem Ipsum')->wordwrap(4);             // 'Lorem\nIpsum'
// по умолчанию разрывается по пробелам; передайте TRUE для безусловного разбивания
u('Lorem Ipsum')->wordwrap(4, "\n", true); // 'Lore\nm\nIpsu\nm'

// заменяет часть строки с заданным содержанием:
// второй аргумент - позиция, с которой начинается замена;
// третий аргумент - количество графем/кодовых точек, удаленных из строки
u('0123456789')->splice('xxx');       // 'xxx'
u('0123456789')->splice('xxx', 0, 2); // 'xxx23456789'
u('0123456789')->splice('xxx', 0, 6); // 'xxx6789'
u('0123456789')->splice('xxx', 6);    // '012345xxx'

// разбивает строку на фрагменты заданной в качестве аргумента длины
u('0123456789')->chunk(3);  // ['012', '345', '678', '9']

// меняет порядок содержания строк на противоположный
u('foo bar')->reverse(); // 'rab oof'
u('さよなら')->reverse(); // 'らなよさ'

Методы, добавленные ByteString

Эти методы доступны только для объектов ByteString:

1
2
3
// возвращает TRUE, если содержание строки является валидным UTF-8 содержанием
b('Lorem Ipsum')->isUtf8(); // true
b("\xc3\x28")->isUtf8();    // false

Методы, добавленные CodePointString и UnicodeString

Эти методы доступны только для объектов CodePointString и UnicodeString:

1
2
3
4
5
6
7
8
9
10
11
// транслитерирует любую строку в латинский алфавит, определенный кодировкой ASCII
// (не используйте этот метод для создания слаггера, потому что этот компонент уже предоставляет
// слаггер, как объясняется далее в этой статье)
u('नमस्ते')->ascii();    // 'namaste'
u('さよなら')->ascii(); // 'sayonara'
u('спасибо')->ascii(); // 'spasibo'

// возвращает массив с кодовой точкой или точками, хранящимися в заданной позиции
// (кодовые точки графем 'नमस्ते' graphemes = [2344, 2350, 2360, 2340])  
u('नमस्ते')->codePointsAt(0); // [2344]
u('नमस्ते')->codePointsAt(2); // [2360]

Эквивалентность Юникода - это спецификация стандарта Юникод, согласно которой различные последовательности кодовых точек представляют один и тот же знак. Например, шведская буква å может быть одной кодовой точкой (U+00E5 = "латинская строчная буква A с кольцом над ней") или последовательностью из двух кодовых точек (U+0061 = "латинская строчная буква A" + U+030A = “объединяющее кольцо сверху"). Метод normalize() позволяет выбрать режим нормализации:

1
2
3
4
5
6
// кодируют букву как одну кодовую точку: U+00E5
u('å')->normalize(UnicodeString::NFC);
u('å')->normalize(UnicodeString::NFKC);
// они кодируют букву как две кодовые точки: U+0061 + U+030A
u('å')->normalize(UnicodeString::NFD);
u('å')->normalize(UnicodeString::NFKD);

Лениво загружаемые строки

Бывает, что создание строки с помощью методов, представленных в предыдущих разделах не является оптимальным. Например, рассмотрим значение хеша, для получения которого требуются определенные вычисления для получения и которое вы можете в итоге не использовать.

В таких случаях лучше использовать класс LazyString, который позволяет хранить строку, значение которой генерируется только тогда, когда она вам нужна:

1
2
3
4
5
6
7
8
9
use Symfony\Component\String\LazyString;

$lazyString = LazyString::fromCallable(function (): string {
    // Вычислить значение строки...
    $value = ...;

    // Затем вернуть финальное значение
    return $value;
});

Обратный вызов будет выполнен только тогда, когда значение ленивой строки будет запрошено во время выполнения программы. Вы также можете создавать ленивые строки из объекта Stringable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Hash implements \Stringable
{
    public function __toString(): string
    {
        return $this->computeHash();
    }

    private function computeHash(): string
    {
        // Вычислить значение хеша с потенциально сложной обработкой
        $hash = ...;

        return $hash;
    }
}

// Затем создать ленивую строку из этого хеша, которая будет запускать
// вычисление хеша только в случае необходимости
$lazyHash = LazyString::fromStringable(new Hash());

Работа с Emoji

Это содержание было перемещено в Документы компонента Emoji.

Слаггер

В некоторых контекстах, таких как URL и именах файлов/каталогов, небезопасно использовать любой знак Юникода. Слаггер преобразует заданную строку в другую строку, которая включает в себя только безопасные знаки ASCII:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Symfony\Component\String\Slugger\AsciiSlugger;

$slugger = new AsciiSlugger();
$slug = $slugger->slug('Wôrķšƥáçè ~~sèťtïñğš~~');
// $slug = 'Workspace-settings'

// вы также можете передать массив с дополнительными заменами знаков    
$slugger = new AsciiSlugger('en', ['en' => ['%' => 'percent', '€' => 'euro']]);
$slug = $slugger->slug('10% or 5€');
// $slug = '10-percent-or-5-euro'

// если для вашей локали не существует карты символов (например, 'en_GB'), то карта символов родительской локали
// будет использоваться вместо нее (например, 'en').
$slugger = new AsciiSlugger('en_GB', ['en' => ['%' => 'percent', '€' => 'euro']]);
$slug = $slugger->slug('10% or 5€');
// $slug = '10-percent-or-5-euro'

// для более динамичных замен передайте PHP-замыкание вместо массива
    $slugger = new AsciiSlugger('en', function (string $string, string $locale): string {
    return str_replace('❤️', 'love', $string);
});

По умолчанию разделителем между словами является тире (-), но вы можете задать другой разделитель в качестве второго аргумента:

1
2
$slug = $slugger->slug('Wôrķšƥáçè ~~sèťtïñğš~~', '/');
// $slug = 'Workspace/settings'

Слаггер транслитерирует исходную строку в латинскую систему письма до применения других преобразований. Локаль исходной строки определяется автоматически, но вы можете задать ее явно:

1
2
3
4
5
6
// это указывает слаггеру транслитерировать с корейского языка ('ko')
$slugger = new AsciiSlugger('ko');

// вы можете переопределить локаль в качестве третьего необязательного параметра slug()
// например, этот слаггер транслитерирует с персидского языка ('fa') 
$slug = $slugger->slug('...', '-', 'fa');

В приложении Symfony вам не нужно создавать слаггер самостоятельно. Благодаря
автомонтированию сервисов autowiring, вы можете внедрить слаггер, указав подсказку типа аргумента конструктора сервиса с помощью SluggerInterface. Локаль внедренного слаггера совпадает с локалью запроса:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Symfony\Component\String\Slugger\SluggerInterface;

class MyService
{
    public function __construct(
        private SluggerInterface $slugger,
    ) {
    }

    public function someMethod(): void
    {
        $slug = $this->slugger->slug('...');
    }
}

Эмодзи Slug

Вы также можете использовать транслитератор эмодзи со слаггером для преобразования любых эмодзи в их текстовое представление:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\String\Slugger\AsciiSlugger;

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

$slug = $slugger->slug('a 😺, 🐈‍⬛, and a 🦁 go to 🏞️', '-', 'en');
// $slug = 'a-grinning-cat-black-cat-and-a-lion-go-to-national-park';

$slug = $slugger->slug('un 😺, 🐈‍⬛, et un 🦁 vont au 🏞️', '-', 'fr');
// $slug = 'un-chat-qui-sourit-chat-noir-et-un-tete-de-lion-vont-au-parc-national';

Если вы хотите использовать определенную локаль для эмодзи или использовать короткие коды из GitHub, Gitlab или Slack, используйте первый аргумент метода withEmoji():

1
2
3
4
5
6
7
use Symfony\Component\String\Slugger\AsciiSlugger;

$slugger = new AsciiSlugger();
$slugger = $slugger->withEmoji('github'); // or "en", or "fr", etc.

$slug = $slugger->slug('a 😺, 🐈‍⬛, and a 🦁');
// $slug = 'a-smiley-cat-black-cat-and-a-lion';

Инфлектор

В некоторых сценариях, таких как генерирование кода и интроспекция кода, вам необходимо преобразовывать слова из/в единственное/множественное число. Например, чтобы узнать свойство связанное с методом adder, необходимо преобразовать из множественного числа (метод addStories()) в единственное число (свойство $story).

Большинство человеческих языков имеют простые правила плюрализации, но в то же время в них существует множество исключений. Например, в английском языке общим правилом является добавление буквы s в конце слова (book -> books), но существует множество исключений даже для обычных слов (woman -> women, life -> lives, news -> news, radius -> radii и т.д.).

Этот компонент предоставляет класс
EnglishInflector для уверенного преобразования английских слов из/в единственное/ множественное число:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\String\Inflector\EnglishInflector;

$inflector = new EnglishInflector();

$result = $inflector->singularize('teeth');   // ['tooth']
$result = $inflector->singularize('radii');   // ['radius']
$result = $inflector->singularize('leaves');  // ['leaf', 'leave', 'leaff']

$result = $inflector->pluralize('bacterium'); // ['bacteria']
$result = $inflector->pluralize('news');      // ['news']
$result = $inflector->pluralize('person');    // ['persons', 'people']

Значение, возвращаемое обоими методами, всегда представляет собой массив, потому что иногда бывает невозможно определить уникальную форму единственного/ множественного числа для данного слова.

Symfony также предоставляет инфлекторы для других языков:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\String\Inflector\FrenchInflector;

$inflector = new FrenchInflector();
$result = $inflector->singularize('souris'); // ['souris']
$result = $inflector->pluralize('hôpital');  // ['hôpitaux']

use Symfony\Component\String\Inflector\SpanishInflector;

$inflector = new SpanishInflector();
$result = $inflector->singularize('aviones'); // ['avión']
$result = $inflector->pluralize('miércoles'); // ['miércoles']

7.2

Класс SpanishInflector был представлен в Symfony 7.2.

Note

Symfony также предоставляет InflectorInterface, если вам нужно реализовать свой собственный инфлектор.