0

Từ khóa clone trong PHP

Từ khóa clone trong PHP là công cụ sao chép đối tượng theo cơ chế tạo bản sao độc lập (shallow copy), giúp giải quyết triệt để bài toán tham chiếu đối tượng (pass by reference) trong các framework như Laravel hay Symfony.

Bản chất cơ chế Object trong PHP

Trong PHP, khi bạn gán một object cho một biến khác ($b = $a), PHP không tạo ra vùng nhớ mới. Cả hai biến chỉ cùng trỏ đến một định danh đối tượng (object identifier).

PHP

$user1 = new stdClass();
$user1->name = 'An';

$user2 = $user1;        // Cùng trỏ tới 1 ô nhớ
$user2->name = 'Bình';

echo $user1->name;      // Kết quả: 'Bình' (user1 bị thay đổi ngoài ý muốn)

Để tách biệt hoàn toàn hai thực thể, từ khóa clone ra đời:

PHP

$user2 = clone $user1;  // Tạo bản sao mới độc lập
$user2->name = 'Bình';

echo $user1->name;      // Kết quả: 'An' (user1 giữ nguyên giá trị)

Ứng dụng thực chiến: Tối ưu Query Builder (Laravel/Doctrine)

Tình huống phổ biến nhất khi viết backend là cần đếm tổng số lượng bản ghi trước khi phân trang (pagination) hoặc xuất dữ liệu theo các định dạng khác nhau.

PHP

// 1. Định nghĩa bộ lọc tìm kiếm phức tạp
$query = Product::query()
    ->where('category_id', 5)
    ->where('is_published', true)
    ->orderBy('created_at', 'desc');

// 2. Đếm tổng số kết quả mà không làm biến dạng $query
$totalRecords = (clone $query)->count();

// 3. Sử dụng $query nguyên bản để lấy 15 sản phẩm của trang hiện tại
$products = $query->offset(0)->limit(15)->get();

Nếu không dùng clone, câu lệnh count() có thể can thiệp vào các thuộc tính nội bộ của Query Builder (ví dụ: mệnh đề SELECT, các logic HAVING hay GROUP BY), dẫn đến câu lệnh get() phía sau bị chạy sai logic hoặc sinh ra cú pháp SQL không hợp lệ.

Phân biệt Shallow Copy và Deep Copy

Khi dùng clone, PHP chỉ thực hiện Shallow Copy (sao chép nông). Nếu đối tượng chứa thuộc tính là một đối tượng khác, thuộc tính con đó vẫn bị chia sẻ tham chiếu.

  • Vấn đề của Shallow Copy:

PHP

class Order {
    public $details;
}

$order1 = new Order();
$order1->details = new stdClass();
$order1->details->item = 'Laptop';

$order2 = clone $order1;
$order2->details->item = 'Phone';

echo $order1->details->item; // Kết quả: 'Phone' (thuộc tính lồng nhau vẫn trỏ chung)

  • Giải pháp Deep Copy với __clone(): PHP cung cấp phương thức ma thuật (magic method) __clone() tự động kích hoạt ngay sau khi thao tác clone diễn ra:

PHP

class Order {
    public $details;

    public function __clone() {
        // Sao chép sâu đối tượng lồng bên trong
        $this->details = clone $this->details;
    }
}

$order2 = clone $order1;
$order2->details->item = 'Phone';

echo $order1->details->item; // Kết quả: 'Laptop' (đã an toàn tuyệt đối)

Khi nào nên dùng clone?

  • Xử lý Query/Pipeline: Giữ nguyên trạng thái cơ sở của một luồng xử lý trước khi rẽ nhánh tính toán (như count, aggregate, sub-query).

  • Áp dụng Prototype Pattern: Khi chi phí khởi tạo một object mới thông qua new và nạp dữ liệu từ database quá nặng, việc clone một mẫu chuẩn có sẵn sẽ tiết kiệm tài nguyên đáng kể.

  • Bảo toàn DTO (Data Transfer Object): Cần thay đổi dữ liệu tạm thời để hiển thị hoặc xử lý một tác vụ phụ mà không muốn làm dơ (dirty) dữ liệu gốc.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí