<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Processes bytes as they pass through a buffer and replaces sequences in it.
*
* This stream filter deals with Byte arrays rather than simple strings.
*
* @author Chris Corbyn
*/
class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilter
{
/** The replacement(s) to make */
private $replace;
/** The Index for searching */
private $index;
/** The Search Tree */
private $tree = [];
/** Gives the size of the largest search */
private $treeMaxLen = 0;
private $repSize;
/**
* Create a new ByteArrayReplacementFilter with $search and $replace.
*
* @param array $search
* @param array $replace
*/
public function __construct($search, $replace)
{
$this->index = [];
$this->tree = [];
$this->replace = [];
$this->repSize = [];
$tree = null;
$i = null;
$last_size = $size = 0;
foreach ($search as $i => $search_element) {
if (null !== $tree) {
$tree[-1] = min(count($replace) - 1, $i - 1);
$tree[-2] = $last_size;
}
$tree = &$this->tree;
if (is_array($search_element)) {
foreach ($search_element as $k => $char) {
$this->index[$char] = true;
if (!isset($tree[$char])) {
$tree[$char] = [];
}
$tree = &$tree[$char];
}
$last_size = $k + 1;
$size = max($size, $last_size);
} else {
$last_size = 1;
if (!isset($tree[$search_element])) {
$tree[$search_element] = [];
}
$tree = &$tree[$search_element];
$size = max($last_size, $size);
$this->index[$search_element] = true;
}
}
if (null !== $i) {
$tree[-1] = min(count($replace) - 1, $i);
$tree[-2] = $last_size;
$this->treeMaxLen = $size;
}
foreach ($replace as $rep) {
if (!is_array($rep)) {
$rep = [$rep];
}
$this->replace[] = $rep;
}
for ($i = count($this->replace) - 1; $i >= 0; --$i) {
$this->replace[$i] = $rep = $this->filter($this->replace[$i], $i);
$this->repSize[$i] = count($rep);
}
}
/**
* Returns true if based on the buffer passed more bytes should be buffered.
*
* @param array $buffer
*
* @return bool
*/
public function shouldBuffer($buffer)
{
$endOfBuffer = end($buffer);
return isset($this->index[$endOfBuffer]);
}
/**
* Perform the actual replacements on $buffer and return the result.
*
* @param array $buffer
* @param int $minReplaces
*
* @return array
*/
public function filter($buffer, $minReplaces = -1)
{
if (0 == $this->treeMaxLen) {
return $buffer;
}
$newBuffer = [];
$buf_size = count($buffer);
$last_size = 0;
for ($i = 0; $i < $buf_size; ++$i) {
$search_pos = $this->tree;
$last_found = PHP_INT_MAX;
// We try to find if the next byte is part of a search pattern
for ($j = 0; $j <= $this->treeMaxLen; ++$j) {
// We have a new byte for a search pattern
if (isset($buffer[$p = $i + $j]) && isset($search_pos[$buffer[$p]])) {
$search_pos = $search_pos[$buffer[$p]];
// We have a complete pattern, save, in case we don't find a better match later
if (isset($search_pos[-1]) && $search_pos[-1] < $last_found
&& $search_pos[-1] > $minReplaces) {
$last_found = $search_pos[-1];
$last_size = $search_pos[-2];
}
}
// We got a complete pattern
elseif (PHP_INT_MAX !== $last_found) {
// Adding replacement datas to output buffer
$rep_size = $this->repSize[$last_found];
for ($j = 0; $j < $rep_size; ++$j) {
$newBuffer[] = $this->replace[$last_found][$j];
}
// We Move cursor forward
$i += $last_size - 1;
// Edge Case, last position in buffer
if ($i >= $buf_size) {
$newBuffer[] = $buffer[$i];
}
// We start the next loop
continue 2;
} else {
// this byte is not in a pattern and we haven't found another pattern
break;
}
}
// Normal byte, move it to output buffer
$newBuffer[] = $buffer[$i];
}
return $newBuffer;
}
}