Unusual for me to suggest regex for XML processing, but this may be more appropriate.
$input = '<g id="1">some content</g> other content <s/>';
echo preg_replace_callback("/(<.*?>)/", function($tag) {
return "<span>".htmlentities($tag[1])."</span>";
},
$input);
This will look for any content in <
and >
and encode it - whilst enclosing it in <span>
tags.
Outputs...
<span><g id="1"></span>some content<span></g></span> other content <span><s/></span>
As this is only a limited example, this may not fit all sizes, but may be worth a go.
Update:
With the update for adding the data-id I've updated the code, it keeps a stack of the levels of tags and adds in when a matching close tag is found (although it doesn't check the type of tag), it will ignore and self closed tags as these don't have any other content.
$input = '<g id="1">some <g>2</g>content</g> other content <s/>';
$tagID = [];
echo preg_replace_callback("/(<.*?>)/", function($tag) use (&$tagID) {
if ( substr($tag[1], -2) == "/>" ) {
$out = "<span>".htmlentities($tag[1])."</span>";
}
else {
$add = "";
if ( substr($tag[1],0,2) == "</" ) {
$id = array_pop($tagID);
if ( !empty($id) ) {
$add = ' data-closingof="'.$id.'"';
}
}
else {
if (preg_match('/id="(.*?)"/', $tag[1], $match)) {
$id = $match[1];
$add = ' data-id="'.$id.'"';
}
else {
$id = "";
}
array_push($tagID, $id);
}
$out = "<span{$add}>".htmlentities($tag[1])."</span>";
}
return $out;
},
$input);