-1

I am trying to call a function from BBCode(like WordPress shortcodes). but I didn't find any code to do that, only I found HTML tag parser like:

[bold]Bold text[/bold]
->
<b>Bold text</b>

But I want to save it as an array, for example:

[date format="j M, Y" type="jalali"]

to something like this:

array(
    'date' => array(
        'format' => 'j M, Y',
        'type' => 'jalali'
    )
)

*Edited

I made a code to do this (sorry if my English is bad):

[date format="Y/m/d" type="jalali"] =>

Step 1: Get code between "[" and "]":
date format="Y/m/d" type="jalali"

Step 2: Explode space in the code:
$code = array('date', 'format="Y/m/d"', 'type="jalali"')

Step 3: Get shortcode name(offset 0 of $code) and get
difference($code excluded offset 0):
$name = 'date'
$attr = array('format="Y/m/d"', 'type="jalali"')

Step 4: Now I have attributes and code name. But the problem is if
put space in attributes value it will explode that too:
[date format="j M, Y" type="jalali"] =>
$code = array('date', 'format="j', 'M,', ' Y"', 'type="jalali"');

Now how can I fix this or get name and attributes with regex or anything else?

3
  • Welcome to Stack Overflow. Please take the tour to learn how Stack Overflow works and read How to Ask on how to improve the quality of your question. It is unclear what your question is or what your problem is. Please edit your question to include a detailed description of the problem you have or what you are trying to do. Commented Jul 22, 2021 at 11:56
  • You can make your own parser Commented Jul 22, 2021 at 12:05
  • 1
    If your code is already working, what is missing? Can you share the code you are using? Commented Jul 22, 2021 at 12:12

2 Answers 2

2

You can try this using regex

$code = '[date format="j M, Y" type="jalali"]';

preg_match_all("/\[([^\]]*)\]/", $code, $matches);

$codes = [];

foreach($matches[1] as $match) {
  // Normalize quotes into double quotes
  $match = str_replace("'",'"',$match);
  // Split by space but ignore inside of double quotes
  preg_match_all('/(?:[^\s+"]+|"[^"]*")+/',$match,$tokens);
  $parsed = [];
  $prevToken = '';
  foreach($tokens[0] as $token) {
    if(strpos($token,'=') !== false) {
      if($prevToken !== '') {
        $parts = explode('=',$token);
        $parsed[$prevToken][$parts[0]] = trim($parts[1],'"\''); 
      }
    } else {
      $parsed[$token] = [];
      $prevToken = $token;
    }
  }

  $codes[] = $parsed;
}

var_dump($codes);

Result:

array(1) {
  [0]=>
  array(1) {
    ["date"]=>
    array(2) {
      ["format"]=>
      string(6) "j M, Y"
      ["type"]=>
      string(6) "jalali"
    }
  }
}
Sign up to request clarification or add additional context in comments.

Comments

1

Using a regex with the \G (continue metacharacter), you can parse the vital segments of the tag, then loop over the matches to construct the 2d array.

Code: (Demo)

$text = <<<TEXT
Some text [date format="j M, Y" type="jalali"] containing a date tag.
TEXT;

preg_match_all(
    '/
     (?|                           #use "branch reset" to ensure all attributes are in column one of result
      \[                           #match literal left brace
      (date)                       #match the literal tag name
      (?=[^\]]*])                  #validate that substring is eventually followed by a right brace
      |                            #OR
      \G(?!^)                      #continue matching from last match, but not from start of string
      (?:                          #encapsulate next OR expression
          ]                        #match ending delimiter of bbtag
          |                        #OR
          \h([a-z\-]+)="([^"]*)"   #match space, capture attribute name, match =", capture value, match "
      )                            #close non-capturing group
     )                             #close branch reset group
     /x',
    $text,
    $m,
    PREG_SET_ORDER
);
$result = [];
foreach ($m as $i => $row) {
    if (!$i) {
        $parent = $row[1];
    } elseif (isset($row[1])) {
        $result[$parent][$row[1]] = $row[2];
    }
}
var_export($result);

If you are going to call a custom function and replace the tag placeholder with dynamic text, then you'll need to parse the whole tag with preg_replace_callback() and break it into its part inside the callback. I'll demonstrate how to make the first key-value pair mandatory and the second key-value pair optional

Code: (Demo)

function replaceTag($m) {
    $result[$m[1]][$m[2]] = $m[3];
    if (isset($m[4], $m[5])) {
        $result[$m[1]][$m[4]] = $m[5];
    }
    return json_encode($result);  // https://stackoverflow.com/q/30377948/2943403
}

$text = <<<TEXT
Some text [date format="j M, Y" type="jalali"] containing a date tag.
TEXT;

echo preg_replace_callback(
    '/\[(date) ([a-z\-]+)="([^"]*)"(?: ([a-z\-]+)="([^"]*)")?]/',
    'replaceTag',
    $text
);
// Some text {"date":{"format":"j M, Y","type":"jalali"}} containing a date tag.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.