Page 1 of 2

Following from the problem of eval() in Service Locator..

Posted: Tue Aug 08, 2006 11:03 pm
by Jenk
I've finally had a chance of coming up with a more secure, and flexible, method of dynamically calling/instantiating objects.. (the previous methods I have seen, seem to cast all parameters as singular primitives.. if we passed an object or associative array, we would have problems.. )

Though as always I expect there will be something I have missed.. unfortunately I couldn't avoid the use of eval() altogether, but this may just have been able to negate any need to worry.. if I haven't missed anything of course.


This example uses a dynamic method call to demonstrate, but the logic applies for any parameter/arguments passing.

Code: Select all

<?php

class A 
{
    private function __call ($name, $args)
    {
        $str = '';
        foreach ($args as $arg => $val) {
            $str .= ', $prefix_' . $arg;
        }
        extract($args, EXTR_PREFIX_ALL, 'prefix');
        eval('$this->bar(' . substr($str, 2) . ');');
    }
    
    public function bar ($a, $b, $c)
    {
        var_dump(func_get_args());
    }
}

$a = new A;

$a->foo(1,2,3);
 
?>
In this example, the eval()'d code equates to:

Code: Select all

$this->bar($prefix_0, $prefix_1, $prefix_2);
and final output:

Code: Select all

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

Posted: Tue Aug 08, 2006 11:28 pm
by feyd
For calling methods, call_user_func_array(). As for instantiating a new object, it varies in how your objects work. For instance, objects I would need to dynamically create most often don't have any constructor arguments. Everything is done through handled properties. So my code does the following, basically

Code: Select all

$instance = new $className();
foreach ($properties as $property => $value)
{
  $instance->$property = $value;
}

return $instance;

Posted: Tue Aug 08, 2006 11:34 pm
by Jenk
Indeed, this was specifically brought about because of the disliking I took to the use of eval() in Ninja Space Goat's example of the Service Locator thread, and could potentially save hours of (re)work if I or anyone else were to change and adopt the handled properties approach as you have shown in your example :)

Posted: Fri Aug 18, 2006 1:32 am
by daedalus__
I didn't quite understand the code you wrote, Jenk. I'm tired.

I am also trying to work around having to use eval().

I have succesfully instantiated objects that have zero or one argument being passed without using eval but not multiple arguments.

I am curious about what you tried?

I've already spent several hours on this and intend to spend as many as it takes. :D

So far the best thing I have come up with is to pass an array to the constructor but that would mean all classes would have to take one array of arguments and that is no good because I doubt everyone wants to do it that way. :- /

There has got to be another way..

Keep ya posted.

EDIT: I'm going to try being tricky with list() but am too tired to explain it, it probably won't work.

Posted: Fri Aug 18, 2006 1:44 am
by feyd
volka has posted, several times now, doing it with a Reflection.

Re: Following from the problem of eval() in Service Locator.

Posted: Fri Aug 18, 2006 2:24 am
by Christopher
You can eliminate the extract by doing:

Code: Select all

class A 
{
    private function __call ($name, $args)
    {
        $params = array();
        $n = count($args);
        for ($i=0;$i<$n;++$i) {
            $params[] = "\$arg[$i]";
        }
        eval('$this->bar(' . explode(',', $params) . ');');
    }

Posted: Fri Aug 18, 2006 6:05 am
by Jenk
I like Reflection :)

Thus I am now using:

Code: Select all

<?php
/**
* Locate singleton objects.
* @class ServiceLocator ServiceLocator.class.php
* @package stkmart
* @author Jenk
* @version 1.0
*/
class ServiceLocator
{
    /**
    * Singleton store for instance of ServiceLocator
    * @private
    */
    private static $instance;

    /**
    * Store for all registered objects.
    * @private
    */
    private $objects;
    
    /**
    * Static Function getInstance(),
    * returns singleton ServiceLocator object.
    * @return ServiceLocator
    * @static
    */
    public static function getInstance ()
    {
        if (is_null(self::$instance)) self::$instance = new ServiceLocator;
        
        return self::$instance;
    }
    
    /**
    * Restrict constructor to in-class only.
    * This is to prevent multiple ServiceLocator objects
    * @private
    */
    private function __construct ()
    {
    }

    /**
    * Dynamic calling of objects. Uses ReflectionClass to instantiate object
    * and stores in class property objects.
    * @param name name of the class
    * @param args array containing the arguments to be passed to the object
    * @return object $this->objects[$name]
    */
    public function __call ($name, $args)
    {
        if (empty($this->objects[$name])) {
            $class = new ReflectionClass($name);
            $this->objects[$name] = $class->newInstanceArgs($args);
        }

        return $this->objects[$name];
    }
}

?>
as my ServiceLocator/SingletonRegistry. :)

EDIT: NB: I use __autoload() in conjunction with this.

Posted: Fri Aug 18, 2006 10:47 am
by daedalus__
I'm not amazing enough to understand what that is doing. :D

It looks neat though.

Posted: Fri Aug 18, 2006 10:55 am
by Jenk
Example usage:

Code: Select all

<?php

$locator = ServiceLocator::getInstance();

$arrayObject = $locator->ArrayObject(range(0, 10));

echo get_class($arrayObject); //outputs 'ArrayObject'

?>
:)

Will make changes to it soon™ to add objectExists() method as well.

Posted: Fri Aug 18, 2006 11:06 am
by volka
But what about

Code: Select all

<?php
$locator = ServiceLocator::getInstance();

$arrayObjectA = $locator->ArrayObject(range(0, 10));
$arrayObjectB = $locator->ArrayObject(range(1, 11));
?>
?
I wouldn't expect to get the same range(0,10) object as before.

Posted: Fri Aug 18, 2006 11:28 am
by Jenk
Indeed. It does need rework, I only completed that yesterday. :)

Posted: Fri Aug 18, 2006 11:43 am
by Christopher
Typically a Service Locator can:

- normally instantiate
- instantiate unique objects ("singletons")
- pass registered parameters upon instatiation
- persist objects in the session.

I made a semi-spec here:

viewtopic.php?t=51794&start=45

Posted: Sat Aug 19, 2006 2:49 pm
by daedalus__
Questionnnnnnn!!

Is reflection injection possible in PHP, and could someone show me an example?

P.S. Reflection is amazing I hope that I can understand it enough to use it soon..

Posted: Sat Aug 19, 2006 3:00 pm
by feyd
Care to define "reflection injection" Daedalus?

Posted: Sat Aug 19, 2006 5:19 pm
by daedalus__
"Reflection injection problems are a subset of injection problem, in which external input is used to construct a string value passed to class reflection APIs. By manipulating the value an attacker can cause unexpected classes to be loaded, or change what method or fields are accessed on an object."

Taken from: http://www.owasp.org/index.php/Reflection_injection

I'm not sure how that works, or if it works, so I thought I would ask.