覆盖php中的__set魔术函数

时间:2012-04-04 18:44:17

标签: php oop

我正在尝试创建一个方法,允许我使用setVal()函数在类中设置属性,如果用户试图从类外部设置值而不使用'forceSet'函数那么它会抛出异常。

问题是,即使$ forceSet为true,它也会抛出异常。如果我在类中手动设置属性以进行私有访问,那么一切正常,但这不是一个选项,因为我希望能够动态地在这个类中设置各种属性。

class test
{
    private $_allowedCols = array('title', 'name', 'surname');

    public function __set($n,$v)
    {
        $this->setVal($n, $v);
    }

    public function setVal($name, $value, $forceSet=false)
    {
        if (!$forceSet && !in_array($this->_allowedCols, $name))
        {
            throw new Exception('cant set value');
        }
        $this->$name = $value;
    }
}

$b = new test;
$b->setVal('blah', 'test', true);
print_r($b);
exit;

我想要做的是将$ _POST中的所有值设置为对象中的属性。我想检查$ _allowedCols以确保只将我想要的值放入对象中,但有时我可能想从代码中强制输入不在$ _allowedCols中的值。

有什么想法吗?

5 个答案:

答案 0 :(得分:1)

黑客可以使用,但使用内部阵列可能更干净。类似的东西:

class test
{
    private $data = array();

    public function __set($n,$v)
    {
        if (isset($this->data[$n])) return $this->data[$n] = $v;

        throw new Exception('cant set value');
    }
    public function __get($n)
    {
        if (isset($this->data[$n])) return $this->data[$n];

        throw new Exception('cant retrieve value');
    }
    public function setVal($name, $value)
    {
        $this->data[$name] = $value;
    }
}

但如果你想坚持你的方法,那么:

class test
{
    private $forceFlag = false;

    public function __set($name,$value)
    {
        if ($this->forceFlag) return $this->$name = $value;
        throw new Exception('cant set value');
    }
    public function setVal($name, $value)
    {
        $this->forceFlag = true;
        $this->$name = $value;
        $this->forceFlag = false;
    }
}

答案 1 :(得分:0)

如果查看异常的堆栈跟踪,您会注意到此行触发了设置__set的调用:

$this->$name = $value;

然后在__set中,$this->setVal($n, $v)使用默认值false,从而抛出异常。要解决此问题,您可以将__set中的来电修改为:

$this->setVal($n, $v, true);

答案 2 :(得分:0)

使用上面的代码,这一行:

$this->$name = $value;

...调用:

test::__set('blah', 'test');

...因为test::$blah未定义,而后者又调用:

test::setVal('blah', 'test', false);

一种可能但并非完美的解决方法是:

public function setVal($name, $value, $forceSet=false)
{
    if (!$forceSet && isset($value)) 
    {
        throw new Exception('cant set value');
    }
    $this->$name = null;
    $this->$name = $value;
}

虽然我不确定你的代码是什么意思。

答案 3 :(得分:0)

在测试了这么多选项后......对我来说是最好的选择

我选择了这个因为

  1. 使用Exception会终止整个脚本,或者必须在声明值的任何时候捕获异常
  2. __set__get可以通过扩展类
  3. 轻松覆盖
  4. 可与多个班级一起使用的实施
  5. 什么能够直接使用Object而无需添加其他getter方法
  6. 锁定可能会导致冲突
  7. 该脚本不会更改您现有的应用程序结构
  8. 可以与Singleton一起使用..
  9. 代码:

    abstract class Hashtable
    {
        final $hashTable = array()  ;
    
        final function __set($n,$v)
        {
            return false ;
        }
    
        final function __get($n)
        {
            return @$this->hashTable[$n] ;
        }
    
        final function _set($n, $v)
        {
            $this->hashTable[$n] = $v ;
    
        }
    }
    
    class Test extends Hashtable {} ;
    
    $b = new Test();
    $b->_set("bar","foo",true);
    $b->_set("hello","world",true);
    //$b->setVal("very","bad"); // false
    $b->bar = "fail" ;
    var_dump($b,$b->bar);
    

    输出

    object(Test)[1]
      public 'hashTable' => 
        array
          'bar' => string 'foo' (length=3)
          'hello' => string 'world' (length=5)
    string 'foo' (length=3)
    

    我希望这会有所帮助

    谢谢

    :)

答案 4 :(得分:0)

看起来你为PHP提供的开箱即用功能编写了很多代码:

$b = new test;
$b->blah = 'test';
print_r($b);

您不需要__set,也不需要setVal(ue)函数。

但是,当您想要控制访问权限时,您需要确保不将其绑定到成员。而是将其作为私人成员存储在地图内:

class test
{
    private $values;
    public function __set($n,$v)
    {
        $this->setVal($n, $v);
    }

    public function setVal($name, $value, $forceSet=false)
    {
        if (!$forceSet)
        {
            throw new Exception('cant set value');
        }
        $this->values[$name] = $value;
    }
}

这可确保存在已设置的成员,以便不再触发__set

相关问题