Well, there are multiple approaches to this issue. One way is to capture the items you want to exclude, find their offsets and lengths and basically just extract those parts out from the original string and all you're left with are the parts outside the tags.
Here is a function as an example:
<?php
function match_all_except ($pattern, $string)
{
preg_match_all($pattern, $string, $match, PREG_OFFSET_CAPTURE);
$parts = array();
$pos = 0;
foreach ($match[0] as $info)
{
$parts[] = substr($string, $pos, $info[1] - $pos);
$pos = $info[1] + strlen($info[0]);
}
$parts[] = substr($string, $pos);
return $parts;
}
$string = 'one<? foo ?>two<? bar ?>three';
$parts = match_all_except('/<\?.*?\?>/s', $string);
// Will output "one, two, three, "
foreach ($parts as $outside)
{
echo "$outside, ";
}
?>
Alternatively, you can use this regular expression /\G(?=.)((?:(?!<\?).)*)(?:<\?((?!\?>).)*(\?>|$)|$)/s
in preg_match_all
to capture all the parts outside the tags into the sub pattern one. Although, it may have it's own difficulties, if the tags are not evenly matched in the document.
For example,
<?php
$string = 'one<? foo ?>two<? bar ?>three';
preg_match_all('/\G(?=.)((?:(?!<\?).)*)(?:<\?((?!\?>).)*(\?>|$)|$)/s', $string, $match);
// Will output "one, two, three, "
foreach ($match[1] as $outside)
{
echo "$outside, ";
}
?>