Работа с объектами в языке PHP исчерпывающее руководство

Раздел: Разработка на PHP -> ООП

Объекты в PHP

Основной способ работы с объектами в PHP - это объявление класса и создание его экземпляра. Класс описывает свойства (данные) и методы (поведение). Для создания объекта используется ключевое слово new.

Пример базового класса и объекта:

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

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

    public function introduce(): string {
        return "Привет, меня зовут {$this->name}, мне {$this->age} лет.";
    }
}

$user = new User('Анна', 28);
echo $user->introduce();

Php class function (методы классов в php)

Привет, меня зовут Анна, мне 28 лет.

Php static (статические методы и свойства в php)

Типичные ошибки:

  • Обращение к несуществующему свойству вызывает фатальную ошибку, если не объявлено магическое __get.
  • При передаче объектов в функции они передаются по ссылке (на самом деле по идентификатору), что может приводить к неожиданным изменениям.
  • Отсутствие конструктора - приходится устанавливать свойства вручную, что нарушает инкапсуляцию.

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

Как создать простой объект без определения класса?

Если требуется хранить данные без методов, используют встроенный класс stdClass. Экземпляр создаётся через new stdClass() или приведение массива к объекту.

$obj = new stdClass();
$obj->name = 'Пётр';
$obj->age = 35;

// или через приведение:
$data = ['city' => 'Москва', 'country' => 'Россия'];
$obj2 = (object) $data;

var_dump($obj, $obj2);

Error class php (создание класса ошибки в php)

object(stdClass)#1 (2) {
  ["name"]=>
  string(10) "Пётр"
  ["age"]=>
  int(35)
}
object(stdClass)#2 (2) {
  ["city"]=>
  string(12) "Москва"
  ["country"]=>
  string(14) "Россия"
}

Php объекты (объекты в php)

Проблема: при обращении к несуществующему свойству объекта stdClass ошибки не будет, вернётся null, что может скрыть баги.

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

Анонимные классы позволяют объявить класс прямо в месте использования. Полезно для одноразовых реализаций или заглушек.

$logger = new class('log.txt') {
    private string $file;
    
    public function __construct(string $file) {
        $this->file = $file;
    }
    
    public function log(string $message): void {
        file_put_contents($this->file, $message . PHP_EOL, FILE_APPEND);
    }
};

$logger->log('Запуск скрипта');

Constructor php (конструкторы классов в php)

Ошибка: анонимные классы не могут быть сериализованы по умолчанию, их нельзя использовать с кэшем или сессиями без явной реализации Serializable.

Как создать объект, когда имя класса определяется динамически?

Используется ReflectionClass или просто переменная с именем класса.

$className = 'User';
$reflection = new ReflectionClass($className);
$user = $reflection->newInstance('Ольга', 30);

echo $user->introduce();
Привет, меня зовут Ольга, мне 30 лет.

Внимание: при динамическом создании без проверок можно получить исключение, если класса не существует. Всегда проверять class_exists() или ловить ReflectionException.

Как создать копию объекта, чтобы изменения не затронули оригинал?

По умолчанию копирование объекта происходит по ссылке (то же самое, что присваивание). Для глубокого копирования используется ключевое слово clone, а для тонкой настройки - магический метод __clone.

class Order {
    public array $items = [];
    
    public function __clone() {
        // копируем массив, иначе элементы останутся ссылками
        $this->items = array_map(fn($item) => clone $item, $this->items);
    }
}

$items = [new Item('book'), new Item('pen')];
$order1 = new Order();
$order1->items = $items;

$order2 = clone $order1;
$order2->items[0]->name = 'pencil';

echo $order1->items[0]->name; // 'book', а не 'pencil'
book

Частая ошибка: забыть реализовать __clone для вложенных объектов, тогда клонирование будет поверхностным.

Расширенные примеры работы с объектами

Readonly свойства и конструктор с promoted properties

Начиная с PHP 8.0, свойства можно объявлять как readonly (неизменяемые после инициализации). В PHP 8.1 появилась возможность объявлять их в конструкторе без дублирования.

Пример
class Product {
    public function __construct(
        private readonly string $id,
        private string $title,
        private float $price
    ) {}

    public function getPrice(): float {
        return $this->price;
    }

    public function applyDiscount(float $percent): void {
        $this->price *= (1 - $percent / 100);
    }
}

$prod = new Product('P001', 'Ноутбук', 75000);
$prod->applyDiscount(10);
echo $prod->getPrice(); // 67500

// Ошибка: нельзя изменить readonly свойство после конструктора
// $prod->id = 'P002'; // Fatal error
67500

Такой подход уменьшает шаблонный код и гарантирует неизменность идентификатора.

Цепочка вызовов методов (Fluent interface) с возвратом $this

Методы, возвращающие $this, позволяют вызывать их последовательно. Это удобно для настройки объекта.

Пример
class QueryBuilder {
    private array $select = [];
    private string $table = '';
    private array $where = [];

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

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

    public function where(string $condition): self {
        $this->where[] = $condition;
        return $this;
    }

    public function build(): string {
        $select = empty($this->select) ? '*' : implode(', ', $this->select);
        $sql = "SELECT $select FROM {$this->table}";
        if (!empty($this->where)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->where);
        }
        return $sql;
    }
}

$query = (new QueryBuilder())
    ->select('name', 'email')
    ->from('users')
    ->where('age > 18')
    ->where('status = "active"')
    ->build();

echo $query;
SELECT name, email FROM users WHERE age > 18 AND status = "active"

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

Сериализация и клонирование сложных объектов

PHP предоставляет магические методы __sleep и __wakeup для управления сериализацией. Покажем на примере объекта с ресурсом (файловый дескриптор).

Пример
class FileLogger {
    private $handle;
    private string $filename;

    public function __construct(string $filename) {
        $this->filename = $filename;
        $this->handle = fopen($filename, 'a');
    }

    public function log(string $msg): void {
        fwrite($this->handle, date('Y-m-d H:i:s') . ' ' . $msg . "\n");
    }

    public function __sleep(): array {
        // Сохраняем только имя файла, ресурс сериализовать нельзя
        return ['filename'];
    }

    public function __wakeup(): void {
        // Восстанавливаем дескриптор
        $this->handle = fopen($this->filename, 'a');
    }

    public function __destruct() {
        fclose($this->handle);
    }
}

$logger = new FileLogger('app.log');
$logger->log('Старт');

$serialized = serialize($logger);
$restored = unserialize($serialized);
$restored->log('После восстановления');

// Результат: обе строки будут в файле app.log
(нет вывода, но файл app.log содержит две строки)

Типичная ошибка: забыть закрыть старый дескриптор перед деструктором или попытаться сериализовать ресурс. Метод __sleep должен исключать свойства-ресурсы.

Использование интерфейсов для полиморфизма объектов

Объект может реализовывать интерфейс, что гарантирует наличие определённых методов. Пример работы с разными платёжными системами.

Пример
interface PaymentInterface {
    public function pay(float $amount): string;
}

class PayPal implements PaymentInterface {
    public function pay(float $amount): string {
        return "Оплата $amount через PayPal (комиссия 2%)";
    }
}

class Stripe implements PaymentInterface {
    public function pay(float $amount): string {
        return "Оплата $amount через Stripe (комиссия 3%)";
    }
}

function processPayment(PaymentInterface $paymentMethod, float $total): void {
    echo $paymentMethod->pay($total) . "\n";
}

$payments = [new PayPal(), new Stripe()];
foreach ($payments as $p) {
    processPayment($p, 1000);
}
Оплата 1000 через PayPal (комиссия 2%)
Оплата 1000 через Stripe (комиссия 3%)

Это позволяет добавлять новые способы оплаты без изменения вызывающего кода.

Объект с типизированными свойствами и автоматической валидацией

В PHP 8.2 появилась поддержка типов true, false и null (union types). Использование перечислений (enum) тоже даёт строгую типизацию.

Пример
enum OrderStatus: string {
    case New = 'new';
    case Paid = 'paid';
    case Shipped = 'shipped';
    case Cancelled = 'cancelled';
}

class OrderAdvanced {
    public function __construct(
        public string $id,
        public float $total,
        public OrderStatus $status = OrderStatus::New,
        public ?string $note = null
    ) {}

    public function ship(): void {
        if ($this->status !== OrderStatus::Paid) {
            throw new \LogicException('Только оплаченный заказ можно отправить');
        }
        $this->status = OrderStatus::Shipped;
    }
}

$order = new OrderAdvanced('O_001', 2500.00);
echo $order->status->value; // 'new'
$order->note = 'Срочная доставка';

// Попытка отправить неоплаченный заказ вызовет исключение
// $order->ship(); // LogicException
new

Типизация свойств помогает избежать некорректных состояний объекта. Перечисления делают код самодокументируемым.

Объекты в PHP - comments

En
Php объекты (php)