BBCode parser con PHP PHP
¿Alguna vez has querido implementar BBCode en tu propio script? No te creas que es muy difícil, en realidad BBCode es bastante sencillo de implementar en php utilizando unas cuántas expresiones regulares y la función preg_replace()
. Vamos a ver como crear una función para pasar una cadena de texto que contiene BBCode y que el código BBCode sea convertido a su equivalente HTML.
BBCode es un código de formato utilizado en muchas aplicaciones web en las que los usuarios pueden crear contenido. Su principal uso ha sido tradicionalmente en foros, conocidos también como Bulletin Board, de ahí su nombre. Algunos pueden decir que permitir BBCode y no permitir HTML es más seguro pero la verdad es que esta afirmación hoy en día no se sustenta. Limpiar un texto de todas las etiquetas HTML cuesta prácticamente el mismo trabajo que limpiarlo de todas las etiquetas menos algunas que se deseen permitir. Las ventajas de BBCode entonces se reducen a su mayor amigabilidad para los usuarios que no tienen por qué lidiar con código HTML, las mismas ventajas que pueda tener cualquier otro lenguaje de formato ligero, como el markdown, algo más moderno y cada vez más popular.
Vamos a construir una función en PHP que muestre los principios básicos para desarrollar un script analizador (parser) que convierta las etiquetas BBCode de una cadena de texto en sus correspondientes etiquetas HMTL. Vamos a utilizar la función preg_replace() y expresiones regulares. Por supuesto podrían implementarse muchas más opciones, pero veremos las más comúnmente usadas y necesitadas. El resultado será una función cuya variable de entrada será la cadena de texto que contiene BBCode y que devolverá la misma cadena con el BBCode convertido a su equivalente HTML.
Para comenzar debemos crear dos arrays. Uno contendrá los patrones de expresiones regulares para cada tag que queramos permitir. En otro array pondremos el juego de sustituciones para cada patrón anterior:
// La matriz de expresiones regulares con las que se busca el BBCode
$patrones = array(
'#\[b\](.*?)\[/b\]#is', // Negrita ([b]texto[/b]
'#\[i\](.*?)\[/i\]#is', // Cursiva ([i]texto[/i]
'#\[u\](.*?)\[/u\]#is', // Subrayado ([u]texto[/u])
'#\[s\](.*?)\[/s\]#is', // Tachado ([s]texto[/s])
'#\[quote\](.*?)\[/quote\]#is', // Cita ([quote]texto[/quote])
'#\(.*?)\[/code\]#is', // Código inline
texto
)
'#\[size=([1-9]|1[0-9]|20)\](.*?)\[/size\]#is', // Tamaño de fuente 1-20px [size=20]texto[/size])
'#\[color=\#?([A-F0-9]{3}|[A-F0-9]{6})\](.*?)\[/color\]#is', // Color de fuente ([color=#00F]texto[/color])
'#\[url=((?:ftp|https?)://.*?)\](.*?)\[/url\]#i', // Enlace con texto de anclaje ([url=http://url]texto[/url])
'#\[url\]((?:ftp|https?)://.*?)\[/url\]#i', // Enlace ([url]http://url[/url])
'#\[img\](https?://.*?\.(?:jpg|jpeg|gif|png|bmp))\[/img\]#i' // Imagen ([img]http://url_de_imagen[/img])
);
// Cadenas correspondientes con las que se reemplazan las coincidencias encontradas
$sustituciones = array(
'<strong>$1</strong>',
'<em>$1</em>',
'<span style="text-decoration: underline;">$1</span>',
'<span style="text-decoration: line-through;">$1</span>',
'<blockquote>$1</blockquote>',
'<pre>$1</'.'pre>',
'<span style="font-size: $1px;">$2</span>',
'<span style="color: #$1;">$2</span>',
'<a href="$1">$2</a>',
'<a href="$1">$1</a>',
'<img src="$1" alt="" />'
);
Como puedes ver, $patrones
es una lista de patrones de expresiones regulares y $sustituciones
es una lista de las correspondientes versiones en HTML. En las expresiones regulares hemos usado:
- ‘#’ como delimitador de apertura y cierre en lugar del habitual ‘/’, así no es necesario escapar ‘/’.
- Modificadores: ‘i’ hace el patrón no sensible a mayúsculas y minúsculas y ‘s’ permite las búsqueda del patrón a través de múltiples líneas
(.*?)
coincide con todos los caracteres, cualesquiera que sean, desde el primero al último hasta la siguiente parte de la expresión regular (por ejemplo,(.*?)[/i]
coincide con todos los caracteres encontrados hasta[/i]
)- En las sustituciones,
$1
hace referencia a la coincidencia del primer subpatrón de la expresión regular (cada subpatrón está entre paréntesis),$2
del segundo, etc.
Ahora pasamos estas matrices a la función preg_replace:
$cadena = preg_replace($patrones, $sustituciones, $cadena);
Ahora ponemos todo esto en una función que acepta la $cadena
que contiene el BBCode y que devuelva la misma $cadena
con las sustituciones realizadas:
function bbcode_parser($cadena){
//Convertimos los caracteres HTML en sus entidades para mostrarlas literalmente
$cadena = htmlentities($cadena);
// La matriz de expresiones regulares con las que se busca el BBCode
$patrones = array( '#\[b\](.*?)\[/b\]#is', // Negrita ([b]texto[/b]
'#\[i\](.*?)\[/i\]#is', // Cursiva ([i]texto[/i]
'#\[u\](.*?)\[/u\]#is', // Subrayado ([u]texto[/u])
'#\[s\](.*?)\[/s\]#is', // Tachado ([s]texto[/s])
'#\[quote\](.*?)\[/quote\]#is', // Cita ([quote]texto[/quote])
'#\(.*?)\[/code\]#is', // Código inline
texto
)
'#\[size=([1-9]|1[0-9]|20)\](.*?)\[/size\]#is', // Tamaño de fuente 1-20px [size=20]texto[/size])
'#\[color=\#?([A-F0-9]{3}|[A-F0-9]{6})\](.*?)\[/color\]#is', // Color de fuente ([color=#00F]texto[/color])
'#\[url=((?:ftp|https?)://.*?)\](.*?)\[/url\]#i', // Enlace con texto de anclaje ([url=http://url]texto[/url])
'#\[url\]((?:ftp|https?)://.*?)\[/url\]#i', // Enlace ([url]http://url[/url])
'#\[img\](https?://.*?\.(?:jpg|jpeg|gif|png|bmp))\[/img\]#i' // Imagen ([img]http://url_de_imagen[/img])
);
// Cadenas correspondientes con las que se reemplazan las coincidencias encontradas
$sustituciones = array(
'<strong>$1</strong>',
'<em>$1</em>',
'<span style="text-decoration: underline;">$1</span>',
'<span style="text-decoration: line-through;">$1</span>',
'<blockquote>$1</blockquote>',
'<pre>$1</'.'pre>',
'<span style="font-size: $1px;">$2</span>',
'<span style="color: #$1;">$2</span>',
'<a href="$1">$2</a>',
'<a href="$1">$1</a>',
'<img src="$1" alt="" />'
);
// Realizar la conversión BBCode - XHMTL
$cadena = preg_replace($patrones, $sustituciones, $cadena);
// Convertir linebras a <br />
$cadena = nl2br($cadena);
return $cadena;
}
Por supuesto, como dije, pueden incluirse más opciones, por ejemplo listas, vídeos de youtube, etc, incluir clases css en las sustituciones, o realizar un análisis más exhaustivo para evitar código mal formateado (por ejemplo [b]texto[i]más texto[/b][i]
daría como resultado <strong>texto<em>más texto</strong></em>
, lo cuál no es un código bien formateado). Este es tan sólo una implementación simple que trata de mostrar el concepto de implementación de BBCode y su análisis con una función PHP.