Technology

PHP Magic Methods demystified

Posted by Pixafy Team

PHP-magic-methods-demystified-blog-graphic

This series of articles will explain how the different PHP Magic Methods work and when they are called. There are more then you may have realized; other than the more common __construct, __get, and __set functions, there are also __sleep, __wakeup, __invoke , __set_state, and more.

Let’s start with the more common methods.

Most developers already know how contructors and destructors work, so I will be brief in these explanations.

__construct()

This is the PHP constructor Magic Method. This gets called when an instance of a class is created.

__construct Example:
class Example
{
function __construct()
{
echo "A new object is born.";
}
}

$a = new Example();

 

OUTPUT:
A new object is born.

__destruct()

This is the PHP destructor Magic Method.

In PHP, the destructor is called when a instance of a class no longer has any references.

class Example
{
function __destruct()
{
echo "An object dies.";
}
}

$a = new Example();
unset($a);

 

OUTPUT:
An object dies.

Overloading

The above is pretty standard, but I included them for completeness. The next group of Magic Methods are related to the concept of PHP overloading. As a C++ developer, when I began developing in PHP, I missed the traditional way of overloading by having a function with the same name but a different parameter list. In PHP, you cannot have a function with the same name, but there is still a way to get the same functionality. Please stay tuned as I show you examples of this in my next article, coming up!

For now, let’s delve into property overloading.

Property overloading allows us to dynamically store data in our objects without having to know the names or amounts. It also allows us to implement custom logic for the isset, empty, and unset language constructs.

There are four Magic Methods that deal with property overloading:

__get()

This function is called when an undefined property is access from an instance of the class.

__set()

This function is called when an attempt in made to assign a value to an undefined property by an instance of the class.

__isset()

This function is called when either the language constructs “isset()” or “empty()” are called on an instance of the class.

__unset()

This function is called when the language construct “unset()” is called on the class.

The __get and  __set Magic Methods allow us to add customization to the assignment operations. In the class below, we store the object’s properties into an array based on the passed value’s type. Before we get into the code, here is a brief description of Magic Methods we will be using.

I am going to illustrate all of this in a single class below and then will explain each section.

class OverProp
{

/*
Initialize the data member variables
These private properties will be used by
the getter and setter methods to store the
data for objects of this class.
*/
private $_arrays = array();
private $_numbers = array();
private $_strings = array();
private $_objects = array();

/*
This property is defined so the
magic __get and __set methods will not
be applied to it. It will NOT be stored in
any of the arrays above.
*/
public $definedProperty;

function __set($name, $value)
{
if (is_array($value))
{
$this->_arrays[$name] = $value;
}
elseif (is_number($value))
{
$this->_numbers[$name] = $value;
}
elseif (is_string($value))
{
$this->_strings[$name] = $value;
}
elseif (is_object)
{
$this->_objects[$name] = $value;
}
}

function __get($name)
{
foreach (array(
'_arrays',
'_numbers',
'_strings',
'_objects'
) as $type)
{
if (isset($this->$type))
{
return $this->$type[$name];
}
}
}

function __isset($name)
{
echo "Checking if '$name' is in the data arraysn";
foreach (array(
'_arrays',
'_numbers',
'_strings',
'_objects'
) as $type)
{
if (isset($this->{$type}[$name]))
{
echo "'$name' was found in '$type' n";
return true;
}
}

return false;
}

function __unset($name)
{
foreach (array(
'_arrays',
'_numbers',
'_strings',
'_objects'
) as $type)
{
if (isset($this->{$type}[$name]))
{
echo "Unsetting '$name' from '$type' n";
unset($this->{$type}[$name]);
}
}
}
}

*Note: There is a fundamental flaw in the logic of the above class. See if you can find it and leave a comment for us. It should not be too hard to find.

Let’s instantiate the class and fill it with random data.

$a = new OverProp;
$a->variable = 55;
$a->cookies = array('chocolate chip', 'oatmeal', 'sugar');
$a->age = 33;
$a->processorType = 'Intel';
$a->foo = 'bar';
$a->baz = new stdClass();
$a->cost = '4.95';
$a->groceries = array('eggs', 'milk', 'bread');

$a->definedProperty = 'I am a defined property in an object of ' . get_class($a);

print_r($a);

The output of the class instance ‘$a‘ will look something like this:

OverProp Object
(
[_arrays:OverProp:private] => Array
(
[cookies] => Array
(
[0] => chocolate chip
[1] => oatmeal
[2] => sugar
)

[groceries] => Array
(
[0] => eggs
[1] => milk
[2] => bread
)

)

[_numbers:OverProp:private] => Array
(
[variable] => 55
[age] => 33
[cost] => 4.95
)

[_strings:OverProp:private] => Array
(
[processorType] => Intel
[foo] => bar
)

[_objects:OverProp:private] => Array
(
[baz] => stdClass Object
(
)

)

[definedProperty] => I am a defined property in an object of OverProp
)

Now let’s experiment with the isset and unset functions:

if (isset($a->variable)) {
unset($a->variable);
}

unset($a->foo);

if (isset($a->definedProperty)) {
unset($a->definedProperty);
}

print_r($a);

 

OUTPUT:

Checking if 'variable' is in the data arrays
'variable' was found in '_numbers'
Unsetting 'variable' from '_numbers'
Unsetting 'foo' from '_strings'
OverProp Object
(
[_arrays:OverProp:private] => Array
(
[cookies] => Array
(
[0] => chocolate chip
[1] => oatmeal
[2] => sugar
)

[groceries] => Array
(
[0] => eggs
[1] => milk
[2] => bread
)

)

[_numbers:OverProp:private] => Array
(
[age] => 33
[cost] => 4.95
)

[_strings:OverProp:private] => Array
(
[processorType] => Intel
)

[_objects:OverProp:private] => Array
(
[baz] => stdClass Object
(
)

)

)

As you can see, the class’s __isset and __unset  methods are called for the undefined properties ‘variable‘ and ‘foo.’ However, the default isset and unset methods are used for the defined property named ‘definedProperty‘.

This concludes this article. Please standby for the next article in which I will explain PHP method overloading using the __call() and __callStatic() Magic Methods.

By the way, were you able to find the flaw above? If not, highlight the block below to see the answer:

-> The problem lies in the logic for __get function in the ‘OverProp’ class above. Properties of different values cannot have the same name. For example, if I set a number property named ‘myProperty’ to 35, then set another property with the same name of ‘myProperty’ and set that to array(1,2,3), it will store both properties in the respective arrays but a get call to ‘myProperty’ will only return the array value. <-

Share your questions and comments below, or tweet us @Pixafy!

Tags