stream is not available then */ if(!$this->buffer){ $this->buffer = stream_bucket_new($this->stream, ''); } while ($bucket = stream_bucket_make_writeable($in)) { // the last "safe" position for replacement $lastBoundary = strripos($bucket->data, $this->boundary); if($lastBoundary === false){ // boundary not found, buffer the bucket and FEED ME more data! $this->buffer->data .= $bucket->data; return PSFS_FEED_ME; }else{ $lastBoundary += strlen($this->boundary); } // buffer + chars from bucket up until (and including) the last boundary string $toReplace = $this->buffer->data . substr($bucket->data, 0, $lastBoundary); // characters from the last boundary string to the end of the bucket data $this->buffer->data = substr($bucket->data, $lastBoundary); // do string replace $bucket->data = $this->replace($toReplace); // ?? no idea if this is correct :-/ $consumed += strlen($toReplace); // stick it into the out brigade stream_bucket_append($out, $bucket); } // this is the last time through, so flush the buffer onto the output brigade if($closing && $this->buffer->data){ $this->buffer->data = $this->replace($this->buffer->data); stream_bucket_append($out, $this->buffer); } // pass on to the next filter return PSFS_PASS_ON; } function replace($str) { return preg_replace_callback('/_x([a-f0-9]{4})_/iu', 'hex2char', $str); } } // usage:- $filename = 'output.txt'; stream_filter_register("II_Xml_Filter", "II_Xml_Filter"); $fp = fopen($filename, "w"); if($fp){ stream_filter_append($fp, "II_Xml_Filter"); // cross boundary test fwrite($fp, "o_x0021_o_x0"); fwrite($fp, "021_o\n"); fclose($fp); readfile($filename); }else{ echo 'could not open ', $filename; }