]*>/i', $content ); if ( $p_count > 0 ) { $middle = max( 1, intval( ceil( $p_count / 2 ) ) ); return self::insert_at_tag_position( $content, $ad_html, 'p', $middle, 'after' ); } // Fallback to start return $ad_html . $content; default: return $content; } } /** * Insert ad before/after the Xth instance of a tag. * * @param string $content The HTML content. * @param string $ad_html The ad HTML. * @param string $tag The tag to target (e.g., 'p', 'img'). * @param int $instance The 1-based instance number. * @param string $position 'before' or 'after'. * @return string The modified content. */ private static function insert_at_tag_position( $content, $ad_html, $tag, $instance, $position ) { // Use regex to locate opening tags while ignoring inline scripts or comments $pattern = '/<' . preg_quote( $tag, '/' ) . '\b[^>]*>/i'; if ( ! preg_match_all( $pattern, $content, $matches, PREG_OFFSET_CAPTURE ) ) { return $content; // Tag not found } $match_index = $instance - 1; if ( ! isset( $matches[0][$match_index] ) ) { // If tag count is less than requested instance, append/prepend as fallback return 'after' === $position ? $content . $ad_html : $ad_html . $content; } $match = $matches[0][$match_index]; $tag_start_pos = $match[1]; $tag_string = $match[0]; if ( 'before' === $position ) { return substr( $content, 0, $tag_start_pos ) . $ad_html . substr( $content, $tag_start_pos ); } else { // Find where this element ends to place the ad 'after' it if ( 'p' === $tag ) { // Find closing

after the opening tag $close_pos = stripos( $content, '

', $tag_start_pos ); if ( false !== $close_pos ) { $insert_pos = $close_pos + 4; // After

return substr( $content, 0, $insert_pos ) . $ad_html . substr( $content, $insert_pos ); } } // For images or self-closing/unknown tags, place right after the opening tag closing '>' $insert_pos = $tag_start_pos + strlen( $tag_string ); return substr( $content, 0, $insert_pos ) . $ad_html . substr( $content, $insert_pos ); } } /** * Insert ad before the Xth heading tag (h1, h2, h3, h4, h5, h6). * * @param string $content The HTML content. * @param string $ad_html The ad HTML. * @param int $instance The 1-based heading index. * @return string The modified content. */ private static function insert_at_heading_position( $content, $ad_html, $instance ) { // Match any h1-h6 tag $pattern = '/]*>/i'; if ( ! preg_match_all( $pattern, $content, $matches, PREG_OFFSET_CAPTURE ) ) { return $content; // Heading not found } $match_index = $instance - 1; if ( ! isset( $matches[0][$match_index] ) ) { // Fallback: prepend if headings don't exist in requested quantity return $ad_html . $content; } $tag_start_pos = $matches[0][$match_index][1]; return substr( $content, 0, $tag_start_pos ) . $ad_html . substr( $content, $tag_start_pos ); } }