Better way to dynamically load instance w/ args

I have a abstract class which has a getinstance function in. The problem I have is with arguments. Right now I am using an eval to pass those arguments to the class constructor. Is there a better way to do this.

See there HERE! part:
abstract class Core {
        /**
         * Arry of the class instances
         *
         * @var array
         */
        static protected $_instances = array();

        /**
         * Retrieves the singleton instance of this class.
         * This is completely self loading
         *
         * @return instance of this class
         */
        static public function getInstance() {
                //Get the called class so we know which class to load
                $class = get_called_class();

                //If there is not an already loaded instance
                if (!isset(self::$_instances[$class])) {
                        //If there were no passed in arguments
                        if (func_num_args() == 0) {
                                //Make the new class instance
                                self::$_instances[$class] = new $class();
                        } else {
                                //--------------- HERE! ------------------
                                $args = func_get_args();
                                $eval = 'self::$_instances[$class] = new $class(';
                                for ($i = 0, $count = func_num_args(); $i < $count; ++$i) {
                                        $eval .= '$args['.$i.'],';
                                }
                                $eval = substr($eval, 0, -1);
                                $eval .= ');';

                                eval($eval);
                        }
                }

                return self::$_instances[$class];
        }
}

Open in new window

LVL 1
Bryan_HeathAsked:
Who is Participating?
 
Slick812Connect With a Mentor Commented:
greetings Bryan_Heath, as you, "Using eval always makes me nervous" is also true for me.

As I looked at your code, you might consider changing your constructor to use an array for the parameters, which would give you a way to have any length of parameters,

function __construct($InArray=null){if ($InArray == null) {//code here to have it work when called  $x = new $class();
    }
}

And, you might could use -

$args = func_get_args()
self::$_instances[$class] = new $class($args);


A method I have used several times when I needed to call a function with some sort of "manipulated" arguments , was to use the  call_user_func ( )
   manual at  http://www.php.net/manual/en/function.call-user-func.php

the call_user_func ( ) worked well for me, however, I never tried it on a class "Static" function call.

I tried the forward_static_call( ) once, but I did not use it as it was to restrictive for what I needed to do, although it is meant for "Static" functions, maybe it just calls a parent class static function, from a subclass, I am not sure, but you might look at it.
0
 
Dushan De SilvaTechnology ArchitectCommented:
I think you should put  eval($eval) inside the loop, because this code will evaluate as php code and create new instance of only the last argument as the class name on the end of the loop. Are you using any common framework or in house build one?

0
 
Bryan_HeathAuthor Commented:
I don't understand your comment as that's not the case.  It will evaluate the to php code creating a new class passing in all the arguments.  For example:

class Test extends Core {
        public function __construct($foo, $bar) {
        }
}

$test = Test::getInstance('foo', 'bar');

Open in new window

This example will work because the $eval will be:
self::$_instances[$class] = new $class($arg[0], $arg[1]);

Open in new window

So it works perfectly fine and not my question.  

My question was is there a better way to do this since this seems sort of hackish.  Using eval always makes me nervous.
0
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
Bryan_HeathAuthor Commented:
Oh and this is in house framework
0
 
Bryan_HeathAuthor Commented:
I thought of using an array but I was trying to keep the constructor clean as this will be the core of 99% of the classes I will be using.  Though a possibility I want to not do that if I can.

However looking at call_user_func for possible solutions (thank you) I found this:
				$args = func_get_args();
				$reflectionObj = new \ReflectionClass($class);
				self::$_instances[$class] = $reflectionObj->newInstanceArgs($args);

Open in new window

Which works. I wonder which is faster?
0
 
Slick812Commented:
you ask "I wonder which is faster?" - - If it works, I would not think that performance (process speed) would be much of a consideration, since it is unlikely it would be used in a large loop executed many times. I always try and use faster code methods, but in php, a scripted language, it is not worth the trouble sometimes to squeak out a slightly faster bit of code.

But I have no idea about the speed, and I would guess,  the only way to really find out is to set up a timed test.

Also thanks for showing me the ReflectionClass, I had not come across that, and it looks very useful for stretching a class, it must be new, many of the methods have no documentation listed.
0
 
Mark GilbertSenior Performance EngineerCommented:
I would really recommend you have a look at the func_get_args reference page:

http://php.net/manual/en/function.func-get-args.php

There are numerous comments relating to how to get args sanitized, as well as referenced args...which would be a much safer bet than using eval. Eval is so dangerous if your input gets tainted. You don't want any tainting so make sure it's safe before processing, otherwise catastrophic things could happen on your box that you wouldn't even want to consider. These are the types of exploits that script kiddies oogle over.

I also highly recommend the tiny book by O'Reilly called Essential PHP Security isbn #059600656x
0
 
Bryan_HeathAuthor Commented:
@Slick812 - Both eval and reflective have the same run time and but appear to use roughly the same resources.  Though obviously reflective seems like a much better choice.

@ingwa - I agree and don't want to use eval.  Which is the entire point of my post.  See my first reply.
0
 
Mark GilbertSenior Performance EngineerCommented:
Hi Bryan, great to see you are on board with getting rid of eval. If you take a look at the link I provided, you will find the best real world examples for the function, as well as all other functions. I believe the comment I made reference to about referenced args would be the lines you would want to take.

On another note, I wouldn't be too concerned with any hits on performance if you are able to ensure security all the way. It would be better to take a performance hit rather than loose your entire server, or have hackers hijack it for illegitimate means. Server hardware has become cheaper recently and adding space, ram and processor power is much more affordable than it used to be. Rackspace also has some really great cloud based servers that you may find useful if you find you need more power.

Hope this helps.
0
 
Bryan_HeathAuthor Commented:
Though I understand your concern you seem to misunderstand how this class will be used.  This makes a class instance thus insuring singleton methodology.  As such I have no idea what the args might be as they will be passed in when a new class is created.  Thus making sanitizing them nearly impossible.  Also the args passed in are merely being pushed off to the parent class.  So if there is a problem its on the side where the class is being created and should be secured there, not here.

As to referencing the args that doesn't make sense in PHP 5.  See the very first note on your link.
0
 
Bryan_HeathAuthor Commented:
I found the solution with help from his response
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.