Вот этот - наиболее производительный и гибкий код, чем тот, что написан у Вас:
$datatext=<<<t
"Он сказал, - поехали"
"Он сказал поехали!"
"Он сказал - поехали?"
"Он сказал поехали?!"
"Он сказал поехали..."
"Он сказал "поехали"
<b>"Он сказал, — поехали"</b>
"Ж"
"Ж" "полная"
"7" "444 "888"
""
t;
$datatext=preg_replace_callback(
'#(([\"]{2,})|(?![^\W])(\"))|([^\s][\"]+(?![\w]))#u',
function ($matches) {
if (count($matches)===3) return "«»";
else if ($matches[1]) return str_replace('"',"«",$matches[1]);
else return str_replace('"',"»",$matches[4]);
},
$datatext
);
echo "<pre>".$datatext."</pre>";
/*
«Он сказал, - поехали»
«Он сказал поехали!»
«Он сказал - поехали?»
«Он сказал поехали?!»
«Он сказал поехали...»
«Он сказал «поехали»
«Он сказал, — поехали»
«Ж»
«Ж» «полная»
«7» «444 «888»
«»
*/