I have following PHP function which converts shortcode such as
[column]
[row]
[column][/column]
[/row]
[/column]
To nested array
Array
(
[0] => Array
(
[tag] => column
[attributes] => Array
(
)
[content] => Array
(
[0] => Array
(
[tag] => row
[attributes] => Array
(
)
[content] => Array
(
[0] => Array
(
[tag] => column
[attributes] => Array
(
)
[content] =>
)
)
)
)
)
)
This works fine, if I have a single [column] as child, but if I have multiple column as child which is
[column]
[row]
[column][/column]
[column][/column]
[/row]
[/column]
Then it gives me incorrect nested array, which is
Array
(
[0] => Array
(
[tag] => column
[attributes] => Array
(
)
[content] => Array
(
[0] => Array
(
[tag] => row
[attributes] => Array
(
)
[content] => Array
(
[0] => Array
(
[tag] => column
[attributes] => Array
(
)
[content] =>
)
)
)
)
)
[1] => Array
(
[tag] => column
[attributes] => Array
(
)
[content] =>
)
)
Here is my PHP function
protected function shortCodeToArray($inputString)
{
$itemArray = [];
$openingTag = '/\[(\w+)(?:\s+([^\]]*))?\](.*?)(\[\/\1\]|$)/s';
preg_match_all($openingTag, $inputString, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$tagName = $match[1];
$paramString = isset($match[2]) ? $match[2] : '';
$content = $match[3];
$nestedShortcodes = $this->shortCodeToArray($content);
$itemArray[] = [
'tag' => $tagName,
'attributes' => $this->parseShortcodeParameters($paramString),
'content' => is_array($nestedShortcodes) && !empty($nestedShortcodes) ? $nestedShortcodes : $content,
];
}
return $itemArray;
}
protected function parseShortcodeParameters($paramString)
{
$params = [];
preg_match_all('/(\w+)\s*=\s*["\']([^"\']+)["\']/', $paramString, $matches);
for ($i = 0; $i < count($matches[0]); $i++) {
$paramName = $matches[1][$i];
$paramValue = $matches[2][$i];
$params[$paramName] = $paramValue;
}
return $params;
}
Where am I going wrong here?
Here is how I ended up doing it, thanks to @Markus Zeller and from another SO post which I am unable to find now.
// 1. Convert shortcode to XML like syntax
$xmlString = str_replace(['[', ']'], ['<', '>'], $shortcode);
// 2. Convert to xml using simple_xml_load_string
$xml = simplexml_load_string($html, "SimpleXMLElement", LIBXML_NOCDATA);
// 3. Convert to JSON
$json = json_encode($xml);
// 4. JSON to Array
$array = json_decode($json, true);
Although this worked, the xml -> json -> array
created its own syntax and I wanted it in specific format with has_children
and children
keys, hence I am ended up using this function
protected function xmlToArray($xml) {
$result = [];
$result[] = [
'tag' => $xml->getName(),
'attributes' => [],
'text' => '',
'has_children' => count($xml->children()) > 0,
'children' => [],
];
foreach ($xml->attributes() as $key => $value) {
$result[0]['attributes'][$key] = (string)$value;
}
$children = $xml->children();
if (count($children) === 0) {
$result[0]['text'] = (string)$xml;
return $result;
}
foreach ($children as $child) {
$childArray = $this->xmlToArray($child);
$result[0]['children'][] = $childArray[0];
}
return $result;
}
Here is the result I am getting.
(
[0] => Array
(
[tag] => column
[attributes] => Array
(
)
[text] =>
[has_children] => 1
[children] => Array
(
[0] => Array
(
[tag] => row
[attributes] => Array
(
)
[text] =>
[has_children] => 1
[children] => Array
(
[0] => Array
(
[tag] => column
[attributes] => Array
(
)
[text] =>
[has_children] =>
[children] => Array
(
)
)
[1] => Array
(
[tag] => column
[attributes] => Array
(
)
[text] =>
[has_children] =>
[children] => Array
(
)
)
)
)
)
)
)