Immutable Object Members in PHP 5

by Castwide on 4-6-2008 • Tags: code, oop, php11 comments

In object-oriented programming, it is often useful to access members of an object within another object, like so:

$object->member->function();

In PHP 4, this type of member access is possible as long as the member is a variable. Unfortunately, this means that it's also possible to change the value of the member.

<?
// Using PHP 4:

class Foo {
function hello() {
echo "Hello, world!";
}
}

class Example {
var $member;
function Example() {
$this->member = new Foo();
}
}

$object = new Example();
$object->member->hello(); // Outputs "Hello, world!"
$object->member = "something else";
$object->member->hello(); // Causes a fatal error; $object->member is now a string
?>

Most object-oriented languages have ways to protect against this type of problem. In C++, for example, the member could be a function that returns a reference to an object:

object->member()->hello();

But this option isn't available in PHP 4, either. The return value of a function cannot be treated as an object in the same line. You need to create a new variable to store the member in order to treat it like an object.

<?
// Using PHP 4:

class Foo {
function hello() {
echo "Hello, world!";
}
}

class Example {
var $_member;
function Example() {
$this->_member = new Foo();
}
function member() {
return $this->_member;
}
}

$object = new Example();
$mem = $object->member();
$mem->hello(); // This works
$object->member()->hello(); // Causes a fatal error
?>

Additionally, PHP 4 functions return objects by value, so the above example would return a copy of the $_member variable instead of a reference.

<?
// Using PHP 4:

class Foo {
var $_num;
function Foo() {
$this->_num = 1;
}
function hello() {
echo $this->_num;
$this->_num++;
}
}

class Example {
var $_member;
function Example() {
$this->_member = new Foo();
}
function member() {
return $this->_member;
}
}

$object = new Example();

$mem1 = $object->member();
$mem1->hello(); // Outputs 1
$mem1->hello(); // Outputs 2

$mem2 = $object->member();
$mem2->hello(); // Outputs 1 because this is a new copy of $_member
?>

Because of these issues, programmers need to ensure that the function returns the object by reference and explicitly declare variables as references. The resulting code if often ugly and fragile.

PHP 5 provides several important improvements for object-oriented programming. First and foremost, its default behavior is to return objects by reference. If you want a copy instead of a reference, you have to use the clone keyword. This makes it easier to ensure that you're using the same instance of an object.

<?
// Using PHP 5:

class Foo {
var $_num;
function Foo() {
$this->_num = 1;
}
function hello() {
echo $this->_num;
$this->_num++;
}
}

class Example {
var $_member;
function Example() {
$this->_member = new Foo();
}
function member() {
return $this->_member;
}
}

$object = new Example();

$mem1 = $object->member();
$mem1->hello(); // Outputs 1
$mem1->hello(); // Outputs 2

$mem2 = $object->member();
$mem2->hello(); // Outputs 3 because $mem1 and $mem2 are both references to $_member
?>

It is also possible to reference a function's return value as an object in the same line:

$object = new Example();
$object->member()->hello(); // Works in PHP 5

But PHP 5 provides yet another option. Instead of using a function to reference an object, we can use the __get() method to provide access to an object as if it were a public variable. Then we can use the __set() method to make sure that the member objects cannot be overwritten.

<?
// Using PHP 5:

class Foo {
private $_x;
public function Foo() {
$this->_x = 1;
}
public function hello() {
echo $this->_x;
$this->_x++;
}
}

class Example {
private $_members;
public function Example() {
$this->_members = array();
$this->_members['foo'] = new Foo();
// We can add more elements to the $_members array here
}
public function __get($member) {
if (isset($this->_members[$member])) return $this->_members[$member];
return false;
}
public function __set($member, $value) {
trigger_error("Unable to modify member '{$member}'");
}
}

$object = new Example();
$object->foo->hello(); // Outputs 1
$object->foo->hello(); // Outputs 2
$object->foo = "something else"; // Returns a warning
$object->foo->hello(); // Outputs 3
?>

With that small amount of boilerplate in the class definition, we get the ability to reference object members much more elegantly everywhere else in the code.

Comments

So true. Honesty and everything recogneizd.
ggJ06H ukarqojupqia
beitrag buy hinzufgen name text tramadol hip Cialis 9237
car insurance 174 homeowners insurance quotes 0217
home insurance vhj cheap car insurance =-O
viagra >:-] pravachol buy tramadol 2592
home insurance 06809 online car insurance obrjml
viagra gcp cialis 8-OOO
tramadol %[ cheap generic cialis quwvc
viagra and levitra 8DDD taking ultram and lexapro together 468410
cialis sales fwyda tramadol uzvdv

Add Comment

More Articles