fix(php): exclude tables and embeds from ad injection
- Add mapForbiddenZones() for tables, iframes, embeds - Remove table from scannable elements pattern - Filter positions inside forbidden zones - Fixes ads inside table cells and YouTube embeds
This commit is contained in:
@@ -163,6 +163,11 @@ final class ContentAdInjector
|
|||||||
/**
|
/**
|
||||||
* PASO 1: Escanea el contenido para encontrar ubicaciones elegibles
|
* PASO 1: Escanea el contenido para encontrar ubicaciones elegibles
|
||||||
*
|
*
|
||||||
|
* IMPORTANTE: No inserta anuncios:
|
||||||
|
* - Dentro de tablas (<table>...</table>)
|
||||||
|
* - Dentro de embeds/iframes (YouTube, etc.)
|
||||||
|
* - Despues de tablas (tables excluidas completamente)
|
||||||
|
*
|
||||||
* @return array{position: int, type: string, tag: string, element_index: int}[]
|
* @return array{position: int, type: string, tag: string, element_index: int}[]
|
||||||
*/
|
*/
|
||||||
private function scanContent(string $content): array
|
private function scanContent(string $content): array
|
||||||
@@ -170,8 +175,12 @@ final class ContentAdInjector
|
|||||||
$locations = [];
|
$locations = [];
|
||||||
$elementIndex = 0;
|
$elementIndex = 0;
|
||||||
|
|
||||||
|
// Primero, mapear zonas prohibidas (tablas, iframes, embeds)
|
||||||
|
$forbiddenZones = $this->mapForbiddenZones($content);
|
||||||
|
|
||||||
// Regex para encontrar tags de cierre de elementos de bloque
|
// Regex para encontrar tags de cierre de elementos de bloque
|
||||||
$pattern = '/(<\/(?:p|h2|h3|figure|ul|ol|table|blockquote)>)/i';
|
// NOTA: Excluimos </table> - no queremos insertar despues de tablas
|
||||||
|
$pattern = '/(<\/(?:p|h2|h3|figure|ul|ol|blockquote)>)/i';
|
||||||
|
|
||||||
// Encontrar todas las coincidencias con sus posiciones
|
// Encontrar todas las coincidencias con sus posiciones
|
||||||
if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
|
if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
|
||||||
@@ -179,6 +188,11 @@ final class ContentAdInjector
|
|||||||
$tag = strtolower($match[0]);
|
$tag = strtolower($match[0]);
|
||||||
$position = $match[1] + strlen($match[0]); // Posicion despues del tag
|
$position = $match[1] + strlen($match[0]); // Posicion despues del tag
|
||||||
|
|
||||||
|
// Verificar que no este dentro de una zona prohibida
|
||||||
|
if ($this->isInForbiddenZone($position, $forbiddenZones)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$type = $this->getTypeFromTag($tag);
|
$type = $this->getTypeFromTag($tag);
|
||||||
if ($type) {
|
if ($type) {
|
||||||
$locations[] = [
|
$locations[] = [
|
||||||
@@ -191,8 +205,8 @@ final class ContentAdInjector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detectar imagenes standalone (no dentro de figure)
|
// Detectar imagenes standalone (no dentro de figure ni zonas prohibidas)
|
||||||
$locations = array_merge($locations, $this->scanStandaloneImages($content, $elementIndex));
|
$locations = array_merge($locations, $this->scanStandaloneImages($content, $elementIndex, $forbiddenZones));
|
||||||
|
|
||||||
// Validar listas (minimo 3 items)
|
// Validar listas (minimo 3 items)
|
||||||
$locations = $this->validateLists($content, $locations);
|
$locations = $this->validateLists($content, $locations);
|
||||||
@@ -208,8 +222,75 @@ final class ContentAdInjector
|
|||||||
return $locations;
|
return $locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapea zonas donde NO se deben insertar anuncios
|
||||||
|
* Incluye: tablas, iframes, embeds de video
|
||||||
|
*
|
||||||
|
* @return array{start: int, end: int}[]
|
||||||
|
*/
|
||||||
|
private function mapForbiddenZones(string $content): array
|
||||||
|
{
|
||||||
|
$zones = [];
|
||||||
|
|
||||||
|
// Tablas: <table>...</table>
|
||||||
|
if (preg_match_all('/<table[^>]*>.*?<\/table>/is', $content, $matches, PREG_OFFSET_CAPTURE)) {
|
||||||
|
foreach ($matches[0] as $match) {
|
||||||
|
$zones[] = [
|
||||||
|
'start' => $match[1],
|
||||||
|
'end' => $match[1] + strlen($match[0]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iframes (YouTube, Vimeo, etc): <iframe>...</iframe>
|
||||||
|
if (preg_match_all('/<iframe[^>]*>.*?<\/iframe>/is', $content, $matches, PREG_OFFSET_CAPTURE)) {
|
||||||
|
foreach ($matches[0] as $match) {
|
||||||
|
$zones[] = [
|
||||||
|
'start' => $match[1],
|
||||||
|
'end' => $match[1] + strlen($match[0]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divs con clase de embed/video (wp-block-embed, youtube, video-container, etc)
|
||||||
|
if (preg_match_all('/<div[^>]*class="[^"]*(?:embed|video|youtube|vimeo|player)[^"]*"[^>]*>.*?<\/div>/is', $content, $matches, PREG_OFFSET_CAPTURE)) {
|
||||||
|
foreach ($matches[0] as $match) {
|
||||||
|
$zones[] = [
|
||||||
|
'start' => $match[1],
|
||||||
|
'end' => $match[1] + strlen($match[0]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure con iframe (embeds de WordPress)
|
||||||
|
if (preg_match_all('/<figure[^>]*class="[^"]*wp-block-embed[^"]*"[^>]*>.*?<\/figure>/is', $content, $matches, PREG_OFFSET_CAPTURE)) {
|
||||||
|
foreach ($matches[0] as $match) {
|
||||||
|
$zones[] = [
|
||||||
|
'start' => $match[1],
|
||||||
|
'end' => $match[1] + strlen($match[0]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $zones;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica si una posicion esta dentro de una zona prohibida
|
||||||
|
*/
|
||||||
|
private function isInForbiddenZone(int $position, array $forbiddenZones): bool
|
||||||
|
{
|
||||||
|
foreach ($forbiddenZones as $zone) {
|
||||||
|
if ($position >= $zone['start'] && $position <= $zone['end']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convierte tag de cierre a tipo de elemento
|
* Convierte tag de cierre a tipo de elemento
|
||||||
|
* NOTA: </table> excluido - no insertamos ads despues de tablas
|
||||||
*/
|
*/
|
||||||
private function getTypeFromTag(string $tag): ?string
|
private function getTypeFromTag(string $tag): ?string
|
||||||
{
|
{
|
||||||
@@ -219,16 +300,15 @@ final class ContentAdInjector
|
|||||||
'</h3>' => 'h3',
|
'</h3>' => 'h3',
|
||||||
'</figure>' => 'image',
|
'</figure>' => 'image',
|
||||||
'</ul>', '</ol>' => 'list',
|
'</ul>', '</ol>' => 'list',
|
||||||
'</table>' => 'table',
|
|
||||||
'</blockquote>' => 'blockquote',
|
'</blockquote>' => 'blockquote',
|
||||||
default => null,
|
default => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detecta imagenes que no estan dentro de figure
|
* Detecta imagenes que no estan dentro de figure ni zonas prohibidas
|
||||||
*/
|
*/
|
||||||
private function scanStandaloneImages(string $content, int $startIndex): array
|
private function scanStandaloneImages(string $content, int $startIndex, array $forbiddenZones = []): array
|
||||||
{
|
{
|
||||||
$locations = [];
|
$locations = [];
|
||||||
|
|
||||||
@@ -241,6 +321,11 @@ final class ContentAdInjector
|
|||||||
$imgTag = $match[0];
|
$imgTag = $match[0];
|
||||||
$imgPosition = $match[1];
|
$imgPosition = $match[1];
|
||||||
|
|
||||||
|
// Verificar que no este en zona prohibida
|
||||||
|
if ($this->isInForbiddenZone($imgPosition, $forbiddenZones)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Verificar si hay un <figure> abierto antes de esta imagen
|
// Verificar si hay un <figure> abierto antes de esta imagen
|
||||||
$contentBefore = substr($content, 0, $imgPosition);
|
$contentBefore = substr($content, 0, $imgPosition);
|
||||||
$lastFigureOpen = strrpos($contentBefore, '<figure');
|
$lastFigureOpen = strrpos($contentBefore, '<figure');
|
||||||
|
|||||||
Reference in New Issue
Block a user