解决Woocommerce woocommerce_email_footer钩子递归与实例丢失问题
解决WooCommerce邮件内容捕获的递归与漏触发问题
我明白你遇到的困境:用woocommerce_email_footer钩子捕获邮件内容时,调用$email->get_content()会触发无限递归导致内存错误,而临时移除钩子又可能让其他并发的邮件请求错过你的自定义逻辑。下面给你两个更优的解决方案,完全满足你的需求:
方案1:用静态变量阻止递归(兼容现有钩子)
如果想继续使用woocommerce_email_footer钩子,可以通过静态变量标记当前是否正在处理函数,避免递归调用,同时无需移除钩子,也就不会有漏触发的风险:
// Capture the contents of the Emails sent and save into local files // These Local files are used for further messaging through different channels function Save_Email_Contents_into_Local_File( $email ) { // 静态变量标记是否正在处理,防止递归 static $is_processing = false; // 如果已经在处理中,直接返回,避免递归 if ( $is_processing ) { return; } // 定义需要捕获的邮件ID列表,后续扩展只需添加新ID $target_email_ids = [ 'customer_processing_order', 'customer_completed_order', 'customer_on_hold_order' ]; if ( in_array( $email->id, $target_email_ids ) ) { $is_processing = true; // 给文件名加上邮件ID,区分不同场景的邮件 $target_filename = '/home/users/....../Sent_Mail_' . $email->id . '.html'; $html_message = $email->get_content(); $formatted_message = $email->style_inline( $html_message ); file_put_contents( $target_filename, $formatted_message ); $is_processing = false; } }; add_action( 'woocommerce_email_footer', 'Save_Email_Contents_into_Local_File', 20, 1 );
原理说明
静态变量$is_processing在函数首次执行时设为true,当$email->get_content()渲染模板触发woocommerce_email_footer钩子再次调用该函数时,检测到$is_processing为true就直接返回,中断递归。处理完成后再将其重置为false,不影响后续其他邮件请求。
方案2:使用更合适的钩子(推荐)
WooCommerce提供了woocommerce_email_send_before钩子,这个钩子在邮件即将发送前触发,此时邮件内容已经完成渲染和内联样式处理,完全不需要调用$email->get_content(),从根源上避免了递归问题,代码更简洁安全:
// Capture the contents of the Emails sent and save into local files // These Local files are used for further messaging through different channels function Save_Email_Contents_into_Local_File( $email, $to, $subject, $message, $headers, $attachments ) { // 定义需要捕获的邮件ID列表 $target_email_ids = [ 'customer_processing_order', 'customer_completed_order', 'customer_on_hold_order' ]; if ( in_array( $email->id, $target_email_ids ) ) { $target_filename = '/home/users/....../Sent_Mail_' . $email->id . '.html'; // 直接保存已经处理好的邮件内容 file_put_contents( $target_filename, $message ); } }; // 注意这个钩子的优先级和参数个数,这里需要6个参数 add_action( 'woocommerce_email_send_before', 'Save_Email_Contents_into_Local_File', 10, 6 );
为什么推荐这个方案?
- 完全规避递归:
$message参数已经是WooCommerce处理好的最终HTML内容(包含内联样式),不需要我们再调用任何会触发钩子的方法。 - 无漏触发风险:每个邮件发送请求都会触发这个钩子,不需要临时移除/重新添加动作,不会出现并发场景下的逻辑丢失。
- 代码更简洁:省去了手动调用
get_content()和style_inline()的步骤,直接使用现成的处理结果。
扩展提示
不管用哪个方案,你只需要在$target_email_ids数组中添加对应的邮件ID(比如customer_refunded_order、admin_new_order等),就能轻松扩展到其他邮件场景。
内容的提问来源于stack exchange,提问作者Sreenivasa Hosamane
相关产品推荐
相关产品推荐

