: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
namespace WPForms\Emails;
use WPForms\Tasks\Actions\EntryEmailsTask;
* Used to send email notifications.
class Notifications extends Mailer {
* List of submitted fields.
* Notification ID that is currently being processed.
public $notification_id = '';
* Current email template.
private $current_template;
protected $field_template;
* Default email template name.
const DEFAULT_TEMPLATE = 'classic';
* Plain/Text email template name.
const PLAIN_TEMPLATE = 'none';
* Legacy email template name.
const LEGACY_TEMPLATE = 'default';
* Get the instance of a class.
public static function get_instance() {
* This method will initialize the class.
* Maybe use the old class for backward compatibility.
* The old class might be removed in the future.
* @param string $template Email template name.
* @return $this|WPForms_WP_Emails
public function init( $template = '' ) {
// Assign the current template.
$this->current_template = Helpers::get_current_template_name( $template );
// If the old class doesn't exist, return the current class.
// The old class might be removed in the future.
if ( ! class_exists( 'WPForms_WP_Emails' ) ) {
// In case user is still using the old "Legacy" default template, use the old class.
// Use the old class if the current template is "Legacy".
if ( $this->current_template === self::LEGACY_TEMPLATE ) {
return new WPForms_WP_Emails();
// Plain text and other html templates will use the current class.
* Maybe send an email right away or schedule it.
* @return bool Whether the email was sent successfully.
// Leave the method if the arguments are empty.
// We will be looking for 3 arguments: $to, $subject, $message.
// The primary reason for this method not to take any direct arguments is to make it compatible with the parent class.
if ( empty( func_get_args() ) || count( func_get_args() ) < 3 ) {
// Don't send anything if emails have been disabled.
if ( $this->is_email_disabled() ) {
list( $to, $subject, $message ) = func_get_args();
// Don't send if email address is invalid.
if ( ! is_email( $to ) ) {
* Fires before the email is sent.
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
* @param Notifications $this An instance of the "Notifications" class.
do_action( 'wpforms_email_send_before', $this ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
// Set the attachments to an empty array.
// We will set the attachments later in the filter.
* Filter the email data before sending.
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
* @param array $data Email data.
* @param Notifications $this An instance of the "Notifications" class.
$data = (array) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_emails_send_email_data',
'headers' => $this->get_headers(),
'attachments' => $attachments,
// Set the recipient email address.
$this->to_email( $data['to'] );
// Set the email subject.
$this->subject( $this->process_subject( $data['subject'] ) );
// Process the email template.
$this->process_email_template( $data['message'] );
// Set the attachments to the email.
$this->__set( 'attachments', $data['attachments'] );
* Filter whether to send the email in the same process.
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
* @param bool $send_same_process Whether to send the email in the same process.
* @param array $fields List of submitted fields.
* @param array $entry Entry data.
* @param array $form_data Form data.
* @param int $entry_id Entry ID.
* @param string $type Email type.
$send_same_process = (bool) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
'wpforms_tasks_entry_emails_trigger_send_same_process',
! empty( wpforms()->get( 'entry' ) ) ? wpforms()->get( 'entry' )->get( $this->entry_id ) : [],
// Send the email immediately.
if ( $send_same_process || ! empty( $this->form_data['settings']['disable_entries'] ) ) {
$results = parent::send();
$results = (bool) ( new EntryEmailsTask() )
$this->__get( 'to_email' ),
$this->__get( 'subject' ),
* Fires after the email has been sent.
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
* @param Notifications $this An instance of the "Notifications" class.
do_action( 'wpforms_email_send_after', $this ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Process the email template.
* @param string $message Email message.
private function process_email_template( $message ) {
$template = self::get_available_templates( $this->current_template );
// Return if the template is not set.
// This can happen if the template is not found or if the template class doesn't exist.
if ( ! isset( $template['path'] ) || ! class_exists( $template['path'] ) ) {
// Set the email template, i.e. WPForms\Emails\Templates\Classic.
$this->template( new $template['path']( '', false, $this->current_template ) );
// Set the field template.
$this->field_template = $this->template->get_field_template();
// Set the email template fields.
$this->template->set_field( $this->process_message( $message ) );
$content = $this->template->get();
// Return if the template is empty.
$this->message( $content );
* Format and process the email subject.
* @param string $subject Email subject.
private function process_subject( $subject ) {
$subject = $this->process_tag( $subject );
$subject = trim( str_replace( [ "\r\n", "\r", "\n" ], ' ', $subject ) );
return wpforms_decode_string( $subject );
* Process the email message.
* @param string $message Email message.
private function process_message( $message ) {
// Check if the placeholder '{all_fields}' is not present in the message.
if ( strpos( $message, '{all_fields}' ) === false ) {
// Wrap the message with a table row after processing tags.
$message = $this->wrap_content_with_table_row( $message );
// If {all_fields} is present, extract content before and after into separate variables.
list( $before, $after ) = array_map( 'trim', explode( '{all_fields}', $message, 2 ) );
// Wrap before and after content with <tr> tags if they are not empty to maintain styling.
// Note that whatever comes after the {all_fields} should be wrapped in a table row to avoid content misplacement.
$before_tr = ! empty( $before ) ? $this->wrap_content_with_table_row( $before ) : '';
$after_tr = ! empty( $after ) ? $this->wrap_content_with_table_row( $after ) : '';
// Replace {all_fields} with $this->process_field_values() output.
$message = $before_tr . $this->process_field_values() . $after_tr;
* Filter and modify the email message content before sending.
* This filter allows customizing the email message content for notifications.
* @param string $message The email message to be sent out.
* @param string $template The email template name.
* @param Notifications $this The instance of the "Notifications" class.
$message = apply_filters( 'wpforms_emails_notifications_message', $message, $this->current_template, $this );
// Leave early if the template is set to plain text.
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
return make_clickable( str_replace( "\r\n", '<br/>', $message ) );
* Process the field values.
private function process_field_values() {
// If fields are empty, return an empty message.
if ( empty( $this->fields ) ) {
// If no message was generated, create an empty message.
$default_message = esc_html__( 'An empty form was submitted.', 'wpforms-lite' );
* Filter whether to display empty fields in the email.
* @param bool $show_empty_fields Whether to display empty fields in the email.
$show_empty_fields = apply_filters_deprecated( // phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
'wpforms_emails_notifications_display_empty_fields',
'1.8.5.2 of the WPForms plugin',
'wpforms_email_display_empty_fields'
/** This filter is documented in /includes/emails/class-emails.php */
$show_empty_fields = apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_email_display_empty_fields',
// Process either plain text or HTML message based on the template type.
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
$message = $this->process_plain_message( $show_empty_fields );
$message = $this->process_html_message( $show_empty_fields );
return empty( $message ) ? $default_message : $message;
* Process the plain text email message.
* @param bool $show_empty_fields Whether to display empty fields in the email.
private function process_plain_message( bool $show_empty_fields = false ): string {
* Filter the form data before it is used to generate the email message.
* @param array $form_data Form data.
* @param array $fields List of submitted fields.
$this->form_data = apply_filters( 'wpforms_emails_notifications_form_data', $this->form_data, $this->fields );
foreach ( $this->form_data['fields'] as $field ) {
$field_message = $this->get_field_plain( $field, $show_empty_fields );
* Filter the field message before it is added to the email message.
* @since 1.8.9.3 The $notifications parameter was added.
* @param string $field_message Field message.
* @param array $field Field data.
* @param bool $show_empty_fields Whether to display empty fields in the email.
* @param array $form_data Form data.
* @param array $fields List of submitted fields.
* @param Notifications $notifications Notifications instance.
$message .= apply_filters( 'wpforms_emails_notifications_field_message_plain', $field_message, $field, $show_empty_fields, $this->form_data, $this->fields, $this );
// Trim the message and return.
return rtrim( $message, "\r\n" );
* Get a single field plain text markup.
* @param array $field Field data.
* @param bool $show_empty_fields Whether to display empty fields in the email.
public function get_field_plain( array $field, bool $show_empty_fields ): string { // phpcs:ignore Generic.Metrics.CyclomaticComplexity
$field_id = $field['id'] ?? '';
$field = $this->fields[ $field_id ] ?? $field;
if ( ! $show_empty_fields && ( ! isset( $field['value'] ) || (string) $field['value'] === '' ) ) {
if ( $this->is_calculated_field_hidden( $field_id ) ) {