: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @package Advanced_Ads_Placements
* @author Thomas Maier <support@wpadvancedads.com>
* @link https://wpadvancedads.com
* @copyright 2014 Thomas Maier, Advanced Ads GmbH
use AdvancedAds\Entities;
use AdvancedAds\Utilities\WordPress;
* Grouping placements functions
* @package Advanced_Ads_Placements
* @author Thomas Maier <support@wpadvancedads.com>
class Advanced_Ads_Placements {
* Gather placeholders which later are replaced by the ads
* @var array $ads_for_placeholders
private static $ads_for_placeholders = [];
* Temporarily change content during processing
private static $replacements = [
'gcse:search' => 'gcse__search', // Google custom search namespaced tags.
* Return placement page description
public static function get_description() {
_deprecated_function( __METHOD__, '1.47.0', '\AdvancedAds\Entities::get_placement_description()' );
return Entities::get_placement_description();
* @return \Advanced_Ads\Placement_Type[] $types array with placement types
public static function get_placement_types() {
'title' => __( 'Before Content', 'advanced-ads' ),
'description' => __( 'Injected before the post content.', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-before.png',
'show_lazy_load' => true,
'uses_the_content' => true,
'title' => __( 'Content', 'advanced-ads' ),
'description' => __( 'Injected into the content. You can choose the paragraph after which the ad content is displayed.', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-within.png',
'show_lazy_load' => true,
'uses_the_content' => true,
'title' => __( 'After Content', 'advanced-ads' ),
'description' => __( 'Injected after the post content.', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/content-after.png',
'show_lazy_load' => true,
'uses_the_content' => true,
'title' => __( 'Sidebar Widget', 'advanced-ads' ),
'description' => __( 'Create a sidebar widget with an ad. Can be placed and used like any other widget.', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/widget.png',
'show_lazy_load' => true,
'title' => __( 'Manual Placement', 'advanced-ads' ),
'description' => __( 'Manual placement to use as function or shortcode.', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/manual.png',
'show_lazy_load' => true,
'title' => __( 'Header Code', 'advanced-ads' ),
'description' => __( 'Injected in Header (before closing </head> Tag, often not visible).', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/header.png',
'title' => __( 'Footer Code', 'advanced-ads' ),
'description' => __( 'Injected in Footer (before closing </body> Tag).', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'admin/assets/img/placements/footer.png',
'options' => [ 'amp' => true ],
$types = (array) apply_filters( 'advanced-ads-placement-types', $types );
foreach ( $types as $type => $definition ) {
$types[ $type ] = new \Advanced_Ads\Placement_Type( $type, $definition );
* Update placements if sent
public static function update_placements() {
// check user permissions.
if ( ! WordPress::user_can( 'advanced_ads_manage_placements' ) ) {
// add hook of last opened placement settings to URL.
$hook = ! empty( $_POST['advads-last-edited-placement'] ) ? '#single-placement-' . $_POST['advads-last-edited-placement'] : '';
if ( isset( $_POST['advads']['placement'] ) && check_admin_referer( 'advads-placement', 'advads_placement' ) ) {
$success = self::save_new_placement( $_POST['advads']['placement'] );
if ( isset( $_POST['advads']['placements'] ) && check_admin_referer( 'advads-placement', 'advads_placement' ) ) {
$success = self::save_placements( $_POST['advads']['placements'] );
$success = apply_filters( 'advanced-ads-update-placements', $success );
if ( isset( $success ) ) {
$message = $success ? 'updated' : 'error';
wp_redirect( esc_url_raw( add_query_arg( [ 'message' => $message ] ) ) . $hook );
* @param array $new_placement information about the new placement.
* @return mixed slug if saved; false if not
public static function save_new_placement( $new_placement ) {
// load placements // -TODO use model.
$placements = Advanced_Ads::get_ad_placements_array();
$new_placement['slug'] = sanitize_title( $new_placement['name'] );
if ( isset( $placements[ $new_placement['slug'] ] ) ) {
// try to save placement until we found an empty slug.
if ( 100 === $i ) { // prevent endless loop, just in case.
Advanced_Ads::log( 'endless loop when injecting placement' );
} while ( isset( $placements[ $new_placement['slug'] . '_' . $i ] ) );
$new_placement['slug'] .= '_' . $i;
$new_placement['name'] .= ' ' . $i;
// check if slug already exists or is empty.
if ( '' === $new_placement['slug'] || isset( $placements[ $new_placement['slug'] ] ) || ! isset( $new_placement['type'] ) ) {
// make sure only allowed types are being saved.
$placement_types = self::get_placement_types();
$new_placement['type'] = ( isset( $placement_types[ $new_placement['type'] ] ) ) ? $new_placement['type'] : 'default';
$new_placement['name'] = esc_attr( $new_placement['name'] );
// add new place to all placements.
$placements[ $new_placement['slug'] ] = [
'type' => $new_placement['type'],
'name' => $new_placement['name'],
'item' => $new_placement['item'],
if ( isset( $new_placement['options'] ) ) {
$placements[ $new_placement['slug'] ]['options'] = $new_placement['options'];
if ( isset( $placements[ $new_placement['slug'] ]['options']['index'] ) ) {
$placements[ $new_placement['slug'] ]['options']['index'] = absint( $placements[ $new_placement['slug'] ]['options']['index'] );
Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $placements );
return $new_placement['slug'];
* @param array $placement_items placements.
* @return mixed true if saved; error message if not
public static function save_placements( $placement_items ) {
// load placements // -TODO use model.
$placements = Advanced_Ads::get_ad_placements_array();
foreach ( $placement_items as $_placement_slug => $_placement ) {
if ( isset( $_placement['delete'] ) ) {
unset( $placements[ $_placement_slug ] );
if ( isset( $_placement['item'] ) ) {
$placements[ $_placement_slug ]['item'] = $_placement['item'];
if ( isset( $_placement['options'] ) ) {
$placements[ $_placement_slug ]['options'] = $_placement['options'];
if ( isset( $placements[ $_placement_slug ]['options']['index'] ) ) {
$placements[ $_placement_slug ]['options']['index'] = absint( $placements[ $_placement_slug ]['options']['index'] );
$placements[ $_placement_slug ]['options'] = [];
Advanced_Ads::get_instance()->get_model()->update_ad_placements_array( $placements );
* Get items for item select field.
* Used for new placement form.
* @return array $select items for select field
public static function items_for_select() {
$model = Advanced_Ads::get_instance()->get_model();
$groups = $model->get_ad_groups();
foreach ( $groups as $_group ) {
$select['groups'][ 'group_' . $_group->term_id ] = $_group->name;
foreach ( $ads as $_ad ) {
$select['ads'][ 'ad_' . $_ad->ID ] = $_ad->post_title;
* Get html tags for content injection
* @return array $tags array with tags that can be used for content injection
public static function tags_for_content_injection() {
$headline_tags = apply_filters( 'advanced-ads-headlines-for-ad-injection', [ 'h2', 'h3', 'h4' ] );
$headline_tags_imploded = '<' . implode( '>, <', $headline_tags ) . '>';
'advanced-ads-tags-for-injection',
// translators: %s is an html tag.
'p' => sprintf( __( 'paragraph (%s)', 'advanced-ads' ), '<p>' ),
// translators: %s is an html tag.
'pwithoutimg' => sprintf( __( 'paragraph without image (%s)', 'advanced-ads' ), '<p>' ),
// translators: %s is an html tag.
'h2' => sprintf( __( 'headline 2 (%s)', 'advanced-ads' ), '<h2>' ),
// translators: %s is an html tag.
'h3' => sprintf( __( 'headline 3 (%s)', 'advanced-ads' ), '<h3>' ),
// translators: %s is an html tag.
'h4' => sprintf( __( 'headline 4 (%s)', 'advanced-ads' ), '<h4>' ),
// translators: %s is an html tag.
'headlines' => sprintf( __( 'any headline (%s)', 'advanced-ads' ), $headline_tags_imploded ),
// translators: %s is an html tag.
'img' => sprintf( __( 'image (%s)', 'advanced-ads' ), '<img>' ),
// translators: %s is an html tag.
'table' => sprintf( __( 'table (%s)', 'advanced-ads' ), '<table>' ),
// translators: %s is an html tag.
'li' => sprintf( __( 'list item (%s)', 'advanced-ads' ), '<li>' ),
// translators: %s is an html tag.
'blockquote' => sprintf( __( 'quote (%s)', 'advanced-ads' ), '<blockquote>' ),
// translators: %s is an html tag.
'iframe' => sprintf( __( 'iframe (%s)', 'advanced-ads' ), '<iframe>' ),
// translators: %s is an html tag.
'div' => sprintf( __( 'container (%s)', 'advanced-ads' ), '<div>' ),
'anyelement' => __( 'any element', 'advanced-ads' ),
'custom' => _x( 'custom', 'for the "custom" content placement option', 'advanced-ads' ),
* Return content of a placement
* @param string $id slug of the display.
* @param array $args optional arguments (passed to child).
public static function output( $id = '', $args = [] ) {
// get placement data for the slug.
$placements = Advanced_Ads::get_ad_placements_array();
$placement = ( isset( $placements[ $id ] ) && is_array( $placements[ $id ] ) ) ? $placements[ $id ] : [];
if ( isset( $args['change-placement'] ) ) {
// some options was provided by the user.
$placement = Advanced_Ads_Utils::merge_deep_array( [ $placement, $args['change-placement'] ] );
if ( isset( $placement['item'] ) && '' !== $placement['item'] ) {
$_item = explode( '_', $placement['item'] );
if ( ! isset( $_item[1] ) || empty( $_item[1] ) ) {
if ( isset( $placement['options'] ) && is_array( $placement['options'] ) ) {
foreach ( $placement['options'] as $_k => $_v ) {
if ( ! isset( $args[ $_k ] ) ) {
// inject placement type.
if ( isset( $placement['type'] ) ) {
$args['placement_type'] = $placement['type'];
$prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();
// return either ad or group content.
case Advanced_Ads_Select::AD:
// create class from placement id (not if header injection).
if ( ! isset( $placement['type'] ) || 'header' !== $placement['type'] ) {
if ( ! isset( $args['output'] ) ) {
if ( ! isset( $args['output']['class'] ) ) {
$args['output']['class'] = [];
if ( ! in_array( $class, $args['output']['class'] ) ) {
$args['output']['class'][] = $class;
$_item[0] = Advanced_Ads_Select::AD;
* Deliver the translated version of an ad if set up with WPML.
* If an ad is not translated, show the ad in the original language when this is the selected option in the WPML settings.
* @source https://wpml.org/wpml-hook/wpml_object_id/
* @source https://wpml.org/forums/topic/backend-custom-post-types-page-overview-with-translation-options/
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
$_item[1] = apply_filters( 'wpml_object_id', $_item[1], 'advanced_ads', $sitepress->is_display_as_translated_post_type( 'advanced_ads' ) );
case Advanced_Ads_Select::PLACEMENT:
// avoid loops (programmatical error).
case Advanced_Ads_Select::GROUP:
if ( ( isset( $placement['type'] ) && $placement['type'] !== 'header' )
&& ( ! isset( $args['output']['class'] )
|| ! is_array( $args['output']['class'] )
|| ! in_array( $class, $args['output']['class'] ) ) ) {
$args['output']['class'][] = $class;
// create placement id for various features.
$args['output']['placement_id'] = $id;
// add the placement to the global output array.
$advads = Advanced_Ads::get_instance();
$name = isset( $placement['name'] ) ? $placement['name'] : $id;
$result = Advanced_Ads_Select::get_instance()->get_ad_by_method( (int) $_item[1], $_item[0], $args );
if ( $result && ( ! isset( $args['global_output'] ) || $args['global_output'] ) ) {
$advads->current_ads[] = [
* Inject ads directly into the content
* @param string $placement_id Id of the placement.
* @param array $placement_opts Placement options.
* @param string $content Content to inject placement into.
* @return string $content Content with injected placement.
public static function &inject_in_content( $placement_id, $placement_opts, &$content ) {
return Advanced_Ads_In_Content_Injector::inject_in_content( $placement_id, $placement_opts, $content );
* Check if the placement can be displayed
* @param int $id placement id.
* @return bool true if placement can be displayed
public static function can_display( $id = 0 ) {
if ( ! isset( $id ) || 0 === $id ) {
return apply_filters( 'advanced-ads-can-display-placement', true, $id );