Contents
Introducción: shortcodes con contenido anidado en WordPress
Los shortcodes de WordPress son una forma poderosa de introducir bloques dinámicos dentro del contenido sin tocar plantillas. Un caso frecuente es crear shortcodes que acepten contenido anidado —es decir, shortcodes que funcionan como contenedores y pueden envolver texto, HTML o incluso otros shortcodes. Este artículo explica en detalle cómo implementar shortcodes que acepten contenido anidado, cubrirá buenas prácticas de seguridad y rendimiento, y ofrecerá soluciones para casos complejos (por ejemplo, shortcodes anidados del mismo tipo).
Conceptos clave
- Shortcode de tipo enclosing: tiene apertura y cierre, p. ej. [box]contenido[/box]. En PHP la callback recibe dos parámetros: atts y content.
- Procesamiento de shortcodes anidados: para que el contenido anidado sea interpretado como shortcode, es necesario ejecutar do_shortcode() sobre el contenido recibido.
- Saneamiento y escapado: nunca devuelvas HTML sin sanear. Usa esc_attr, esc_html, wp_kses_post según convenga.
1) Implementación básica de un shortcode contenedor
A continuación un ejemplo sencillo de un shortcode llamado box que acepta atributos y contenido. El contenido se procesa con do_shortcode para permitir anidamiento de otros shortcodes dentro del box.
lt?php function my_box_shortcode( atts, content = null ) { // Valores por defecto atts = shortcode_atts( array( class => , title => , ), atts, box ) // Procesa shortcodes anidados y elimina autoparagraphs no deseados content = do_shortcode( shortcode_unautop( content ) ) // Sanitiza salida class = esc_attr( atts[class] ) title = esc_html( atts[title] ) // Permite HTML seguro en el contenido safe_content = wp_kses_post( content ) return ltdiv class=my-box . class . gt . ( title ? lth3gt . title . lt/h3gt : ) . safe_content . lt/divgt } add_shortcode( box, my_box_shortcode ) ?gt
Uso en el editor:
[box class=important title=Aviso] Este es el contenido del box. Aquí puedes incluir otro shortcode como [button]¡Acción![/button] [/box]
2) Shortcodes anidados de distintos tipos
Si vas a tener varios shortcodes que se combinen (por ejemplo, column y box), la técnica es la misma: en la callback del contenedor ejecuta do_shortcode() sobre content. Ejemplo de registro de dos shortcodes y un uso combinado.
lt?php function my_column_shortcode( atts, content = null ) { atts = shortcode_atts( array( width =gt 50% ), atts, column ) content = do_shortcode( shortcode_unautop( content ) ) return ltdiv class=column style=width: . esc_attr( atts[width] ) . gt . wp_kses_post( content ) . lt/divgt } add_shortcode( column, my_column_shortcode ) function my_button_shortcode( atts, content = null ) { atts = shortcode_atts( array( url =gt # ), atts, button ) content = trim( content ) return lta class=btn href= . esc_url( atts[url] ) . gt . esc_html( content ) . lt/agt } add_shortcode( button, my_button_shortcode ) ?gt
Uso:
[column width=50%] [box title=Columna izquierda]Contenido con un [button url=https://ejemplo.com]Enlace[/button][/box] [/column] [column width=50%] [box title=Columna derecha]Otro contenido[/box] [/column]
3) Casos complejos: shortcodes anidados del mismo tipo
WordPress maneja razonablemente bien nested shortcodes de distinto tipo, pero cuando anidas shortcodes del mismo nombre (por ejemplo: [note][note]…[/note][/note]) puede haber problemas dependiendo de la complejidad del contenido y de cómo los shortcodes están definidos. Para tener mayor control, puedes implementar un analizador recursivo que utilice get_shortcode_regex() y preg_replace_callback() para procesar correctamente todos los niveles.
Ejemplo de función recursiva que procesa todos los shortcodes registrados y mantiene el orden y anidamiento:
lt?php / do_shortcode_nested: procesa shortcodes recursivamente respetando el anidado. Sustituye a do_shortcode() en contextos donde necesites máxima robustez. / function do_shortcode_nested( content ) { if ( empty( content ) ) { return content } // Obtiene los nombres de todos los shortcodes registrados tagnames = array_keys( GLOBALS[shortcode_tags] ) if ( empty( tagnames ) ) { return content } pattern = get_shortcode_regex( tagnames ) // Callback recursivo content = preg_replace_callback( /pattern/s, function( m ) { // m: 0=full, 1=[, 2=tag, 3=atts, 4=shortcode_self_close, 5=content, 6=] según get_shortcode_regex tag = m[2] atts_text = m[3] inner = isset( m[5] ) ? do_shortcode_nested( m[5] ) : // Parsea atributos a array atts = shortcode_parse_atts( atts_text ) // Si existe handler registrado, lo llamamos if ( isset( GLOBALS[shortcode_tags][ tag ] ) is_callable( GLOBALS[shortcode_tags][ tag ] ) ) { return call_user_func( GLOBALS[shortcode_tags][ tag ], atts, inner, tag ) } // Si no hay handler, devolvemos el original sin cambios return m[0] }, content ) return content } ?gt
En lugar de usar do_shortcode( post->post_content ) puedes usar do_shortcode_nested( post->post_content ) para un procesamiento más controlado. Ten en cuenta que esta función recursiva puede afectar el rendimiento si el contenido es muy grande o hay muchos shortcodes evalúa su uso según tu caso.
4) Buenas prácticas de seguridad y saneamiento
- Sanea todos los atributos: usa esc_attr() o esc_url() según corresponda.
- Sanea el contenido: si esperas HTML permitido, usa wp_kses_post() o wp_kses() con un conjunto de etiquetas permitido. Si esperas texto plano, usa esc_html().
- Evita ejecutar código peligroso: no evalúes PHP ni permitas que usuarios no confiables definan atributos con JavaScript.
- Limita qué shortcodes permiten HTML bruto: sólo los controles administrativos deben poder insertar HTML sin filtros.
5) Depuración y consejos prácticos
- Si un shortcode no se procesa, comprueba que está registrado (isset( GLOBALS[shortcode_tags][ tag ] )).
- Usa var_dump() o error_log() en la callback para inspeccionar atts y content durante desarrollo.
- Si ves auto-paragraph problemático, emplea shortcode_unautop() para limpiar los ltpgt añadidos por wpautop.
- Para salida compleja, construye HTML con plantillas o partials y no con cadenas concatenadas largas.
6) Rendimiento
Los shortcodes se ejecutan en tiempo de renderizado del contenido si realizas operaciones costosas (llamadas remotas, consultas pesadas) piensa en:
- Cachear la salida del shortcode con transients o fragment caching.
- Limitar el uso de shortcodes complejos en listados masivos.
- Ejecutar operaciones costosas sólo en admin o en hooks específicos si no son necesarias en cada carga.
7) Resumen de ejemplos prácticos y uso
Shortcode contenedor simple | [box]Contenido[/box] — callback usa do_shortcode(shortcode_unautop(content)). |
Shortcodes combinados | [column][box]…[/box][/column] — procesar siempre content con do_shortcode(). |
Shortcodes anidados del mismo tipo | Usar parser recursivo con get_shortcode_regex() o do_shortcode_nested() para mayor robustez. |
Ejemplo de uso completo en una entrada
[column width=33%] [box title=Ficha 1][note]Detalle A[/note][/box] [/column] [column width=33%] [box title=Ficha 2][note]Detalle B[/note][/box] [/column] [column width=33%] [box title=Ficha 3][note]Detalle C[/note][/box] [/column]
Referencias útiles
Con esto tienes todo lo necesario para crear shortcodes seguros y robustos que admitan contenido anidado en WordPress, desde casos simples hasta situaciones complejas con anidamiento recursivo. Implementa saneamiento y cache donde haga falta para garantizar seguridad y rendimiento.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |