Реализация класса строк в PHP с нуля

Раздел: PHP разработка -> Объектно-ориентированное программирование в PHP

Создание класса для работы со строками в PHP

Основное решение – класс StringHandler

Данный класс предоставляет удобный интерфейс для выполнения основных операций над строками. Он инкапсулирует строку как приватное свойство, а все методы работают с этим значением. Такой подход позволяет избежать разрозненных вызовов встроенных функций и делает код более читаемым.


class StringHandler {
    private string $string;

    public function __construct(string $string) {
        $this->string = $string;
    }

    public function length(): int {
        return mb_strlen($this->string, 'UTF-8');
    }

    public function toUpper(): self {
        $this->string = mb_strtoupper($this->string, 'UTF-8');
        return $this;
    }

    public function toLower(): self {
        $this->string = mb_strtolower($this->string, 'UTF-8');
        return $this;
    }

    public function trim(): self {
        $this->string = trim($this->string);
        return $this;
    }

    public function contains(string $needle): bool {
        return mb_strpos($this->string, $needle, 0, 'UTF-8') !== false;
    }

    public function replace(string $search, string $replace): self {
        $this->string = str_replace($search, $replace, $this->string);
        return $this;
    }

    public function substring(int $start, ?int $length = null): self {
        $this->string = mb_substr($this->string, $start, $length, 'UTF-8');
        return $this;
    }

    public function get(): string {
        return $this->string;
    }
}
  

Пример использования:


$handler = new StringHandler('  Привет, мир!  ');
echo $handler->trim()->toUpper()->substring(0, 6)->get(); // ВЫВОД: ПРИВЕТ
  
ПРИВЕТ
  

Типичные ошибки и способы их решения

  • Использование strlen вместо mb_strlen – для многобайтовых кодировок (например, UTF-8) длина считается некорректно. Решение: всегда применять мультибайтовые аналоги.
  • Забыли указать кодировку в функциях mb_* – без явной передачи 'UTF-8' могут использоваться настройки сервера, что приведёт к ошибкам. Решение: всегда передавать кодировку вторым параметром.
  • Изменение исходной строки через методы, возвращающие новое значение – в примере выше методы изменяют внутреннее свойство и возвращают $this для цепочек, но это может быть неочевидно. Решение: документировать поведение.

Вариант 1: Как сделать объект строки, который ведёт себя как простая строка?

Реализация через магический метод __toString позволяет использовать объект в строковых контекстах, а __invoke – вызывать объект как функцию для получения строки.


class MagicString {
    private string $value;

    public function __construct(string $value) {
        $this->value = $value;
    }

    public function __toString(): string {
        return $this->value;
    }

    public function __invoke(): string {
        return $this->value;
    }

    public function upper(): string {
        return mb_strtoupper($this->value, 'UTF-8');
    }
}

$obj = new MagicString('hello');
echo $obj;              // hello
echo $obj();            // hello
echo $obj->upper();     // HELLO
  

Проблема: при таком подходе каждый метод возвращает новую строку, а не изменяет объект. Это нарушает единообразие, если ожидается модификация исходного экземпляра.

Вариант 2: Как реализовать цепочку методов (fluent interface)?

Каждый метод возвращает $this, что позволяет выстраивать последовательные преобразования в одной строке без создания временных переменных.


class FluentString {
    private string $str;

    public function __construct(string $str) {
        $this->str = $str;
    }

    public function append(string $suffix): self {
        $this->str .= $suffix;
        return $this;
    }

    public function prepend(string $prefix): self {
        $this->str = $prefix . $this->str;
        return $this;
    }

    public function reverse(): self {
        $this->str = strrev($this->str);
        return $this;
    }

    public function get(): string {
        return $this->str;
    }
}

$result = (new FluentString('мир'))->prepend('Привет, ')->append('!')->reverse()->get();
echo $result; // !рим ,тевирП
  

Особенность: цепочки удобны, но каждый вызов изменяет исходный объект. Если необходимо сохранить промежуточные состояния, стоит использовать неизменяемые (immutable) версии.

Вариант 3: Как работать со строками в UTF‑8 без потери символов?

Специализированный класс MbString всегда использует мультибайтовые функции и явно задаёт кодировку.


class MbString {
    private string $string;
    private string $encoding;

    public function __construct(string $string, string $encoding = 'UTF-8') {
        $this->string = $string;
        $this->encoding = $encoding;
    }

    public function length(): int {
        return mb_strlen($this->string, $this->encoding);
    }

    public function toLower(): self {
        $this->string = mb_strtolower($this->string, $this->encoding);
        return $this;
    }

    public function search(string $needle): int|false {
        return mb_strpos($this->string, $needle, 0, $this->encoding);
    }

    public function get(): string {
        return $this->string;
    }
}

$text = new MbString('Привет, мир!');
echo $text->length(); // 12
  

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

Вариант 4: Как организовать проверку и очистку строк?

Класс-валидатор предоставляет методы для проверки email, URL, удаления HTML-тегов, фильтрации спецсимволов и т.д.


class ValidatingString {
    private string $value;

    public function __construct(string $value) {
        $this->value = $value;
    }

    public function isEmail(): bool {
        return filter_var($this->value, FILTER_VALIDATE_EMAIL) !== false;
    }

    public function stripTags(): self {
        $this->value = strip_tags($this->value);
        return $this;
    }

    public function escapeHtml(): self {
        $this->value = htmlspecialchars($this->value, ENT_QUOTES, 'UTF-8');
        return $this;
    }

    public function get(): string {
        return $this->value;
    }
}

$input = new ValidatingString('<script>alert(1)</script>');
echo $input->stripTags()->escapeHtml()->get(); // &lt;script&gt;alert(1)&lt;/script&gt;
  

Назначение: защита от XSS и проверка формата ввода пользователя.

Расширенные примеры использования класса строк

Преобразование в camelCase и snake_case

Пример

class StringTransformer {
    private string $str;
    
    public function __construct(string $str) {
        $this->str = $str;
    }

    public function toCamelCase(): string {
        $parts = preg_split('/[\s_-]+/', $this->str);
        $parts = array_map(fn($part) => ucfirst($part), $parts);
        return lcfirst(implode('', $parts));
    }

    public function toSnakeCase(): string {
        $result = preg_replace('/([a-z])([A-Z])/', '$1_$2', $this->str);
        return mb_strtolower(preg_replace('/[\s-]+/', '_', $result), 'UTF-8');
    }

    public function get(): string {
        return $this->str;
    }
}

$t = new StringTransformer('hello world example');
echo $t->toCamelCase();  // helloWorldExample
$t2 = new StringTransformer('helloWorldExample');
echo $t2->toSnakeCase(); // hello_world_example
helloWorldExample
hello_world_example

Генерация случайных строк заданной длины

Пример

class RandomStringGenerator {
    private const CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    public static function generate(int $length = 16): string {
        $chars = self::CHARS;
        $max = strlen($chars) - 1;
        $result = '';
        for ($i = 0; $i < $length; $i++) {
            $result .= $chars[random_int(0, $max)];
        }
        return $result;
    }
}

echo RandomStringGenerator::generate(10);
A7kL9x2Q5v

Обрезка строки до заданного количества слов

Пример

class WordTruncator {
    public static function truncateWords(string $text, int $limit = 10, string $end = '...'): string {
        $words = preg_split('/\s+/', trim($text));
        if (count($words) <= $limit) {
            return $text;
        }
        return implode(' ', array_slice($words, 0, $limit)) . $end;
    }
}

$long = 'Один два три четыре пять шесть семь восемь девять десять одиннадцать';
echo WordTruncator::truncateWords($long, 4);
Один два три четыре...

Транслитерация кириллицы в латиницу (slug)

Пример

class Slugifier {
    private static array $map = [
        'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
        'е' => 'e', 'ё' => 'yo', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
        'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n',
        'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
        'у' => 'u', 'ф' => 'f', 'х' => 'kh', 'ц' => 'ts', 'ч' => 'ch',
        'ш' => 'sh', 'щ' => 'shch', 'ъ' => '', 'ы' => 'y', 'ь' => '',
        'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
    ];

    public static function toSlug(string $text): string {
        $text = mb_strtolower($text, 'UTF-8');
        $text = strtr($text, self::$map);
        $text = preg_replace('/[^a-z0-9\s-]/', '', $text);
        $text = preg_replace('/[\s]+/', '-', $text);
        $text = trim($text, '-');
        return $text;
    }
}

echo Slugifier::toSlug('Привет, мир! Как дела?');
privet-mir-kak-dela

Подсветка совпадений в строке (highlight)

Пример

class Highlighter {
    public static function highlight(string $text, string $term, string $tag = 'mark'): string {
        $escaped = preg_quote($term, '/');
        return preg_replace(
            '/(' . $escaped . ')/iu',
            '<' . $tag . ' class="fw-bold">$1',
            htmlspecialchars($text, ENT_QUOTES, 'UTF-8')
        );
    }
}

echo Highlighter::highlight('PHP – популярный язык программирования', 'PHP');
PHP – популярный язык программирования

Класс для работы со строками PHP - comments

En
Class string php (php)