Como establecer automáticamente la primera imagen de una entrada como miniatura? O enlazar el título de la entrada a un recurso externo? Función lista para usar.
ES

Como extraer el primer enlace del contenido de una entrada (fragmento PHP)

5.00 /5 - (22 votes )
Última verificación: 1 de mayo de 2026
7min de lectura
Tutorial
Desarrollador full-stack

A veces construimos temas tipo “Agregador de Noticias”, donde una entrada no tiene su propio contenido, sino que solo enlaza a un artículo externo. O queremos que la primera imagen del contenido se convierta automáticamente en la “Imagen Destacada” si el editor olvida establecerla.

Descubre más sobre desarrollo profesional WordPress en WPPoland. En ambos casos, necesitamos “escanear” el contenido de la entrada (the_content) y extraer la primera etiqueta <a> o <img> de el.

#Método 1: Clase DOMDocument (recomendado)

Muchos desarrolladores usan Expresiones Regulares (Regex) para esto, pero analizar HTML con Regex es una mala practica. Es mejor usar la clase integrada DOMDocument de PHP.

Aqui tienes una función lista para pegar en functions.php:

function get_first_link_url( $content ) {
    // Si el contenido esta vacio, devolver false
    if ( empty( $content ) ) return false;

    $doc = new DOMDocument();

    // Suprimir errores de HTML5 (DOMDocument es antiguo y a veces se queja de <section> etc.)
    libxml_use_internal_errors(true);

    // Cargar HTML (con hack UTF-8)
    $doc->loadHTML( mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8') );

    $links = $doc->getElementsByTagName('a');

    if ( $links->length > 0 ) {
        // Devolver href del primer enlace
        return $links->item(0)->getAttribute('href');
    }

    return false;
}

#Uso en el Loop

$link = get_first_link_url( get_the_content() );

if ( $link ) {
   echo '<a href="' . esc_url($link) . '" class="read-more-external">Leer original</a>';
}

Esta solución es solida, segura y maneja errores en la estructura HTML mejor que cualquier Regex.

#Método 2: Extraer la primera imagen

El mismo enfoque funciona para imágenes. Esto es útil para temas de “lista de posts” donde necesitas una miniatura automática:

function get_first_image_url( $content ) {
    if ( empty( $content ) ) return false;

    $doc = new DOMDocument();
    libxml_use_internal_errors(true);
    $doc->loadHTML( mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8') );

    $images = $doc->getElementsByTagName('img');

    if ( $images->length > 0 ) {
        return $images->item(0)->getAttribute('src');
    }

    return false;
}

#Uso como imagen destacada automática

/**
 * Establecer automáticamente la primera imagen del contenido como imagen destacada
 * si no se ha establecido una manualmente.
 */
add_action( 'save_post', function( $post_id ) {
    // Evitar autoguardados y revisiones
    if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
        return;
    }

    // Si ya tiene imagen destacada, no hacer nada
    if ( has_post_thumbnail( $post_id ) ) {
        return;
    }

    $post = get_post( $post_id );
    $image_url = get_first_image_url( $post->post_content );

    if ( $image_url ) {
        // Buscar el attachment por URL
        $attachment_id = attachment_url_to_postid( $image_url );
        if ( $attachment_id ) {
            set_post_thumbnail( $post_id, $attachment_id );
        }
    }
});

#Método 3: Regex (solo como referencia)

Aunque no se recomienda, aquí esta el enfoque con Regex para comparación:

function get_first_link_url_regex( $content ) {
    if ( empty( $content ) ) return false;

    // Patron para encontrar la primera etiqueta <a>
    $pattern = '/<a\s[^>]*href=["\']([^"\']+)["\'][^>]*>/i';

    if ( preg_match( $pattern, $content, $matches ) ) {
        return $matches[1];
    }

    return false;
}

Por que no usar Regex:

  • Falla con HTML malformado
  • No maneja correctamente atributos en diferente orden
  • Problemás con HTML anidado
  • Dificil de mantener y depurar
  • No entiende la estructura del DOM

#Versión avanzada: Extraer multiples datos del primer enlace

A veces necesitas más que solo la URL. Aqui hay una versión que extrae toda la información del enlace:

function get_first_link_data( $content ) {
    if ( empty( $content ) ) return false;

    $doc = new DOMDocument();
    libxml_use_internal_errors(true);
    $doc->loadHTML( mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8') );

    $links = $doc->getElementsByTagName('a');

    if ( $links->length > 0 ) {
        $first_link = $links->item(0);

        return array(
            'url'    => $first_link->getAttribute('href'),
            'text'   => $first_link->textContent,
            'title'  => $first_link->getAttribute('title'),
            'target' => $first_link->getAttribute('target'),
            'rel'    => $first_link->getAttribute('rel'),
            'class'  => $first_link->getAttribute('class'),
        );
    }

    return false;
}

#Uso:

$link_data = get_first_link_data( get_the_content() );

if ( $link_data ) {
    printf(
        '<a href="%s" title="%s" target="%s" rel="noopener noreferrer">%s</a>',
        esc_url( $link_data['url'] ),
        esc_attr( $link_data['title'] ),
        esc_attr( $link_data['target'] ?: '_blank' ),
        esc_html( $link_data['text'] )
    );
}

#Filtrar enlaces por tipo

Para temas más complejos, puedes necesitar filtrar enlaces por tipo (internos vs externos):

function get_first_external_link( $content ) {
    if ( empty( $content ) ) return false;

    $doc = new DOMDocument();
    libxml_use_internal_errors(true);
    $doc->loadHTML( mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8') );

    $links = $doc->getElementsByTagName('a');
    $site_url = home_url();

    foreach ( $links as $link ) {
        $href = $link->getAttribute('href');

        // Saltar enlaces vacios, anclas y enlaces internos
        if ( empty( $href ) || strpos( $href, '#' ) === 0 ) {
            continue;
        }

        // Comprobar si es externo
        if ( strpos( $href, $site_url ) === false && strpos( $href, 'http' ) === 0 ) {
            return $href;
        }
    }

    return false;
}

#Consideraciones de rendimiento

#Cache de resultados

Si extraes enlaces frecuentemente, almacena en cache los resultados:

function get_cached_first_link( $post_id = null ) {
    $post_id = $post_id ?: get_the_ID();
    $cache_key = 'first_link_' . $post_id;

    $result = get_transient( $cache_key );
    if ( false === $result ) {
        $post = get_post( $post_id );
        $result = get_first_link_url( $post->post_content );
        $result = $result ?: 'none'; // Almacenar 'none' para evitar consultas repetidas

        set_transient( $cache_key, $result, HOUR_IN_SECONDS );
    }

    return $result === 'none' ? false : $result;
}

// Invalidar cache al actualizar la entrada
add_action( 'save_post', function( $post_id ) {
    delete_transient( 'first_link_' . $post_id );
});

#Uso con Object Cache

Para sitios con Redis o Memcached:

function get_cached_first_link_object_cache( $post_id = null ) {
    $post_id = $post_id ?: get_the_ID();
    $cache_key = 'first_link_' . $post_id;

    $result = wp_cache_get( $cache_key, 'post_links' );
    if ( false === $result ) {
        $post = get_post( $post_id );
        $result = get_first_link_url( $post->post_content ) ?: '';
        wp_cache_set( $cache_key, $result, 'post_links', 3600 );
    }

    return $result ?: false;
}

#Seguridad: Validación de URLs

Siempre válida las URLs extraidas antes de usarlas:

function get_safe_first_link( $content ) {
    $url = get_first_link_url( $content );

    if ( ! $url ) return false;

    // Validar que es una URL valida
    if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
        return false;
    }

    // Solo permitir protocolos seguros
    $allowed_protocols = array( 'http', 'https' );
    $parsed = wp_parse_url( $url );

    if ( ! isset( $parsed['scheme'] ) || ! in_array( $parsed['scheme'], $allowed_protocols ) ) {
        return false;
    }

    return esc_url( $url );
}

#Integración con formatos de entrada de WordPress

WordPress tiene formatos de entrada como “Link” que se benefician de esta funcionalidad:

// En functions.php - Soporte para formatos de entrada
add_theme_support( 'post-formats', array( 'link', 'image', 'video', 'quote' ) );

// En el template - Obtener URL para formato 'link'
if ( has_post_format( 'link' ) ) {
    $link = get_first_link_url( get_the_content() );
    if ( $link ) {
        echo '<a href="' . esc_url( $link ) . '" class="post-link-format" target="_blank" rel="noopener">';
        the_title();
        echo '</a>';
    }
} else {
    echo '<a href="' . get_permalink() . '">';
    the_title();
    echo '</a>';
}

#Aplicación práctica: Tema de agregador de noticias

Un caso de uso completo para un tema estilo “agregador de noticias”:

// En archive.php o index.php
while ( have_posts() ) : the_post();
    $external_link = get_first_link_url( get_the_content() );
    $target_url = $external_link ?: get_permalink();
    $is_external = (bool) $external_link;
    ?>

    <article class="news-item <?php echo $is_external ? 'external' : 'internal'; ?>">
        <?php if ( has_post_thumbnail() ) : ?>
            <a href="<?php echo esc_url( $target_url ); ?>"
               <?php echo $is_external ? 'target="_blank" rel="noopener noreferrer"' : ''; ?>>
                <?php the_post_thumbnail( 'medium' ); ?>
            </a>
        <?php endif; ?>

        <h2>
            <a href="<?php echo esc_url( $target_url ); ?>"
               <?php echo $is_external ? 'target="_blank" rel="noopener noreferrer"' : ''; ?>>
                <?php the_title(); ?>
                <?php if ( $is_external ) : ?>
                    <span class="external-icon" aria-label="Enlace externo">&#8599;</span>
                <?php endif; ?>
            </a>
        </h2>

        <div class="meta">
            <?php if ( $is_external ) : ?>
                <span class="source"><?php echo esc_html( wp_parse_url( $external_link, PHP_URL_HOST ) ); ?></span>
            <?php endif; ?>
            <time datetime="<?php echo get_the_date('c'); ?>"><?php echo get_the_date(); ?></time>
        </div>
    </article>

<?php endwhile; ?>

#Resumen

La extraccion de enlaces del contenido es una técnica fundamental para desarrolladores WordPress. DOMDocument es siempre la opción correcta sobre Regex para analizar HTML. Recuerda siempre:

  • Usar DOMDocument en lugar de Regex para analizar HTML
  • Manejar correctamente la codificacion UTF-8
  • Validar y sanitizar las URLs extraidas
  • Almacenar en cache los resultados para rendimiento
  • Considerar la seguridad al mostrar enlaces externos
Siguiente paso

Transforma el artículo en una implementación real

Este bloque refuerza el enlazado interno y lleva al lector al siguiente paso más útil dentro de la arquitectura del sitio.

¿Quieres implementar esto en tu sitio?

Si quieres transformar el artículo en mejoras concretas, rediseño o un plan de implementación, puedo cerrar el alcance y ejecutar.

Cluster relacionado

Explora otros servicios WordPress y base de conocimiento

Refuerza tu negocio con soporte técnico profesional en áreas clave del ecosistema WordPress.

FAQ del artículo

Preguntas Frecuentes

Respuestas prácticas para aplicar el tema en la ejecución real.

SEO-ready GEO-ready AEO-ready 3 Q&A
Por que usar DOMDocument en lugar de Regex para extraer enlaces?
DOMDocument es más fiable y seguro para analizar HTML. Regex puede fallar con HTML malformado, atributos en orden diferente o HTML anidado. DOMDocument maneja estos casos correctamente.
Como manejar contenido con caracteres UTF-8?
Usa mb_convert_encoding con HTML-ENTITIES antes de cargar el contenido en DOMDocument para asegurar que los caracteres especiales se manejen correctamente.
Se puede extraer la primera imagen en lugar del primer enlace?
Si. Simplemente cambia getElementsByTagName('a') por getElementsByTagName('img') y getAttribute('href') por getAttribute('src').

¿Necesitas un FAQ adaptado a tu sector y mercado? Preparamos una versión alineada con tus objetivos de negocio.

Hablemos

Artículos Relacionados

Necesitas mostrar la jerarquía de categorías en tu tema? Aprende como obtener la categoría actual y su padre usando get_the_category() y cat_is_ancestor_of().
development

Como mostrar la categoría actual y padre de una entrada (fragmento WordPress)

Necesitas mostrar la jerarquía de categorías en tu tema? Aprende como obtener la categoría actual y su padre usando get_the_category() y cat_is_ancestor_of().

Un snippet práctico de WordPress para agregar slugs de páginas padre y ancestros a body_class(), permitiendo apuntar a secciones del sitio con CSS más limpio.
wordpress

Agregar slugs de páginas padre a las clases body de WordPress

Un snippet práctico de WordPress para agregar slugs de páginas padre y ancestros a body_class(), permitiendo apuntar a secciones del sitio con CSS más limpio.

Descubre cuándo una reconstrucción de sitio web es necesaria. 7 señales técnicas y de negocio medibles que indican que tu sitio necesita modernización en 2026.
wordpress

¿Cuándo reconstruir tu sitio web? 7 señales de que necesita modernización

Descubre cuándo una reconstrucción de sitio web es necesaria. 7 señales técnicas y de negocio medibles que indican que tu sitio necesita modernización en 2026.