+1

PHP Return Type Declaration: Bản hợp đồng cam kết cho dữ liệu đầu ra

Nếu Type Hinting là "cửa vào" thì Return Type chính là "cửa ra". Việc kiểm soát chặt chẽ đầu ra sẽ giúp code của bạn trở nên cực kỳ tiên đoán được (predictable) và giảm thiểu tối đa các lỗi logic tiềm ẩn.

1. Tại sao phải quan tâm đến Return Type?

Hãy tưởng tượng bạn đang gọi một hàm xử lý thanh toán. Bạn kỳ vọng hàm đó trả về một boolean (true/false) để biết thành công hay thất bại. Nhưng vì một lỗi logic nào đó, hàm lại trả về null hoặc một string chứa thông báo lỗi.

Kết quả? Câu lệnh if ($payment->process()) của bạn có thể chạy sai hướng, hoặc tệ hơn là gây lỗi ở các bước xử lý sau đó.

Return Type Declaration sinh ra để bắt buộc hàm phải trả về đúng "lời hứa" của nó.

2. Các cấp độ của Return Type

Cấp độ cơ bản: Scalar Types & Void Từ PHP 7.0, chúng ta đã có thể khai báo các kiểu cơ bản: string, int, float, bool, array, callable.

// Hàm tính tổng nhất định phải trả về số nguyên
public function sum(int $a, int $b): int {
    return $a + $b;
}

// Hàm thực thi tác vụ và không trả về gì cả
public function logActivity(string $message): void {
    // Ghi log vào database...
}

Cấp độ nâng cao: Class & Interface

Giống như Type Hinting, bạn có thể ép buộc hàm trả về một Instance của một Class cụ thể. Điều này cực kỳ hữu ích trong Factory Pattern hoặc Service Layer.

interface PaymentResponse {
    public function getStatus(): string;
}

class SuccessResponse implements PaymentResponse {
    public function getStatus(): string { return 'success'; }
}

class PaymentService {
    // Cam kết luôn trả về một đối tượng thực thi Interface PaymentResponse
    public function createResponse(): PaymentResponse {
        return new SuccessResponse();
    }
}

3. Những "vũ khí" mới từ PHP 8.x

Đây là phần sẽ làm bài viết của Hiếu trên Viblo trở nên "Pro" hơn vì cập nhật những kiến thức mới nhất:

Nullable Types (?) Khi một hàm có thể trả về dữ liệu hoặc không có gì (null).

public function findUser(int $id): ?User {
    return User::find($id); // Có thể trả về User object hoặc null
}

Union Types (PHP 8.0) Hàm có thể trả về một trong nhiều kiểu dữ liệu khác nhau.

public function getIdentifier(): int | string {
    return $this->useUuid ? "abc-123" : 456;
}

Kiểu staticself Rất quan trọng trong các Fluent Interface (như Builder Pattern).

class QueryBuilder {
    public function select(array $columns): static {
        $this->columns = $columns;
        return $this; // Trả về chính instance của class đang gọi
    }
}

Kiểu never (PHP 8.1) Đây là kiểu trả về đặc biệt, dùng cho các hàm chắc chắn sẽ dừng chương trình hoặc quăng Exception (không bao giờ kết thúc bình thường).

public function redirectAndExit(): never {
    header('Location: /login');
    exit();
}

4. Kinh nghiệm "xương máu": Khi nào dùng cái nào?

Trong quá trình làm các dự án Backend thực tế, mình rút ra vài quy tắc cho anh em:

  1. Ưu tiên void thay vì mặc định: Nếu hàm không cần trả về gì, hãy ghi rõ : void. Nó giúp người đọc code sau này không phải đi tìm lệnh return trong một hàm dài 50 dòng.
  2. Hạn chế mixed: Kiểu mixed (trả về cái gì cũng được) là kẻ thù của Clean Code. Nếu phải dùng mixed, hãy xem xét lại liệu hàm của bạn có đang làm quá nhiều việc hay không (vi phạm Single Responsibility).
  3. Sức mạnh của Interface: Luôn ưu tiên trả về một Interface thay vì một Concrete Class. Điều này giúp hệ thống của bạn linh hoạt hơn khi cần thay đổi logic bên dưới mà không làm hỏng code ở tầng gọi (Consumer).

5. Kết hợp: Type Hinting + Return Type = Hoàn hảo

Khi kết hợp cả hai, code của Hiếu sẽ giống như một cỗ máy được bôi trơn hoàn hảo:

class OrderProcessor {
    public function process(Order $order, ProcessorInterface $processor): ProcessResult {
        // Logic xử lý...
        return new ProcessResult(true);
    }
}

Nhìn vào dòng khai báo này, bất kỳ ai cũng biết:

  • Cần đưa vào cái gì (Order, ProcessorInterface).
  • Nhận lại được cái gì (ProcessResult).
  • Không cần đọc log, không cần mò code, IDE tự gợi ý 100%.

Lời kết

Việc sử dụng Return Type Declaration không chỉ giúp PHP Engine tối ưu hiệu suất hơn mà còn là cách để chúng ta khẳng định sự chuyên nghiệp trong từng dòng code. Đừng để code của bạn là một ẩn số, hãy biến nó thành một bản hợp đồng rõ ràng.

Hiếu thấy phần này thế nào? Nếu muốn "hạng nặng" hơn nữa, mình có thể lồng ghép thêm phần Variance (Covariance & Contravariance) - một chủ đề cực khó trong OOP PHP để tạo điểm nhấn cho bài viết nhé!


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í