config = wp_parse_args($placement_config, [ 'position' => 2, 'location' => 'after_paragraph', 'alignment' => 'default', 'readmore_text' => __('Read More', 'adpagex'), 'target_element' => '', 'show_readmore' => 'no' ]); } /** * Process content - hỗ trợ tất cả vị trí với hoặc không có readmore */ public function process_content($content, $ad_html = '', $placement_config = []) { if (!empty($placement_config)) { $this->set_config($placement_config); } // Kiểm tra có bật readmore không $use_readmore = !empty($this->config['show_readmore']) && $this->config['show_readmore'] === 'yes'; $location = $this->config['location'] ?? 'after_paragraph'; switch ($location) { case 'after_content': return $use_readmore ? $this->insert_after_content_with_readmore($content, $ad_html) : $this->insert_after_content_simple($content, $ad_html); case 'before_content': return $use_readmore ? $this->insert_before_content_with_readmore($content, $ad_html) : $this->insert_before_content_simple($content, $ad_html); case 'after_paragraph': case 'before_paragraph': return $use_readmore ? $this->insert_at_paragraph_with_readmore($content, $ad_html) : $this->insert_at_paragraph_simple($content, $ad_html); case 'after_image': case 'before_image': return $use_readmore ? $this->insert_at_image_with_readmore($content, $ad_html) : $this->insert_at_image_simple($content, $ad_html); case 'after_heading': case 'before_heading': return $use_readmore ? $this->insert_at_heading_with_readmore($content, $ad_html) : $this->insert_at_heading_simple($content, $ad_html); case 'custom_element': return $use_readmore ? $this->insert_at_custom_element_with_readmore($content, $ad_html) : $this->insert_at_custom_element_simple($content, $ad_html); default: return $use_readmore ? $this->insert_at_paragraph_with_readmore($content, $ad_html) : $this->insert_at_paragraph_simple($content, $ad_html); } } // ========================= // SIMPLE INSERTIONS (NO READMORE) // ========================= private function insert_after_content_simple($content, $ad_html) { return $content . $ad_html; } private function insert_before_content_simple($content, $ad_html) { return $ad_html . $content; } private function insert_at_paragraph_simple($content, $ad_html) { $paragraph = max(1, (int)$this->config['position']); $location = $this->config['location']; if ($location === 'before_paragraph') { return $this->insert_before_paragraph_simple($content, $ad_html, $paragraph); } else { return $this->insert_after_paragraph_simple($content, $ad_html, $paragraph); } } private function insert_after_paragraph_simple($content, $ad_html, $paragraph_number) { $closingTag = '

'; $offset = 0; $paragraphs_found = 0; while (($pos = stripos($content, $closingTag, $offset)) !== false) { $paragraphs_found++; if ($paragraphs_found == $paragraph_number) { return substr($content, 0, $pos + strlen($closingTag)) . $ad_html . substr($content, $pos + strlen($closingTag)); } $offset = $pos + strlen($closingTag); } return $content . $ad_html; } private function insert_before_paragraph_simple($content, $ad_html, $paragraph_number) { $pattern = '/]*)>/i'; preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) >= $paragraph_number) { $target_paragraph = $matches[0][$paragraph_number - 1]; $insert_pos = $target_paragraph[1]; return substr($content, 0, $insert_pos) . $ad_html . substr($content, $insert_pos); } return $content . $ad_html; } private function insert_at_image_simple($content, $ad_html) { $image_number = max(1, (int)$this->config['position']); $location = $this->config['location']; $pattern = '/]+>/i'; preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) < $image_number) { return $content . $ad_html; } $target_image = $matches[0][$image_number - 1]; $img_offset = $target_image[1]; $img_length = strlen($target_image[0]); if ($location === 'before_image') { $insert_pos = $img_offset; } else { $insert_pos = $img_offset + $img_length; } return substr($content, 0, $insert_pos) . $ad_html . substr($content, $insert_pos); } private function insert_at_heading_simple($content, $ad_html) { $heading_number = max(1, (int)$this->config['position']); $location = $this->config['location']; $pattern = '/]*>.*?<\/h[1-6]>/is'; preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) < $heading_number) { return $content . $ad_html; } $target_heading = $matches[0][$heading_number - 1]; $heading_offset = $target_heading[1]; $heading_length = strlen($target_heading[0]); if ($location === 'before_heading') { $insert_pos = $heading_offset; } else { $insert_pos = $heading_offset + $heading_length; } return substr($content, 0, $insert_pos) . $ad_html . substr($content, $insert_pos); } private function insert_at_custom_element_simple($content, $ad_html) { $target_element = $this->config['target_element'] ?? ''; $position = max(1, (int)$this->config['position']); if (empty($target_element)) { return $content . $ad_html; } $pattern = $this->build_element_pattern($target_element); if (!$pattern) { return $content . $ad_html; } preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) < $position) { return $content . $ad_html; } $target = $matches[0][$position - 1]; $element_offset = $target[1]; $element_length = strlen($target[0]); $insert_pos = $element_offset + $element_length; return substr($content, 0, $insert_pos) . $ad_html . substr($content, $insert_pos); } // ========================= // READMORE INSERTIONS // ========================= private function insert_after_content_with_readmore($content, $ad_html) { $readmore_id = 'adpagex-readmore-' . uniqid(); $readmore_button = $this->build_readmore_button($readmore_id); $position = max(1, (int)$this->config['position']); $content_parts = $this->split_content_by_percentage($content, $position); return $content_parts['before'] . $ad_html . $readmore_button . ''; } private function insert_before_content_with_readmore($content, $ad_html) { $readmore_id = 'adpagex-readmore-' . uniqid(); $readmore_button = $this->build_readmore_button($readmore_id); return $ad_html . $readmore_button . ''; } private function insert_at_paragraph_with_readmore($content, $ad_html) { $paragraph = max(1, (int)$this->config['position']); $location = $this->config['location']; $readmore_id = 'adpagex-readmore-' . uniqid(); if (extension_loaded('dom')) { return $this->process_paragraph_with_dom($content, $ad_html, $paragraph, $location, $readmore_id); } return $this->process_paragraph_with_regex($content, $ad_html, $paragraph, $location, $readmore_id); } private function insert_at_image_with_readmore($content, $ad_html) { $image_number = max(1, (int)$this->config['position']); $location = $this->config['location']; $readmore_id = 'adpagex-readmore-' . uniqid(); $pattern = '/]+>/i'; preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) < $image_number) { if (count($matches[0]) === 0) { return $this->insert_after_content_with_readmore($content, $ad_html); } $image_number = count($matches[0]); } $target_image = $matches[0][$image_number - 1]; $img_offset = $target_image[1]; $img_length = strlen($target_image[0]); $readmore_button = $this->build_readmore_button($readmore_id); if ($location === 'before_image') { $insert_pos = $img_offset; $remaining_content = substr($content, $insert_pos); } else { $insert_pos = $img_offset + $img_length; $remaining_content = substr($content, $insert_pos); } return substr($content, 0, $insert_pos) . $ad_html . $readmore_button . ''; } private function insert_at_heading_with_readmore($content, $ad_html) { $heading_number = max(1, (int)$this->config['position']); $location = $this->config['location']; $readmore_id = 'adpagex-readmore-' . uniqid(); $pattern = '/]*>.*?<\/h[1-6]>/is'; preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) < $heading_number) { if (count($matches[0]) === 0) { return $this->insert_after_content_with_readmore($content, $ad_html); } $heading_number = count($matches[0]); } $target_heading = $matches[0][$heading_number - 1]; $heading_offset = $target_heading[1]; $heading_length = strlen($target_heading[0]); $readmore_button = $this->build_readmore_button($readmore_id); if ($location === 'before_heading') { $insert_pos = $heading_offset; $remaining_content = substr($content, $insert_pos); } else { $insert_pos = $heading_offset + $heading_length; $remaining_content = substr($content, $insert_pos); } return substr($content, 0, $insert_pos) . $ad_html . $readmore_button . ''; } private function insert_at_custom_element_with_readmore($content, $ad_html) { $target_element = $this->config['target_element'] ?? ''; $position = max(1, (int)$this->config['position']); $readmore_id = 'adpagex-readmore-' . uniqid(); if (empty($target_element)) { return $this->insert_after_content_with_readmore($content, $ad_html); } $pattern = $this->build_element_pattern($target_element); if (!$pattern) { return $this->insert_after_content_with_readmore($content, $ad_html); } preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) < $position) { return $this->insert_after_content_with_readmore($content, $ad_html); } $target = $matches[0][$position - 1]; $element_offset = $target[1]; $element_length = strlen($target[0]); $readmore_button = $this->build_readmore_button($readmore_id); $insert_pos = $element_offset + $element_length; $remaining_content = substr($content, $insert_pos); return substr($content, 0, $insert_pos) . $ad_html . $readmore_button . ''; } // ========================= // DOM & REGEX PROCESSING // ========================= private function process_paragraph_with_dom($content, $ad_html, $paragraph, $location, $readmore_id) { libxml_use_internal_errors(true); $dom = new \DOMDocument('1.0', 'UTF-8'); $wrapped = mb_convert_encoding( '' . $content . '', 'HTML-ENTITIES', 'UTF-8' ); @$dom->loadHTML($wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); $body = $dom->getElementsByTagName('body')->item(0); if (!$body) { return $content . $ad_html; } $xpath = new \DOMXPath($dom); // Exclude

nested inside blockquote/figure/li (same as Ad Inserter defaults) $paragraphs = $xpath->query('//p[normalize-space() != "" and not(ancestor::blockquote) and not(ancestor::figure) and not(ancestor::li)]'); if ($paragraphs->length < $paragraph) { if ($paragraphs->length === 0) { return $this->insert_after_content_with_readmore($content, $ad_html); } $paragraph = $paragraphs->length; } $target_paragraph = $paragraphs->item($paragraph - 1); if (!$target_paragraph) { return $this->insert_after_content_with_readmore($content, $ad_html); } $readmore_button = $this->build_readmore_button($readmore_id); // Parse ad HTML as real HTML (not XML) — appendXML fails silently on