skip to Main Content

I’m working on a type of page builder with Laravel and try to implement shortcodes WordPress style. I have HTML code as a string like this:

$string = '<div class="temp">
    [title type="text" tag="h2" value="This is my cool title" disabled some-attribute]
    [text type="text" tag="p" value="Lorem ipsum dolor sit amet"]
</div>';

Ok, now I want to extract and store all the "shortcodes" (the stuff in the brackets […]) in an array like this:

[
  [
   "id" => "title",
   "type" => "text",
   "tag" => "h2",
   "value" => "This is my cool title",
   "disabled" => true,
   "some-attribute" => true
  ],
  [
   "id" => "text",
   "type" => "text",
   "tag" => "p",
   "value" => "Lorem ipsum dolor sit amet"
  ]
]

My problem is to explode the shortcode strings to an array because of the spaces in "".

Here is my PHP code:

// Extract all brackets from the string
preg_match_all('/(?<=[)[^][]*(?=])/', $string, $matches);

$itemsArray = array();
// Explode each shortcode by spaces
foreach ($matches[0] as $match) {
    $shortcode = explode( ' ', $match );
    $itemArray = array();
    // split shortcode in attributes
    foreach ($shortcode as $key => $attr) {
        $temp = explode('=', $attr);
        // First attribute is the id
        if ($key === 0) {
            $itemArray['id'] = $temp[0];
        } else {
            if ( count($temp) > 1 ) {
                $itemArray[ $temp[0] ] = trim($temp[1],'"');
            } else {
                $itemArray[ $temp[0] ] = true;
            }
        }
    }
    $itemsArray[] = $itemArray;
}

The result is close but the spaces in the quotes "" screw up the explode of the strings:

[
  [
    "id" => "title"
    "type" => "text"
    "tag" => "h2"
    "value" => "This"
    "is" => true
    "my" => true
    "cool" => true
    "title"" => true
    "disabled" => true
    "some-attribute" => true
  ],
  [
    "id" => "text"
    "type" => "text"
    "tag" => "p"
    "value" => "Lorem"
    "ipsum" => true
    "dolor" => true
    "sit" => true
    "amet"" => true
  ]
]

How can I exclude the spaces in the string explode?

4

Answers


  1. Maybe this will work for you? Pls let me know

    $string = '<div class="temp">
        [title type="text" tag="h2" value="This is my cool title" disabled some-attribute]
        [text type="text" tag="p" value="Lorem ipsum dolor sit amet"]
    </div>';
    
    preg_match_all('/[(w+)([^]]*)]/', $string, $matches);
    
    $itemsArray = array();
    
    foreach ($matches[0] as $match) {
        preg_match('/[(w+)([^]]*)]/', $match, $shortcodeMatches);
        $id = $shortcodeMatches[1];
        $attributes = $shortcodeMatches[2];
        
        $itemArray = array();
        $itemArray['id'] = $id;
        
        preg_match_all('/(w+)=("[^"]*"|'[^']*'|S+)/', $attributes, $attributeMatches, PREG_SET_ORDER);
        foreach ($attributeMatches as $attrMatch) {
            $key = $attrMatch[1];
            $value = trim($attrMatch[2], '"'');
            $itemArray[$key] = $value;
        }
        
        $itemsArray[] = $itemArray;
    }
    
    print_r($itemsArray);
    
    Login or Signup to reply.
  2. Due to the value attributes also containing spaces, the result is not expected. Instead of exploding by spaces, you need to apply a second regex:

    $string = '<div class="temp">
    [title type="text" tag="h2" value="This is my cool title" disabled some-attribute]
    [text type="text" tag="p" value="Lorem ipsum dolor sit amet"]</div>';
    
    // Extract all brackets from the string
    preg_match_all('/(?<=[)[^][]*(?=])/', $string, $matches);
    $itemsArray = [];
    
    foreach ($matches as $items) {
      foreach($items as $item){
        // Regex to match with =".." and spaces
        preg_match_all('/(w+)(?:s*=s*"([^"]*)")?/', $item, $parts, PREG_SET_ORDER);    
        $converted = ["id"=>$parts[0][0]];
    
        foreach ($parts as $match) {
          if (isset($match[2])) { // Check if value exists
            $converted[$match[1]] = $match[2];
          } else {
            $converted[$match[1]] = true; // attribute defaults are true
          }
        }
        $itemsArray[] = $converted;
      }
    }
    
    Login or Signup to reply.
  3. If you look at your string, other than the items inside the DIV starting with a [ and ending with ] they’re pretty much the same as a standard HTML DOM element anyway so it might be easier to convert those lines to HTML and then access it all via the DOMDocument.


    Try this quick example

    If you know the values will never contain square brackets you could try getting that data this way via PHP’s DOMDocument feature.

    // Your string
    $string = '<div class="temp">
        [title type="text" tag="h2" value="This is my cool title" disabled some-attribute]
        [text type="text" tag="p" value="Lorem ipsum dolor sit amet"]
    </div>';
    
    // Set all the '[word ' items as pretend <span id='word'
    $string = preg_replace("/[([a-z]*.?) /i","<span id="$1" ",$string);
    
    // Replace the outer ] with '>' to make them all appear to be HTML tags
    $string = str_replace("]",">",$string);
    
    // Start a DOMDocument instance
    $dom = new DOMDocument;
    
    // Load $string now that it resembles full HTML
    $dom->loadHTML($string);
    $array = Array();
    
    // Loop through the span elements
    foreach($dom->getElementsByTagName('span') as $node){
        $attrs = Array();
    
        // Collect their attributes
        foreach($node->attributes as $attr){
            $attrs[$attr->nodeName] = $attr->nodeValue;
        }
        $array[] = $attrs;
    }
    
    // Show the results
    echo "<textarea style='width:100%;height:500px'>";
    print_r($array);
    echo "</textarea>";
    

    It might need tweaking.
    Like I said, the capturing of the elements via [ and ] might need a stricter way of achieving that to avoid any nasty surprises when a value contains a "]" inside it.

    I hope this helps a little for ideas.

    Login or Signup to reply.
  4. You can make use of the PHP internal tokenizer.

    Demo: https://3v4l.org/1ThIF

    • Extract the codes between brackets and loop over each of them.
    • Tokenize to the first space and assign to $tag with id key.
    • Run a while loop tokenizing to the next space or stop if end is reached.
      • Check if the token contains an equal sign, if not it is a single attribute and add to $tag list as key and a true value.
      • When the token contains an equal sign, explode it to $key and $value.
        • If the value ends with a double quote, add it to the $tag list with key and trim the quotes off the value.
        • If the value does not end with a double quote, then tokenize until one is found. Add the key $tag list trim the quotes off the previous and tokenized value concatenated with a space.
      • Add the $tag to the $result.
    $html = '
    <div class="temp">
        [title type="text" tag="h2" value="This is my cool title" disabled some-attribute]
        [text type="text" tag="p" value="Lorem ipsum dolor sit amet"]
    </div>
    ';
    
    preg_match_all('/[(.*?)]/', $html, $matches);
    $result = [];
    
    foreach ($matches[1] as $match) {
        $id = strtok($match, ' ');
        $tag = ['id' => $id];
        while ($token = strtok(' ')) {
            if (str_contains($token, '=')) {
                [$key, $value] = explode('=', $token);
                if (str_ends_with($token, '"')) {
                    $tag[$key] = trim($value, '"');
                    continue;
                }
                $value2 = strtok('"');
                $tag[$key] = trim("$value $value2", '"');
                continue;
            }
            $tag[$token] = true;
        }
        $result[] = $tag;
    }
    
    var_export($result);
    
    array (
      0 => 
      array (
        'id' => 'title',
        'type' => 'text',
        'tag' => 'h2',
        'value' => 'This is my cool title',
        'disabled' => true,
        'some-attribute' => true,
      ),
      1 => 
      array (
        'id' => 'text',
        'type' => 'text',
        'tag' => 'p',
        'value' => 'Lorem ipsum dolor sit amet',
      ),
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search