Объектно-ориентированное программирование: примеры классов и их использование в PHP

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

Основные подходы к созданию классов

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

Как спроектировать класс с надёжной инкапсуляцией данных?

Наиболее эффективный способ - использовать приватные свойства и публичные методы доступа (геттеры/сеттеры) с валидацией. Это гарантирует, что объект всегда находится в корректном состоянии.


class User {
    private string $name;
    private int $age;

    public function __construct(string $name, int $age) {
        $this->setName($name);
        $this->setAge($age);
    }

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

    public function setName(string $name): void {
        if ($name === '') {
            throw new InvalidArgumentException('Name cannot be empty');
        }
        $this->name = $name;
    }

    public function getAge(): int {
        return $this->age;
    }

    public function setAge(int $age): void {
        if ($age < 0 || $age > 150) {
            throw new InvalidArgumentException('Age must be between 0 and 150');
        }
        $this->age = $age;
    }
}

$user = new User('Alice', 30);
echo $user->getName(); // Alice
    

классы в php файлы (определение классов в php-файлах)

Типичные проблемы и их решения: при большом количестве свойств написание геттеров и сеттеров становится утомительным. Решение - использовать IDE для автоматической генерации или перейти на readonly свойства, если мутация не требуется. Также можно применить магические методы, но это снижает типобезопасность.

Когда можно пожертвовать инкапсуляцией для ускорения разработки?

Если данные не требуют валидации (например, конфигурация), допустимо использовать публичные свойства. Это упрощает код и ускоряет написание, но нарушает инварианты.


class User {
    public string $name;
    public int $age;
}

$user = new User();
$user->name = 'Bob';
$user->age = 25;
    

Php interface файл (файл с php-интерфейсом (interface))

Ошибки: при изменении схемы данных придётся менять все места, где напрямую присваиваются значения. Решение - использовать публичные свойства только для DTO без логики, либо комбинировать с геттерами в будущем.

Как создать неизменяемый объект (DTO)?

Readonly свойства (PHP 8.1) позволяют задать значение один раз в конструкторе и запрещают последующие изменения. Подходит для передачи данных без побочных эффектов.


class User {
    public function __construct(
        public readonly string $name,
        public readonly int $age
    ) {}
}

$user = new User('Charlie', 40);
// $user->name = 'Dave'; // Fatal error
    

Php примеры классов (примеры классов в php)

Проблема: попытка изменить свойство после создания приводит к фатальной ошибке. Следует заранее проектировать объект как неизменяемый, если это соответствует бизнес-логике.

Как обеспечить гибкий доступ к свойствам без явного объявления каждого?

Магические методы __get и __set позволяют перехватывать чтение/запись необъявленных свойств. Это полезно для обёрток (прокси), но снижает производительность и типобезопасность.


class User {
    private array $data = [];

    public function __get(string $key): mixed {
        return $this->data[$key] ?? null;
    }

    public function __set(string $key, mixed $value): void {
        $this->data[$key] = $value;
    }
}

$user = new User();
$user->name = 'Diana';
echo $user->name; // Diana
    

Php get set (геттеры и сеттеры в php)

Ошибки: IDE не подсказывает свойства, легко ошибиться в имени. Решение - документировать динамические свойства через PHPDoc @property.

Как создать иерархию классов с общим интерфейсом?

Наследование и интерфейсы позволяют строить полиморфные структуры. Абстрактные классы задают базовую функциональность, а интерфейсы - контракты.


interface Animal {
    public function makeSound(): string;
}

abstract class Mammal implements Animal {
    abstract public function makeSound(): string;

    public function breathe(): string {
        return 'Breathing';
    }
}

class Dog extends Mammal {
    public function makeSound(): string {
        return 'Woof!';
    }
}

$dog = new Dog();
echo $dog->makeSound(); // Woof!
    
Типичные ошибки: неправильное использование наследования (is-a) ведёт к хрупкой иерархии. Лучше применять композицию или интерфейсы, избегая наследования ради наследования.

Расширенные примеры классов

Класс с магическим методом __invoke

Позволяет использовать объект как функцию. Удобно для callback-замыканий.

Пример

class Multiplier {
    private int $factor;

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

    public function __invoke(int $value): int {
        return $value * $this->factor;
    }
}

$double = new Multiplier(2);
echo $double(5); // 10

Класс-одиночка (Singleton)

Гарантирует единственный экземпляр класса. Часто используется для подключения к базе данных.

Пример

class Database {
    private static ?self $instance = null;
    private PDO $pdo;

    private function __construct() {
        $this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    }

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getPdo(): PDO {
        return $this->pdo;
    }
}

$db = Database::getInstance();

Класс, реализующий Iterator для обхода коллекции

Позволяет использовать объект в цикле foreach.

Пример

class NumberCollection implements Iterator {
    private array $numbers;
    private int $position = 0;

    public function __construct(array $numbers) {
        $this->numbers = array_values($numbers);
    }

    public function current(): mixed {
        return $this->numbers[$this->position];
    }

    public function key(): int {
        return $this->position;
    }

    public function next(): void {
        $this->position++;
    }

    public function rewind(): void {
        $this->position = 0;
    }

    public function valid(): bool {
        return isset($this->numbers[$this->position]);
    }
}

$collection = new NumberCollection([10, 20, 30]);
foreach ($collection as $value) {
    echo $value . ' '; // 10 20 30
}

Класс с трейтом и изменением приоритета метода

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

Пример

trait Logger {
    public function log(string $msg): void {
        echo "[LOG] $msg\n";
    }
}

trait DebugLogger {
    public function log(string $msg): void {
        echo "[DEBUG] $msg\n";
    }
}

class Application {
    use Logger, DebugLogger {
        DebugLogger::log insteadof Logger;
    }
}

$app = new Application();
$app->log('test'); // [DEBUG] test

Класс с конструктором, использующим именованные аргументы (PHP 8+)

Именованные аргументы улучшают читаемость, особенно при большом количестве параметров.

Пример

class User {
    public function __construct(
        private string $name,
        private int $age,
        private string $email = ''
    ) {}
}

$user = new User(age: 25, name: 'Eve', email: 'eve@example.com');
var_dump($user);
object(User)#1 (3) {
  ["name":"User":private] => string(3) "Eve"
  ["age":"User":private] => int(25)
  ["email":"User":private] => string(17) "eve@example.com"
}

Класс с union types и mixed (PHP 8+)

Типизация аргументов и возвращаемых значений становится более гибкой.

Пример

class Converter {
    public function convert(int|string $value): int|string {
        if (is_string($value)) {
            return strlen($value);
        }
        return (string) $value;
    }
}

$conv = new Converter();
echo $conv->convert('hello') . ' '; // 5
echo $conv->convert(42); // 42 (string)
5 42

Примеры классов в PHP - comments

En
Php примеры классов (php)