: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
foreach ( $notices as $notice ) {
// Skip the duplicate notice if it already exists.
if ( ! empty( $notice['code'] ) && in_array( $notice['code'], $code_fields, true ) ) {
$next_field_id = ++$current_field_id;
$warning[ $next_field_id ] = [
'type' => 'internal-information',
'code' => ! empty( $notice['code'] ) ? esc_attr( $notice['code'] ) : '',
$warning[ $next_field_id ]['description'] .= ! empty( $notice['title'] ) ? '<strong>' . esc_html( $notice['title'] ) . '</strong>' : '';
$warning[ $next_field_id ]['description'] .= ! empty( $notice['message'] ) ? '<p>' . wp_kses_post( $notice['message'] ) . '</p>' : '';
// Do not add notice with empty body.
if ( empty( $warning[ $next_field_id ]['description'] ) ) {
unset( $warning[ $next_field_id ] );
--$next_field_id; // Reset next field ID to the previous value.
if ( ! empty( $warning ) ) {
$new_form_data['fields'] = $warning + $new_form_data['fields'];
// Update next field ID to be used for future created fields. Otherwise, IIF field would be overwritten.
$new_form_data['field_id'] = $next_field_id + 1;
* Add a notice about Zapier zaps disconnected after form being duplicated or converted to/from template.
* @WPFormsBackCompat Support Zapier v1.5.0 and earlier.
* @param array $notices Array of notices.
* @param array $new_form_data Form data.
* @param int $form_id Original form ID.
public function _zapier_disconnected_on_duplication( $notices, array $new_form_data, int $form_id ): array { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
// Check if original form had any Zaps connected.
$is_zapier_connected = get_post_meta( $form_id, 'wpforms_zapier', true );
if ( ! $is_zapier_connected ) {
'title' => esc_html__( 'Zaps Have Been Disabled', 'wpforms-lite' ),
'code' => 'disconnected_on_duplication',
'message' => sprintf( /* translators: %s - URL the to list of Zaps. */
__( 'Head over to the Zapier settings in the Marketing tab or visit your <a href="%s" target="_blank" rel="noopener noreferrer">Zapier account</a> to restore them.', 'wpforms-lite' ),
esc_url( 'https://zapier.com/app/zaps' )
* Get the next available field ID and increment by one.
* @param string|int $form_id Form ID.
* @param array $args Additional arguments.
* @return mixed int or false
public function next_field_id( $form_id, $args = [] ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
if ( empty( $form_id ) ) {
if ( isset( $args['cap'] ) ) {
$defaults['cap'] = $args['cap'];
$form = $this->get( $form_id, $defaults );
$max_field_id = ! empty( $form['fields'] ) ? max( array_keys( $form['fields'] ) ) : 0;
// We pass the `field_id` after duplicating the Layout field that contains a bunch of fields.
// This is needed to avoid multiple AJAX calls after duplicating each field in the Layout.
if ( isset( $args['field_id'] ) ) {
$set_field_id = absint( $args['field_id'] ) - 1;
$field_id = $set_field_id > $max_field_id ? $set_field_id : $max_field_id + 1;
} elseif ( ! empty( $form['field_id'] ) ) {
$field_id = absint( $form['field_id'] );
$field_id = $max_field_id > $field_id ? $max_field_id + 1 : $field_id;
$form['field_id'] = $field_id + 1;
// Skip creating a revision for this action.
remove_action( 'post_updated', 'wp_save_post_revision' );
$this->update( $form_id, $form );
// Restore the initial revisions state.
add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );
* Get private meta information for a form.
* @param string|int $form_id Form ID.
* @param string $field Field.
* @param array $args Additional arguments.
public function get_meta( $form_id, $field = '', $args = [] ) {
if ( empty( $form_id ) ) {
if ( isset( $args['cap'] ) ) {
$defaults['cap'] = $args['cap'];
$data = $this->get( $form_id, $defaults );
if ( ! isset( $data['meta'] ) ) {
if ( isset( $data['meta'][ $field ] ) ) {
return $data['meta'][ $field ];
* Update or add form meta information to a form.
* @param string|int $form_id Form ID.
* @param string $meta_key Meta key.
* @param mixed $meta_value Meta value.
* @param array $args Additional arguments.
* @return false|int|WP_Error
public function update_meta( $form_id, $meta_key, $meta_value, $args = [] ) {
if ( empty( $form_id ) || empty( $meta_key ) ) {
// Add filter of the link rel attr to avoid JSON damage.
add_filter( 'wp_targeted_link_rel', '__return_empty_string', 50, 1 );
// This filter breaks forms if they contain HTML.
remove_filter( 'content_save_pre', 'balanceTags', 50 );
if ( ! isset( $args['cap'] ) ) {
$args['cap'] = 'edit_form_single';
$form = $this->get_single( absint( $form_id ), $args );
$data = wpforms_decode( $form->post_content );
$meta_key = wpforms_sanitize_key( $meta_key );
$data['meta'][ $meta_key ] = $meta_value;
'post_content' => wpforms_encode( $data ),
$form = apply_filters( 'wpforms_update_form_meta_args', $form, $data );
$form_id = wp_update_post( $form );
do_action( 'wpforms_update_form_meta', $form_id, $form, $meta_key, $meta_value );
* Delete form meta information from a form.
* @param string|int $form_id Form ID.
* @param string $meta_key Meta key.
* @param array $args Additional arguments.
* @return false|int|WP_Error
public function delete_meta( $form_id, $meta_key, $args = [] ) {
if ( empty( $form_id ) || empty( $meta_key ) ) {
// Add filter of the link rel attr to avoid JSON damage.
add_filter( 'wp_targeted_link_rel', '__return_empty_string', 50, 1 );
// This filter breaks forms if they contain HTML.
remove_filter( 'content_save_pre', 'balanceTags', 50 );
if ( ! isset( $args['cap'] ) ) {
$args['cap'] = 'edit_form_single';
$form = $this->get_single( absint( $form_id ), $args );
$data = wpforms_decode( $form->post_content );
$meta_key = wpforms_sanitize_key( $meta_key );
unset( $data['meta'][ $meta_key ] );
'post_content' => wpforms_encode( $data ),
$form = apply_filters( 'wpforms_delete_form_meta_args', $form, $data );
$form_id = wp_update_post( $form );
do_action( 'wpforms_delete_form_meta', $form_id, $form, $meta_key );
* Get private meta information for a form field.
* @param string|int $form_id Form ID.
* @param string $field_id Field ID.
* @param array $args Additional arguments.
public function get_field( $form_id, $field_id = '', $args = [] ) {
if ( empty( $form_id ) ) {
if ( isset( $args['cap'] ) ) {
$defaults['cap'] = $args['cap'];
$data = $this->get( $form_id, $defaults );
return isset( $data['fields'][ $field_id ] ) ? $data['fields'][ $field_id ] : false;
* Get private meta information for a form field.
* @param string|int $form_id Form ID.
* @param string $field_id Field ID.
* @param array $args Additional arguments.
public function get_field_meta( $form_id, $field_id = '', $args = [] ) {
$field = $this->get_field( $form_id, $field_id, $args );
return isset( $field['meta'] ) ? $field['meta'] : false;
* Checks if any forms are present on the site.
public function forms_exist(): bool {
return (bool) $this->get(
'suppress_filters' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
* Get forms count per page.
public function get_count_per_page(): int {
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
* Give developers an ability to modify number of forms per page.
* @param array $count Forms count per page.
return (int) apply_filters( 'wpforms_forms_per_page', 20 );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName