tags:

views:

78

answers:

4

Is there a way to make a read-only property of an object in PHP? I have an object with a couple arrays in it. I want to access them as I normally would an array

echo $objObject->arrArray[0];

But I don't want to be able to write to those arrays after they're constructed. It feels like a PITA to construct a local variable:

$arrArray = $objObject->getArray1();
echo $arrArray[0];

And anyways, while it keeps the array in the object pristine, it doesn't prevent me from re-writing the local array variable.

+2  A: 
Viper_Sb
Boss: "I thought you said you finished your algorithm" Client: "I did boss, what is the problem" Boss: "it does not work" Client: "it works depending on what you need"
Chris
A: 

If defined, the magic functions __get() and __set() will be called whenever a non-existing or private property is accessed. This can be used to create "get" and "set" methods for private properties, and for instance make them read-only or manipulate the data when stored or retrieved in it.

For instance:

class Foo
{
  private $bar = 0;
  public $baz = 4; // Public properties will not be affected by __get() or __set()
  public function __get($name)
  {
    if($name == 'bar')
      return $this->bar;
    else
      return null;
  }
  public function __set($name, $value)
  {
    // ignore, since Foo::bar is read-only
  }
}

$myobj = new Foo();
echo $foo->bar; // Output is "0"
$foo->bar = 5;
echo $foo->bar; // Output is still "0", since the variable is read-only

See also the manual page for overloading in PHP.

Frxstrem
I would recommend throwing an exception in `set` if you're not allowing a particular variable to be set. The reason is that otherwise it can be quite confusing when someone new needs to do something, and it's not working. "But I set that array to include this variable. The assignment succeeds, so why the *#@$ isn't it working?"...
ircmaxell
@ircmaxell: good point...
Frxstrem
+7  A: 

Well, the question is where do you want to prevent writing from?

The first step is making the array protected or private to prevent writing from outside of the object scope:

protected $arrArray = array();

If from "outside" of the array, a GETTER will do you fine. Either:

public function getArray() { return $this->arrArray; }

And accessing it like

$array = $obj->getArray();

or

public function __get($name) {
    return isset($this->$name) ? $this->$name : null;
}

And accessing it like:

$array = $obj->arrArray;

Notice that they don't return references. So you cannot change the original array from outside the scope of the object. You can change the array itself...

If you really need a fully immutable array, you could use a Object using ArrayAccess...

Or, you could simply extend ArrayObject and overwrite all of the writing methods:

class ImmutableArrayObject extends ArrayObject {
    public function append($value) {
        throw new LogicException('Attempting to write to an immutable array');
    }
    public function exchangeArray($input) {
        throw new LogicException('Attempting to write to an immutable array');
    }
    public function offsetSet($index, $newval) {
        throw new LogicException('Attempting to write to an immutable array');
    }
    public function offsetUnset($index) {
        throw new LogicException('Attempting to write to an immutable array');
    }
}

Then, simply make $this->arrArray an instance of the object:

public function __construct(array $input) {
    $this->arrArray = new ImmutableArrayObject($input);
}

It still supports most array like usages:

count($this->arrArray);
echo $this->arrArray[0];
foreach ($this->arrArray as $key => $value) {}

But if you try to write to it, you'll get a LogicException...

Oh, but realize that if you need to write to it, all you need to do (within the object) is do:

$newArray = $this->arrArray->getArrayCopy();
//Edit array here
$this->arrArray = new ImmutableArrayObject($newArray);
ircmaxell
A: 

in the class, do this:

private $array;

function set_array($value) {
    $this->array = $value;
}

then you just set like this:

$obj->set_array($new_array);
hugo_leonardo