If you want to have this in a class (as the question title implies), then you should create Filter classes. This is a common thing to do. However, compared to the simple function nesting, it will be much more code to do it properly. The advantage is, you can easily extend and combine filters to virtually any filtering needs you have.
I have whipped up something for you quickly.
interface IFilter {
/**
* @param Mixed $value The value to be filtered
* @return Mixed The filtered value
*/
public function filter($value);
}
All filters must implement the IFilter
interface. This is to make sure that whenever you are using a Filter, it has a filter()
method that accepts a single $value
argument. We cannot enforce return values, but the doc block indicated we expect it to return the filtered value. Two very simple filters would look like this:
class ucFirstFilter implements IFilter
{
public function filter($value) {
return ucfirst($value);
}
}
class TrimFilter implements IFilter
{
public function filter($value) {
return trim($value);
}
}
This is nothing but an object wrapper around two of PHP's native functions. You use it like this:
$trimFilter = new TrimFilter;
echo trimFilter->filter(' trim me ');
// returns 'trim me'
The other two filters are somewhat more complex, because they can be passed more than one argument:
class SeparatorToSeparatorFilter implements IFilter
{
protected $_separator;
protected $_replacement;
public function __construct($separator = '_', $replacement = ' ')
{
$this->_separator = $separator;
$this->_replacement = $replacement;
}
public function filter($value) {
return str_replace($this->_separator, $this->_replacement, $value);
}
}
class HtmlEntityDecodeFilter implements IFilter
{
protected $_quoteStyle;
protected $_charset;
public function __construct($quoteStyle=ENT_COMPAT, $charset='ISO-8859-1')
{
$this->_quoteStyle = $quoteStyle;
$this->_charset = $charset;
}
public function filter($value) {
return html_entity_decode($value, $this->_quoteStyle, $this->_charset);
}
}
As you can see, the configuration of the additional arguments is done through the constructor. I have used some default values, so you only have to supply them when you need to deviate from those. In the case of the second filter, I have used the native function's default settings. This is how you use them:
$trimFilter = new TrimFilter;
$separatorFilter = new SeparatorToSeparatorFilter('-');
echo $separatorFilter->filter($trimFilter->filter(' trim-me '));
// returns 'trim me';
Now you might be tempted to add multiple filterings into a single Filter class. Dont. Each Filter should do exactly one thing only. There is a better way to combine filters. All you need is a Filter that aggregates multiple other filters aka a FilterChain:
class FilterChain implements IFilter
{
protected $_filters;
public function __construct()
{
$this->_filters = new SplObjectStorage;
}
public function chain(IFilter $filter)
{
$this->_filters->attach($filter);
return $this;
}
public function remove(IFilter $filter)
{
$this->_filters->detach($filter);
return $this;
}
public function filter($value) {
foreach($this->_filters as $filter) {
$value = $filter->filter($value);
}
return $value;
}
}
The FilterChain
accepts any object that implements IFilter
and if you call it's filter()
method, it will iterate over all chained Filters in the order you chain()
ed them and return the passed in $value
:
$filterChain = new FilterChain;
$filterChain->chain(new ucFirstFilter)
->chain(new SeparatorToSeparatorFilter)
->chain(new HtmlEntityDecodeFilter(ENT_QUOTES, 'UTF-8'))
->chain(new TrimFilter);
echo $filterChain->filter(' i am a "string_to_be_filtered" ');
// outputs 'i am a "string to be filtered"'
Because the FilterChain
also implements IFilter
itself, you can also add it to other FilterChains. This is a Composite Pattern. The filter above could be written as
$chain1 = new FilterChain;
$chain1->chain(new ucFirstFilter)
->chain(new SeparatorToSeparatorFilter);
$chain2 = new FilterChain;
$chain2->chain($chain1);
$chain2->chain(new HtmlEntityDecodeFilter(ENT_QUOTES, 'UTF-8'))
->chain(new TrimFilter);
As you can see, it is much more code, but it is also very extensible. The main advantage over having a single function that wraps all native functions into one function is you can combine anything any way you want. If you decided you need another function that does not utilize the trim()
function, you'd have to write a completely new function and you'll inadvertently end up with a lot of functions and redundant code for any possible combination of filters. With a FilterChain
you simply add the Filters and FilterChains together as needed. And since a FilterChain
is an object, you can pass it around freely.
Fortunately, Filter libraries like this already exist, for instance Zend_Filter offers a number of premade filters and can used standalone (e.g. without having to migrate your app to ZF).