views:

1311

answers:

6

I have a very special case in which I need to call a protected method from outside a class. I am very conscious about what I do programmingwise, but I would not be entirely opposed to doing so in this one special case I have. In all other cases, I need to continue disallowing access to the internal method, and so I would like to keep the method protected.

What are some elegant ways to access a protected method outside of a class? So far, I've found this.

I suppose it may be possible create some kind of double-agent instance of the target class that would sneakily provide access to the internals...

+2  A: 

I'm just throwing this out there since I haven't programmed in PHP in two years. Could you just add a function to the class that calls the protected method like so?

$obj->publicFunc = create_function('$arg', 'return $this->protectedFunc($arg);');

Edit: I think Tom's correct in looking at the documentation for create_function. It looks like the scope of $this will be "wrong" when you try to call it with this example.


It looks like traditional anonymous functions are supported since PHP 5.3.0 as well (and my first solution probably won't work), so I'd probably write it like this instead:

$obj->publicFunc = function($arg) { 
     return $this->protectedFunc($arg); 
};

Since I think it looks a little cleaner (and your IDE of choice will highlight it better of course).


Ugh, I tried using Reflection to call the method but PHP won't allow you to do that either. It seems that you're going to have to use some sort of child class like the other posters have suggested. If you find a method that works, the developers will likely classify it as a bug in the future and break your code when you upgrade to the next version.

I recommend extending the class.

Dave L
Nono, it's a good shot. I'll probably give that a try. If it works, I'll let you know.
Chad Johnson
I think this will just create a string $obj->publicFunc which is the name of the new function. When you try to call it the scope is as for a normal function.
Tom Haigh
I'm not getting this working: http://chadjohnson.ath.cx:8080/static/anonymous_function.phps. The error I get is "Call to undefined method Chad::publicFunction()." What might I be doing wrong?
Chad Johnson
Reflection is a good idea, in 5.3 you could use setAccessible() to let you call a protected method
Tom Haigh
Good to know. Turns out I don't need to access this protected method...thank goodness. I learned things though!
Chad Johnson
Tom: setAccessible() is only available on the ReflectionProperty class. If you try to create a new ReflectionProperty where bar is the method and foo is the class name you will get this error: Uncaught exception 'ReflectionException' with message 'Property foo::$bar does not exist'. If only you could do it on methods too...
Dave L
+1  A: 

Depending on how much it is worth to you you could emulate most of it with __get/__set/__call and debug_backtrace and testing the "class" element of the last entry in the call stack.

Maybe you can preserve the $this context in closures with php 5.3, too. Something like

return function($arg) use($this) { $this->foo = (int)$args; }

VolkerK
+1  A: 

This is a little kludgy, but might be an option.

Add a child class for the sake of accessing your protected function

public class Child extends Parent {
    public function protectedFunc() {
        return parent::protectedFunc();
    }
}

Then, instantiate an instance of Child instead of Parent where you need to call that function. You might also be able to re-cast an instance of Parent:

$childObj = (Child)$parentObj;

But don't quote me on that.

Brock Boland
Hmm, yea, this looks like my only option at this point. Maybe I should call this class Spy o_O.
Chad Johnson
You can't cast objects like that in PHP
Tom Haigh
A: 

I would think that in this case, refactoring so you don't require this sort of thing is probably the most elegant way to go. In saying that one option is to use __call and within that parse debug_backtrace to see which class called the method. Then check a friends whitelst

class ProtectedClass {

    // Friend list
    private $friends = array('secret' => array('FriendClass')); 

    protected function secret($arg1, $arg2) {
        // ...
    }

    public function __call($method, $args) {

        $trace = debug_backtrace();
        $class = $trace[1]['class'];
        if(in_array($class, $this->friends[$method]))
            return $this->$method($args[0], $args[1]);

        throw new Exception();
    }
}

I think I need a shower.

rojoca
A: 

I'd think about what is the matter with the program design if I have to call a private function?

It used to be the case when

  • your class is responsible for several things (it is really two or thre calsses wrapped together) or
  • the rules of encapsulation are broken (utility functions, for example)

By finding any way to walk around this questions, you'll be nowhere nearer to the real solution.

Csaba Kétszeri
A: 

Suppose your method declaration goes like so:

protected function getTheFoo() {
    ...
}

protected function setTheFoo($val) {
    ...
}

Usage:

$obj->__get('the_foo');
$obj->__set('the_foo', 'myBar');

This bypasses the protected methods and goes directly straight to the instance variables.

Island Joe