0

我在尝试中使用了三个技巧:

  • 反射不能与动态类属性一起使用
  • 访问动态类属性时必须调用 __get() 或 __set()
  • debug_backtrace() 可以用来模拟类似的东西private

对于Foo具有私有非静态属性的类$bar,我想禁止外部$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;
        }
    }
}

这应该(未经测试)不受三种访问的影响:

  • 直接访问
    • debug_backtrace() 检查调用上下文是否来自 $this。$this 之外的直接访问将被禁止。
  • 反射属性
    • PHP 致命错误:未捕获的 ReflectionException:属性栏不存在
    • 反射不适用于动态属性。它甚至没有通过ReflectionClass::hasProperty():-)检测到它的存在
  • Closure::bind
    • 未经测试,但我相信 debug_backtrace() 应该返回与FILE不同的“文件” ,而是定义闭包的文件。我只有正确的用法Foo,所以我不在乎只要加载了正确的代码。

假设没有写任何文件的权限,也没有重新定义类方法的扩展名,但是可以加载任意PHP代码,有没有办法改变这个Foo->bar属性?

4

1 回答 1

1

经过一番修修补补,我得出结论,您想要实现的目标是不可能的,也是毫无意义的。我会解释为什么。

一个类属性有 3 种可能的可见性:publicprotectedprivate。如果它是public,您可以通过简单的赋值来修改值:

$foo->bar = "new value";

我们可以用 拦截这个__set()吗?来自PHP 手册

__set() 在将数据写入不可访问的属性时运行。

公共财产始终是可访问的。因此,根本没有机会拦截并进行检查,因为__set()不会被调用。

接下来,如果属性是protectedprivate,你必须在类中声明它,这意味着你可以使用ReflectionPropertyClosure::bind修改它的值。

__set()最后,您实际上可以通过将值存储在其他地方来确保调用它。例如,在同一个对象中使用不同的名称,甚至在另一个对象中。不幸的是,无论您将值存储在哪里,我上面的解释都再次适用,这就是它毫无意义的原因。

于 2017-01-09T00:48:52.057 回答