Usando wp_trim_excerpt para obtener the_excerpt() fuera del loop
Estoy construyendo un tema que mostrará extractos en la página de inicio para potencialmente docenas de posts. No tengo extractos manuales en todos mis posts, por lo que $post->post_excerpt
está vacío para muchos posts. En caso de que no haya un extracto manual, me gustaría usar la función incorporada get_the_excerpt(), pero no está disponible fuera del loop.
Rastreando la función, parece que utiliza wp_trim_excerpt de wp-includes/formatting.php para crear extractos dinámicamente. Lo estoy llamando en mi código como wp_trim_excerpt( $item->post_content )
, pero simplemente está devolviendo el contenido completo. ¿Estoy haciendo algo mal?
Sé que puedo crear mi propia función para crear un extracto, pero prefiero usar funciones incorporadas cuando es posible, manteniendo mi código compatible con otros potenciales plugins / filtros.
http://adambrown.info/p/wp_hooks/hook/wp_trim_excerpt?version=3.0&file=wp-includes/formatting.php

Desde WP 3.3.0, wp_trim_words()
es útil si puedes obtener el contenido para el cual deseas generar un extracto. Espero que esto sea útil para alguien y evite tener que crear tu propia función de recuento de palabras.

wp_trim_excerpt()
tiene una mecánica un poco curiosa: si se le pasa cualquier parámetro, no hace nada.
Esta es la lógica básica detrás de ella:
get_the_excerpt()
verifica si hay un extracto manual;wp_trim_excerpt()
interviene si no hay extracto manual y crea uno a partir del contenido o del teaser.
Ambas funciones están estrechamente ligadas a variables globales y, por lo tanto, al Loop.
Fuera del Loop, es mejor tomar el código de wp_trim_excerpt()
y escribir tu propia función de recorte.

Actualización:
Aquí hay una derivada de wp_trim_excerpt() que utilicé. Funciona perfectamente. Derivada de la versión de WordPress 3.0.4
function my_excerpt($text, $excerpt)
{
if ($excerpt) return $excerpt;
$text = strip_shortcodes( $text );
$text = apply_filters('the_content', $text);
$text = str_replace(']]>', ']]>', $text);
$text = strip_tags($text);
$excerpt_length = apply_filters('excerpt_length', 55);
$excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
$words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
if ( count($words) > $excerpt_length ) {
array_pop($words);
$text = implode(' ', $words);
$text = $text . $excerpt_more;
} else {
$text = implode(' ', $words);
}
return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
}

No es necesario que publiques una nueva respuesta, siempre puedes editar tu respuesta anterior para incluir nueva información. Por ejemplo, podrías copiar el enlace al código de WP de tu primera respuesta a esta y luego eliminar tu primera respuesta.

Aquí está mi versión de una función "trim_excerpt" que toma el objeto del post o un ID de post como parámetro.
Obviamente basado en lo que hay en el núcleo. No sé por qué esto (y get_the_author()) no tienen equivalentes fuera del bucle.
/**
* Genera un extracto del contenido, si es necesario.
*
* @param int|object $post_or_id puede ser el ID del post, o el objeto $post en sí mismo
* @param string $excerpt_more el texto que se aplica al final del extracto si lo recortamos algorítmicamente
* @return string el extracto recortado o el extracto manual si existe
*/
function zg_trim_excerpt($post_or_id, $excerpt_more = ' [...]') {
if ( is_object( $post_or_id ) ) $postObj = $post_or_id;
else $postObj = get_post($post_or_id);
$raw_excerpt = $text = $postObj->post_excerpt;
if ( '' == $text ) {
$text = $postObj->post_content;
$text = strip_shortcodes( $text );
$text = apply_filters('the_content', $text);
$text = str_replace(']]>', ']]>', $text);
$text = strip_tags($text);
$excerpt_length = apply_filters('excerpt_length', 55);
// no asumamos automáticamente que usaremos el enlace "leer más" global proporcionado por el tema
// $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
$words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
if ( count($words) > $excerpt_length ) {
array_pop($words);
$text = implode(' ', $words);
$text = $text . $excerpt_more;
} else {
$text = implode(' ', $words);
}
}
return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
}

+1 para Rast. Es muy extraño que no exista algo como get_the_excerpt($post->ID), cuando debería ser bastante obvio que debería existir. De todos modos, aquí está wp_trim_excerpt() en la versión 3.0.4 de WordPress:
http://core.trac.wordpress.org/browser/tags/3.0.4/wp-includes/formatting.php
function wp_trim_excerpt($text) {
1824 $raw_excerpt = $text;
1825 if ( '' == $text ) {
1826 $text = get_the_content('');
1827
1828 $text = strip_shortcodes( $text );
1829
1830 $text = apply_filters('the_content', $text);
1831 $text = str_replace(']]>', ']]>', $text);
1832 $text = strip_tags($text);
1833 $excerpt_length = apply_filters('excerpt_length', 55);
1834 $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
1835 $words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
1836 if ( count($words) > $excerpt_length ) {
1837 array_pop($words);
1838 $text = implode(' ', $words);
1839 $text = $text . $excerpt_more;
1840 } else {
1841 $text = implode(' ', $words);
1842 }
1843 }
1844 return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
1845 }
Puedes ver en la línea 1826 que está vinculado a la variable global $post a través de get_the_contents. Y sí, no tengo idea en qué estaban pensando. Pero a partir de aquí, reemplaza get_the_content con $text en tu propio my_excerpt, y debería comportarse de manera similar.

La función get_the_content() devolvería el contenido completo si $more != 0. Debes establecer la variable global $more a 0 para asegurarte de que la función get_the_content() devuelva el extracto.
Función wp_trim_excerpt() modificada:
function wp_trim_excerpt($text) {
$raw_excerpt = $text;
if ( '' == $text ) {
global $more;
$tmp = $more;
$more = 0;
$text = get_the_content('');
$more = $tmp;
$text = strip_shortcodes( $text );
$text = apply_filters('the_content', $text);
$text = str_replace(']]>', ']]>', $text);
$text = strip_tags($text);
$excerpt_length = apply_filters('excerpt_length', 55);
$excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
$words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
if ( count($words) > $excerpt_length ) {
array_pop($words);
$text = implode(' ', $words);
$text = $text . $excerpt_more;
} else {
$text = implode(' ', $words);
}
}
return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
}

Utilizando las respuestas anteriores, aquí hay una solución más simple que parece funcionar bien:
global $post;
$excerpt = apply_filters('get_the_excerpt', get_post_field('post_excerpt', $post->ID));
if ( $excerpt == '' ) {
$excerpt = wp_trim_words( $post->post_content, 55 );
}
Lo estoy usando en las etiquetas <meta>
dentro de una función para definir las descripciones de OpenGraph. Entonces simplemente agrego:
<meta property="og:description" content="<?php echo esc_html( $excerpt ); ?>" />
