Jump to content

Recommended Posts

Hi Guys

 

I’m trying to get my head around the magic functions __get and __set and property overloading.  From what I’ve read this sounds like a really useful thing to learn/use in the right scenario but the examples I have seen have all confused me greatly because I can’t see that the code is actually doing anything.

 

Take the snippet below (an example of Devshed). I can follow the logic of the get set bit.

 


class User
{
// constructor (not implemented)
public function _construct(){}

// set undeclared property
function __set($property, $value)
{
$this->$property = $value;
}

// get defined property
function __get($property)
{
if (isset($this->$property))
{
return $this->$property;
}
}




// example of usage of 'User' class with property overloading
$user = new User();
$user->fname = 'Alejandro';
$user->lname = 'Gervasio';
$user->email = 'alejandro@mydomain.com';
$user->address = 'My address 1234';

// display user data
echo 'First Name: ' . $user->fname . ' Last Name: ' . $user->lname . ' Email: ' . $user->email . ' Address: ' . $user->address;
/*
displays the following
First Name: Alejandro Last Name: Gervasio Email: alejandro@mydomain.com Address: My address 1234
*/
}

 

 

 

The part I don’t understand is where you assign something to a variable i.e.

 

$user->fname = 'Alejandro';

Because even if I wiped out everything in the class (so there is no __get __set functions) and then just did

$user->fname = 'Alejandro';
echo $user->fname;

It still shows "Alejandro" in the browser. So I can’t see that the __get __set is actually doing anything at all.

 

So am I missing the point and doing something wrong (likely)?

 

Or is this just a bad example?

 

Can someone help me to understand please :(

 

Drongo

 

Anyone able to help me on this one?  :confused:

 

Hi Guys

 

I’m trying to get my head around the magic functions __get and __set and property overloading.  From what I’ve read this sounds like a really useful thing to learn/use in the right scenario but the examples I have seen have all confused me greatly because I can’t see that the code is actually doing anything.

 

Take the snippet below (an example of Devshed). I can follow the logic of the get set bit.

 


class User
{
// constructor (not implemented)
public function _construct(){}

// set undeclared property
function __set($property, $value)
{
$this->$property = $value;
}

// get defined property
function __get($property)
{
if (isset($this->$property))
{
return $this->$property;
}
}




// example of usage of 'User' class with property overloading
$user = new User();
$user->fname = 'Alejandro';
$user->lname = 'Gervasio';
$user->email = 'alejandro@mydomain.com';
$user->address = 'My address 1234';

// display user data
echo 'First Name: ' . $user->fname . ' Last Name: ' . $user->lname . ' Email: ' . $user->email . ' Address: ' . $user->address;
/*
displays the following
First Name: Alejandro Last Name: Gervasio Email: alejandro@mydomain.com Address: My address 1234
*/
}

 

 

 

The part I don’t understand is where you assign something to a variable i.e.

 

$user->fname = 'Alejandro';

Because even if I wiped out everything in the class (so there is no __get __set functions) and then just did

$user->fname = 'Alejandro';
echo $user->fname;

It still shows "Alejandro" in the browser. So I can’t see that the __get __set is actually doing anything at all.

 

So am I missing the point and doing something wrong (likely)?

 

Or is this just a bad example?

 

Can someone help me to understand please :(

 

Drongo

One of the weaknesses of OOP in PHP is that it allows you to make public fields on the fly.  Good OOP is all about encapsulation, which means having private or protected fields.  That said, sometimes it's easier/more readable to reference a field as though it was public, in order to remove otherwise necessary getField()/setField() methods that pollute classes.

 

So, if your class isn't going to be doing anything aside from setting or returning values, using __get and __set is an easier way to go.

 

Note, however, that you don't really use __get and __set properly in your code.  Again, those two magic methods are used to give the appearance of public fields.  Internally, they should still be private or protected:

 

class Example
{
    private $allowedProps = array('prop1', 'prop2', 'prop3');
    private $data = array();

    public function __set($propName, $propValue)
    {
        if (in_array($propName, $this->allowedProps))
        {
            // set property
        }
        else
        {
            // error
        }
    }

    public function __get($propName)
    {
        if (array_key_exists($propName, $this->data))
        {
            // get property
        }
        else
        {
            // error
        }
    }
}

 

In the example above, there are two private arrays.  The first contains the names of all allowed fields, so that not just any field can be created/used.  Ideally, you'd have some idea of what an object should contain.  The second array is the actual data.  __get and __set check to see that:

 

1. The field that's trying to be accessed should be accessed

2. That data exists, or should be created

As mentioned, the reason your example works without get and set is because PHP allows you to make new properties on the fly.  One way to override this, and something I do on most of my objects, is to define __get and __set:

 

abstract class LockedObject {
   public function __get($nm){ throw new Exception('Property '.$nm.' not defined'); }
   public function __set($nm, $v){ throw new Exception('Property '.$nm.' not defined'); }
}

 

You can use __get and __set to enforce certain rules or perform certain operations on your protected/private members.  For example, say your data member needed to be of a certain object, say DateTime.  You could do:

class Posting extends LockedObject {
protected $mPostedDate;

public function __set($nm, $v){
	switch ($nm){
		case 'PostedDate':
			if (!is_object($v)){ //Assume string, try and make a new date time
				$v = new DateTime($v);
			}

			if (!(i$v instanceof DateTime)){
				throw new Exception('PostedDate must be a DateTime object or date string.');
			}
			break;
		default:
			parent::__set($nm, $v);
	}
}

public function __get($nm){
	switch ($nm){
		case 'PostedDate':
			return $this->mPostedDate;
		default:
			return parent::__get($nm);
	}
}
}

 

With that, you can access the posted date property as if it were a public property ($obj->PostedDate).  When you assign something to the property it will automatically validate it by attempting to convert any non-object value (for example a string such as '2011-11-25') into a DateTime.  It will then only allow a valid DateTime object to be finally assigned to the property, anything else will cause an exception.

 

Thanks for your replies guys.

 

I think it's a hard thing to wrap your head around at first but your examples have helped me understand it a bit better - especially where you've mentioned setting private variables.  What i wasn't taking into account is the logic you put into the methods which makes them infinitely more useful.

 

I guess this can help make your code a lot more secure if you write your __get/__set correctly.

 

Thanks a lot for taking the time to respond chaps.

 

Drongo

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.