: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// For each tag located before/after an ad placeholder, find its offset among the same tags.
foreach ( $tag_matches[0] as $r ) {
if ( preg_match( '/%advads_placeholder_(\d+)%/', $r, $result ) ) {
foreach ( $ads_for_placeholders as $n => $ad ) {
if ( (int) $ad['id'] === (int) $id ) {
switch ( $found_ad['position'] ) {
$ads_for_placeholders[ $n ]['offset'] = $count;
$ads_for_placeholders[ $n ]['offset'] = $count - 1;
// Find tags before/after which we need to inject ads.
preg_match_all( "#{$tag_regexp}#i", $content_orig, $orig_tag_matches, PREG_OFFSET_CAPTURE );
foreach ( $orig_tag_matches[0] as $n => $r ) {
// Check if we need to inject an ad at this offset.
foreach ( $ads_for_placeholders as $ad ) {
if ( isset( $ad['offset'] ) && $ad['offset'] === $n ) {
foreach ( $to_inject as $item ) {
switch ( $item['position'] ) {
$found_pos = $r[1] + strlen( $r[0] );
$new_content .= substr( $content_orig, $pos, $found_pos - $pos );
$new_content .= $item['ad'];
$new_content .= substr( $content_orig, $pos );
* Callback function for usort() to sort ads for placeholders.
* @param array $first The first array to compare.
* @param array $second The second array to compare.
* @return int 0 if both objects equal. -1 if second array should come first, 1 otherwise.
public static function sort_ads_for_placehoders( $first, $second ) {
if ( $first['position'] === $second['position'] ) {
return $num[ $first['position'] ] > $num[ $second['position'] ] ? 1 : - 1;
* Add a warning to 'Ad health'.
public static function add_ad_health_node( $nodes ) {
'parent' => 'advanced_ads_ad_health',
'id' => 'advanced_ads_ad_health_the_content_not_enough_elements',
/* translators: %s stands for the name of the "Disable level limitation" option and automatically translated as well */
__( 'Set <em>%s</em> to show more ads', 'advanced-ads' ),
__( 'Disable level limitation', 'advanced-ads' )
'href' => admin_url( '/admin.php?page=advanced-ads-settings#top#general' ),
'class' => 'advanced_ads_ad_health_warning',
* Get paths of ancestors that should not contain ads.
* @param object $xpath DOMXPath object.
* @return array Paths of ancestors.
private static function get_ancestors_to_limit( $xpath ) {
$query = self::get_ancestors_to_limit_query();
$node_list = $xpath->query( $query );
$ancestors_to_limit = [];
foreach ( $node_list as $a ) {
$ancestors_to_limit[] = $a->getNodePath();
return $ancestors_to_limit;
* Remove paragraphs that has ancestors that should not contain ads.
* @param array $paragraphs An array of `DOMNode` objects to insert ads before or after.
* @param array $ancestors_to_limit Paths of ancestor that should not contain ads.
* @return array $new_paragraphs An array of `DOMNode` objects to insert ads before or after.
private static function filter_by_ancestors_to_limit( $paragraphs, $ancestors_to_limit ) {
foreach ( $paragraphs as $k => $paragraph ) {
foreach ( $ancestors_to_limit as $a ) {
if ( 0 === stripos( $paragraph->getNodePath(), $a ) ) {
$new_paragraphs[] = $paragraph;
* Get query to select ancestors that should not contain ads.
* @return string/false DOMXPath query or false.
private static function get_ancestors_to_limit_query() {
* - support `%` (rand) at the start
* - support plain text that node should contain instead of CSS selectors
* - support `prev` and `next` as `type`
* Filter the nodes that limit injection.
* @param array An array of arrays, each of which contains:
* @type string $type Accept: `ancestor` - limit injection inside the ancestor.
* @type string $node A "class selector" which targets one class (.) or "id selector" which targets one id (#),
* optionally with `%` at the end.
'advanced-ads-content-injection-nodes-without-ads',
// a class anyone can use to prevent automatic ad injection into a specific element.
'node' => '.advads-stop-injection',
// Product Slider for Beaver Builder by WooPack.
'node' => '.woopack-product-carousel',
// GeoDirectory Post Slider.
'node' => '.geodir-post-slider',
foreach ( $items as $p ) {
$sel_type = substr( $sel, 0, 1 );
$sel = substr( $sel, 1 );
$rand_pos = strpos( $sel, '%' );
$sel = str_replace( '%', '', $sel );
$sel = sanitize_html_class( $sel );
if ( '.' === $sel_type ) {
if ( false !== $rand_pos ) {
$query[] = "@class and contains(concat(' ', normalize-space(@class), ' '), ' $sel')";
$query[] = "@class and contains(concat(' ', normalize-space(@class), ' '), ' $sel ')";
if ( '#' === $sel_type ) {
if ( false !== $rand_pos ) {
$query[] = "@id and starts-with(@id, '$sel')";
$query[] = "@id and @id = '$sel'";
return '//*[' . implode( ' or ', $query ) . ']';