Object-Oriented PHP: Delving Deeper into Properties and Methods

Learn how to work with PHP properties and methods in depth. Looks at constructors and destructors; static properties and methods; class constants; type hinting; and overloading.

Object-Oriented PHP: Delving Deeper into Properties and Methods

Welcome to the second article in my series on object-oriented PHP programming. In the first article, we looked at the basics of OOP in PHP — including the concepts of classes, objects, properties and methods — and we saw how to create basic classes and objects in PHP.

In this article, you're going to delve a bit deeper into properties and methods in PHP. This will give you a solid grounding when it comes to using objects in PHP, before we move on to more advanced topics such as inheritance in later articles.

These are the concepts you'll explore in this article:

  • Constructors and destructors, which let your objects do specific things whenever they're created or deleted
  • Static properties and methods, for creating class properties and methods that are not tied to specific objects
  • Class constants, which are handy for storing fixed values related to a class
  • Type hinting, for restricting the types of objects that can be passed to a method
  • The __get() and __set() magic methods for overloading property accesses
  • The __call() magic method for overloading method calls

All set to delve deeper into properties and methods? Great! Let's go!

Constructors and destructors

When you create a new object, there are sometimes things that are good to do at the same time. For example, you might want to set some or all of the object's properties to initial values, or you might want to load the object's data automatically from a database table.

Likewise, when an object is removed from memory, you might want to do things like removing dependent objects, closing files, or closing database connections.

How do you remove an object?

PHP automatically removes an object from memory when there are no more variables left that reference the object. For example, if you create a new object and store it in a variable called $myObject, and then remove the variable by calling unset($myObject), the object is also removed from memory. Similarly, if you create $myObject as a local variable in a function, the variable — and therefore the object — are removed when the function exits.

PHP gives you 2 special methods that you can use to automatically do stuff when an object is created or removed:

  • An object's constructor is called automatically just after the object is created.
  • An object's destructor is called automatically just before the object is removed.

Working with constructors

Cranes

Use a constructor whenever you want to initialize stuff when an object is created. This might include setting properties, opening files, and reading data.

To create a constructor method, simply add a method with the name __construct() to your class (that's 2 underscores before the word "construct"). PHP then automatically calls this method when an object based on the class is created.

Here's an example of a constructor:

class MyClass {
  public function __construct() {
    echo "I've just been created!";
  }
}

$myObject = new MyClass();  // Displays "I've just been created!"

MyClass contains a simple constructor that displays a message using echo. The last line of code in the script creates a new object based on MyClass. When this happens, PHP automatically calls the constructor, displaying the message.

Here's a somewhat more practical use of a constructor — initializing properties:

class Member {

  private $username;
  private $location;
  private $homepage;

  public function __construct( $username, $location, $homepage ) {
    $this->username = $username;
    $this->location = $location;
    $this->homepage = $homepage;
  }
  
  public function showProfile() {
    echo "<dl>";
    echo "<dt>Username:</dt><dd>$this->username</dd>";
    echo "<dt>Location:</dt><dd>$this->location</dd>";
    echo "<dt>Homepage:</dt><dd>$this->homepage</dd>";
    echo "</dl>";
  }
}

$aMember = new Member( "fred", "Chicago", "http://example.com/" );
$aMember->showProfile();

When you run the above script, it displays the following:


Username:
    fred
Location:
    Chicago
Homepage:
    http://example.com/

Our Member class contains 3 private properties, and a constructor that accepts 3 arguments — one for each property. The constructor sets the object's properties to the argument values. The class also contains a showProfile() method that displays the property values using echo.

The script then creates a new Member object, passing 3 values — "fred", "Chicago", and "http://example.com/" — to the constructor as it does so. The constructor stores the values in the new object's properties. Finally, the script calls the object's showProfile() method to display the stored values.

Working with destructors

Drills

Use a destructor to clean things up when an object is deleted. You might need to save the object to a database, or close open files related to the object.

To create a destructor, add a method called __destruct() to your class. The destructor is called automatically just before the object is deleted. Here's a simple example:

class MyClass {
  public function __destruct() {
    echo "I'm about to disappear - bye bye!";
    // (Clean things up here)
  }
}

$myObject = new MyClass();  
unset( $myObject );       // Displays "I'm about to disappear - bye bye!"

Here, we've created a simple destructor that displays a message. We then create a new object from our class, then immediately delete the object by unsetting the variable that points to the object. Just before the object is deleted, PHP calls the destructor, which displays the message in the page.

Unlike constructors, you can't pass arguments to a destructor.

Destructors are also called just before the script exits, since all objects (and other variables) are deleted when the script stops running. So this code also triggers the destructor:

class MyClass {
  public function __destruct() {
    echo "I'm about to disappear - bye bye!";
    // (Clean things up here)
  }
}

$myObject = new MyClass();  
exit;                     // Displays "I'm about to disappear - bye bye!"

Similarly, if the script stops running due to an error then any destructors will also be called.

When objects of a child class are created, its parent class's constructor isn't automatically called. Only the child's constructor is called. However, you can explicitly call the parent class's constructor from within the child's constructor by writing parent::__construct(). The same is true of destructors — you can call the destructor explicitly using parent:__destruct(). We'll look at parent and child classes in the next tutorial on inheritance.

Static properties

We looked at static function variables in PHP Variable Scope: All You Need to Know. Like a regular local variable, a static variable can only be accessed from within a function. However, unlike a local variable, a static variable retains its value between function calls.

Static properties work in a similar way. A static property is tied to its class, but it retains its value for the lifetime of the script. Compare this to a regular property, which is tied to a particular object, and which disappears when the object is deleted.

Static properties are useful in situations where you want to store a persistent value that is related to a class, but not to specific objects. They're a bit like global variables for classes.

To create a static property, just add the keyword static before the property name:

class MyClass {
  public static $myProperty;
}

Here's an example that shows how static properties work:

class Member {

  private $username;
  public static $numMembers = 0;

  public function __construct( $username ) {
    $this->username = $username;
    self::$numMembers++;
  }
}

echo Member::$numMembers . "<br>";  // Displays "0"
$aMember = new Member( "fred" );
echo Member::$numMembers . "<br>";  // Displays "1"
$anotherMember = new Member( "mary" );
echo Member::$numMembers . "<br>";  // Displays "2"

There are a few interesting things going on here, so let's work through the script:

  1. Our Member class contains 2 properties: a private $username property, and a static $numMembers property, which is initially set to zero.
  2. The constructor takes a $username argument and sets the newly-created object's $username property to this value. At the same time, it increments the $numMembers static property to record the fact that we now have one more member.

    Notice that the constructor refers to the static $numMembers property using self::$numMembers. self is similar to the $this keyword that we saw in the last tutorial. Whereas $this refers to the current object, self refers to the current class. Similarly, whereas you use -> to access properties and methods of a specific object, you use :: to refer to properties and methods of a class.
  3. Finally, the script creates a couple of new Member objects, displaying the value of the $numMembers static property as it goes. Notice that the property retains its value throughout the lifetime of the script, independent of the objects derived from the class.

    Again, the script uses the :: operator to access the static property. We can't use self here because the code is outside the class, so instead we write the class name, followed by ::, followed by the property name (Member::$numMembers). (We could also have used Member instead of self inside the constructor, if we'd wanted to.)

It's worth noting that our script accessed the $numMembers static property before it created its first Member object. You don't need to create objects to access static properties of a class.

Static methods

As well as static properties, you can also create static methods. As with static properties, a static method is tied to a class, but you don't need to create an object from the class in order to call the static method. This makes static methods useful when you want to create a method that doesn't need to work with actual objects.

To create a static method, just use — you guessed it — the static keyword:

class MyClass {
  public static function myMethod() {
    // (do stuff)
  }
}

Our static property example earlier used a public static $numMembers property. It's good practice to make properties private, and write public methods to access them instead. So let's modify the example to make the $numMembers property private, and add a public static method to retrieve the property's value:

class Member {

  private $username;
  private static $numMembers = 0;

  public function __construct( $username ) {
    $this->username = $username;
    self::$numMembers++;
  }

  public static function getNumMembers() {
    return self::$numMembers;
  }
}

echo Member::getNumMembers() . "<br>";  // Displays "0"
$aMember = new Member( "fred" );
echo Member::getNumMembers() . "<br>";  // Displays "1"
$anotherMember = new Member( "mary" );
echo Member::getNumMembers() . "<br>";  // Displays "2"

Here, we've created a static method called getNumMembers() that returns the value of the $numMembers static property. We've also made $numMembers private so that the calling code can't access the property directly.

We then changed our calling code to retrieve the value of $numMembers by calling the getNumMembers() static method. Notice that the code can call the method without needing to create an object first, because the method is static.

Class constants

Hammer and nail

We looked at regular constants in an earlier tutorial. A constant lets you define a global fixed value within your code; the constant's value remains fixed throughout the running of the script, and can't be changed.

Class constants are similar to regular constants. The main difference is that, rather than being global, a class constant is accessed through the class that defined it. Class constants are useful for storing fixed values or settings that are related to a specific class.

You define a class constant by using the const keyword, like this:


class MyClass {
  const CONSTANT_NAME = value;
}

You can then access the constant using the class name and the :: operator, much like static properties:


MyClass::CONSTANT_NAME

As with static properties and methods, you can also access a class constant from within a method of the same class method by using the self keyword.

Let's try out class constants with an example. We'll add some constants to our Member class to define some numeric values that represent a member's privilege level (member, moderator, or administrator). By using constants instead of the numeric values in the code, we make the code easier to read. Here's the script:

class Member {

  const MEMBER = 1;
  const MODERATOR = 2;
  const ADMINISTRATOR = 3;

  private $username;
  private $level;

  public function __construct( $username, $level ) {
    $this->username = $username;
    $this->level = $level;
  }

  public function getUsername() {
    return $this->username;
  }

  public function getLevel() {
    if ( $this->level == self::MEMBER ) return "a member";
    if ( $this->level == self::MODERATOR ) return "a moderator";
    if ( $this->level == self::ADMINISTRATOR ) return "an administrator";
    return "unknown";
  }
}

$aMember = new Member( "fred", Member::MEMBER );
$anotherMember = new Member( "mary", Member::ADMINISTRATOR );
echo $aMember->getUsername() . " is " . $aMember->getLevel() . "<br>";  // Displays "fred is a member"
echo $anotherMember->getUsername() . " is " . $anotherMember->getLevel() . "<br>";  // Displays "mary is an administrator"

Here we've created 3 class constants — MEMBER, MODERATOR, and ADMINISTRATOR — and given them values of 1, 2, and 3 respectively. Next we've added a $level property to store each member's privilege level, and modified the constructor to initialize the $level property. Our class also includes a getLevel() method that returns an appropriate message depending on the member's privilege level. It compares $level against the 3 class constants to determine which string to return.

The script itself creates a couple of Member objects using different privilege levels. Again, it uses the class constants to represent the levels, rather than using the raw numeric values. The script then calls each member's getUsername() and getLevel() methods, and displays the results in the page.

Checking method arguments with hints

Whisper

PHP is a loosely-typed language, which means it's not too fussy about the types of data that you pass around a script. For example, you can happily pass a numeric value to PHP's strlen() function for calculating the length of a string. PHP first converts the number into the string, then returns its length:

echo strlen( 123 ); // Displays "3"

Often this implicit data type conversion is quite convenient, but it can lead to bugs that are hard to track down, especially when you work with more complex data types such as objects.

An example scenario

Consider this example:

class Member {

  private $username;

  public function __construct( $username ) {
    $this->username = $username;
  }

  public function getUsername() {
    return $this->username;
  }
}

class Topic {

  private $member;
  private $subject;

  public function __construct( $member, $subject ) {
    $this->member = $member;
    $this->subject = $subject;
  }

  public function getUsername() {
    return $this->member->getUsername();
  }
}

$aMember = new Member( "fred" );
$aTopic = new Topic( $aMember, "Hello everybody!" );
echo $aTopic->getUsername();    // Displays "fred"

This script works as follows:

  • We create our usual Member class, with a $username property, a constructor, and a getUsername() method.
  • We also create a Topic class to hold forum topics. It has 2 properties: $member and $subject. $member will hold a Member object representing the member that created the topic. $subject will hold the topic subject.
  • The Topic class also has a constructor that accepts a Member object — the member that created the topic — and a subject string. It stores the Member object and subject string in the Topic object's properties. The class also has a getUsername() method to return the username of the member that created the topic. It does this by calling its Member object's getUsername() method.
  • You can see this working towards the end of the script. We create a new Member object with a username of "fred". Then we create a new Topic object, passing in the "fred" Member object and "Hello everybody!" as the subject. Finally, we call the topic's getUsername() method to retrieve the topic creator's username, and display the return value ("fred").

So far so good.

Let's break things!

Now we'll add the following code to the end of the script:

class Widget {

  private $colour;

  public function __construct( $colour ) {
    $this->colour = $colour;
  }

  public function getColour() {
    return $this->colour;
  }
}


$aWidget = new Widget( "blue" );
$anotherTopic = new Topic( $aWidget, "Oops!" );

// Displays "Fatal error: Call to undefined method Widget::getUsername()"
echo $anotherTopic->getUsername();  

Here, we add an unrelated Widget class, with a $colour property, a constructor, and a getColour() method to retrieve the widget's colour.

Then, at the end of our script, we create a new Widget object, followed by a new Topic object, passing in the Widget object as the topic's "author", instead of a Member object as expected.

Finally, we attempt to display the username of the topic's creator by calling the Topic object's getUsername() method. This method in turn attempts to call the getUsername() method of the Widget object. Since the Widget object doesn't contain a getUsername() method, we get this error:


Fatal error: Call to undefined method Widget::getUsername()

The problem here is that the cause of the error is not immediately obvious. Why is the Topic object trying to call Widget::getUsername()? In complex classes this sort of problem can be hard to track down.

Type hinting to the rescue

It would be better if we could ensure that the Topic class's constructor only accepts Member objects in the first place, thereby avoiding potential problems further down the line.

This is exactly what type hinting does. To use it, you put the name of the class that a method should accept before the parameter name in the method definition:


function myMethod( ClassName $object ) {
  // (do stuff)
}

Let's modify our Topic constructor so that it will only accept Member objects:

class Topic {

  private $member;
  private $subject;

  public function __construct( Member $member, $subject ) {
    $this->member = $member;
    $this->subject = $subject;
  }

  public function getUsername() {
    return $this->member->getUsername();
  }
}

Now let's try again to create a Topic object using our Widget object as the topic "author":

$aWidget = new Widget( "blue" );
$anotherTopic = new Topic( $aWidget, "Oops!" );

This time, PHP immediately raises the following error:


Catchable fatal error: Argument 1 passed to Topic::__construct()
must be an instance of Member, instance of Widget given,
called in script.php on line 55 and defined in script.php on line 24

This error is much more useful, because it shows you exactly what the problem is — we tried to pass a Widget object to a method that expected a Topic object. It even tells you the exact place in the script where the method was called, and also the place where the method is defined. This makes debugging the script so much easier.

Overloading properties with __get() and __set()

Levers

As you know, classes usually contain properties, like this:

class MyClass {

  public $aProperty;
  public $anotherProperty;
}

If your class properties are public then your calling code can read and set them using the -> operator:

$myObject = new MyClass;
$myObject->aProperty = "hello";

However, PHP also lets you create "virtual" properties that don't actually exist in the class, but can still be accessed using the -> operator. This can be useful in certain situations, such as:

  • You have a lot of properties and you want to store them in an array, instead of having to declare each property individually
  • You want to store a property somewhere outside of an object, such as in another object, or even in a file or database table
  • You want to calculate property values on-the-fly, rather than storing them somewhere

To create these "virtual" properties, you add a couple of PHP magic methods to your class:

__get( $propName )
Called automatically when something attempts to read $propName, an invisible property of the object
__set( $propName, $propValue )
Called automatically when something attempts to set $propName, an invisible property of the object, to a value, $propValue

"Invisible" in this context means that the property isn't accessible to the code that tries to use it. This can mean that the property doesn't exist in the class at all, or that the property exists but the code isn't allowed to access it — for example, the property is private, and code outside the class is trying to read it.

Let's try this out. We'll modify our Member class so that, in addition to the predefined $username property, it can accept additional arbitrary properties, which it stores in a $data array:

class Member {

  private $username;
  private $data = array();

  public function __get( $property ) {
    if ( $property == "username" ) {
      return $this->username;
    } else {
      if ( array_key_exists( $property, $this->data ) ) {
        return $this->data[$property];
      } else {
        return null;
      }
    }
  }

  public function __set( $property, $value ) {
    if ( $property == "username" ) {
      $this->username = $value;
    } else {
      $this->data[$property] = $value;
    }
  }
}

$aMember = new Member();
$aMember->username = "fred";
$aMember->location = "San Francisco";
echo $aMember->username . "<br>";  // Displays "fred"
echo $aMember->location . "<br>";  // Displays "San Francisco"

Here's how this code works:

  • Our Member class contains a regular private $username property, and it also has a private $data array to hold additional "virtual" properties.
  • The __get() method takes a single argument, $property, which is the name of the property to retrieve. If $property is "username" then the method returns the value of the $username private property. Otherwise, it checks to see if the property name appears as a key in the $data array. If it does then it returns the element's value; otherwise, it returns null.
  • The __set() method takes 2 arguments: $property, the name of the property to set, and $value, the value to set the property to. If $property is "username", the method sets the $username property to the value stored in $value. Otherwise, it sets the element in the $data array that has a key of $property to the value stored in $value.
  • Once we've created our Member class, we create a new Member object and set its $username property to "fred". This triggers the __set() method, which sets the actual $username property in the object. We then set the object's $location property to "San Francisco". Since this property doesn't exist in the object, __set() stores it in the $data array instead.
  • Finally, we retrieve the values of the $username and $location properties and display them. __get() retrieves the $username value from the actual $username property, and the $location value from the "location" element in the $data array.

As you can see, by using __get() and __set() we've created a class that can hold both a regular $username property, and also any arbitrary property that the calling code wants to set. The calling code doesn't need to know whether each property really exists in the object or not. It just sets and reads the property using the -> operator.

The example also shows how you can use __get() and __set() to easily create so-called "getter" and "setter" methods to access private properties. We didn't need to write separate getUsername() and setUsername() methods to access the private $username property. Instead, we used __get() and __set() to manipulate $username. This approach means you only have to write 2 methods in total, instead of 2 methods for each private property.

A note on encapsulation

Using private properties, combined with getter and setter methods, is generally a better approach than using public properties.

Getter and setter methods can do things to the data going in and out of the object, such as checking that the data is in the right format, or converting the data to a different format.

Getters and setters also hide the details of how each property is implemented, which makes it easier to rewrite the innards of a class at a later date without affecting the code that uses the class. For example, you might later decide to store a particular property directly in a database table, instead of in an object's property. Provided the property is accessed only through getter and setter methods, all you have to do is rewrite those methods. The calling code doesn't need to be changed.

This technique is called encapsulation, and it's one of the main advantages of object-oriented programming.

Overloading methods with __call()

As well as providing __get() and __set() to intercept property accesses, PHP gives you another magic method, __call(), that you can use to intercept method calls. Whenever some code tries to call a method in your class that either doesn't exist, or that it's not allowed to call, PHP automatically calls your __call() method instead.

Here's the general syntax of the __call() method:


public function __call( $methodName, $arguments ) {
  // (do stuff)
}

When an attempt is made to call an invisible method in your class, PHP calls your __call() method, passing in the name of the method that was called as a string, and a list of any arguments supplied as an array. Your __call() method should then handle the call appropriately, optionally returning a value to the calling code.

__call() can be useful for situations where you want to hand off some parts of your class's functionality to another class. Here's a simple example:

class Member {

  private $username;

  public function __construct( $username ) {
    $this->username = $username;
  }

  public function getUsername() {
    return $this->username;
  }
}

class Topic {

  private $member;
  private $subject;

  public function __construct( $member, $subject ) {
    $this->member = $member;
    $this->subject = $subject;
  }

  public function getSubject() {
    return $this->subject;
  }

  public function __call( $method, $arguments ) {
    return $this->member->$method( $arguments );
  }
}

$aMember = new Member( "fred" );
$aTopic = new Topic( $aMember, "Hello everybody!" );
echo $aTopic->getSubject() . "<br>";     // Displays "Hello everybody!"
echo $aTopic->getUsername() . "<br>";    // Displays "fred"

This is based on the type hinting example from earlier in the article. We have a Member class with a $username property, and a Topic class that can hold a Member object (the topic author) and a subject. The Member class contains a getSubject() method to retrieve the topic subject, but it doesn't explicitly have a method to retrieve the author's username. Instead, it contains a __call() method that traps any call to a nonexistent method, and passes the call off to the Member object to handle.

When the script then tries to call $aTopic->getUsername(), PHP realizes that this method doesn't exist in the Topic class. So it calls __call() instead, which in turn calls the Member object's method of the same name (getUsername()). That method returns the member's username to __call(), which in turn returns the username to the calling code.

PHP features some other methods related to overloading that you might find useful, including __isset(), __unset(), and __callStatic(). Find out more.

Summary

In this tutorial you've taken your object-oriented PHP skills further as you've explored properties and methods in depth. You've looked at:

  • Constructors and destructors, which are useful for initializing and cleaning up objects
  • Static properties and methods, which you can use to create properties and methods that work at the class level, rather than the object level
  • Class constants, that are handy for storing class-related configuration settings and fixed values
  • Type hinting, which limits the type of objects that can be passed to a method — this can make your code easier to debug, and
  • The __get(), __set(), and __call() magic methods for intercepting property accesses and method calls. These let you create "virtual" properties and methods that don't exist in a class, but can still be accessed by code that uses the class.

Armed with the knowledge in this tutorial, and the previous one, you're now ready to start writing fairly complex object-oriented code in PHP. But it doesn't end there! In the next tutorial in the series, we dive into one of the truly powerful aspects of OOP: the ability of one class to inherit behaviours from another class.

Happy coding!

Learn PHP With Ease!

Written by Matt Doyle — ELATED's resident Web programming expert — Beginning PHP 5.3 is a complete introduction to PHP, covering everything in these tutorials and lots more besides. Find out how to:

  • Set up PHP on your computer
  • Use strings, arrays, functions and objects
  • Create interactive Web forms
  • Handle cookies and sessions
  • Work with files on the server
  • Build database-driven sites with MySQL
  • Send emails from your scripts
  • Create images on the fly with PHP
  • Work with regular expressions
  • Write robust, secure PHP applications

...and lots more!

“What a pleasure it's been spending hours and hours studying PHP with this magical book.” — Lulio, Florida
“The book is not only great for learning, but I find myself using it constantly as a reference as well!” — David A. Stoltz

Buy Beginning PHP 5.3 now from Amazon.comBeginning PHP 5.3 or Amazon.co.ukBeginning PHP 5.3.

Follow Elated

Related articles

Responses to this article

7 responses (oldest first):

18-Jun-11 17:05
Thank you for this tutorial.

I've avoided teaching myself PHP objects/classes for several years now, but find myself with a very appropriate use for them at this point. Your tutorial is laid out nicely, starting with basics and progressing appropriately, providing good explanations of each additional aspect!

Paul
23-Jun-11 03:31
@TunerGeek: Thanks very much for your feedback! It's great to know that people are finding these tutorials helpful.

Cheers,
Matt
15-Feb-12 12:57
Matt, i've very appreciative that you put time and effort in making very simplistic tutorials on the web. There are about 2 tutorials I've read and both arent at as smooth as yours.

I'm a new (trying to be) php dev that really needs great resources like these to fast track me to a dev position.
Really glad I stumbled upon your tutorials. Keep up the fantastic work.

THANKS!
17-Feb-12 03:13
@xarejay28x: Thanks for the kind words Good luck with your PHP coding!
19-May-13 08:14
Actually, I was avoiding to read what are magic methods are but now I've got a full understanding of magic method because of your article.

Thanks.
03-Jun-14 13:27
Hello . I think you have a fail in the last lines where you write:
The Member class contains a getSubject() method to retrieve the topic subject, but it doesn't explicitly have a method to retrieve the author's username. Instead, it contains a __call() method that traps any call to a nonexistent method, and passes the call off to the Member object to handle.

should be

The Topic class

Thanks a lot for your supernices tutorials
04-Jun-14 15:30
A "fail"???

What you mean is an error


Important note for the Americans:

One cannot "have" a fail!

you CAN have a failure or you can fail at something

Post a response

Want to add a comment, or ask a question about this article? Post a response.

To post responses you need to be a member. Not a member yet? Signing up is free, easy and only takes a minute. Sign up now.

Top of Page