Created
July 24, 2025 23:43
-
-
Save scrubmx/db685f091ce9104b0f0092ef2f4b3a03 to your computer and use it in GitHub Desktop.
WordPress Wehook Request When Post is Published
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Utility class for handling web-hook requests. | |
* It provides a method to send POST requests to a specified URL with retries and error handling. | |
*/ | |
class WP_Webhook_Request | |
{ | |
protected const RETRY_COUNT_LIMIT = 3; | |
protected const RETRY_DELAY_SECONDS = 2; | |
protected const RETRY_MAX_DELAY_SECONDS = 8; | |
protected const REQUEST_TIMEOUT_SECONDS = 5; | |
private const WEBHOOK_ENDPOINT_URL = 'https://example.com/webhook'; | |
private const WEBHOOK_SIGNATURE_SECRET = SECURE_AUTH_KEY; | |
/** | |
* Posts a web-hook request with the given post data. | |
* It encodes the post data as JSON, sets the necessary headers, and retries the request if it fails. | |
*/ | |
public static function post($post) | |
{ | |
$json = wp_json_encode([ | |
'event' => 'post_published', | |
'data' => [ | |
'post' => [ | |
'id' => $post->ID, | |
'type' => $post->post_type, | |
'status' => $post->post_status, | |
'title' => get_the_title($post), | |
'excerpt' => get_the_excerpt($post), | |
'permalink' => get_permalink($post), | |
'published_at' => get_post_time('c', true, $post), | |
'images' => [ | |
'thumbnail' => get_the_post_thumbnail_url($post, 'thumbnail'), | |
'medium' => get_the_post_thumbnail_url($post, 'medium'), | |
'large' => get_the_post_thumbnail_url($post, 'large'), | |
'full' => get_the_post_thumbnail_url($post, 'full'), | |
], | |
], | |
], | |
]); | |
if (! $json) { | |
error_log('Webhook Error: Failed to encode data'); | |
return false; | |
} | |
$idempotency_key = hash('sha256', $post->ID); | |
return static::postWithRetries($json, $idempotency_key); | |
} | |
/** | |
* Posts a web-hook request with retries. | |
* It sends a POST request to the web-hook URL with the provided JSON data and idempotency key. | |
* If the request fails, it retries up to a specified limit with exponential back-off. | |
* | |
* @param string $json The JSON-encoded data to be sent in the request body. | |
* @param string $idempotency_key The idempotency key to be used in the request headers. | |
* @param integer $attempt_count The current attempt count for retries. | |
* @return boolean True if the request was successful, false otherwise. | |
*/ | |
protected static function postWithRetries($json, $idempotency_key, $attempt_count = 0) | |
{ | |
$success = false; | |
$response = wp_remote_post(static::WEBHOOK_ENDPOINT_URL, [ | |
'body' => $json, | |
'data_format' => 'body', | |
'timeout' => static::REQUEST_TIMEOUT_SECONDS, | |
'headers' => [ | |
'Content-Type' => 'application/json', | |
'X-Webhook-Event' => 'post_published', | |
'X-Webhook-Timestamp' => gmdate('c'), | |
'X-Idempotency-Key' => $idempotency_key, | |
'X-Signature' => hash_hmac('sha256', $json, static::WEBHOOK_SIGNATURE_SECRET), | |
], | |
]); | |
if (! is_wp_error($response)) { | |
$code = (int) wp_remote_retrieve_response_code($response); | |
$success = (bool) $code >= 200 && $code < 300; | |
} | |
// If the request was successful, we're done | |
if ($success) return true; | |
// If we haven't reached the maximum number of retries, retry the request | |
if ($attempt_count < static::RETRY_COUNT_LIMIT) { | |
$attempt_count += 1; | |
// Sleep with exponential back off and a maximum delay of 8 seconds | |
sleep( | |
min(pow(static::RETRY_DELAY_SECONDS, $attempt_count), static::RETRY_MAX_DELAY_SECONDS) | |
); | |
return static::postWithRetries($json, $idempotency_key, $attempt_count); | |
} | |
// If we reach here, all attempts failed | |
if (is_wp_error($response)) { | |
error_log('Webhook Error (after ' . static::RETRY_COUNT_LIMIT . ' retries): ' . $response->get_error_message()); | |
} else { | |
$code = is_array($response) ? wp_remote_retrieve_response_code($response) : 'N/A'; | |
$body = is_array($response) ? wp_remote_retrieve_body($response) : ''; | |
error_log('Webhook Error (after ' . static::RETRY_COUNT_LIMIT . " retries):\r\nHTTP Status Code: $code\r\nResponse Body: $body"); | |
} | |
return false; | |
} | |
} | |
/** | |
* Custom post type for Anuncios (Notices). | |
*/ | |
class WP_Anuncios | |
{ | |
public const SLUG = 'anuncios'; | |
public const INFO_MESSAGE = "Cuando publiques un Anuncio, todos los usuarios recibirán un correo electrónico notificándoles sobre el nuevo anuncio."; | |
/** | |
* Initialize the Anuncios post type and its related functionality. | |
*/ | |
public static function init() | |
{ | |
add_theme_support('post-thumbnails'); | |
WP_Anuncios::actionRegisterPostType(); | |
WP_Anuncios::actionDisableComments(); | |
WP_Anuncios::actionAdminInfoMessage(); | |
WP_Anuncios::actionBlockEditorInfoMessage(); | |
WP_Anuncios::actionPublishAnuncios(); | |
WP_Anuncios::actionLoadTranslations(); | |
} | |
/** | |
* Check if the current screen is the Anuncios post type. | |
*/ | |
public static function isActiveScreen() | |
{ | |
/** @var WP_Screen|null $screen */ | |
$screen = get_current_screen(); | |
return isset($screen->post_type) && $screen->post_type === WP_Anuncios::SLUG; | |
} | |
/** | |
* Check if the given post is of the Anuncios post type. | |
*/ | |
public static function isPostType($post) | |
{ | |
return isset($post->post_type) && $post->post_type === WP_Anuncios::SLUG; | |
} | |
/* | |
|-------------------------------------------------------------------------- | |
| WordPress Register Post Type & Disable Comments | |
|-------------------------------------------------------------------------- | |
*/ | |
/** | |
* Create custom post type for notices. | |
*/ | |
public static function actionRegisterPostType() | |
{ | |
/** | |
* Create custom post type for notices. | |
*/ | |
add_action('init', function () { | |
register_post_type(WP_Anuncios::SLUG, [ | |
'public' => true, | |
'show_in_menu' => true, | |
'has_archive' => true, | |
'show_in_rest' => true, | |
'publicly_queryable' => true, | |
'capability_type' => 'post', | |
'menu_icon' => 'dashicons-megaphone', | |
'rewrite' => ['slug' => WP_Anuncios::SLUG], | |
'supports' => ['title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'], | |
'labels' => [ | |
'name' => _x('Anuncios', 'Post type general name', 'zafiro-child'), | |
'singular_name' => _x('Anuncio', 'Post type singular name', 'zafiro-child'), | |
'menu_name' => _x('Anuncios', 'Admin Menu text', 'zafiro-child'), | |
'name_admin_bar' => _x('Anuncio', 'Add New on Toolbar', 'zafiro-child'), | |
'add_new' => __('Agregar nuevo', 'zafiro-child'), | |
'add_new_item' => __('Agregar nuevo anuncio', 'zafiro-child'), | |
'new_item' => __('Nuevo anuncio', 'zafiro-child'), | |
'edit_item' => __('Editar anuncio', 'zafiro-child'), | |
'view_item' => __('Ver anuncio', 'zafiro-child'), | |
'all_items' => __('Todos los anuncios', 'zafiro-child'), | |
'search_items' => __('Buscar anuncios', 'zafiro-child'), | |
'not_found' => __('No se encontraron anuncios.', 'zafiro-child'), | |
'not_found_in_trash' => __('No hay anuncios en la papelera.', 'zafiro-child'), | |
'featured_image' => __('Imagen destacada', 'zafiro-child'), | |
'set_featured_image' => __('Establecer imagen destacada', 'zafiro-child'), | |
'remove_featured_image' => __('Eliminar imagen destacada', 'zafiro-child'), | |
'use_featured_image' => __('Usar como imagen destacada', 'zafiro-child'), | |
'archives' => __('Archivo de anuncios', 'zafiro-child'), | |
'insert_into_item' => __('Insertar en el anuncio', 'zafiro-child'), | |
'uploaded_to_this_item' => __('Subido a este anuncio', 'zafiro-child'), | |
'filter_items_list' => __('Filtrar lista de anuncios', 'zafiro-child'), | |
'items_list_navigation' => __('Navegación de la lista de anuncios', 'zafiro-child'), | |
'items_list' => __('Lista de anuncios', 'zafiro-child'), | |
], | |
]); | |
}); | |
} | |
/** | |
* Disable comments for the Anuncios post type. | |
* This function modifies the comments behavior, hides the comments meta-box in the admin area, | |
* and removes the comments column from the admin index for the Anuncios post type. | |
*/ | |
public static function actionDisableComments() | |
{ | |
// Explicitly close comments for the post type | |
add_filter('comments_open', function ($open, $post_id) { | |
/** @var WP_Post|null $post */ | |
$post = get_post($post_id); | |
return WP_Anuncios::isPostType($post) ? false : $open; | |
}, 10, 2); | |
add_filter('pings_open', function ($open, $post_id) { | |
/** @var WP_Post|null $post */ | |
$post = get_post($post_id); | |
return WP_Anuncios::isPostType($post) ? false : $open; | |
}, 10, 2); | |
// Hide comments meta-box for the post type | |
add_action('admin_menu', function () { | |
remove_meta_box('commentsdiv', WP_Anuncios::SLUG, 'normal'); | |
remove_meta_box('commentstatusdiv', WP_Anuncios::SLUG, 'normal'); | |
}); | |
// Hide comments column in admin index for the post type | |
add_filter('manage_' . WP_Anuncios::SLUG . '_posts_columns', function ($columns) { | |
if (isset($columns['comments'])) { | |
unset($columns['comments']); | |
} | |
return $columns; | |
}); | |
} | |
/* | |
|-------------------------------------------------------------------------- | |
| WordPress Display Info Message to Admins | |
|-------------------------------------------------------------------------- | |
*/ | |
/** | |
* Add custom info notice on classic admin pages. | |
*/ | |
public static function actionAdminInfoMessage() | |
{ | |
add_action('admin_notices', function () { | |
if (! WP_Anuncios::isActiveScreen()) return; | |
$message = esc_html(WP_Anuncios::INFO_MESSAGE); | |
echo <<<HTML | |
<div class="notice notice-info"><p><strong>Importante:</strong> $message</p></div> | |
HTML; | |
}); | |
} | |
/** | |
* Add custom info notice on block editor admin pages. | |
*/ | |
public static function actionBlockEditorInfoMessage() | |
{ | |
add_action('enqueue_block_editor_assets', function () { | |
wp_enqueue_script('wp-notices'); | |
if (! WP_Anuncios::isActiveScreen()) return; | |
$message = esc_js(strip_tags(WP_Anuncios::INFO_MESSAGE)); | |
wp_add_inline_script('wp-notices', <<<JS | |
window.wp && wp.domReady && wp.domReady(function() { | |
wp.data | |
.dispatch('core/notices') | |
.createNotice('info', 'Importante: $message', { isDismissible: false }); | |
}); | |
JS); | |
}); | |
} | |
/* | |
|-------------------------------------------------------------------------- | |
| WordPress Bootstrap Web-Hook to Send Emails on Post Publish | |
|-------------------------------------------------------------------------- | |
*/ | |
/** | |
* Call email web-hook when a new post is published. | |
*/ | |
public static function actionPublishAnuncios() | |
{ | |
add_action('publish_' . WP_Anuncios::SLUG, function ($post_id) { | |
/** @var WP_Post|null $post */ | |
$post = get_post($post_id); | |
if (! isset($post->post_status) || $post->post_status !== 'publish') return; | |
WP_Webhook_Request::post($post); | |
}); | |
} | |
/* | |
|-------------------------------------------------------------------------- | |
| WordPress Add Translations for the Anuncios Post Type | |
|-------------------------------------------------------------------------- | |
*/ | |
/** | |
* Load translations for the Anuncios post type. | |
*/ | |
public static function actionLoadTranslations() | |
{ | |
add_action('after_setup_theme', function () { | |
load_theme_textdomain('zafiro-child', get_stylesheet_directory() . '/languages'); | |
}); | |
} | |
} | |
WP_Anuncios::init(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This implements a custom post-type
The webhook request has a naive implementation of retries with exponential backoff
The headers include a signature for validation and idempotency key enable handling duplicated requests