I often see this idiom when reading php code:
public function __construct($config)
{
if (array_key_exists('options', $config)) {
...
}
if (array_key_exists('driver_options', $config)) {
...
}
}
Here I am concern with the way the parameter is used.
If I were in lisp I would do:
(defun ct (&key options driver_options)
(do-something-with-option-and-driver_option))
But since I am in PHP I would rather have a constructor that take a list of parameter and let them be null if there a not require.
So what do you guys think about having an array as parameter in other to do some initialization-or-whatever?
In other to answer you have to take in account the point of view of the user of the function and the designer of the API.
Personally, I dislike that idiom. I prefer to have a long parameter list instead, if necessary.
The problem is that I can't know the elements the array can't take by looking at the function signature. On top of that, the implementations almost never check if there's any key that's not recognized, so if I mispell an array key, I get no warning.
A better alternative would be passing a configuration object. At least, there the IDE can provide me hints on the available configuration objects and the calculated default values for missing options can be moved away from the constructor you show to the getters in the configuration object. The obvious alternative is to provide setters for the several configuration options; though this doesn't help for the required ones for each no default can be provided.
I very much like the design pattern of "options arrays". If PHP supported Python's argument expansion, then I would agree to go with a long parameter list. But I just find foo(1, 2, 'something', true, 23, array(4), $bar); to be REALLY un-readable. I typically will use arrays when there are more than about 3 or 4 parameters that need to be set...
What I would suggest to "clean up" the constructor, is create a protected method for accessing config vars (preferably in a base class):
abstract class Configurable {
protected $options = array();
protected $requiredOptions = array();
public function __construct(array $options = array()) {
$this->options = $options;
foreach ($this->requiredOptions as $option) {
if (!isset($this->options[$option])) {
throw new InvalidArgumentException('Required argument [$'.$option.'] was not set');
}
}
}
protected function _getOption($key, $default = null) {
return isset($this->options[$key]) ? $this->options[$key] : $default;
}
}
Then, in your class, you can overload the requireOptions array to define things that need to be set
class Foo extends Configurable {
protected $requiredOptions = array(
'db',
'foo',
);
public function __construct(array $options = array()) {
parent::__construct($options);
if ($this->_getOption('bar', false)) {
//Do Something
}
}
}
One thing. If you do this, PLEASE document the options required. It will make life a lot easier for those that follow you.
I find using arrays as parameters to be helpful when there are a lot of optional parameters. Usually I'll use an array_merge to merge the passed array with the "defaults" array. No checking required. If you have required parameters, you can use array_diff_key to determine if any required parameters are missing.
function params($p_array) {
static $default_vals = array('p1'=>1, 'p2'=>null, 'p3'=>'xyz');
static $rqd_params = array('p1'=>null, 'p3'=>null);
// check for missing required params
$missing_params = array_diff_key($rqd_params, $p_array);
if ( count($missing_params)>0 ) {
//return an error (i.e. missing fields)
return array_keys($missing_params);
}
// Merge passed params and override defaults
$p_array = array_merge($default_vals, $p_array);
}
Related
This is a bit philosophical but I think many people encountered this problem. The goal is to access various (dynamically declared) properties in PHP and get rid of notices when they are not set.
Why not to __get?
That's good option if you can declare your own class, but not in case of stdClass, SimpleXML or similar. Extending them is not and option since you usually do not instantiate these classes directly, they are returned as a result of JSON/XML parsing.
Example:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
We have simple stdClass object. The problems is obvious:
$b = $data->birthday;
The property is not defined and therefore a notice is raised:
PHP Notice: Undefined property: stdClass::$birthday
This can happen very often if you consider that you get that object from parsing some JSON. The naive solution is obvious:
$b = isset($data->birthday) ? $data->birthday : null;
However, one gets tired very soon when wrapping every accessor into this. Especially when chaining the objects, such as $data->people[0]->birthday->year. Check whether people is set. Check if the first element is set. Check if birthday is set. Check if year is set. I feel a bit overchecked...
Question:
Finally, my question is here.
What is the best approach to this issue? Silencing notices does not seem to be the best idea. And checking every property is difficult. I have seen some solutions such as Symfony property access but I think it is still too much boilerplate. Is there any simpler way? Either third party library, PHP setting, C extension, I don't care as far as it works... And what are the possible pitfalls?
If I understand correctly, you want to deal with 3rd party Objects, where you have no control, but your logic requires certain properties that may not be present on the Object. That means, the data you accepting are invalid (or should be declared invalid) for your logic. Then the burden of checking the validity goes into your validator. Which I hope you already have following best practices to deal with 3rd party data. :)
You can use your own validator or one by frameworks. A common way is to write a set of Rules that your data needs to obey in order to be valid.
Now inside your validator, whenever a rule is not obeyed, you throw an Exception describing the error and attaching Exception properties that carry the information you want to use. Later when you call your validator somewhere in your logic, you place it inside try {...} block and you catch() your Exceptions and deal with them, that is, write your special logic reserved for those exceptions. As general practice, if your logic becomes too large in a block, you want to "outsource" it as function.
Quoting the great book by Robert Martin "Clean Code", highly recommended for any developer:
The first rule of function is that they should be small. The second is that they should be smaller than that.
I understand your frustration dealing with eternal issets and see as cause of the problem here that each time you need to write a handler dealing with that technical issue of this or that property not present. That technical issue is of very low level in your abstraction hierarchy, and in order to handle it properly, you have to go all the way up your abstraction chain to reach a higher step that has a meaning for your logic. It is always hard to jump between different levels of abstraction, especially far apart. It is also what makes your code hard to maintain and is recommended to avoid.
Ideally your whole architecture is designed as a tree where Controllers sitting at its nodes only know about the edges going down from them.
For instance, coming back to your example, the question is -
Q - What is the meaning for your app of the situation that $data->birthday is missing?
The meaning will depend on what the current function throwing the Exception wants to achieve. That is a convenient place to handle your Exception.
Hope it helps :)
One solution (I don't know if it's the better solution, but one possible solution) is to create a function like this:
function from_obj(&$type,$default = "") {
return isset($type)? $type : $default;
}
then
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$name = from_obj( $object->name , "unknown");
$job = from_obj( $object->job , "unknown");
$skill = from_obj( $object->skills[0] , "unknown");
$skills = from_obj( $object->skills , Array());
echo "Your name is $name. You are a $job and your main skill is $skill";
if(count($skills) > 0 ) {
echo "\n\nYour skills: " . implode(",",$skills);
}
I think it's convienent because you have at the top of your script what you want and what it should be (array, string, etc)
EDIT:
Another solution. You could create a Bridge class that extends ArrayObject:
class ObjectBridge extends ArrayObject{
private $obj;
public function __construct(&$obj) {
$this->obj = $obj;
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new ObjectBridge($tmp);
}
}
public function __set($key,$value) {
$this->obj->$key = $value;
}
public function __call($method,$args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$bridge = new ObjectBridge($object);
echo "My name is {$bridge->name}, I have " . count($bridge->skills). " skills and {$bridge->donald->duck->is->paperinik}<br/>";
// output: My name is Pavel, I have 0 skills and
// (no notice, no warning)
// we can set a property
$bridge->skills = Array('php','javascript');
// output: My name is Pavel, my main skill is php
echo "My name is {$bridge->name}, my main skill is {$bridge->skills[0]}<br/>";
// available also on original object
echo $object->skills[0]; // output: php
Personally I would prefer the first solution. It's more clear and more safe.
Data formats which have optional fields are quite difficult to deal with. They're problematic in particular if you have third parties accessing or providing the data, since there rarely is enough documentation to comprehensively cover all causes for the fields to appear or disappear. And of course, the permutations tend to be harder to test, because coders won't instinctively realize that the fields may be there.
That's a long way of saying that if you can avoid having optional fields in your data, the best approach to dealing with missing object properties in PHP is to not have any missing object properties...
If the data you're dealing with is not up to you, then I'd look into forcing default values on all fields, perhaps via a helper function or some sort of crazy variation of the prototype pattern. You could build a data template, which contains default values for all fields of the data, and merge that with the real data.
However, if you do that, are you failing, unless? (Which is another programming philosophy to take into heart.) I suppose one could make the case that providing safe default parameters satisfies data validation for any missing fields. But particularly when dealing with third party data, you should exercise high level of paranoia against any field you're plastering with default values. It's too easy to just set it to null and -- in the process -- fail to understand why it was missing in the first place.
You should also ask what are you trying to achieve? Clarity? Safety? Stability? Minimal code duplication? These are all valid goals. Being tired? Less so. It suggests a lack disciprine, and a good programmer is always disciprined. Of course, I'll accept that people are less likely to do something, if they view it as a chore.
My point is, the answer to your question may differ depending on why it's being asked. Zero effort solution is probably not going to be available, so if you're only exchanging one menial programming task to another one, are you solving anything?
If you are looking for a systematic solution that will guarantee that the data is always in the format you have specified, leading to reduced number of logical tests in the code that processes that data, then perhaps what I suggested above will be of help. But it will not come without a cost and effort.
in PHP version 8
you can use Nullsafe operator as follow:
$res = $data?->people[0]?->birthday?->year;
The best answers have been given, but here is a lazy one:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
if(
//...check mandatory properties: !isset($object->...)&&
){
//error
}
error_reporting(E_ALL^E_NOTICE);//Yes you're right, not the best idea...
$b = $object->birthday?:'0000-00-00';//thanks Elvis (php>5.3)
//Notice that if your default value is "null", you can just do $b = $object->birthday;
//assign other vars here
error_reporting(E_ALL);
//Your code
Use a Proxy object - it will add just one tiny class and one line per object instantiation to use it.
class ProxyObj {
protected $obj;
public function __construct( $obj ) {
$this->_obj = $obj;
}
public function __get($key) {
if (isset($this->_obj->$key)) {
return $this->_obj->$key;
}
return null;
}
public function __set($key, $value) {
$this->_obj->$key = $value;
}
}
$proxy = new ProxyObj(json_decode($data));
$b = $proxy->birthday;
You can decode the JSON object to an array:
$data = '{"name": "Pavel", "job": "programmer"}';
$jsonarray = json_decode($data, true);
$b = $jsonarray["birthday"]; // NULL
function check($temp=null) {
if(isset($temp))
return $temp;
else
return null;
}
$b = check($data->birthday);
I've hit this problem, mainly from getting json data from a nosql backed api that by design has inconsistent structures, eg if a user has an address you'll get $user->address otherwise the address key just isn't there. Rather than put tons of issets in my templates I wrote this class...
class GracefulData
{
private $_path;
public function __construct($d=null,$p='')
{
$this->_path=$p;
if($d){
foreach(get_object_vars($d) as $property => $value) {
if(is_object($d->$property)){
$this->$property = new GracefulData($d->$property,$this->_path . '->' . $property);
}else{
$this->$property = $value;
}
}
}
}
public function __get($property) {
return new GracefulData(null,$this->_path . '->' . $property);
}
public function __toString() {
Log::info('GracefulData: Invalid property accessed' . $this->_path);
return '';
}
}
and then instantiate it like so
$user = new GracefulData($response->body);
It will gracefully handle nested calls to existing and non existing properties. What it can't handle though is if you access a child of an existing non-object property eg
$user->firstName->something
Lots of good answers here, I consider #Luca 's answer as one of the best - I extended his a little so that I could pass in either an array or object and have it create an easy to use object. Here's mine:
<?php
namespace App\Libraries;
use ArrayObject;
use stdClass;
class SoftObject extends ArrayObject{
private $obj;
public function __construct($data) {
if(is_object($data)){
$this->obj = $data;
}elseif(is_array($data)){
// turn it into a multidimensional object
$this->obj = json_decode(json_encode($data), false);
}
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new SoftObject($tmp);
}
}
public function __set($key, $value) {
$this->obj->$key = $value;
}
public function __call($method, $args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
// attributions: https://stackoverflow.com/questions/18361594/how-to-solve-the-missing-object-properties-in-php | Luca Rainone
I have written a helper function for multilevel chaining, for example, let's say you want to do something like $obj1->obj2->obj3->obj4, and my helper will return empty string if one of the tiers is not defined or null
class MyUtils
{
// for $obj1->obj2->obj3: MyUtils::nested($obj1, 'obj2', 'obj3')
// returns '' if some of tiers is null
public static function nested($obj1, ...$tiers)
{
if (!isset($obj1)) return '';
$a = $obj1;
for($i = 0; $i < count($tiers); $i++){
if (isset($a->{$tiers[$i]})) {
$a = $a->{$tiers[$i]};
} else {
return '';
}
}
return $a;
}
}
In perl I'm used to doing
my $foo = new WhatEver( bar => 'baz' );
and now I'm trying to figure out if PHP objects can ever be constructed this way. I only see this:
my $foo = new WhatEver();
$foo->{bar} = 'baz';
is it possible to do it in one step?
You can lay out your constructor as follows:
class MyClass {
public function __construct($obj=null) {
if ($obj && $obj instanceof Traversable || is_array($obj)) {
foreach ($obj as $k => $v) {
if (property_exists($this,$k)) {
$this->{$k} = $v;
}
}
}
}
}
This has a serie of drawbacks:
This is inefficient
The variables you create will not show up on any doc software you use
This is the open door to all forms of slackery
However, it also presents the following benefits:
This can be extended pretty safely
It allows you to lazy-implement variables
It also allows you to set private variables, provided that you know their names. It is pretty good in that respect if not abused.
The parameters passed in the parentheses (which can be omitted, by the way, if there aren't any) go to the constructor method where you can do whatever you please with them. If a class is defined, for example, like this:
class WhatEver
{
public $bar;
public function __construct($bar)
{
$this -> bar = $bar;
}
}
You can then give it whatever values you need.
$foo = new WhatEver('baz');
There are a few ways to accomplish this, but each has its own drawbacks.
If your setters return an instance of the object itself, you can chain your methods.
my $foo = new WhatEver();
$foo->setBar("value")->setBar2("value2");
class WhatEver
{
public $bar;
public $bar2;
public function setBar($bar)
{
$this->bar = $bar;
return $this;
}
public function setBar2($bar2)
{
$this->bar2 = $bar2;
return $this;
}
}
However, this doesn't reduce it to one step, merely condenses every step after instantiation.
See: PHP method chaining?
You could also declare your properties in your constructor, and just pass them to be set at creation.
my $foo = new WhatEver($bar1, $bar2, $bar3);
This however has the drawback of not being overtly extensible. After a handful of parameters, it becomes unmanageable.
A more concise but less efficient way would be to pass one argument that is an associative array, and iterate over it setting each property.
The implicit assumption here is that objects have meaningful, presumably public, properties which it is up to the calling code to provide values for. This is by no means a given - a key aspect of OOP is encapsulation, so that an object's primary access is via its methods.
The "correct" mechanism for initialising an object's state is its constructor, not a series of property assignments. What arguments that constructor takes is up to the class definition.
Now, a constructor might have a long series of named parameters, so that you could write $foo = new WhatEver(1, "hello", false, null) but if you want these to act like options, then it could take a single hash - in PHP terms, an Array - as its argument.
So, to answer the question, yes, if your constructor is of the form function __construct(Array $options) and then iterates over or checks into $options. But it's up to the constructor what to do with those options; for instance passing [ 'use_safe_options' => true ] might trigger a whole set of private variables to be set to documented "safe" values.
As of PHP 5.4 (which introduced [ ... ] as an alternative to array( ... )), it only takes a few more character strokes than the Perl version:
$foo = new WhatEver( ['bar' => 'baz'] );
I'm new to OOP and I'm having some trouble on understanding the structures behind it.
I've created a library in Codeigniter (Template), which I pass some parameters when loading it, but I want to pass those parameters to the functions of the library.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Template {
public function __construct($params)
{
echo '<pre>'; print_r($params); echo '</pre>';
//these are the parameters I need. I've printed them and everything seems fine
}
public function some_function()
{
//I need the above parameters here
}
}
Try this:
class Template {
// Set some defaults here if you want
public $config = array(
'item1' => 'default_value_1',
'item2' => 'default_value_2',
);
// Or don't
// public $config = array();
// Set a NULL default value in case we want to use defaults
public function __construct($params = NULL)
{
// Loop through params and override defaults
if ($params)
{
foreach ($params as $key => $value)
{
$this->config[$key] = $value;
}
}
}
public function some_function()
{
//i need the above parameters here
// Here you go
echo $this->config['item1'];
}
}
This would turn array('item1' => 'value1', 'item2' => 'value2'); into something you can use like $this->config['item1']. You are just assigning the array to the class variable $config. You could also loop through the variables and validate or alter them if you wish.
If you don't want to override the defaults you set, just don't set the item in your $params array. Use as many different variables and values as you want, it's up to you :)
As Austin has wisely advised make sure to read up on php.net and experiment yourself. The docs can be confusing because they give a lot of edge case examples, but if you check out the Libraries in Codeigniter you can see some examples or how class properties are used. It's really bread-and-butter stuff that you must be familiar with to get anywhere.
Make class members like this:
class Template {
var $param1
var $param2
public function __construct($params)
{
$this->param1 = $params[1]
$this->param2 = $params[2]
//and so on
}
}
Then you can use them in your function
You may want to store the parameters as properties in your class so all your methods will have access to them.
See this documentation about properties in PHP 5: http://www.php.net/manual/en/language.oop5.properties.php
EDIT: Actually, if you're completely new to OOP, you'll find that it can be difficult to wrap your head around at first. Asking questions on SO one at a time as you run into problems will be a very inefficient way to go about it. If you want to save some time, I would recommend starting by reading a basic text that explains the concepts of OOP separate from language-specific implementation details (e.g. The Object-Oriented Thought Process). Then, when you want details, the PHP docs on the subject are pretty good (and free).
I would recommend deciding weather the variables of the class are private or public. This helps greatly with the readability. Private variables should be used for internal variables where as public variables should be used for things that are attributes of the object.
When we take a look at Javascript frameworks like Dojo, Mootools, jQuery, JS Prototype, etc. we see that options are often defined through an array like this:
dosomething('mainsetting',{duration:3,allowothers:true,astring:'hello'});
Is it a bad practice to implement the same idea when writing a PHP class?
An example:
class Hello {
private $message = '';
private $person = '';
public function __construct($options) {
if(isset($options['message'])) $this->message = $message;
if(isset($options['person'])) $this->person = $person;
}
public function talk() {
echo $this->person . ' says: ' . $this->message;
}
}
The regular approach:
class Hello {
private $message = '';
private $person = '';
public function __construct() {}
public function setmessage($message) {
$this->message = $message;
}
public function setperson($person) {
$this->person = $person;
}
public function talk() {
echo $this->person . ' says: ' . $this->message;
}
}
The advantage in the first example is that you can pass as much options as you want and the class will only extract those that it needs.
For example, this could be handy when extracting options from a JSON file:
$options = json_decode($options);
$hello = new Hello($options);
This is how I do this regulary:
$options = json_decode($options);
$hello = new Hello();
if(isset($options['message'])) $hello->setmessage($options['message']);
if(isset($options['person'])) $hello->setperson($options['person']);
Is there a name for this pattern and do you think this is a bad practice?
I have left validation etc. in the examples to keep it simple.
There are good and bad aspects.
The good:
No need for multiple method signatures (i.e. overloading, where supported)
In keeping with the previous point: methods can be invoked with arguments in any order
Arguments can be dynamically generated, without needing to specify each one that will be present (example: you dynamically create an array of arguments based on user input and pass it to the function)
No need for "boilerplate" methods like setName, setThis, setThat, etc., although you might still want to include them
Default values can be defined in the function body, instead of the signature (jQuery uses this pattern a lot. They frequently $.extend the options passed to a method with an array of default values. In your case, you would use array_merge())
The bad:
Unless you properly advertise every option, your class might be harder to use because few will know what options are supported
It's one more step to create an array of arguments when you know ahead of time which you will need to pass
It's not always obvious to the user that default values exist, unless documentation is provided or they have access to the source code
In my opinion, it's a great technique. My favorite aspect is that you don't need to provide overloaded methods with different signatures, and that the signature isn't set in stone.
There's nothing wrong with that approach, especially if you have a lot of parameters you need to pass to a constructor. This also allows you to set default values for them and array_merge() them inside a constructor (kinda like all jQuery plugins do)
protected $default_params = array(
'option1' => 'default_value'
);
public function __construct($params = array()) {
$this->params = array_merge($this->default_params, $params);
}
If you want live examples of this "pattern", check out symfony framework, they use it almost every where: here's an example of sfValidatorBase constructor
When you give the arguments names it's called "Named Notation" v.s. "Positional Notation" where the arguments must be in a specific order.
In PHP you can pass an "options" parameter to give the same effect as other languages (like Python) where you can use a genuine Named Notation. It is not a bad practice, but is often done where there is a good reason to do it (i.e. in your example or a case where there are lots of arguments and they do not all need to set in any particular order).
I don't know the name, but i really doubt it is a bad practice, since you usally use this when you wan't to declare a small o quick function or class property
If there are mandatory options, they should be in the constructor's parameter list. Then you add the optional options with default values.
public function __construc($mandatory1, $mandatory2, $optional1="value", $optional2="value") { }
If all of your options are optional, then it can be useful to create a constructor taking an array. It would be easier to create the object than with a "normal constructor" : you could provide just the options you want, while with a "normal constructor" if you want to provide $optional2, you have to provide $optional1 (even setting it to the default value).
I wouldn't say its bad practice, at least if you trust the source of the data.
Another possibility would be dynamically calling the setters according to the options array key, like the following:
public function __construct($options) {
foreach($options as $option => $value) {
$method = 'set'.$option;
if(method_exists($this, $method)
call_user_func(array($this, $method, $value);
}
}
Why not do both? Have your constructor cake and eat it too with a static factory "named constructor":
$newHello = Hello::createFromArray($options);
You first have your constructor with the options in order. Then add a static method like this to the same class:
public static function createFromArray($options){
$a = isset($options['a']) ? $options['a'] : NULL;
$b = isset($options['b']) ? $options['b'] : NULL;
$c = isset($options['c']) ? $options['c'] : NULL;
return new Hello($a, $b, $c);
}
This will keep new developers and IDE's happy as they can still see what it takes to construct your object.
I agree with the general attitude of the answers here in that either way is a viable solution depending on your needs and which is more beneficial for your app.
I made a class. I give it some objects (mostly retreived database rows) as input, and tell it which fields it has to show, and which buttons I want. Then it renders a very nice html table! It's pretty awesome, I think.
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->buttons = array('edit','delete');
$ot->render();
However, I also want to be able to manipulate the data it shows. For example, i want to be able to truncate the 'description' field. Or to display an image thumbnail (instead of 'picture.jpg' as text). I don't know how to pass these functions to the class. Perhaps something like:
$ot->functions = array(null,null,'truncate','thumbnail');
But then I don't know how to convert these strings to run some actual code, or how to pass parameters.
There must be a nice way to do what I want. How?
Check this question and the answer is:
As of PHP5.3 you could use closures
or functors to pass methods
around. Prior to that, you could write
an anonymous function with
create_function(), but that is
rather awkward.
But what you are trying to achieve is best solved by passing Filter Objects to your renderer though. All filters should use the same method, so you can use it in a Strategy Pattern way, e.g. write an interface first:
interface Filter
{
public function filter($value);
}
Then write your filters implementing the interface
class TruncateFilter implements Filter
{
protected $_maxLength;
public function __construct($maxLength = 50)
{
$this->_maxLength = (int) $maxLength;
}
public function filter($value)
{
return substr(0, $this->_maxLength, $value) . '…';
}
}
Give your ObjectTable a method to accept filters
public function addFilter($field, Filter $filter)
{
if(in_array($field, $this->fields)) {
$this->_filters[$field][] = $filter;
}
return $this;
}
And when you do your ObjectTable instantiation, use
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->addFilter('description', new TruncateFilter)
->addFilter('name', new TruncateFilter(10))
->addFilter('image', new ThumbnailFilter);
Then modify your render() method to check if there is any Filters set for the fields you are rendering and call the filter() method on them.
public function render()
{
foreach($this->fields as $field) {
$fieldValue = // get field value somehow
if(isset($this->filters[$field])) {
foreach($this->filters[$field] as $filter) {
$fieldValue = $filter->filter($fieldValue)
}
}
// render filtered value
}
}
This way you can add infinite filters.
PHP has a pseudo-type called "callback", which is actually an ugly closure in disguise. You can call such callbacks using call_user_func() or call_user_func_array():
$callback = 'strlen';
echo call_user_func($callback, '123');
// will output 3 (unless you're using a strange encoding)
You are looking for create_function().
However, creating functions on runtime and adding them to a class doesn't sound right to me. It's likely to become a maintenance nightmare very quickly. There are better ways to achieve what you want. What kind of manipulation would the functions to to the data? How about storing them in a "tools" class and connecting that with the table object when needed?
$functions = array(null,null,'truncate','thumbnail');
$function_1 = $functions[3];
$my_string = 'string to truncate';
$result = call_user_func($functions[2], $my_string);
If you want to pass multiple parameters, use call_user_func_array instead.
You might also want to explore call_user_func, which allows you to call a function based on a string representing its name.