A principios del año pasado, mi colega Iván Barrasa me pidió ayuda a la hora de implementar un sistema que permitiera mencionar a otros usuarios en las biografías de los perfiles de cierta red social para la que estamos trabajando (nexbi.pro).
Así pues, fuimos a Discord y nos pusimos manos a la obra. Desde el primer momento, le sugerí hacer uso de expresiones regulares para identificar los casos en los que se podría considerar a una palabra precedida por @
como una mención.
Lo siento Jamie Zawinski.
Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems
Tras realizar algunas pruebas mediante regex101 (herramienta que recomiendo) y contemplar varios escenarios (casos en los que una palabra precedida por @
no es una mención hacia otro usuario, como por ejemplo un correo electrónico) acabamos determinando que la expresión regular más acorde a lo que buscábamos era la siguiente:
(^|\s)@([a-zA-Z0-9_]+)
Procedamos a explicarla por partes:
(^|\s)
-> Da por buenos patrones que se encuentren al principio del sujeto o que empiecen con un espacio en blanco (esto último obliga a omitir casos como el del correo electrónico descrito anteriormente).@
-> Inmediatamente después de lo contemplado en el punto anterior, debe haber un@
.([a-zA-Z0-9_]+)
-> Inmediatamente después de lo contemplado en el punto anterior, debe haber al menos un carácter alfanumérico o un underscore (_
).
Así pues, basándonos en las reglas anteriores:
@Wifft
-> Válido@Hola_Mundo
-> Válido@Hello-world
-> Inválidouser@domain.tld
-> Inválido (x2)@Wifft_
-> Válido
Como ya tenemos identificado el patrón que necesitamos, vamos a proceder a implementarlo en programación. Obviamente, en este caso, aparte de identificar un patrón, también debemos ser capaces de reemplazarlo de tal forma que por ejemplo, a partir de la mención se genere un enlace al perfil de usuario en cuestión.
Esto que a priori puede parecer un dolor de cabeza, con PHP es muy sencillo, ya que posee una función nativa llamada preg_replace() que permite reemplazar todas las ocurrencias que coincidan con la expresión regular especificada.
Esta función permite que en su segundo parámetro se puedan segmentos específicos de la cadena de destino coincidentes con partes del patrón dado. Anteriormente, hemos indicado que nuestra expresión consta de tres partes, ¿verdad?
Pues... ¿Y si os digo que cada parte es identificable mediante las pseudo-variables $0
, $1
y $2
, cada una correspondiente a la posición ordinal de la misma?
En concreto, $2
correspondería a ([a-zA-Z0-9_]+)
, que en la práctica, es la cadena de texto correspondiente al nombre del usuario que pretendemos mencionar.
Así que, una vez realizado todo el análisis necesario, procedamos a observar una implementación práctica:
$bio = preg_replace(
"/(^|\s)@([a-zA-Z0-9_]+)/",
" <a href='usuario/$2'>@$2</a>",
"Hola, soy colega de @barrasa."
);
echo $bio;
Nos imprime:
Hola, soy colega de <a href='usuario/barrasa'>@barrasa</a>
Si sin embargo, tratamos de ejecutar:
$bio = preg_replace(
"/(^|\s)@([a-zA-Z0-9_]+)/",
" <a href='usuario/$2'>@$2</a>",
"Hola, soy colega de hola@barrasa.dev"
);
echo $bio;
Nada cambia, al no encontrase ninguna coincidencia con el patrón:
Hola, soy colega de hola@barrasa.dev
Pero... ¿Y qué sucede si colocamos nuestra mención al principio?
$bio = preg_replace(
"/(^|\s)@([a-zA-Z0-9_]+)/",
" <a href='usuario/$2'>@$2</a>",
"@barrasa es mi ídolo."
);
echo $bio;
El resultado es el siguiente, ya que si os fijáis, el reemplazo debe de tener un espacio al principio para forzar la separación del string contiguo al sustituido.
<a href='usuario/barrasa'>@barrasa</a> es mi ídolo.
Casuística fácilmente solucionable mediante la útil y fantástica función trim(), nativa de PHP :)
$bio = trim(
preg_replace(
"/(^|\s)@([a-zA-Z0-9_]+)/",
" <a href='usuario/$2'>@$2</a>",
"@barrasa es mi ídolo."
)
);
echo $bio;
Bonus: Con esto ya lo clavamos, ¿verdad? ;)
$rawBio = strip_tags(filter_input(INPUT_POST, 'bio', FILTER_UNSAFE_RAW));
$formattedBio = trim(preg_replace("/(^|\s)@([a-zA-Z0-9_]+)/", $rawBio));
echo $formattedBio;