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

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

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

Post 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)
}
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post 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;
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post 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 :)
User avatar
daedalus__
DevNet Resident
Posts: 1925
Joined: Thu Feb 09, 2006 4:52 pm

Post 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.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

volka has posted, several times now, doing it with a Reflection.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

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

Post 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) . ');');
    }
(#10850)
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post 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.
User avatar
daedalus__
DevNet Resident
Posts: 1925
Joined: Thu Feb 09, 2006 4:52 pm

Post by daedalus__ »

I'm not amazing enough to understand what that is doing. :D

It looks neat though.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post 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.
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post 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.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

Indeed. It does need rework, I only completed that yesterday. :)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post 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
(#10850)
User avatar
daedalus__
DevNet Resident
Posts: 1925
Joined: Thu Feb 09, 2006 4:52 pm

Post 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..
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Care to define "reflection injection" Daedalus?
User avatar
daedalus__
DevNet Resident
Posts: 1925
Joined: Thu Feb 09, 2006 4:52 pm

Post 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.
Post Reply