2

I'm trying to dynamically create the base for a DB entity generalization for a project I'm working on. I basically want to dynamically create a set of standard methods and tools for the properties in any class that extends this. Much like the tools you get for free with Python/Django.

I got the idea from this guy: http://www.stubbles.org/archives/65-Extending-objects-with-new-methods-at-runtime.html

So I've implemented the __call function as described in the post above,

public function __call($method, $args) {
    echo "<br>Calling ".$method;
    if (isset($this->$method) === true) {
        $func = $this->$method;
        $func();
    }
}

I have a function which gives me the objects public/protected properties through get_object_vars,

public function getJsonData() {
    $var = get_object_vars($this);
    foreach($var as &$value) {
        if (is_object($value) && method_exists($value, 'getJsonData')) {
            $value = $value->getJsonData;
        }
    }
    return $var;
}

and now I want to create some methods for them:

public function __construct() {
    foreach($this->getJsonData() as $name => $value) {
        // Create standard getter
        $methodName = "get".$name;
        $me = $this;
        $this->$methodName = function() use ($me, $methodName, $name) { 
            echo "<br>".$methodName." is called";
            return $me->$name; 
        };
    }
}

Thanks to Louis H. which pointed out the "use" keyword for this down below. This basically creates an anonymous function on the fly. The function is callable, but it is no longer within the context of it's object. It produces a "Fatal error: Cannot access protected property"

Unfortunately I'm bound to PHP version 5.3, which rules out Closure::bind. The suggested solution in Lazy loading class methods in PHP will therefore not work here.

I'm rather stumped here... Any other suggestions?

Update

Edited for brevity.

Community
  • 1
  • 1
Øystein Amundsen
  • 3,993
  • 8
  • 44
  • 63
  • why on earth would you want to do that? Just use the public attributes. – Gordon Jan 25 '13 at 13:48
  • Try implementing this behaviour using traits, then $this will be available. – jasir Jan 25 '13 at 13:48
  • @Gordon: making the properties public would allow others to change them directly, which is not the behaviour I'm looking for. I want read only access. – Øystein Amundsen Jan 25 '13 at 15:11
  • I might be missing the big picture here. Is there any chance that someone *would* actually change them? After all, those seem to be runtime created classes. Also, you said you are not in an object scope and there is public/protected properties. – Gordon Jan 25 '13 at 15:49
  • No, the classes are not runtime created. They are a part of a small generic orm I'm creating. I'm trying to duplicate the ease, readability and maintaiability in which python/django models are made. Having a set of standard functions operating on the model properties is what I want to achieve. – Øystein Amundsen Jan 25 '13 at 15:59
  • Well, if the answer by Louis below is not solving your problem, my answer in the linked dupe probably does. If not, please update the question to point out why. That will help others to hopefully what you are looking for. – Gordon Jan 25 '13 at 16:19
  • No, neither does. See update in original question... – Øystein Amundsen Jan 25 '13 at 22:12

2 Answers2

2

Try it like this (you have to make the variables you'll need available to the method)

$this->$methodName = function() use ($this, $methodName, $name){ 
    echo "<br>".$methodName." is called";
    return $this->$$name; 
};

You should have access to the object context through $this.

Louis Huppenbauer
  • 3,719
  • 1
  • 18
  • 24
  • That produced: Fatal error: Cannot use $this as lexical variable... :-( – Øystein Amundsen Jan 25 '13 at 18:10
  • But assigning $this to $me and then including $me in the use statement, did work. But it is obviously still not in object context. I get a "Fatal error: Cannot access protected property"... – Øystein Amundsen Jan 27 '13 at 11:45
  • Even though this solution did not quite work for me, you definitely pointed me in the right direction. I will therefore mark this as the correct answer. I've updated the original question with the solution. – Øystein Amundsen Jan 27 '13 at 22:18
0

Instead of updating the original question above, I include the complete solution here for anybody struggling with the same issues:

First of all, since the closure cannot have real object access, I needed to include the actual value with the "use" declaration when creating the closure function (see original __construct function above):

$value =& $this->$name;
$this->$methodName = function() use ($me, $methodName, &$value)  { 
    return $value; 
};

Secondly the __call magic method did not just need to call the closure function, it needed also to return any output from it. So instead of just calling $func(), I return $func();

This did the trick! :-)

Øystein Amundsen
  • 3,993
  • 8
  • 44
  • 63