5

Even though there's some discussions regarding this issue I wanted to check on certain example what would be the best approach.
Instead of using existing solutions I created my own persistence layer (like many do) So my approach is also in question here.

For every table in db I have model class that has appropriate getters and setters and some mandatory methods. I also created only one generic DAO class that handles all types of model objects.
So, for example to save any model object I instantiate genericDAO class and call save method that I pass model object as attribute. Problem is that in runtime genericDAO class doesn't know whitch model object it gets and what methods (getters and setters) exist in it, so I need to call mandatory model class method that retrieves list of attributes as multiple string array.
For example for every attribute there's array(table_column_name,attribute_name,is_string).

When I call save function it looks like this:

public function save(&$VO) {  
$paramArray = $VO->getParamArray();//get array of attributes
$paramIdArray = $paramArray[0];  //first attribute is always id
/*create and execute getId() and store value into $void to check if it's save or update*/
eval('$voId = $VO->get'.ucfirst($paramIdArray[1]).'();');  
...

Currently I'm using eval to execute those methods, but as it is well known eval is very slow.
I'm thinking of changing that into call_user_func method
Something like:

$voId = call_user_func(array($VO, 'get'.ucfirst($paramIdArray[1])));

But also there's other solutions. I can maybe use something like this

$method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->$method();
or else
$method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->{$method}();

What would be the best way?

Aleksandar
  • 181
  • 2
  • 11

2 Answers2

9

First of all, there's no need to pass references like you are doing. You should give this a read to try to understand how PHP handles object references.

So public function save(&$VO) { should become public function save($VO) {.

Second, there is no need to use eval (in fact, it's better not to because of speed, debugability, etc). You can't stack-trace an eval call like you can a dynamic one.

Third, call_user_func is all but useless since PHP supports dynamic variable functions. Instead of call_user_func(array($obj, $method), $arg1), just call $obj->$foo($arg1). The call_user_func_array function is still useful since it supports variable length arguments and supports passing references.

So, ultimately, I would suggest this:

$method = 'get' . ucfirst($paramIdArray[1]);
$voId = $VO->$method();

Note that there's no need to call method_exists, since it may be callable and not exist due to __get magic method support...

Community
  • 1
  • 1
ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • I think `call_user_func()` is still the only way to call an anonymous function stored in object variable: `$o->f = function() {};` trying to do `$o->f()` will result in `$o doesn't hav a method named 'f'` warning. – Mchl Feb 04 '11 at 18:42
  • @Mchl: `$c = $o->f; $c();` will work. Or you can implement a `__call` magic method to do it for you. But you don't *have* to use `call_user_func`. And that's the point of what I was trying to say. It doesn't do anything that you can't do in native language... It may be easier to do and read, but it's not doing anything special... – ircmaxell Feb 04 '11 at 18:46
  • Ah yes. Forgot about `$c = $o->f; $c();`, You're right about that. – Mchl Feb 04 '11 at 18:51
  • @ircmaxell Thanks for advice but the reason I decided to do pass-by-reference is because after save I can retrieve ID from database and set ID in the object. That way I can reuse that object in the remaining code. – Aleksandar Feb 06 '11 at 12:53
  • @aleksandar: Objects are passed by reference by default... as long as it is an object there is no reason to use the `&` operator... – prodigitalson Feb 06 '11 at 16:08
  • @ircmaxell: I was confused a bit after reading a docs. Now you clarify that. Thanks again. – Aleksandar Feb 07 '11 at 08:30
  • I'm developing an MVC similar to SilverStripe, and this: "Note that there's no need to call method_exists, since it may be callable and not exist due to __get magic method support..." helped me see that I could simplify my dispatcher method and at the same time add Controller class flexibility. Thanks! – AVProgrammer Jan 19 '13 at 19:48
  • Another alternative to above... `$c = array($o,'f'); $c();` – MrWhite Dec 03 '14 at 02:22
  • "Objects are passed by reference by default" - this is not strictly true. The reason we don't need to use the `&` operator is because objects are stored differently in PHP 5. An object _identifier_ is still copied (but not the object itself as what would have happened in PHP 4). – MrWhite Dec 03 '14 at 02:26
  • I hadn't; I have now. :) I was just responding to @prodigitalson's comment above (although it was a while ago). Yes, I agree with your linked post, specifically, "variables which store an object as storing the pointer to that object" - the [PHP manual](http://php.net/manual/en/language.oop5.references.php) refers to this "pointer" as an "object identifer". However, personally, I would not use the term "object reference" as you have done, specifically the word "reference". Since it's not a user-land "reference" and using that word is just confusing IMO. – MrWhite Dec 04 '14 at 00:15
2

I normally would use:

$method = 'get'.ucfirst($attribute);
if(method_exists($obj, $method){
  $obj->$method();
} 

But unless there is a very good reason i would just return a key => value array from getParamArray. And operate on that instead of using the getters...

prodigitalson
  • 60,050
  • 10
  • 100
  • 114