Trong WordPress, hook save_post được gọi mỗi khi bạn lưu hoặc cập nhật một bài viết (post, page, custom post type). Tuy nhiên, trong thực tế, có nhiều tình huống làm hàm của bạn bị gọi nhiều lần không mong muốn, ví dụ:

  • Khi plugin khác gọi lại wp_update_post().
  • Khi bạn lưu nhiều bài viết trong cùng một request (bulk edit).
  • Khi có autosave hoặc revision.

Nếu bạn muốn đảm bảo mỗi ID bài viết chỉ được xử lý đúng một lần trong mỗi request, bạn cần sử dụng cơ chế "flag" để đánh dấu.


1. Dùng static array để lưu trạng thái

Cách đơn giản nhất là khai báo một mảng tĩnh trong function, lưu lại các ID đã xử lý.

function trigger_post_processing_after_manual_edit($post_ID, $post, $update) {
    static $processed = [];

    // Nếu ID này đã được xử lý rồi thì bỏ qua
    if (isset($processed[$post_ID])) {
        return;
    }

    // Đánh dấu ID này đã xử lý
    $processed[$post_ID] = true;

    // ... Code xử lý thật sự ở đây
}
add_action('save_post', 'trigger_post_processing_after_manual_edit', 999, 3);

🔹 Ưu điểm: nhanh, gọn, không phụ thuộc thêm gì.
🔹 Nhược điểm: chỉ hiệu quả trong một request (thường là đủ cho hầu hết use case).


2. Dùng did_action() với tên hook động

Một cách khác là tận dụng hệ thống hook có sẵn của WordPress.
Thay vì chỉ gọi did_action('tên_hook_cố_định'), bạn thêm $post_ID vào tên hook → tạo flag riêng cho từng ID.

function trigger_post_processing_after_manual_edit($post_ID, $post, $update) {
    // Nếu ID này đã được xử lý rồi thì bỏ qua
    if (did_action('trigger_post_processing_after_manual_edit_run_' . $post_ID)) {
        return;
    }

    // Đánh dấu ID này đã xử lý
    do_action('trigger_post_processing_after_manual_edit_run_' . $post_ID);

    // ... Code xử lý thật sự ở đây
}
add_action('save_post', 'trigger_post_processing_after_manual_edit', 999, 3);

🔹 Ưu điểm: tận dụng WordPress core, dễ đọc.
🔹 Nhược điểm: hơi dài dòng hơn một chút so với static array.


3. Kết luận

  • Nếu bạn chỉ cần chặn lặp trong cùng một request → dùng static array là gọn nhất.
  • Nếu bạn muốn tận dụng hệ thống hook và dễ debug hơn → dùng did_action() với tên động.

👉 Cả hai cách đều đảm bảo: mỗi bài viết (post_ID) chỉ được xử lý một lần duy nhất trong mỗi lần lưu.