0

I'm using three tricks in my attempt:

  • Reflections can't be used with dynamic class properties
  • __get() or __set() must be called when accessing dynamic class properties
  • debug_backtrace() can be utilized to emulate something similar to private

For the class Foo with the private non-static property $bar, I want to disallow any scopes outside $this to modify its value. Therefore I'm doing it like this:

/** @property object $bar */
class Foo{
    public function __get($k){
        if($k === "bar") return $this->bar;
    }
    public function __set($k, $v){
        if($k === "bar"){
            $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
            if($trace[0]["object"] !== $this or $trace[0]["file"] !== __FILE__) throw new RuntimeException("Illegal access");
            $this->bar = $v;
        }
    }
}

This should be (not tested) invulnerable to three kinds of access:

  • Direct access
    • debug_backtrace() checks if the calling context is from $this. Direct access outside $this will be disallowed.
  • ReflectionProperty
    • PHP Fatal Error: Uncaught ReflectionException: Property bar does not exist
    • Reflections don't work with dynamic properties. It doesn't even detect the existence of it through ReflectionClass::hasProperty() :-)
  • Closure::bind
    • Not tested, but I believe debug_backtrace() should return the "file" something different from FILE, but rather the file that defined the Closure. I only have correct usage of Foo, so I don't care as long as the correct code is loaded.

Assuming that there is no permission to write any files and no extension to redefine class methods, but arbitrary PHP code can be loaded, are there any methods to change this Foo->bar property?

SOFe
  • 7,867
  • 4
  • 33
  • 61
  • You are trying to prevent changes to `$bar` property from outside the class? What's wrong with a simple `private $bar`? – Rei Jan 08 '17 at 17:24
  • @RayRadin as I mentioned already, it can be accessed through reflections. I'm trying to prevent reflections from touching it through dynamic properties. – SOFe Jan 08 '17 at 17:35
  • I see. So you want a behavior like `private $bar` without declaring it inside the object? – Rei Jan 08 '17 at 17:43

1 Answers1

1

After a bit of tinkering, I concluded that what you are trying to achieve is impossible and also pointless. I'll explain why.

A class property has 3 possible visibilities: public, protected, and private. If it is public, you can modify the value with a simple assignment:

$foo->bar = "new value";

Can we intercept this with __set()? From PHP Manual:

__set() is run when writing data to inaccessible properties.

A public property is always accessible. Therefore, there is simply no chance to intercept and do your checks because __set() will not be called.

Next, if the property is protected or private, you have to declare it in the class, which means you can use ReflectionProperty or Closure::bind to modify its value.

Finally, you can actually make sure that __set() is called by storing the value somewhere else. For instance, under a different name in the same object, or even in another object. Unfortunately, no matter where you store the value, my explanation above applies again, which is why it is pointless.

Rei
  • 6,263
  • 14
  • 28
  • Nice recursion! So if we want to save the salt that generates the other name, we first have to save that salt somewhere private :P I realized local variables are truly private. Let's see if I can access it through some backtrace extension :D Jk. I know this is really pointless. – SOFe Jan 10 '17 at 09:29
  • 1
    @PEMapModder Yes, but I didn't know if it's possible or not, so it was a good mental exercise. I like it. – Rei Jan 10 '17 at 10:04