Simon Tong
Simon Tong

Reputation: 410

Override __set magic function in php

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?

Upvotes: 1

Views: 2043

Answers (5)

Baba
Baba

Reputation: 95101

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

:)

Upvotes: 0

Cerad
Cerad

Reputation: 48865

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

Upvotes: 1

hakre
hakre

Reputation: 197732

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.

Upvotes: 0

netcoder
netcoder

Reputation: 67695

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.

Upvotes: 0

mfonda
mfonda

Reputation: 7993

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

Upvotes: 0

Related Questions