How to create a simple markdown with PHP?

7

I would like to create a simple markdown, for bold and italic for now only, for example:

  • **foo** turns <b>foo</b>
  • __bar__ turns <i>bar</i>

Of course some details are needed, for example in italian this can not work:

 __ foo __

Because this is separate, the first and last letter should be attached to the "delimiters" , however this would be valid:

 __foo bar__     => <i>foo bar</i>
 __f o o b a r__ => <i>f o o b a r</i>

Because spaces between the first and last letter are accepted.

At the moment I created this:

  • Bold:

    $str = preg_replace('#(^|[^\*])\*\*([^\s\*]([^\*]+?)?[^\s\*])\*\*([^\*]|$)#', '$1<b>$2</b>$4', $str);
    
  • Italic:

    $str = preg_replace('#(^|[^_])__([^\s_]([^_]+?)?[^\s_])__([^_]|$)#', '$1<i>$2</i>$4', $str);
    

Both are very similar and seem to work fine, to better explain regx:

(^|[^_])__([^\s_]([^_]+?)?[^\s_])__([^_]|$)

  ^     ^   ^     ^        ^     ^   ^
  |     |   |     |        |     |   |
  |     |   |     |        |     |   |
  |     |   |     |        |     |   |
  |     |   |     |        |     |   |
  |     |   |     |        |     |   +-- verifica se após o delimitador não é underscore ou se é o final da string
  |     |   |     |        |     |
  |     |   |     |        |     +-- verifica se o delimitador são 2 underscores
  |     |   |     |        |
  |     |   |     |        +-- o ultimo caractere antes do delimitador não pode ser espaço e nem underscore
  |     |   |     |
  |     |   |     +-- pega qualquer coisa que não seja underscore, esse grupo é opicional
  |     |   |
  |     |   +-- verifica se o que vem após o primeiro delimitador é diferente de espaço e diferente de underscore
  |     |
  |     +-- verifica se o delimitador são 2 underscores
  |
  +-- checa se é o começo da string ou se o que vem antes do delimitador é diferente de underscore _

Example on ideone: link

However, the way I did can not do this:

__foo_bar__

And not even this:

**foo*bar**

I would like some improvement suggestions on this or even something totally different from this, even if it is without regex.

    
asked by anonymous 26.07.2018 / 00:57

1 answer

1

After several tests I created a solution, which I believe will contemplate all cases of string identifying the correct and wrong. For this, I started from the following premise:

Cases that are right:

Entry:

__correto__
__c o r r e t o__
__c_o_r_r_e_t_o__
__cor   re  to__
__co rre _to__
__a__

Output:

  • correct
  • C o r r t t o
  • c_o_r_r_e_t_o
  • color re to
  • run
  • a

Cases that are wrong:

__errado __
__ errado__
__errado___
___errado__

This holds true for cases in bold

Using this regex:

(.?)(__([^_\s]+\s*_?)*[^\s_]+__)([^_]|$)

along with preg_match_all of php, we can analyze the groups as follows:

(.?) ---> pega qualquer caractere ou não, antes do próximo grupo

(__([^_\s]+\s*_?)*[^\s_]+__)  ([^_]|$) --> verifica se após o delimitador não é underline ou se é o final da string
^   ^             ^      ^
.   .             .      ----------> finaliza grupo com 2 underlines
.   .             .
.   .             ----------> pega um ou mais caracteres diferente de espaço e undeline
.   .
.   ------------------> este grupo pode ter ou não qualquer caractere seguido de 1 ou mais espaços(ou não) seguido de 1 underline(ou não)
.
----------------> inicia grupo com 2 underline

With the help of php, we'll do this:

    $string = "Boa __tarde__ **Bacco**, isto é um **teste** com diversos **negritos** e __sublinha_dos__

    __**um** dois__  **__um__ dois**
    __aqui nao_funciona __ __ nem_aqui,pois está errado__
    __aqui está certo__ ___errado__ __certo__";

    preg_match_all("/(.?)(\*\*([^\*\s]+\s*\*?)*[^\s\*]+\*\*)([^\*]|$)/", $string, $resultNegrito);

    $negrito = $resultNegrito[2];
    $iniNegrito = $resultNegrito[1]; // valores do grupo (.?)
    for($x = 0; $x < count($negrito); $x++){
        if($iniNegrito[$x] != "*"){
            $res = "<b>".substr($negrito[$x],2,strlen($negrito[$x]) -4)."</b>";
            $string = str_replace($negrito[$x],$res,$string);
        }
    }

    preg_match_all("/(.?)(__([^_\s]+\s*_?)*[^\s_]+__)([^_]|$)/", $string, $resultSublinhado);

   $sublinhado = $resultSublinhado[2];
    $iniSublinhado = $resultSublinhado[1]; // valores do grupo (.?)
    for($x = 0; $x < count($sublinhado); $x++){
        if($iniSublinhado[$x] != "_"){
            $res = "<u>".substr($sublinhado[$x],2,strlen($sublinhado[$x]) -4)."</u>";
            $string = str_replace($sublinhado[$x],$res,$string);
        }
    }

    echo $string;

IDEONE

ESCAPES ..

In this script you can use backslashes to create underlined or bold text that are out of standard. Imagine that user wants to underline this: __METHOD__ .To do this, he just apply it like this: __\_\_METHOD_\_\__

Using php stripslashes stripslashes you remove the backslashes used by leaving the clean text.

An example with the text:

Para gerar um construtor nas recentes **\*versões do php*\**, 
usa-se o __**\_\_construct()**__ 
**esta é a __forma correta__** para usar. 

No **php** Existe a possibilidade de usar a 
contante mágica __\_\_FUNCTION_\_\__ para pegar o nome da função. 

__Neste script__, se eu quiser usar um **\*escape*\** para a 
barra inversa dentro de um sublinhado ou negrito, basta 
multiplica-lo por 3. Assim:

            __\\_teste_\\__

The output using echo stripslashes($string) will be:

Para gerar um construtor nas recentes <b>*versões do php*</b>, usa-se o <u><b>__construct()</b></u> <b>esta é a <u>forma correta</u></b> para usar. 

No <b>php</b> Existe a possibilidade de usar a contante mágica <u>__FUNCTION__</u> para pegar o nome da função. 

<u>Neste script</u>, se eu quiser usar um <b>*escape*</b> para a barra inversa dentro de um sublinhado ou negrito, basta multiplica-lo por 3. Assim:

<u>\_teste_\</u>
    
28.07.2018 / 02:12