Display conditional text snippet with regex

Asked

Viewed 83 times

6

I am trying to create a dynamic text "generator" that will replace patterns with data from an array using regex, the structure is as follows:

Hello, [user], your registration was made in [data_registration] {aceitou_terms=yes} ,and you won 1 million reais {aceitou_terms}, your first name is [user,1] {subscriber=no} subscribe today and get 10% discount{subscriber}

where {aceitou_termos=sim} TEXTO {aceitou_termos} and {assinante=nao} TEXTO {assinante} would be conditions for display depending solely on the corresponding values in the array.

example:

$array['aceitou_termos'] = 'sim';
$array['assinante'] = 'nao';

The result text would be:

Hello, [user], your registration was made in [data_registration],and you won 1 million reais ,his first name is [user,1] sign up today and gets a 10% discount

I can even capture what is inside the array, but how can I display or remove the entire "set" based on the value coming from the array using regex?

  • updated the question, in fact it is to display the text depending on the condition, it can display either with "yes" or "no"

1 answer

5


The idea is to first check if the tag has the same value as the array, and make the replacement accordingly.

If the value is the same as the array, I remove only the tags and keep the text between them. Otherwise, I remove everything (tags and text):

function remove($texto, $array, $tag) {
    $opcao = $array[$tag];
    // se a tag tem o mesmo valor do array
    if (preg_match("/\{$tag=$opcao\}/", $texto)) {
        $replace = '$1'; // substitui pelo texto entre as tags
    } else { // senão, remove tudo
        $replace = '';
        $opcao = '[^}]+';
    }
    $regex = "/\{$tag=$opcao\}([^{]+)\{$tag\}/";
    return preg_replace($regex, $replace, $texto);
}

$texto = 'Olá, [usuario], seu cadastro foi efetuado em [data_cadastro] {aceitou_termos=sim} ,e você ganhou 1 milhão de reais {aceitou_termos}, seu primeiro nome é [usuario,1] {assinante=nao} assine hoje e ganha 10% de desconto{assinante}';

$array['aceitou_termos'] = 'sim';
$array['assinante'] = 'nao';

$textoFinal = remove($texto, $array, 'aceitou_termos');
$textoFinal = remove($textoFinal, $array, 'assinante');

That is, if you have the tag with the same value as the array, I replace it with the text between the tags. For this I use [^{]+ (one or more characters other than {). I mean, I’m assuming that the text between the tags doesn’t have any {.

In case you keep the text, I put the excerpt [^{]+ in brackets to form a catch group, so I can get its contents later, with $1.

If the tag does not have the same value, I remove everything (do the replace by empty string).


If the array contains only the tags and their respective values, a loop for them, and go changing all:

function remove($texto, $array, $tag, $opcao) {
    if (preg_match("/\{$tag=$opcao\}/", $texto)) {
        $replace = '$1';
    } else {
        $replace = '';
        $opcao = '[^}]+';
    }
    $regex = "/\{$tag=$opcao\}([^{]+)\{$tag\}/";
    return preg_replace($regex, $replace, $texto);
}

$texto = 'Olá, [usuario], seu cadastro foi efetuado em [data_cadastro] {aceitou_termos=sim} ,e você ganhou 1 milhão de reais {aceitou_termos}, seu primeiro nome é [usuario,1] {assinante=nao} assine hoje e ganha 10% de desconto{assinante}. {obs=sim}Obs: etc blabla{obs}';

$array = [
    'aceitou_termos' => 'sim',
    'assinante' => 'nao',
    'obs' => 'sim'
];
$textoFinal = $texto;
foreach ($array as $tag => $opcao) {
    $textoFinal = remove($textoFinal, $array, $tag, $opcao);
}

echo $textoFinal;

If tags exist more than once in the text, then it’s a little more boring (and inefficient, maybe it’s better use/build a parser or something like that), because you have to check each occurrence of the tag, check if the value is equal to that of the array and do the substitution:

function remove($texto, $array, $tag, $opcao) {
    while (preg_match("/\{$tag=([^}]+)\}/", $texto, $matches)) {
        if ($matches[1] == $opcao) {
            $replace = '$1';
            $opt = $opcao;
        } else {
            $replace = '';
            $opt = '[^}]+';
        }
        $regex = "/\{$tag=$opt\}([^{]+)\{$tag\}/";
        $texto = preg_replace($regex, $replace, $texto, 1);
    }
    return $texto;
}

$texto = 'Olá {aceitou_termos=sim}aceitou termos {aceitou_termos}bla bla etc {assinante=nao}não assinou{assinante} xyz{aceitou_termos=nao} não aceitou{aceitou_termos}.';

$array = [
    'aceitou_termos' => 'sim',
    'assinante' => 'nao'
];
$textoFinal = $texto;
foreach ($array as $tag => $opcao) {
    $textoFinal = remove($textoFinal, $array, $tag, $opcao);
}

echo $textoFinal;

That is, as long as you have the tag, you check that the value is equal to the array and overwrite accordingly (either remove everything, or keep the text between the tags).

In case, if aceitou_termos is "yes", the result will be:

Olá aceitou termos bla bla etc não assinou xyz.

What if aceitou_termos is "no", the result will be:

Olá bla bla etc não assinou xyz não aceitou.

The rest of the answer below is for the first version of the question, that I think can be useful to those who care.

I don’t think you need regex. If you only have a "tag" {aceitou_termos}, can only be done with strpos and substr:

$texto = 'Olá, [usuario], seu cadastro foi efetuado em [data_cadastro]{aceitou_termos}, e você ganhou 1 milhão de reais {aceitou_termos}, seu primeiro nome é [usuario,1]';
$array['aceitou_termos'] = 'sim';

$tag = '{aceitou_termos}';
$len = strlen($tag);
$inicioTag = strpos($texto, $tag); // primeira ocorrência da tag
$fimTag = strpos($texto, $tag, $inicioTag + $len); // segunda ocorrência da tag

$textoFinal = substr($texto, 0, $inicioTag);  // pega do início até a primeira ocorrência da tag
if ($array['aceitou_termos'] == 'sim') { // pega o texto entre as tags
    $textoFinal .= substr($texto, $inicioTag + $len, $fimTag - $inicioTag - $len);
}
$textoFinal .= substr($texto, $fimTag + $len); // pega da segunda ocorrência da tag até o final da string
echo $textoFinal;

That is, I take the positions of the first and last occurrence of the tag, and see whether what is between them should be concatenated or not.


But of course you can do with regex:

$texto = 'Olá, [usuario], seu cadastro foi efetuado em [data_cadastro]{aceitou_termos}, e você ganhou 1 milhão de reais {aceitou_termos}, seu primeiro nome é [usuario,1]';
if ($array['aceitou_termos'] == 'sim') {
    // sim, basta remover as tags
    $textoFinal = str_replace('{aceitou_termos}', '', $texto);
} else {
    // remove as tags e o texto entre elas
    $textoFinal = preg_replace('/\{aceitou_termos\}[^{]+\{aceitou_termos\}/', '', $texto);
}
echo $textoFinal;

Although it has fewer lines, has it become simpler? It’s relative, but anyway, the idea is to take the tags themselves (and the brackets should be escaped with \), and between them I use [^{]+ (one or more characters other than {). I mean, I’m assuming that the text between the tags doesn’t have any {.

Note that if you have to include text between tags, simply remove the tags themselves using a replace simple (without regex).


As for the second case, it would look like this:

// se for "nao", remove tudo entre as tags
$textoFinal = preg_replace('/\{aceitou_termos=nao\}[^{]+\{aceitou_termos\}/', '', $texto);
// se for "sim", mantém o texto entre as tags
$textoFinal = preg_replace('/\{aceitou_termos=sim\}([^{]+)\{aceitou_termos\}/', '$1', $textoFinal);

The description of the question seems to me to be in reverse, because it says that you should display the text if it is {aceitou_termos=nao}, but I believe that the text should only be displayed if it is {aceitou_termos=sim} (but if not, just invert the "yes" and "no" in the above code).

In case you keep the text, I put the excerpt [^{]+ in brackets to form a catch group, so I can get its contents later, with $1.

  • Thank you for the answer, but I had to update the question because there was a detail missing, the text can be displayed depending on the value, it can be yes or no {aceitou_terms=yes|no}

  • @Nbayoungcoder I updated the answer

  • sorry, I had expressed myself badly, I updated the question again to be clearer

  • @Nbayoungcoder Updated again..

  • Thanks, how can I make it work if there are several conditions with the same tags? if I use 2 signatures, one equal to "yes" and the other equal to "no", it does not work and it is displayed

  • 1

    @Nbayoungcoder I updated the answer once again. As a hint, I suggest you always try to put examples closer to the real, so avoid this "come and go" information. For me it had not been clear whether the same tag could repeat (of course it was a possibility, but I ended up not considering in the answer by "laziness") :-)

  • I understand, thank you very much!

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.