Override __set magic function in php

Go To StackoverFlow.com

1

I'm trying to create a method that will allow me to set properties within a class using the setVal() function, if the user is trying to set the value from outside the class without using the 'forceSet' function then it will throw an exception.

The problem is that its throwing an exception even if the $forceSet is true. If i set the property manually in the class to have private access then everything works fine, but this is not an option as I wish to be able to set various properties in this class dynamically.

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;

What I want to be able to do is set all the values from a $_POST into properties in the object. I want to check against the $_allowedCols to make sure only values I want are being put into the object but sometimes I might want to force values in from the code that aren't in the $_allowedCols.

Any ideas?

2012-04-04 18:44
by Simon Tong
This line in setVal $this->$name = $value; will trigger the __set method which in case will trigger the setVal with $forceSet=falsehuysentruitw 2012-04-04 18:53
protecting a property with a bool is no protection anyway. You should find a better implementation for what you want to achieve. Imagine how long it will take before the 'user' knows he needs to set the third parameter to true : - huysentruitw 2012-04-04 18:54
While I hesitate to down vote, significantly changing your question is frowned upon and make perusing the answers quite confusing as most of them now address something completely different - Cerad 2012-04-04 21:01
It hasn't changed I was just clarifying what I'm trying to do. Adding a bit more contex - Simon Tong 2012-04-05 04:29
Hmmm. As of this writing you have 6 answers. None of them address allowedCols so I guess they should all be down voted? And most of them seem to be talking about why an exception is being thrown almost as though that was part of the question. Very strange - Cerad 2012-04-05 13:31
Arguments aside, I have decided to go with the answer you gave by setting a flag in the object when the user sets forceSet to true - Simon Tong 2012-04-06 08:12


1

The hacks will work but it might be cleaner to use an internal array. Something like:

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;
    }
}

But if you want to stick with your approach then:

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;
    }
}
2012-04-04 19:11
by Cerad
your second approuch is not taking the $forceSet function paremeter into accoun - huysentruitw 2012-04-04 19:30
Oops. forceSet is not required using this approach. Accidently left it in. Safe to say that anytime setval is called then the user really wants to set it - Cerad 2012-04-04 19:34


0

If you look at the stack trace of your exception, you'll notice the call to set __set is being triggered by this line:

$this->$name = $value;

Then in __set, it does $this->setVal($n, $v), which uses the default value of false, and thus throws the exception. To fix this, you can modify your call in __set to be:

$this->setVal($n, $v, true);
2012-04-04 18:53
by mfonda


0

With the above code, this line:

$this->$name = $value;

...invokes:

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

...because test::$blah is undefined, which in turn invokes:

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

A possible, yet not perfect, workaround is this:

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

Although I'm not sure what the point of your code is.

2012-04-04 18:53
by netcoder


0

After testing so many options .. the is the one that works the best for me

I chose this because

  1. Use of Exception terminates the entire scripts or one has to catch exception anything time a value is declared
  2. __set and __get can easily be overriding by extending class
  3. Implementation that can be used with multiple class
  4. What to be able to use the Object directly without having to add another getter method
  5. Locking can cause conflict
  6. The script would not change your existing application structure
  7. Can be used with Singleton ..

Code :

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);

Output

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

I hope this helps

Thanks

:)

2012-04-04 18:57
by Baba
You can still override $b->bar simply by doing $b->bar = "fail"huysentruitw 2012-04-04 19:07
Corrected .. what do you think about the new approach ? - Baba 2012-04-04 19:45


0

It looks like you write much code for a functionality PHP offers out of the box:

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

You don't need __set for this, nor the setVal(ue) function.

However when you want to control the access, you need to ensure that you're not binding it to members. Instead store it inside of a map as a private member:

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;
    }
}

This ensures, that a member exists that is set, so that __set is not triggered again.

2012-04-04 19:08
by hakre
Ads