I've run into the same problems. Here is my solution so far. Pretty messy, but it works:
function _stripUTF8InvalidHexValues($data)
{
for($i=0;$i<strlen($data);$i++)
{
$chr = substr($data,$i,1);
if((ord($chr) > 128) || (ord($chr) == 0))
{
$data = substr($data,0,$i) . substr($data,$i+1,strlen($data)-($i+1));
}
}
//strip out weird values that will muck up xml parser
//see ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252/TXT
//for a listing of hex values and what they mean
$data = ereg_replace("\x00","",$data);
$data = ereg_replace("\x01","",$data);
$data = ereg_replace("\x02","",$data);
$data = ereg_replace("\x03","",$data);
$data = ereg_replace("\x04","",$data);
$data = ereg_replace("\x05","",$data);
$data = ereg_replace("\x06","",$data);
$data = ereg_replace("\x07","",$data);
$data = ereg_replace("\x0e","",$data);
$data = ereg_replace("\x0f","",$data);
$data = ereg_replace("\x10","",$data);
$data = ereg_replace("\x11","",$data);
$data = ereg_replace("\x12","",$data);
$data = ereg_replace("\x13","",$data);
$data = ereg_replace("\x14","",$data);
$data = ereg_replace("\x15","",$data);
$data = ereg_replace("\x16","",$data);
$data = ereg_replace("\x17","",$data);
$data = ereg_replace("\x18","",$data);
$data = ereg_replace("\x19","",$data);
$data = ereg_replace("\x1a","",$data);
$data = ereg_replace("\x1b","",$data);
$data = ereg_replace("\x1c","",$data);
$data = ereg_replace("\x1d","",$data);
$data = ereg_replace("\x1e","",$data);
$data = ereg_replace("\x1f","",$data);
$data = ereg_replace("\xf3","",$data);
return $data;
}
function _handleProblemXMLCharacters($data)
{
$data = $this->_stripUTF8InvalidHexValues($data);
$changed_data = ereg_replace("& ","&",$data);
$changed_data = ereg_replace(":",":",$changed_data);
return $changed_data;
}
//from main loop
while($data = fread($fp,4096))
{
$data = $this->_handleProblemXMLCharacters($data);
if(!xml_parse($this->mParser,$data,feof($fp)))
{
}
}
Hope it helps.