: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
<?php // phpcs:ignoreFile
use AdvancedAds\Entities;
use AdvancedAds\Utilities\WordPress;
* Class Advanced_Ads_Import
class Advanced_Ads_Import {
* @var Advanced_Ads_Export
private static $instance;
* Imported data mapped with previous data, e.g. ['ads'][ new_ad_id => old_ad_id (or null if does not exist) ]
public $imported_data = [
* Created groups during this import session ['slug' => 'id']
private $created_groups = [];
* Attachments, created for Image Ads and images in ad content
private $created_attachments = [];
private function __construct() {}
* Return an instance of this class.
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
* Manages stages of the XML import process
public function dispatch() {
if ( ! isset( $_POST['_wpnonce'] ) ||
! wp_verify_nonce( $_POST['_wpnonce'], 'advads-import' ) ||
! WordPress::user_can( 'advanced_ads_manage_options' )
if ( ! isset( $_POST['import_type'] ) ) {
switch ( $_POST['import_type'] ) {
if ( empty( $_POST['xml_textarea'] ) ) {
$this->messages[] = [ 'error', __( 'Please enter XML content', 'advanced-ads' ) ];
$content = stripslashes( $_POST['xml_textarea'] );
$this->import( $content );
if ( $this->handle_upload() ) {
$content = file_get_contents( $this->import_id );
$this->import( $content );
@unlink( $this->import_id );
* The main controller for the actual import stage
* @param string $xml_content XML content to import.
public function import( &$xml_content ) {
@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
$xml_content = trim( $xml_content );
if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
error_log( 'source XML:' );
error_log( $xml_content );
$decoded = Advanced_Ads_XmlEncoder::get_instance()->decode( $xml_content );
} catch ( Exception $e ) {
error_log( $e->getMessage() );
$this->messages[] = [ 'error', $e->getMessage() ];
if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
error_log( 'decoded XML:' );
error_log( print_r( $decoded, true ) );
$this->import_ads_and_groups( $decoded );
$this->import_empty_groups( $decoded );
$this->import_placements( $decoded );
$this->import_options( $decoded );
do_action_ref_array( 'advanced-ads-import', [ &$decoded, &$this->imported_data, &$this->messages ] );
* Create new ads and groups based on import information
* @param array $decoded decoded XML.
private function import_ads_and_groups( &$decoded ) {
if ( isset( $decoded['ads'] ) && is_array( $decoded['ads'] ) ) {
foreach ( $decoded['ads'] as $k => $ad ) {
$ad_title = $ad['post_title'] ?? '';
$ad_date = $ad['post_date'] ?? '';
if ( isset( $ad['meta_input'] ) && is_array( $ad['meta_input'] ) ) {
foreach ( $ad['meta_input'] as $meta_k => &$meta_v ) {
if ( Advanced_Ads_Ad::$options_meta_field !== $meta_k ) {
$meta_v = WordPress::maybe_unserialize( $meta_v );
// upload images for Image ad ad type.
if ( isset( $ad['attached_img_url'] ) && isset( $ad['meta_input']['advanced_ads_ad_options']['output']['image_id'] ) ) {
$attached_img_url = $this->replace_placeholders( $ad['attached_img_url'] );
if ( isset( $this->created_attachments[ $attached_img_url ] ) ) {
$attachment_id = $this->created_attachments[ $attached_img_url ]['post_id'];
} else if ( $attachment = $this->upload_image_from_url( $attached_img_url ) ) {
$link = ( $link = get_attachment_link( $attachment['post_id'] ) ) ? sprintf( '<a href="%s">%s</a>', esc_url( $link ), __( 'Edit', 'advanced-ads' ) ) : '';
/* translators: 1: Attachment ID 2: Attachment link */
$this->messages[] = [ 'update', sprintf( __( 'New attachment created <em>%1$s</em> %2$s', 'advanced-ads' ), $attachment['post_id'], $link ) ];
$attachment_id = $attachment['post_id'];
$this->created_attachments[ $attached_img_url ] = $attachment;
$ad['meta_input']['advanced_ads_ad_options']['output']['image_id'] = $attachment_id;
'post_title' => $ad_title,
'post_date_gmt' => isset( $ad['post_date_gmt'] ) ? $ad['post_date_gmt'] : '',
'post_content' => isset( $ad['post_content'] ) ? $this->process_ad_content( $ad['post_content'] ) : '',
'post_password' => isset( $ad['post_password'] ) ? $ad['post_password'] : '',
'post_name' => isset( $ad['post_name'] ) ? $ad['post_name'] : '',
'post_status' => isset( $ad['post_status'] ) ? $ad['post_status'] : 'publish',
'post_modified' => isset( $ad['post_modified'] ) ? $ad['post_modified'] : '',
'post_modified_gmt' => isset( $ad['post_modified_gmt'] ) ? $ad['post_modified_gmt'] : '',
'guid' => $ad['guid'] ?? '',
'post_author' => get_current_user_id(),
'post_type' => Entities::POST_TYPE_AD,
'comment_status' => 'closed',
'ping_status' => 'closed',
'meta_input' => isset( $ad['meta_input'] ) ? $ad['meta_input'] : '',
$post_id = wp_insert_post( $insert_ad, true );
if ( is_wp_error( $post_id ) ) {
/* translators: %s Ad title */
$this->messages[] = [ 'error', sprintf( __( 'Failed to import <em>%s</em>', 'advanced-ads' ), esc_html( $ad['post_title'] ) ) ];
if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
$this->messages[] = [ 'error', ' > ' . $post_id->get_error_message() ];
$link = get_edit_post_link( $post_id );
$link = $link ? sprintf( '<a href="%s">%s</a>', esc_url( $link ), __( 'Edit', 'advanced-ads' ) ) : '';
/* translators: 1: Post ID 2: Post link */
$this->messages[] = [ 'update', sprintf( __( 'New ad created: <em>%1$s</em> %2$s', 'advanced-ads' ), $post_id, $link ) ];
// new ad id => old ad id, if exists.
$this->imported_data['ads'][ $post_id ] = isset( $ad['ID'] ) ? absint( $ad['ID'] ) : null;
if ( ! empty( $ad['groups'] ) && is_array( $ad['groups'] ) ) {
$advads_ad_groups = get_option( 'advads-ad-groups', [] );
$advads_ad_weights = get_option( 'advads-ad-weights', [] );
foreach ( $ad['groups'] as $_group ) {
if ( ! $group_id = $this->create_group_term( $_group ) ) {
if ( isset( $ad['meta_input']['advanced_ads_ad_options']['output']['group_id'] ) ) {
$ad_group_id = $ad['meta_input']['advanced_ads_ad_options']['output']['group_id'];
// do not save the ad group, if this is the group assigned as ad content.
if ( $ad_group_id !== $group_id ) {
$groups_to_set[] = (int) $group_id;
if ( ! isset( $advads_ad_groups[ $group_id ] ) ) {
$advads_ad_groups[ $group_id ] = [
'type' => $_group['type'] ?? 'default',
'ad_count' => $_group['ad_count'] ?? 1,
'options' => $_group['options'] ?? [],
update_option( 'advads-ad-groups', $advads_ad_groups );
$advads_ad_weights[ $group_id ][ $post_id ] = absint( $_group['weight'] ?? Advanced_Ads_Group::MAX_AD_GROUP_DEFAULT_WEIGHT );
update_option( 'advads-ad-weights', $advads_ad_weights );
/* translators: 1: Group IDs 2: Post ID */
$this->messages[] = [ 'update', sprintf( __( 'Assigned terms: <em>%1$s</em>, to post: <em>%2$s</em>', 'advanced-ads' ), implode( ',',$groups_to_set ), $post_id ) ];
$tt_ids = wp_set_post_terms( $post_id, $groups_to_set, Entities::TAXONOMY_AD_GROUP );
* Create new empty groups based on import information
* @param array $decoded decoded XML.
private function import_empty_groups( &$decoded ) {
if ( isset( $decoded['groups'] ) && is_array( $decoded['groups'] ) ) {
$advads_ad_groups = get_option( 'advads-ad-groups', [] );
foreach ( $decoded['groups'] as $_group ) {
if ( $group_id = $this->create_group_term( $_group ) ) {
if ( ! isset( $advads_ad_groups[ $group_id ] ) ) {
$advads_ad_groups[ $group_id ] = [
'type' => isset( $_group['type']) ? $_group['type'] : 'default',
'ad_count' => isset( $_group['ad_count'] ) ? $_group['ad_count'] : 1,
'options' => isset( $_group['options'] ) ? $_group['options'] : []
update_option( 'advads-ad-groups', $advads_ad_groups );
* Create new group term if it haven't already been created
* @param array $_group decoded XML.
* @return int group_id, false on failure
private function create_group_term( $_group ) {
if ( empty( $_group['slug'] ) || empty( $_group['name'] ) ) {
$slug = $original_slug = $_group['slug'];
if ( isset( $this->created_groups[ $original_slug ] ) ) {
return $this->created_groups[ $slug ];
if ( term_exists( $slug, Entities::TAXONOMY_AD_GROUP ) ) {
while ( term_exists( $slug . '_' . $count, Entities::TAXONOMY_AD_GROUP ) ) {
$slug = $slug . '_' . $count;
$t = wp_insert_term( $_group['name'], Entities::TAXONOMY_AD_GROUP, [ 'slug' => $slug] );
if ( ! is_wp_error( $t ) ) {
$this->created_groups[ $original_slug ] = $t['term_id'];
$group_id = $t['term_id'];
/* translators: 1: Group ID 2: Group name */
$this->messages[] = [ 'update', sprintf( __( 'New group created, id: <em>%1$s</em>, name: <em>%2$s</em>', 'advanced-ads' ), $group_id, esc_html( $_group['name'] ) ) ];
/* translators: 1: Group taxonomy name 2: Group name */
$this->messages[] = [ 'error', sprintf( __( 'Failed to import taxonomy: <em>%1$s</em>, term: <em>%2$s</em>', 'advanced-ads' ), esc_html( Entities::TAXONOMY_AD_GROUP ), esc_html( $_group['name'] ) ) ];
if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
$this->messages[] = [ 'error', ' > ' . $t->get_error_message() ];
// new group id => old group id, if exists.
$this->imported_data['groups'][ $group_id ] = isset( $_group['term_id'] ) ? absint( $_group['term_id'] ) : null;
* Create new placements based on import information
* @param array $decoded decoded XML.
private function import_placements( &$decoded ) {
if ( isset( $decoded['placements'] ) && is_array( $decoded['placements'] ) ) {
$existing_placements = $updated_placements = Advanced_Ads::get_instance()->get_model()->get_ad_placements_array();
$placement_types = Advanced_Ads_Placements::get_placement_types();
foreach ( $decoded['placements'] as &$placement ) {
$use_existing = ! empty( $placement['use_existing'] );
// use existing placement.
if ( empty( $placement['key'] ) ) {
$placement_key_uniq = sanitize_title( $placement['key'] );
if ( ! isset( $existing_placements[ $placement_key_uniq ] ) ) {
$existing_placement = $existing_placements[ $placement_key_uniq ];
$existing_placement['key'] = $placement_key_uniq;
if ( empty( $placement['key'] ) || empty( $placement['name'] ) || empty( $placement['type'] ) ) {
$placement_key_uniq = sanitize_title( $placement['key'] );
if ( '' === $placement_key_uniq ) {
$placement['type'] = ( isset( $placement_types[ $placement['type'] ] ) ) ? $placement['type'] : 'default';
$placement['name'] = esc_attr( $placement['name'] );
// make sure the key in placement array is unique.
if ( isset( $existing_placements[ $placement_key_uniq ] ) ) {
while ( isset( $existing_placements[ $placement_key_uniq . '_' . $count ] ) ) {
$placement_key_uniq .= '_' . $count;
/* translators: %s is a placement name */
$this->messages[] = [ 'update', sprintf( __( 'Placement <em>%s</em> created', 'advanced-ads' ), esc_html( $placement['name'] ) ) ];
// new placement key => old placement key.
$this->imported_data['placements'][ $placement_key_uniq ] = $placement['key'];
// try to set "Item" (ad or group).
if ( ! empty( $placement['item'] ) ) {
$_item = explode( '_', $placement['item'] );
if ( ! empty( $_item[1] ) ) {
case Advanced_Ads_Select::AD:
$found = $this->search_item( $_item[1], Advanced_Ads_Select::AD );
if ( false === $found ) {
// assign new ad to an existing placement
// - if the placement has no or a single ad assigned, it will be swapped against the new one
// - if a group is assigned to the placement, the new ad will be added to this group with a weight of 1
$placement = $existing_placement;
if ( ! empty( $placement['item'] ) ) {
// get the item from the existing placement.
$_item_existing = explode( '_', $placement['item'] );
if ( ! empty( $_item_existing[1] ) && $_item_existing[0] === Advanced_Ads_Select::GROUP ) {
$advads_ad_weights = get_option( 'advads-ad-weights', [] );
if ( term_exists( absint( $_item_existing[1] ), Entities::TAXONOMY_AD_GROUP ) ) {
wp_set_post_terms( $found, $_item_existing[1], Entities::TAXONOMY_AD_GROUP, true );
* By default, a new add added to a group receives the weight of 5
* so that users could set the weight of existing ads either higher or lower
* depending on whether they want to show the new ad with a higher weight or not.
* This is especially useful with Selling Ads to replace an existing ad in a group
* Advanced users could use the `advanced-ads-import-default-group-weight` filter
* to manipulate the value
$advads_ad_weights[ $_item_existing[1] ][ $found ] = apply_filters( 'advanced-ads-import-default-group-weight', 5 );
update_option( 'advads-ad-weights', $advads_ad_weights );
// new placement key => old placement key.
$this->imported_data['placements'][ $placement_key_uniq ] = $placement_key_uniq;
$placement['item'] = 'ad_' . $found;
// new placement key => old placement key.
$this->imported_data['placements'][ $placement_key_uniq ] = $placement_key_uniq;
case Advanced_Ads_Select::GROUP:
$found = $this->search_item( $_item[1], Advanced_Ads_Select::GROUP );
if ( false === $found ) {
$placement['item'] = 'group_' . $found;
// new placement key => old placement key.
$this->imported_data['placements'][ $placement_key_uniq ] = $placement_key_uniq;
$updated_placements[ $placement_key_uniq ] = apply_filters( 'advanced-ads-import-placement', $placement, $this );
if ( $existing_placements !== $updated_placements ) {
Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $updated_placements );
* @param string $id ad/group Group id.
* @param string $type Group type.
* - int id of the imported ad/group if exists
* - or int id of the existing ad/group if exists
public function search_item( $id, $type ) {
case Advanced_Ads_Select::AD:
// if the ad was was imported.
if ( ! $found = array_search( $id, $this->imported_data['ads'] ) ) {
// if the ad already exists.
if ( get_post_type( $id ) === Entities::POST_TYPE_AD ) {
case Advanced_Ads_Select::GROUP:
if ( ! $found = array_search( $id, $this->imported_data['groups'] ) ) {
if ( term_exists( absint( $id ), Entities::TAXONOMY_AD_GROUP ) ) {