Now that we’ve established what object-oriented programming is in theory, it will be useful to go through how this theoretical idea is actually implemented in PHP.
The skeleton of a class definition
The following is a bare-bones definition of a Dog class in PHP. This code would go into a file called Dog.class.php:
<?php
//file: Dog.class.php
class Dog {
public function __construct() {
}
}
?>
This class, as it currently stands, has no properties, no methods, and is basically useless. But it shows you the bare minimum amount of code necessary to define a class in PHP.
The __construct() method is a special type of method that is automatically called by PHP when an instance of the class is created. This is known as a “constructor function”, and is generally used to do some initial setup of the properties of the object that is being created.
For example, let’s say we have another script, index.php, which creates a Dog object:
<?
//file: index.php
require_once("Dog.class.php"); //include the Dog class definition file
$sparky = new Dog(); //create a new Dog object and store it in a variable
?>
When the script runs the “new Dog()” command, this tells PHP to create an object of type Dog. PHP creates this object, and then calls its __construct() method automatically.
Adding properties
Let’s now try to do some basic setup of the Dog object when it is created. Most likely, we would conceptually want to give the Dog a name at the time it is created.
To do this, we need to give the Dog class a “name” property, and then allow the constructor code to set that property to some value when the object is created.
Here is the updated Dog.class.php file:
<?php
//file: Dog.class.php
class Dog {
private $name;
public function __construct($dogName) {
$this->name = $dogName;
}
}
?>
The code to create a new Dog object would now look something like this:
<?
//file: index.php
require_once("Dog.class.php"); //include the Dog class definition file
$sparky = new Dog("Sparky"); //create a new Dog object and store it in a variable
?>
The first thing to note is the “private $dogName” code in the updated Dog class definition. This declares a “name” property of any Dog objects. Note that we define this property in the class definition file, but it actually comes into use for Dog objects created from that class. This is an important distinction: you define things in the class file that you intend to use for the objects created from that class. Sometimes these properties are called “instance variables” because they apply to all object instances of the specified class.
The keyword “private” indicates that any property or method will only be accessible to code within the class definition itself. Any code outside of this class definition will be ignorant of the existance of the “name” property of any Dog. This is an example of the concepts of abstraction and encapsulation: the inner workings of the Dog class can be hidden from world outside world.
The alternative is “public”, which we see being used in the constructor function, but can be used for any property or method that you want to make accessible to code outside of the class definition.
In index.php, we have supplied an argument to the constructor function by putting the word “Sparky” inside the parentheses of the “new Dog()” command. You’ll recognize that this is not so different from how we pass arguments to regular functions in PHP and Javascript.
PHP automatically calls the __construct() function when we declare “new Dog()”, and the constructor function now puts the word “Sparky” into the variable called $dogName.
The following line of code in the constructor function then modifies the $name property of the Dog object so that it too has the word “Sparky” in it:
$this->name = $dogName;
The $this keyword is a special variable that refers to the current object. It can only be used inside of a method in a class definition file. It refers to the current instance of the class. In this case, when the code is run, $this refers to the Dog object that we are calling “Sparky”, and not to any other Dog object that may exist at that time.
The syntax “$this->name” is how you would refer to the “name” property of the current object. You can use similar syntax for any property or method that is encapsulated inside of an object. For example, $this->doSomething() would call the doSomething() method of the $this object.
So we have effective created a Dog object, and set its name property to be “Sparky”. But what use is that?
Accessor methods
Obviously, you want to make it possible to use that “name” property in some way. Let’s say we want to be able to echo the name of the dog we created somewhere in our index.php script. We need a way to access this “name” property from outside of the class definition file.
But I’ve already said that the “name” property is “private”, and therefore not accessible to any scripts outside of the class definition file. So how do we access it?
The answer is that we have to create what are known as “accessor methods” for our class. By convention, an objects properties are not directly accessible to code outside of the class definition, so we supply public methods to get and set those property values (known sometimes as “getter and setter functions”.
Our updated Dog.class.php file will now look like this:
<?php
//file: Dog.class.php
class Dog {
private $name;
public function __construct($dogName) {
$this->setName($dogName);
}
public function getName() {
return $this->name;
}
public function setName($dogName) {
$this->name = $dogName;
}
}
?>
And our index.php file will echo out the dog’s name like this:
<?
//file: index.php
require_once("Dog.class.php"); //include the Dog class definition file
$sparky = new Dog("Sparky"); //create a new Dog object and store it in a variable
echo $sparky->getName();
?>
We have thus created accessor methods for the “name” property. These are two new public methods in the Dog.class.php file: one to set the “name” property of the Dog object, and another to get the “name” property of the Dog object. It is important that they are both “public”, so they can be called from index.php. And the “name” property itself is “private”, and therefore not directly accessible from the code in index.php.
Note that I have also updated the constructor function so that it too calls the setter method, rather than duplicating the same code found in that method.
The script in index.php now creates a new Dog object, and then outputs the name property of that object by calling its getter method.
Finishing the example
Now let’s finish this example by adding another property and a few new methods. We’ll add an “age” property, as well as the getter and setter methods for that property. We’ll also add a “bark()” method that will just output some text. And finally, we’ll add a static method, which we’ll discuss below.
Here’s the updated Dog.class.php:
<?php
//file: Dog.class.php
class Dog {
//declare private variables that will be accessible from scripts outside of this class
private $name;
private $age;
//function __construct is a "constructor function", which means it is called automatically when a new object is created
public function __construct($name) {
$this->name = $name;
}
//a function that tells the current Dog object to bark...
public function bark() {
echo "<p>{$this->name} says 'woof'</p>";
}
//this is a setter function - it allows you to set the value of a private property of the current object
public function setAge($dogAge) {
$this->age = $dogAge;
}
//this is a getter function - it allows you to get the value of a private property of the current object
public function getAge() {
return $this->age;
}
//example of a static function - these are not dependent on any particular instance of this class
//in fact you CANNOT use the variable $this in a static function, because there is not current object to refer to
public static function scratch() {
echo "<p>Oooo that feels good</p>";
}
} //end class
?>
And the updated code from index.php:
<?
//file: index.php
require_once("Dog.class.php"); //include the Dog class definition file
$sparky = new Dog("Sparky");
$sparky->setAge(5);
$petie = new Dog("Petie");
$petie->setAge(3);
$sparky->bark(); //call the bark function of the Dog, "Sparky"
$petie->bark(); //call the bark function of the Dog, "Petie"
//echo $sparky->getAge();
Dog::scratch(); //call the static "scratch" method of the Dog class
?>
This script now creates a Dog object and gives it the name “Sparky” and sets its age property to be 5. Then it creates a second Dog object, gives it the name “Petie”, and sets its age property to be 3.
The code then calls the bark() method of both Dog objects. Each Dog object will run its own bark() method. Each object will run this command:
echo "<p>{$this->name} says 'woof'</p>";
And since each object has a different value in the “name” instance variable, they will output different things. Sparky will echo “Sparky says woof”, while Petie will echo “Petie says woof”.
Static vs. instance methods
Finally, we call the static method scratch(). The syntax to call a static method is this:
Dog::scratch(); //call the static "scratch" method of the Dog class
The double colon, “::” indicates that the scratch() method is not a method of any particular Dog object (i.e., not an instance method), but is rather a method of the class itself. In otherwords, an object does not need to have been created in order to call this method.
We can call this method on a particular object, like we do with any other method of an object:
$sparky->scratch()
But the only reason to make a method static is so that we don’t have to have a particular object in order to call it. So we usually call static methods directly from the class itself instead:
Dog::scratch();
As we saw, Sparky’s bark() method does something slightly different from Petie’s bark() method. Static methods are called static because they are never dynamic like this, and they never refer to the properties of a particular object instance.
Whereas the processing of the bark() method depends on the $this->name property of the current object, static methods cannot ever use the $this keyword because they do not refer to any particular object at all. They are usually used for general utility methods that never need to change depending on context. Hence the keyword static.
A slight variation on this example code is available here.