×
Author:
Website:
Page title:
URL:
Published:
Last revised:
Accessed:

An Introduction to Classes and Objects

Overview

Classes and objects are central to the concept of Object-Oriented Programming (OOP). One of the central tenets of OOP is the encapsulation of related data and functionality within objects. This contrasts with procedural programming in which the emphasis is on creating procedures (functions and subroutines) that call and are called by other procedures in order to implement the program’s logic. In procedural programming, data and functional code are completely separate.

A class is basically a template - we can maybe think of it as a blueprint - for an object. But what is an object? Essentially, an object is a representation in code of some real-world entity. That entity could be anything - a bank account, a person, a vehicle, a sporting event - there is nothing you can think of that could not be described by an object.

As we have already stated, a class is a template for an object, but what does that mean exactly? Let’s say we want to create an object to represent a person. There are certain attributes - we’ll call them properties - that all people have in common, such as a name, a date of birth, a gender, a blood type, and an ethnicity. There are other properties that they may or may not have, such as an email address, or a telephone number.

If we have an application that needs to store the personal details of a large number of people in a database, we don’t want to have to redefine all of those properties each time we add a new person to the database. That’s where classes come in. We can define a class - let’s call it Person - that sets out all of the properties that describe a person.

Each property defined in the Person class is a placeholder for a particular characteristic. Each time we create an object of type Person, we will add the details we have about the person to the object, such as their name, date of birth, and so on, by filling those placeholders with actual data.

We can take this idea further by adding behaviours - we’ll call these behaviours methods - to the Person class. Methods are very much like functions except that, rather than being out there in the global namespace as separate entities, they are defined as an integral part of the class definition, and will be used to carry out tasks that are specific to the Person class.

We need a method that creates a new object of type Person, and we will need other methods that allow us to add, retrieve and update the data that the object holds about the person. We might also need a method to remove the object if and when we no longer need it. These methods can be defined when we create the Person class and, like the properties defined for that class, they will be inherited by every object created from it.

An object is thus an instance of a class, and the process of creating an object is called instantiation. We can create as many objects from a particular class as we need to, using that class as our blueprint. No two instances of a particular class will hold the same data, but they will all implement the same inherited methods.

This means that, even though no two instances of a class store the same data, our script can interact with all of these objects in the same way because they all exhibit the same behaviours. We don’t need to worry about the code used to implement those behaviours, we just need to know what methods are available to us and how to use them.

The implementation of class methods is hidden from external code. If we need to change the implementation - maybe to make the code more efficient or correct any perceived shortcomings in the way it functions, we can do so in just one place - the class definition - without having to worry about making changes to every object that is an instance of that class. The changes will be propagated to all of those objects automatically.

There are other benefits to an object-oriented approach to programming. We can, for example, restrict access to the individual properties and methods in an object by specifying the level of access available for each property or method when we define the class. Many of the internal details of the class can thus remain completely hidden from external code, and only those we want to share will be exposed.

We obviously need to allow external code to access an object’s data and functionality at some level, but only on terms that we dictate when we define the class it is derived from. This prevents external code putting an object into an invalid state or modifying its behaviour, safeguarding data integrity and ensuring that the object will behave in the way we expect.

One very important aspect of a class-based approach to program design is that we can use an existing class (the base class) to derive a new, more specialised class (the derived class) that has all of the properties and methods of the base class plus whatever new properties and methods we need for a particular task. This concept is referred to as inheritance, because as well as implementing new methods or adding new properties, the derived class can do anything the base class can do.

Inheritance allows us to create a class hierarchy in which the base class defines a relatively generic set of properties and methods. Descendents of that class inherit those properties and methods, and add their own properties and methods designed to handle more specialised use cases. They may even redefine some of the inherited properties and methods - a process known as polymorphism.

In this article we will describe how to create and use classes and objects with the aid of numerous examples. We will explore concepts such as inheritance in some detail, and briefly look at some of the more advanced aspects of object-oriented programming as implemented by PHP. We’ll start at the beginning by describing how to define a simple class.

Defining a class

Let’s suppose we want a simple class that will be used to create objects that represent people (we described how we might approach this earlier). We’ll call that class Person, and we’ll make it fairly generic. Before we do anything, let’s ask ourselves what properties every person on the planet shares (remembering, of course, that the value assigned to that property can vary widely).

Everybody has a name, so we’ll definitely include that as one of the properties in our class definition. Every body has a date of birth, so we’ll include that too. In fact, there are numerous attributes that apply to everybody - the colour of their eyes, hair colour, ethnicity, and gender, for example. There are also attributes that relate to aspects of their life, such as an address, a telephone number, or an email address.

We need to decide which of those attributes to include as properties in our class definition. It doesn’t mean that a particular person must have all of the attributes we have described. Not everybody has a phone or an email account, for example. Some people are unfortunate enough to be homeless, or maybe they are travellers, in which case they will not even have a permanent address.

Nevertheless, we probably want to include those properties because, in this day and age, they apply to the great majority of people. And it doesn’t have to be the case that every person whose records we are storing must have all of the attributes defined by our Person class. Anyway, let’s make start and create the class. We can always make changes later if required.

A class is defined using the keyword class, followed by the name of the class and a pair of curly braces. The convention is for class names to begin with an uppercase character. For example:

<?php
class Person {
// properties and methods
// are defined here
}
?>

As you can see, the class properties and methods will be defined inside the curly braces. Let’s add some properties:

<?php
class Person {
public $firstName;
public $lastName;
public $dateOfBirth;
}
?>

Note that the property names are prefixed with the dollar sign ($) just like any other PHP variables. That’s because they are essentially variables. We call them properties in order to identify them as class variables, and to distinguish them from the variables we use elsewhere in our code.

So far so good, we now have a Person class that has some properties. We have declared each property using the public keyword, which means they can be accessed and modified by external code (we will probably want to change that later). In the meantime, we can’t actually do anything because we haven’t yet used the class to create an object of type Person. We’ll do that next.

Creating an object

Having created a class, we will want to make use of it by creating an instance of that class. In fact, we can create as many instances of it as we like. Each instance will be an independent object that inherits all of the properties and methods contained within the class definition.

An object variable is created in similar fashion to other PHP variables except that, instead of simply assigning a value to the variable, we are allocating memory for the object and assigning the address of that memory to the object variable. The name of the object variable is followed by the new keyword, which is followed by the name of the class, followed by parentheses. For example:

<?php
class Person{
public $firstName;
public $lastName;
public $dateOfBirth;
}

$cwells = new Person();
var_dump($cwells);
// object(Person)#1 (3) { ["firstName"]=> NULL ["lastName"]=> NULL ["dateOfBirth"]=> NULL }
?>

We now have an object variable called $cwells. As we can see from the output of the var_dump() function, the object is of type Person - the very first instance of the Person class, in fact - and has three properties: firstName, lastName and dateOfBirth. All of these properties currently have the value null, because we have not assigned values to them. Maybe we should do that now:

<?php
class Person{
public $firstName;
public $lastName;
public $dateOfBirth;
}

$cwells = new Person();
$cwells->firstName = "Chris";
$cwells->lastName = "Wells";
$cwells->dateOfBirth = "25.02.1955";
var_dump($cwells);
// object(Person)#1 (3) { ["firstName"]=> string(5) "Chris" ["lastName"]=> string(5) "Wells" ["dateOfBirth"]=> string(10) "25.02.1955" }
?>

Our object is now at least a little more useful - at least it contains some data! We have passed in the date of birth as a string value for the moment. We might want to use a DateTime object for this property at some point but let’s keep things simple for now. At this stage, we are just exploring possibilities.

There are a couple of things to note here. First, if we are nor passing any data to the object when we create it, the parentheses are not required. We could just as easily have created the $cwells object using this line of code:

$cwells = new Person;

If we want to create a new object and initialise it with some actual data, we can use a constructor method (more about that shortly) that can accept arguments. These arguments will be passed to the constructor function as a comma separated list inside the parentheses that follow the class name, in the same way that arguments are passed to a function in a function call.

The second thing to note is that, in order to access a function’s public properties, we use the name of the object variable, followed by the object operator - or as most people call it the arrow operator (->), and then the name of the property we want to access (minus the dollar sign). We used this line of code in our previous example to assign the value “Chris” to the firstName property:

$cwells->firstName = "Chris";

One final thing to note before we proceed is that we do not always need to create a class in order to instantiate an object. PHP provides a built-in class called stdClass that we can use if we wish to create an object “on the fly”. This class is a generic, empty class with dynamic properties. It allows us to create new objects in the same way we create objects using other classes, using the new keyword. For example:

<?php
$cwells = new stdClass();
$cwells->firstName = "Chris";
$cwells->lastName = "Wells";
$cwells->dateOfBirth = "25.02.1955";

var_dump($cwells);
// object(stdClass)#1 (3) { ["firstName"]=> string(5) "Chris" ["lastName"]=> string(5) "Wells" ["dateOfBirth"]=> string(10) "25.02.1955" }
?>

The stdClass class has no methods or default properties. It could be useful if we need to create an object quickly for one-off use somewhere in an application, or maybe if we need to create one for debugging and testing purposes. You can find a complete definition of this class on the PHP Group website.

The __construct() method

PHP allows us to define a special constructor method called __construct() for a class. This constructor method is one of PHP’s so-called “magic” methods because, if defined, it will be invoked automatically when we create a new instance of that class. The __construct() method has a return value of void - it doesn’t return a value as such, but it can take any number of arguments.

A constructor method is typically used to initialise an object before it is used by assigning the arguments passed to the object during instantiation to object properties. The arguments passed to a constructor method can be required or optional (optional arguments must appear after any required arguments in the parameter list). We can also specify a datatype for an argument.

If you have come across constructors before when working with PHP, you may be familiar with a now-deprecated syntax in which a class method that has the same name as the class itself is automatically recognised as a constructor method. As of PHP 8.0, this is no longer the case, and you should always use __construct() for the constructor method.

If a constructor method is defined for a class using the old syntax, that method will be called when a new object of that class is created, but an error will be generated. If the class contains a method that has the same name as the class, but also has a __construct() method, it is the __construct() method that will be called when a new object is created. The method that has the same name as the class is deemed to have no special meaning.

To demonstrate the principle, let’s define a constructor method for our Person class that initialises the firstName and lastName properties with actual values:

<?php
class Person{
public $firstName;
public $lastName;
public $dateOfBirth;

public function __construct(string $lName, string $fName) {
$this->lastName = $lName;
$this->firstName = $fName;
}
}

$cwells = new Person("Wells", "Chris");
$cwells->dateOfBirth = "25.02.1955";
print_r($cwells);

// Person Object ( [firstName] => Chris [lastName] => Wells [dateOfBirth] => 25.02.1955 )
?>

As you can see from this example, the __construct() method is called automatically when we create a new object of type Person, and the two arguments passed to the __construct() method have been assigned to the lastName and firstName properties, respectively. We can still provide values for other properties once an object has been instantiated. In this example, we provide a value for the dateOfBirth property using a separate program statement.

As of PHP 8.0, the parameters specified for the _construct() method can be used to create class properties using a new syntax known as constructor property promotion. The arguments received that correspond to those parameters are assigned to properties that have the same names as the parameters automatically. This can streamline both the process of defining a class and the instantiation of a new object. For example, we could rewrite the previous example like this:

<?php
class Person{
public function __construct(public string $lastName, public string $firstName) {}
public $dateOfBirth;
}

$cwells = new Person("Wells", "Chris");
$cwells->dateOfBirth = "25.02.1955";
print_r($cwells);

// Person Object ( [lastName] => Wells [firstName] => Chris [dateOfBirth] => 25.02.1955 )
?>

As you can see, calling the __construct() method now not only creates a new object of type Person and initialises the lastName and firstName properties, it actually creates those properties. We can still define other properties for the Person class and assign values to them in the usual way, as we see here with the dateOfBirth property.

In order for PHP to interpret a parameter as a property name, the parameter name (and any type specifier) must be preceded by a modifier. In our example, we use the public access modifier for the $lastName and $firstName parameters. As a result, these parameters create the lastName and firstName properties, and assign the values they receive as arguments to them.

Note that not all of the parameters in the parameter list need to be promoted. We can mix promoted and not-promoted parameters in any order. The modifier used to identify a parameter as promotional can be any kind of modifier, although an access modifier (public, private or protected) is normally used.

A class may only have one __construct() method, but it can take any number of arguments. Parameters may include type declarations, and can have default values. If a class is defined without a constructor, an empty constructor will be created automatically.

Getter and setter methods

So far, we have only looked at the __construct() method, which is called automatically (if defined) when an instance of a class is created. In order to define the functionality of an object of a particular class, however, the class will need to provide additional methods that will be used to implement that functionality.

Class methods are typically used to assign values to properties, and to retrieve those same values when they are needed. Methods that assign values to class properties are generally referred to as setter methods, while those used to retrieve values from properties are collectively called getter methods. To demonstrate, we’ll add some setter and getter methods to our Person class:

<?php class Person{
public function __construct(public string $lastName, public string $firstName) {}
public $dateOfBirth;

public function setDateOfBirth($dob) {
$this->dateOfBirth = $dob;
}
public function getFirstName() {
return $this->firstName;
}
public function getLastName() {
return $this->lastName;
}
public function getDateOfBirth() {
return $this->dateOfBirth;
}
}

$cwells = new Person("Wells", "Chris");
$cwells->setDateOfBirth("25.02.1955");

print_r("Name: " . $cwells->getFirstName() . " ");
print_r($cwells->getLastName() . "<br>");
print_r("Date of Birth: " . $cwells->getDateOfBirth());

// Name: Chris Wells
// Date of Birth: 25.02.1955
?>

The getter and setter methods access the $cwells object’s properties using the pseudo-variable $this followed by the object operator (->) followed by the name of the property. The $this variable is a reference to the object in which it is used - in this case the $cwells object.

To access an object’s methods from outside the object, we use the object name, again followed by the object operator, and then the name of the method. Accessing an object’s methods and properties is achieved using the same syntax, i.e. the object’s name, followed by the object operator, followed by the name of the property or method.

In fact, class properties and methods occupy different namespaces, so it is possible to have a property and a method with the same name. Whether we are accessing a property or calling a method will depend on context. If we are calling a method, the name is followed by parentheses - like any other function call - which may or may not contain arguments.

Note that PHP version 8.4 saw the introduction of property hooks, also known as property accessors, that are intended to replace traditional getter and setter methods with a more concise and versatile mechanism for reading and writing object property values, and reducing the coding overhead.

At the time of writing, the version of PHP distributed with XAMPP for Windows or Linux is 8.2.12, so we are not going to get into how property hooks are used in this article. For those interested in pursuing this topic, you can find more information on the PHP Group website.

Properties and access modifiers

You may be wondering, since we have just been talking about setter and getter methods, why we actually need these methods when we can access an object’s properties directly. The short answer is that the only reason this has been possible so far is because we have declared those properties to be publicly accessible using the public keyword.

Generally speaking, access to the properties of an object should be more tightly restricted. Data hiding through encapsulation is, after all, one of the principles behind the concept of object-oriented programming. We have three options when it comes to the degree to which object properties are visible to external code, depending on which access modifier we use:

  • public - the property or method can be accessed from anywhere (this is the default)
  • protected - the property or method can only be accessed from within the class in which they are defined or classes derived from that class
  • private - the property or method can only be accessed from within the class in which they are defined

If we were defining our Person class for use in a real-world application, it is highly unlikely that we would declare the firstName, lastName and dateOfBirth properties using the public access modifier. This is personal information and would normally be declared using the protected, or more likely the private access modifier. Let’s see what happens when we declare these properties to be private, and then try to access one of them directly:

<?php
class Person{
private $firstName;
private $lastName;
private $dateOfBirth;
}

$cwells = new Person();
$cwells->firstName = "Chris";

// Fatal error: Uncaught Error: Cannot access private property Person::$firstName . . .
?>

As you can see, we didn’t get very far. This is where our getter and setter methods come into play. These methods provide access to the object’s properties indirectly. We can access the getter and setter methods because we declare them using the public keyword. For example:

<?php
class Person{
private $firstName;
private $lastName;
private $dateOfBirth;

public function setFirstName($fName) {
$this->firstName = $fName;
}
}

$cwells = new Person();
$cwells->setFirstName("Chris");
print_r($cwells);
echo $cwells->firstName;

// Person Object ( [firstName:Person:private] => Chris [lastName:Person:private] => [dateOfBirth:Person:private] => )
// Fatal error: Uncaught Error: Cannot access private property Person::$firstName
?>

Clearly, the setter method has done its job here and set the value of the $cwells object’s firstName property. We still can’t directly access this property in order to read the value though. We need a getter method:

<?php
class Person{
private $firstName;
private $lastName;
private $dateOfBirth;

public function setFirstName($fName) {
$this->firstName = $fName;
}
public function getFirstName() {
return $this->firstName;
}
}

$cwells = new Person();
$cwells->setFirstName("Chris");
echo "First name: " . $cwells->getFirstName();

// First name: Chris
?>

What happens if we create a sub-class of the Person class, let’s say an Employee class that has the same properties and methods as the Person class but with one additional property, namely an employee number?

<?php
class Person {
public function __construct(private string $lastName, private string $firstName) {}
private $dateOfBirth;

public function setDateOfBirth($dob) {
$this->dateOfBirth = $dob;
}
public function getFirstName() {
return $this->firstName;
}
public function getLastName() {
return $this->lastName;
}
public function getDateOfBirth() {
return $this->dateOfBirth;
}
}

class Employee extends Person {
private $employeeNumber;

public function getName() {
return $this->firstName . " " . $this->lastName;
}
public function getEmployeeNumber() {
return $this->employeeNumber;
}
public function setEmployeeNumber($empNum) {
$this->employeeNumber = $empNum;
}
}

$cwells = new Employee("Wells", "Chris");
$cwells->setEmployeeNumber("000123");

echo "Employee number: " . $cwells->getEmployeeNumber() . "<br>";
echo "Employee name: " . $cwells->getName();

// Employee number: 000123
//
// Warning: Undefined property: Employee::$firstName . . .
//
// Warning: Undefined property: Employee::$lastName . . .
// Employee name:
?>

The thing to note here is the fact that the getName() method defined in the Employee subclass fails when it attempts to access the firstName and lastName properties inherited from the Person class because they were declared as private in that class.

In order to access these properties from the Employee subclass, we have to use the getter/setter methods inherited by the Employee class but defined in the Person superclass. But what if, instead of declaring them as private properties in Person, we declared them as protected instead? Let’s see:

<?php
class Person {
public function __construct(protected string $lastName, protected string $firstName) {}
private $dateOfBirth;

public function setDateOfBirth($dob) {
$this->dateOfBirth = $dob;
}
public function getFirstName() {
return $this->firstName;
}
public function getLastName() {
return $this->lastName;
}
public function getDateOfBirth() {
return $this->dateOfBirth;
}
}

class Employee extends Person {
private $employeeNumber;

public function getName() {
return $this->firstName . " " . $this->lastName;
}
public function getEmployeeNumber() {
return $this->employeeNumber;
}
public function setEmployeeNumber($empNum) {
$this->employeeNumber = $empNum;
}
}

$cwells = new Employee("Wells", "Chris");
$cwells->setEmployeeNumber("000123");

echo "Employee number: " . $cwells->getEmployeeNumber() . "<br>";
echo "Employee name: " . $cwells->getName();

// Employee number: 000123
// Employee name: Chris Wells
?>

We could of course still access the firstName and lastName properties from the Employee class, even if they were to remain as private properties in the Person class, by using the getter and setter methods defined in the Person class. This is possible because, not only are these methods inherited from the Person class, they have also been declared as public methods.

You might be asking yourself what the point is of declaring a class property either private or protected if we are going to make it accessible using public setter and getter methods anyway, which is a fair question. However, the simple getter and setter methods we have defined so far are just the start.

For example, more sophisticated setter methods can be written that check whether a value is valid before allowing a private property to be updated. Indeed, we can make a private property read-only by defining a getter method but no setter method (we can also explicitly declare the property to be read-only using the readonly modifier - we’ll talk about that shortly).

Another advantage of making class properties private or protected is that the internal code representation of both the properties themselves and the setter and getter methods used to access them can be changed without affecting how external code interacts with an object, so long as the external code can continue to call the same methods, and so long as the results of calling those methods do not change.

The read-only modifier

The readonly modifier was introduced with PHP version 8.1.0. If a property is declared as read-only, it cannot be modified once it has been given an initial value and its access level is implicitly set to private, which means its value can only be set from within the class in which it is declared (although this implied access level changes to protected with PHP 8.4.0). This behaviour can be overridden by explicitly setting the access level).

Note that static properties (more about these later) cannot be read-only properties. Note also that the readonly modifier can only be applied to typed properties (although we can get around this constraint by specifying a type of mixed). Let’s look at an example:

<?php
class Employee {
public function __construct(private string $lastName, private string $firstName) {}
readonly mixed $employeeNumber;

public function setEmployeeNumber($empNum) {
$this->employeeNumber = $empNum;
}

public function getEmployeeNumber() {
return $this->employeeNumber;
}
}

$cwells = new Employee("Wells", "Chris");
$cwells->setEmployeeNumber("000123");

echo "Employee number: " . $cwells->getEmployeeNumber();
$cwells->setEmployeeNumber("000124");

// Employee number: 000123
// Fatal error: Uncaught Error: Cannot modify readonly property Employee::$employeeNumber
?>

As you can see from this example, the first call to the setEmployeeNumber() method assigns a value to the employeeNumber property, but a subsequent call to setEmployeeNumber() fails because the property has already been initialised. It is also not permitted to assign a default value to a read-only property. This makes sense, because a read-only property with a default value is basically a constant, and therefore not especially useful.

If a read-only property is an object variable, then the property holds a reference to an object that cannot be changed once the property has been initialised. In other words, it cannot be made to point to another object. The object to which the variable points, on the other hand, can be changed. For example:

<?php
class Circle {
private float $radius;
public function setRadius($rad = 1.0) {
$this->radius = $rad;
}
}

$myCirc = new Circle;
$myCirc->setRadius(5);

class Test {
readonly mixed $pointer;
public function setPointer($ptr) {
$this->pointer = $ptr;
}
public function getPointer() {
return $this->pointer;
}
}

$myTest = new Test;

$myTest->setPointer($myCirc);
print_r($myTest);
echo "<br>";
$myTest->getPointer()->setRadius(7.0);
print_r($myTest);

// Test Object ( [pointer] => Circle Object ( [radius:Circle:private] => 5 ) )
// Test Object ( [pointer] => Circle Object ( [radius:Circle:private] => 7 ) )
?>

Although this is a rather contrived example, it serves to demonstrate that object variables stored as read only properties in another object may be modified internally.

Read-only properties are useful when we want to assign a value to a property once only. We might want to define a GeoLocation class, for example, so that we can create objects to hold the map coordinates of specific locations. Once initialised, the properties holding the coordinates would not need to be changed, so it would make sense to declare them as read-only.

If we have a class in which all of the properties will be read-only, we don’t need to declare each property as read-only individually. We can declare the class itself as read only. For example:

<?php
readonly class GeoLocation {
public function __construct(private float $latitude, private float $longitude) {}

public function getLatitude() {
return $this->latitude;
}
public function getLongitude() {
return $this->longitude;
}
public function setLocation($lat, $long) {
$this->latitude = $lat;
$this->longitude = $long;
}
}

$myLocation = new GeoLocation(53.54130987804806, 9.983932698717952);

echo "Latitude: " . $myLocation->getLatitude() . "<br>";
echo "Longitude: " . $myLocation->getLongitude() . "<br>";

$myLocation->setLocation(0.0, 0.0);

// Latitude: 53.54130987804806
// Longitude: 9.983932698717952
//
// Fatal error: Uncaught Error: Cannot modify readonly property GeoLocation::$latitude . . .
?>

In this example, we have declared the GeoLocation class to be read-only using the readonly modifier. Thus, the latitude and longitude properties are read only, even though we have not explicitly declared them as such. As a consequence, although our getLatitude() and getLongitude() methods work as expected, the setLocation() method, which attempts to change the value of these properties, generates an error.

One further point with regard to read-only classes is that, because read-only properties must be typed, all of the properties in read-only classes must also be typed (although as mentioned previously, we can overcome any unwanted restrictions this might create by declaring property types as mixed if necessary). In addition, because static properties cannot be read-only properties, a read-only class cannot contain static properties.

Static properties and methods

Static properties and methods are properties and methods that belong to the class in which they are defined rather than to a specific instance of that class. That means that we can access them directly by preceding the property or method name with the name of the class followed by a double colon, officially called the scope resolution operator (::).

We don’t need to create an instance of a class in order to access a static property or a static method defined on that class. We can’t access a static class property from an instance of that class, but we can access a static method from within a class instance, as we shall see. First, though, we’ll look at an example of using a static method defined on one of PHP’s built-in classes - the DateTime class. If you have read the article “Working with Dates and Times” in this section, you will be familiar with this class. The following example is taken from that article:

<?php
$date = DateTime::createFromFormat("d-m-Y H:i:s", "24-12-2025 00:00:00");
var_dump($date);
/*
object(DateTime)#1 (3) {
["date"]=> string(26) "2025-12-24 00:00:00.000000"
["timezone_type"]=> int(3)
["timezone"]=> string(13) "Europe/Berlin" }
*/
?>

In this example we call the static createFromFormat() method on the DateTime class directly in order to create a new instance of the DateTime class and assign it to the $date variable. Methods like createFromFormat(), which can create an instance of a class without calling a class constructor using the new operator, are called factory methods.

We can also call the createFromFormat() method on an instance of the DateTime class using the object operator (->), as this example demonstrates:

<?php
$date = DateTime::createFromFormat("d-m-Y H:i:s", "24-12-2025 00:00:00");
$newDate = $date->createFromFormat("d-m-Y H:i:s", "24-12-2026 00:00:00");
var_dump($newDate);
/*
object(DateTime)#2 (3) {
["date"]=> string(26) "2026-12-24 00:00:00.000000"
["timezone_type"]=> int(3)
["timezone"]=> string(13) "Europe/Berlin" }
*/
?>

As you can see from the above examples, a static property or method is defined using the static keyword. However, because static class methods can be called without creating an instance of a class, the pseudo variable $this cannot be used inside a method declared as static, because if the method is called on a class rather than on an instance of that class, $this would refer to a non-existent object.

Static properties can be used in cases where we want to share information across all instances of a class, such as a counter property that can be used to track the number of instances of a class that have been created. Consider the following example:

<?php
class Person {
public static $counter = 0;
public function __construct(private $fName, private $lName) {
self::$counter ++;
}
}

$cwells = new Person("Chris", "Wells");
$jsmith = new Person("John", "Smith");
$mmalone = new Person("Molly", "Malone");
echo "There are " . Person::$counter . " objects of type Person.<br>";
$fbloggs = new Person("Fred", "Bloggs");
echo "There are now " . Person::$counter . " objects of type Person.";

// There are 3 objects of type Person.
// There are now 4 objects of type Person.
?>

In this example, we have declared the static class property $counter and assigned it an initial value of zero. Because $counter is a static property, it belongs to the Person class rather than to any instance of that class. Each time a new instance of Person is created, the __construct() method is called and increments the value of $counter by one.

Note how in this example we used the self keyword with the scope resolution operator (::) to access the static class property $counter. The self keyword is used to refer to the current class, in much the same way as the $this pseudo variable is used to refer to the current object. The scope resolution operator is used inside the class together with the self keyword in the same way it would be used outside the class with the class name.

Static methods are useful when we want to provide specialised functionality defined within a class without having to first create an instance of the class itself, because that way we use less memory, and can access the functions more quickly.

As we have seen, static methods can also be used as factory methods to create one or more instances of a class without using the class constructor (actually, there’s a lot more to the story of factory classes, but we’ll be exploring that particular topic elsewhere).

Static properties are useful when we want to store values that are used by all of the objects of a given class. We have already demonstrated how a static property can be used to implement a basic counter to keep track of the number of instances we have created of a particular class. We can think of static properties as class-level variables that are visible from anywhere in our code.

Class constants

Class constants, like static class properties, belong to the class in which they are defined rather than to an instance of that class. The difference is that a static property may be redefined, whereas a class constant may not. Class constants are inherited by every instance of the class in which they are declared.

We saw some examples of class constants in the article “Working with Dates and Times” in this section. If you have read this article, you may remember that the DateTime and DateTimeImmutable classes inherit a number of constants from DateTimeInterface that represent standard, predefined date formatting strings. We can access these constants directly using the scope resolution operator. For example:

<?php
echo "ATOM: " . DateTimeInterface::ATOM . "<br>";
echo "COOKIE: " . DateTimeInterface::COOKIE . "<br>";
echo "ISO8601: " . DateTimeInterface::ISO8601 . "<br>";
echo "ISO8601_EXPANDED: " . DateTimeInterface::ISO8601_EXPANDED;

// ATOM: Y-m-d\TH:i:sP
// COOKIE: l, d-M-Y H:i:s T
// ISO8601: Y-m-d\TH:i:sO
// ISO8601_EXPANDED: X-m-d\TH:i:sP
?>

A class constant is declared using the const keyword and is case sensitive by default (the convention is for class constant names to contain only uppercase letters). Like PHP constants in general, class constants are accessible from anywhere in a script by default, although as of PHP 7.1.0 access modifiers (i.e. public, protected or private) may be used in the constant declaration. Consider the following example:

<?php
class MyClass {
const WEATHER_FORECAST = "It's going to rain!";
const PUBLIC_KNOWLEDGE = "Rain is wet.";
private const SECRET = "Don't tell anybody!";
public function spillTheBeans() {
echo self::SECRET . "<br>";
}
}

$myObject = new MyClass;

echo MyClass::WEATHER_FORECAST . "<br>";
echo $myObject::PUBLIC_KNOWLEDGE . "<br>";
$myObject->spillTheBeans();
echo MyClass::SECRET;

// It's going to rain!
// Rain is wet.
// Don't tell anybody!
//
// Fatal error: Uncaught Error: Cannot access private constant MyClass::SECRET . . .
?>

In this example we declare three string constants (WEATHER_FORECAST, PUBLIC_KNOWLEDGE and SECRET. The SECRET constant is declared using the private access modifier. The following line of code accesses the WEATHER_FORECAST constant directly on the class itself (this is the recommended way of accessing a class constant):

echo MyClass::WEATHER_FORECAST . "<br>";

The next line of code accesses the PUBLIC_KNOWLEDGE constant from $myObject, an instance of (this is obviously possible but considered poor practice):

echo $myObject::PUBLIC_KNOWLEDGE . "<br>";

Next, we use the class method spillTheBeans() to access the SECRET constant (note how the self keyword is used with the scope resolution operator to access the constant from within the class). This works, but again, it’s probably not a particularly good idea. If we’ve gone to the trouble of declaring a constant as private, it’s probably intended for internal use only. As you can see, when we try to access this constant directly, an error is generated.

There has been some discussion in the past about whether access modifiers should be used with class constants. The general (though not totally unanimous) consensus is that, since one of the guiding principles of object-oriented programming is that the internal details of a class should remain hidden as far as possible, the option of making class constants protected or private should exist (see https://wiki.php.net/rfc/class_const_visibility).

A class constant may be of any scalar type (string, int, float, or bool). It can even be an array, as long as all of the array elements have scalar types. It is also possible to declare several constants using a comma-separated syntax. The following example demonstrated how we might use class constants:

<?php
class CaseFile {
const ACTIVE=1, SUSPENDED=2, CLOSED=3;
public function __construct(private int $status = self::ACTIVE) {}

public function suspendFile(){
$this->status = self::SUSPENDED;
}
public function closeFile(){
$this->status = self::CLOSED;
}
public function reopenFile(){
$this->status = self::ACTIVE;
}
public function unsuspendFile(){
$this->status = self::ACTIVE;
}
public function getStatus() {
$currentStatus = $this->status;
switch ($currentStatus) {
case self::ACTIVE:
echo "The case is active.<br>";
break;
case self::SUSPENDED:
echo "The case has been suspended.<br>";
break;
case self::CLOSED:
echo "The case is closed.<br>";
break;
default:
echo "Status unknown.<br>";
}
}
}

$myFile = new CaseFile;
$myFile->getStatus(); // The case is active.
$myFile->suspendFile();
$myFile->getStatus(); // The case has been suspended.
$myFile->unsuspendFile();
$myFile->getStatus(); // The case is active.
$myFile->closeFile();
$myFile->getStatus(); // The case is closed.
$myFile->reopenFile();
$myFile->getStatus(); // The case is active.
?>

Class constants can be used wherever we have a constant value that has special relevance to a particular class. For example, we might have a club membership class that requires members to be of over a certain age, in which case we could define a constant called MIN_AGE. We might want to set upper and lower temperature limits for a class modelling an industrial process, in which case we might define the constants MAX_TEMP and MIN_TEMP.

Class constants should be defined for values that will not change, that are shared across all objects of that class, and that are not defined elsewhere. Don’t re-invent the wheel. For example, if you are creating a class that uses mathematical constants, you should check PHP’s existing built-in mathematical constants before defining your own.

If, on the other hand, you are defining a specialised class that requires you to define a number of application-specific status codes or configuration values, there is a strong argument for using class constants. Before we leave the topic of class constants, we should also mention that class constants can be redefined in a child class, unless they are defined as final in the base class using the final keyword (we’ll be looking at class inheritance shortly).

Cloning objects

Cloning an object is a process in which we create a copy of an existing object. The copy can potentially be identical to the original, with all of the same property values. We can’t simply create a new variable and assign the value of an existing object variable to it, because what we would actually doing is assigning a reference to the original object to the new variable. Consider the following example:

<?php
class Person{
public $firstName;
public $lastName;

public function __construct(string $lName, string $fName) {
$this->lastName = $lName;
$this->firstName = $fName;
}
}

$cwells = new Person("Wells", "Chris");
$jdoe = $cwells;
$jdoe->lastName = "Doe";
$jdoe->firstName = "John";
echo "\$jdoe: " . $jdoe->firstName . " " . $jdoe->lastName . "<br>";
echo "\$cwells: " . $cwells->firstName . " " . $cwells->lastName . "<br>";
// $jdoe: John Doe
// $cwells: John Doe
?>

In this example, we have created a new object called $cwells as an instance of the Person class. Actually, it would be more accurate to say that we have allocated a block of memory for the new object, and assigned the address of that block of memory to the variable $cwells. We subsequently assign the value of $cwells to the variable $jdoe.

As you can see from the example, any changes we make to the lastName and firstName properties in the object referenced by $jdoe can also be seen in the object referenced by $cwells. That’s because they are the same object. We have not created a new object by assigning the value of $cwells to $jdoe. We have simply created a new variable that holds the same address.

If we want to create a copy of a Person object without creating a new instance of that class and manually assigning identical values to each of its properties - which could be rather tedious if there are a lot of properties - we can instead create a clone of that object using the clone keyword which will be an exact copy of the original. Consider this example:

<?php
class Person{
public $firstName;
public $lastName;

public function __construct(string $lName, string $fName) {
$this->lastName = $lName;
$this->firstName = $fName;
}
}

$cwells = new Person("Wells", "Chris");
$jdoe = clone $cwells;
$jdoe->lastName = "Doe";
$jdoe->firstName = "John";
echo "\$jdoe: " . $jdoe->firstName . " " . $jdoe->lastName . "<br>";
echo "\$cwells: " . $cwells->firstName . " " . $cwells->lastName . "<br>";
// $jdoe: John Doe
// $cwells: Chris Wells
?>

This example is identical to the previous example except that we have inserted the clone keyword into the assignment statement:

$jdoe = clone $cwells;

As you can see, any changes we make to the object referenced by the variable $jdoe do not affect the object referenced by $cwells. They are two separate and independent objects. When PHP clones an object in this way it creates a shallow copy of the original - i.e. it makes a copy of any property that is a scalar value or an array, but if a property holds a reference to another variable, only the reference is copied; the variable it points to is not duplicated.

Duplicating an object exactly through cloning is usually just a means to an end. There are few if any situations in which we need multiple identical copies of an object. A more likely scenario is a situation in which we need a number of objects that are almost identical but differ in one or two details.

That being the case, creating the requisite number of clones, and modifying only those properties that differ from one object to the next, might be more efficient than creating multiple new instances of the relevant class and assigning values to all of the properties in each new instance.

If we are creating a class, and if we know in advance that we will be creating multiple similar instances of that class through cloning rather than through instantiation, we can define a __clone() method for that class. When an object of that class is cloned - and once the cloning process is complete - the object’s __clone() method is called.

The __clone() method is typically used if we want to duplicate both the object itself and any objects that are referenced by one or more of its properties. The following example demonstrates how we might use the __clone() method:

<?php
class ModuleList {
static $instances = 0;
public $instance;
public function __construct() {
$this->instance = ++self::$instances;
}
public function __clone() {
$this->instance = ++self::$instances;
}
}

class Enrolment {
public $coreModules;
public $optionsSelected;
function __clone() {
$this->optionsSelected = clone $this->optionsSelected;
}
}

$enrolment1 = new Enrolment ();
$enrolment1->coreModules = new ModuleList();
$enrolment1->optionsSelected = new ModuleList();
$enrolment2 = clone $enrolment1;

print "Original Enrolment:<br>";
print_r($enrolment1);
echo "<br>";
print "Cloned Enrolment:<br>";
print_r($enrolment2);

/*
Original Enrolment:
Enrolment Object (
[coreModules] => ModuleList Object(
[instance] => 1
)
[optionsSelected] => ModuleList Object (
[instance] => 2
)
)

Cloned Enrolment:
Enrolment Object (
[coreModules] => ModuleList Object (
[instance] => 1
)
[optionsSelected] => ModuleList Object (
[instance] => 3
)
)
*/
?>

This example an adaptation of the example given on the PHP Group website, but hopefully one that adds a little more context. The Enrolment class contains two properties, $coreModules and $optionsSelected. Once the first instance of the Enrolment class ($enrolment1) has been created, each of these properties is assigned a new instance of the ModuleList class.

The Enrolment class defines a __clone() method that ensures that, when an instance of the Enrolment class is cloned, a clone of the object referenced by the $optionsSelected property is also cloned.

The thinking behind this example is that the Enrolment class could be used to represent an enrolment on a college or university course that has a set of core modules, and a set of optional modules from which applicants can choose. The core modules will not change, but the optional modules chosen could vary considerably, hence the need to create a new ModuleList object to hold the optional modules chosen for each enrolment.

Note that the only purpose of the $instance and $instances properties in the ModuleList class is to keep track of how many instances of each class are being created, and to demonstrate that each time we clone an instance the Enrolment class, the cloning process also creates a clone of the ModuleList object referenced by the $optionsSelected property. These properties are otherwise superfluous.

Before we leave the topic of cloning, this might be a good point at which to review how two objects are tested for equality. There are two primary comparison operators we can use. The first (==) can be used to determine whether two objects are equal, i.e. whether they are instances of the same class, and have the same attributes and values. The second (===) is used to determine if two objects are identical, which in PHP terms means they are the same instance of the same class. For example:

<?php
class myClass {
public $someVar;
public $someOtherVar;
}

$inst1 = new myClass;
$inst2 = clone $inst1;
$inst3 = $inst1;

echo $inst1 == $inst2 ? "true<br>" : "false<br>"; // true
echo $inst1 == $inst3 ? "true<br>" : "false<br>"; // true
echo $inst2 == $inst3 ? "true<br>" : "false<br>"; // true
echo $inst1 === $inst2 ? "true<br>" : "false<br>"; // false
echo $inst1 === $inst3 ? "true<br>" : "false<br>"; // true
echo $inst2 === $inst3 ? "true<br>" : "false<br>"; // false
?>

Class inheritance

Class inheritance allows us to derive a new class from an existing class - the base class or parent class - by extending the existing class. The new class - known as the derived class or child class - inherits all of the properties, methods and constants of the base class.

In order to inherit the characteristics of a particular base class, the derived class is declared using the extends keyword, followed by the name of the base class. We saw an example of how a class can be extended earlier when we looked at properties and access modifiers. Here is a slightly simplified version of that example:

<?php class Person {
public function __construct(private $lastName, private $firstName) {}

public function getName() {
return $this->lastName . ", " . $this->firstName;
}
}

class Employee extends Person {
private $employeeNumber;

public function getEmployeeNumber() {
return $this->employeeNumber;
}
public function setEmployeeNumber($empNum) {
$this->employeeNumber = $empNum;
}
}

$cwells = new Employee("Wells", "Chris");
$cwells->setEmployeeNumber("000123");

echo "Employee number: " . $cwells->getEmployeeNumber() . "<br>";
echo "Employee name: " . $cwells->getName();

// Employee number: 000123
// Employee name: Wells, Chris
?>

As you can see, in this example the Employee class inherits the lastName and firstName properties of the Person class, together with the __construct() method and the getName() method. The new class has one additional property - employeeNumber - and two additional methods, one to set the employeeNumber property and one to get it.

Although a derived class can only inherit from one base class, the methods and constants inherited from the base class can be overridden by re-declaring them using the same name they were given in the base class (unless they were declared as final in the base class definition). Consider the following example:

<?php
class UTCTime
{
public function showTime() {
$time = new DateTime("now", new DateTimeZone("UTC"));
echo "The current UTC time is: " . $time->format("H:i (Y-m-d)") . "<br>";
}
}
$time = new UTCTime;
$time->showTime();
// The current UTC time is: 23:10 (2026-02-12)

class GlobalTime extends UTCTime {
public function showTime($timezone = "UTC") {
$time = new DateTime("now", new DateTimeZone($timezone));
echo "The current time in timezone " . $timezone . " is: " . $time->format("H:i (Y-m-d)") . "<br>";
}
}
$globalTime = new GlobalTime;
$globalTime->showTime("Asia/Shanghai");
// The current time in timezone Asia/Shanghai is: 07:10 (2026-02-13)
$globalTime->showTime("Europe/Athens");
// The current time in timezone Europe/Athens is: 01:10 (2026-02-13)
?>

In this example, we have extended the UTCTime class to create the GlobalTime class. The only significant difference is that we have re-defined the showTime() method to take an optional timezone parameter that defaults to “UTC”).

Clearly, overriding methods is allowed, but there are rules. The signature of an overridden method must be the same as that of the base class method it is replacing. This means that the number of mandatory arguments in an overridden method must match the number of mandatory arguments in the corresponding base class method.

Note that these rules do not apply to any private methods declared in a base class, since these methods are not available to the derived class. Nor do they apply to the base class’s __construct() method, if one has been declared. It is also permissible to rename an inherited method’s parameters, but this is considered poor practice because it will cause a runtime error if named arguments are used.

Because a derived class inherits all of the (non-private) methods of the base class, it can implement additional functionality without having to re-implement the functionality it shares with the base class.

In addition, because the private methods of a base class are not accessible to the derived class, the derived class can implement a method with the same name as a private method in the base class without regard for the normal rules of inheritance.

The __construct() method defined in the base class - if it has not been declared using the private keyword - is also inherited from the base class in the same way as other non-private methods (as demonstrated by our Employee class example). It will only be called, however, if the derived class does not have its own __construct() method.

That doesn’t mean it can’t be called; it just means that if we want the child class to call the parent class’s constructor despite having a __construct() method of its own, we have to do so explicitly using a call to parent::__construct() from within the child class’s __construct() method. For example:

<?php
class Person {
private $lastName, $firstName;
public function __construct($lName, $fName) {
$this->lastName = $lName;
$this->firstName = $fName;
}

public function getName() {
return $this->lastName . ", " . $this->firstName;
}
}

class Employee extends Person {
public function __construct($lName, $fName, private $employeeNumber) {
parent::__construct($lName, $fName);
}

public function getEmployeeNumber() {
return $this->employeeNumber;
}
}

$cwells = new Employee("Wells", "Chris", "000123");

echo "Employee number: " . $cwells->getEmployeeNumber() . "<br>";
echo "Employee name: " . $cwells->getName();

// Employee number: 000123
// Employee name: Wells, Chris
?>

Note that, in addition to the argument supplied against the Employee constructor’s $employeeNumber parameter, the arguments expected by the Person constructor method must also be passed to Employee‘s constructor. However, we don’t have to use the original parameter names, and we don’t need to re-declare them as private.

If a base class method is overridden in a derived class, the overridden method can add new parameters if they are optional parameters. It can also make a previously mandatory parameter optional, but not the other way round. Furthermore, it can also relax the visibility of a method but it cannot restrict visibility further. Consider this example:

<?php
class Calculate
{
public function toPower($num, $exp = 2) {
return $num ** $exp;
}
}

class MyCalculate extends Calculate
{
protected function toPower($num, $exp) {
return $num ** $exp;
}
}
// Fatal error: Declaration of MyCalculate::toPower($num, $exp) must be compatible with Calculate::toPower($num, $exp = 2)
?>

The are a couple of problems here. In the Calculate class, the toPower() method has two parameters, the second of which is optional because it has a default value. We have modified the toPower() method in the MyCalculate class so that the second parameter is mandatory. If we swap these parameters around, or make them identical, that particular problem goes away:

<?php
class Calculate
{
public function toPower($num, $exp) {
return $num ** $exp;
}
}

class MyCalculate extends Calculate
{
protected function toPower($num, $exp = 2) {
return $num ** $exp;
}
}
// Fatal error: Access level to MyCalculate::toPower() must be public (as in class Calculate)
?>

As you can see, we still have a problem because, in the MyCalculate class, we have restricted access to the toPower() method using the protected keyword (in the Calculate base class, this method was declared to be publicly available using the public keyword).

We can correct this situation by making sure that the toPower() method in the MyCalculate class has either the same visibility or a less restricted visibility than it does in the base class. Let’s do that now, and add some additional functionality to the MyCalculate class:

<?php
class Calculate
{
public function toPower($num, $exp) {
return $num ** $exp;
}
}

class MyCalculate extends Calculate
{
public function toPower($num, $exp = 2) {
return $num ** $exp; }
public function product_ab($a, $b) {
return $a * $b;
}
}
$calc = new Calculate;
$result = $calc->toPower(5, 3);
echo "5 ^ 3 = $result<br>";

$myCalc = new MyCalculate;
$result = $myCalc->toPower(5);
echo "5 ^ 2 = $result<br>";
$result = $myCalc->product_ab(5, 3);
echo "5 x 3 = $result";

// 5 ^ 3 = 125
// 5 ^ 2 = 25
// 5 x 3 = 15
?>

The rules governing class inheritance are determined by something called the Liskov Substitution Principle (LSP) which states that we should be able to replace an object that is an instance of a base class with an object that is an instance of one of its subclasses without detriment to our program’s functionality or correctness. Essentially, this means that a derived class should extend, rather than replace, the base class functionality.

This essentially means that the visibility of methods, properties and constants in a derived class can be either the same or less restrictive than in the base class, but never more restrictive. For example, a protected method can remain protected or be re-declared as public, but it cannot be re-declared as private. A public method must remain public, and a read-write property cannot be re-declared as read-only.

The only exception to this rule concerns the __construct() method, which can have a more restricted visibility in the derived class than it has in the base class. So, for example, a constructor method that is public in the base class can be declared as private in the derived class.

Late static binding

Late static bindings were introduced in 2009 with PHP version 5.3 in order to resolve questions such as which version of a method should be used when a child class needs to use the services of a method that exists in a parent class, but that has been re-defined in the child class. A comprehensive explanation of late static binding is a little beyond the scope of an introductory article, but we’ll try and give you an idea of how it works. Consider the following example:

<?php
class Arthropod {
public static function getInfo() {
return self::getClass();
}
protected static function getClass() {
return "Arthropod";
}
}

class Arachnid extends Arthropod {
protected static function getClass() {
return "Arachnid";
}
}

$scorpion = new Arachnid;
echo "Scorpions belong to the " . $scorpion->getInfo() . " class.<br>";

// Scorpions belong to the Arthropod class.
?>

This is not quite the outcome we were looking for. Certainly, scorpions are arthropods - as are spiders, centipedes, millipedes, crabs, lobsters, beetles etc. In fact, the term arthropod is a general term for all kinds of invertebrate creatures that have exoskeletons, segmented bodies, and jointed appendages.

Many kinds of creature fall into this general category, including insects, crustaceans and arachnids. However, what we were aiming for here was to have our $scorpion object identified as belonging to the Arachnid class of which it is an instance.

The problem here is that the getInfo() method defined in the Arthropod class calls the getClass() method using the self:: keyword. When we call the inherited getInfo() method from an instance of the Arachnid class, it calls the getClass() method belonging to the Arthropod class rather than the re-defined getClass() method belonging to the Arachnid class due to the use of the self:: keyword.

The self:: keyword is resolved at compile time, and always refers to the class in which it is used, regardless of whether the method to which it refers is called from the parent class or a child class. This is OK when we want to access static methods that are specific to the class where the self:: keyword it is used.

In the above example, however, even though we are calling the inherited getInfo() method from an instance of the Arachnid class, the getInfo() method calls the getClass() method defined in the Arthropod class, because the self:: keyword points to that class.

We can resolve the problem by replacing the self:: keyword with the static:: keyword. The static:: keyword is resolved at runtime (which is why it is described as “late” static binding), and refers to the class from which a method is called rather than the class in which the method is defined. Thus, when getInfo() is called from an instance of Arachnid, it will now call the version of getClass() defined in that class. Let’s see how this works:

<?php
class Arthropod {
public static function getInfo() {
return static::getClass();
}
protected static function getClass() {
return "Arthropod";
}
}

class Arachnid extends Arthropod {
protected static function getClass() {
return "Arachnid";
}
}

$scorpion = new Arachnid;
echo "Scorpions belong to the " . $scorpion->getInfo() . " class.<br>";

// Scorpions belong to the Arachnid class.
?>

As you can see, in this revised version of our earlier example we have replaced the self:: keyword with the static:: keyword. This directs the call to getClass() to the re-defined version of that method in Arachnid, as the calling class, rather than forwarding the call to the version defined in the Arthropod class.

Abstract classes

An abstract class is a class that declares at least one abstract method, i.e. a public or protected method that is declared but not implemented in the abstract class. Any class that contains one or more abstract methods must be declared as abstract using the abstract keyword. The abstract class may also declare private, protected and public properties.

Note that as of PHP 8.4, an abstract class may also declare both protected and public abstract properties. At the time of writing, however, as mentioned earlier in this article, the version of PHP distributed with XAMPP for Windows or Linux is 8.2.12, so we are not going to discuss the use of abstract properties here. For those interested in pursuing this topic, you can find more information on the PHP Group website.

An abstract class cannot be instantiated. It is rather meant to act as a blueprint for one or more child classes. Abstract classes are useful if we want to define a set of properties and methods that are common to a number of other classes. An abstract class serves to ensure the consistent implementation of those common properties and methods in all of the classes derived from it. Let’s look at an example:

<?php
abstract class Shape2D
{
abstract public function getArea();
abstract public function getPerimeter();
}

class Circle extends Shape2D {
public function __construct(private $radius) {}
public function getArea() {
return M_PI * ($this->radius ** 2);
}
public function getPerimeter() {
return 2 * M_PI * $this->radius;
}
}

class Square extends Shape2D {
public function __construct(private $side) {}
public function getArea() {
return $this->side ** 2;
}
public function getPerimeter() {
return 4 * $this->side;
}
}

$myCircle = new Circle(5);
echo "The circle has an area of {$myCircle->getArea()}<br>";
echo " and a circumference of {$myCircle->getPerimeter()}<br>";

$mySquare = new Square(2.5);
echo "The square has an area of {$mySquare->getArea()}<br>";
echo " and a perimeter of {$mySquare->getPerimeter()}<br>";

// The circle has an area of 78.53981633974483
// and a circumference of 31.41592653589793
// The square has an area of 6.25
// and a perimeter of 10
?>

In this example we have defined an abstract class called Shape2D that represents a two-dimensional shape. If we asked someone to draw a shape, the first question they would ask is “What shape do you want me to draw?”, which is a perfectly reasonable question, given that there are an infinite number of possibilities.

We can narrow down the requirements by specifying what kind of shape we want them to draw (a circle, a square, a triangle, or whatever). We can be a little more precise by providing dimensions for the shape, e.g. the radius of the circle or the side length of the square, and so on.

The term “shape” on its own is an abstract term that lends itself to the creation of an abstract class, which is exactly what we have done with our abstract Shape2D class. A concrete class that extends Shape2D can be more specific. In our example, we have created the concrete classes Circle and Square, both of which extend Shape2D.

In both cases we know what the shape is going to look like. All we need to determine is the radius (in the case of the Circle class) or the side length (in the case of the Square class) in order to be able to instantiate an object of either class with specific dimensions.

The abstract Shape2D class has two abstract methods - getArea() and getPerimeter(). This reflects the fact that every two-dimensional shape you can think of has two things in common, i.e. a surface area and a perimeter length.

The Shape2D class does not specify a __construct() method, since the constructor methods specified for the concrete classes derived from it will vary, depending on the kind of shape they represent.

An abstract class can be thought of as being a generic template for any number of concrete classes that represent specific real-world entities. It cannot be instantiated, but may be extended by a concrete class. An abstract class can declare abstract methods that must be implemented by any class derived from it. It can also declare and implement non-abstract methods that will be inherited by those derived classes.

Interfaces

An interface is similar in some ways to an abstract class in that it cannot be instantiated as an object, and specifies the methods a class must implement without having to define how those methods are implemented. An interface can also declare constants (as of PHP 8.1, these can be overridden by a class implementing the interface). Note that, because interfaces share the same namespace as classes, an interface cannot have the same name as a class.

An interface is declared in the same way as a class except that the interface keyword replaces the class keyword. Interface methods are declared but not implemented, in similar fashion to the abstract methods found in abstract classes. In addition, all methods must be declared public.

As of PHP version 8.4, an interface can also declare properties. At the time of writing, however, as mentioned elsewhere in this article, the version of PHP distributed with XAMPP for Windows or Linux is 8.2.12, so the examples we provide here will not include properties. For those interested in pursuing this topic, you can find more information on the PHP Group website.

An interface may also declare magic methods (methods that are called automatically when certain conditions are met) which must also be implemented by any class that implements the interface, although including a __construct() method, whilst allowed, is strongly discouraged because it reduces the options available to classes that implement the interface and can lead to unpredictable behaviour.

At this point you might be thinking that an interface sounds an awful lot like an abstract class, and in some ways that’s true. One significant difference is that, whereas a concrete class can only extend a single abstract class, it can implement more than one interface. A class implements an interface using the implements operator. For example:

<?php
interface EventInterface {
public function setDateTime(string $scheduled);
public function setDuration(string $duration);
public function setPlace(string $venue, string $location);
public function publishEvent();
}

class Concert implements EventInterface {
public $date, $time, $duration, $venue, $location;
public function __construct(public $eventName, public $artist){}

public function setDateTime(string $scheduled) {
$dateTime = new DateTime($scheduled);
$this->date = $dateTime->format("d-m-Y");
$this->time = $dateTime->format("H:i");
}
public function setDuration($duration) {
$this->duration = $duration;
}
public function setPlace($venue, $location) {
$this->venue = $venue;
$this->location = $location;
}
public function publishEvent() {
echo "Coming soon: \"" . $this->artist . "\"" . " in concert - ";
echo "\"" . $this->eventName . "\"<br>";
echo "Venue: " . $this->venue . ", " . $this->location . "<br>";
echo "Date: " . $this->date . "<br>";
echo "Time: " . $this->time . " (for " . $this->duration . ")";
}
}

$myConcert = new Concert("One More Last Stand", "The Custers");
$myConcert->setDateTime("2026-06-30 20:00");
$myConcert->setDuration("2 hours");
$myConcert->setPlace("Wembly Stadium", "London");
$myConcert->publishEvent();

// Coming soon: "The Custers" in concert - "One More Last Stand"
// Venue: Wembly Stadium, London
// Date: 30-06-2026
// Time: 20:00 (for 2 hours)
?>

An interface acts as a contract that any class implementing the interface must fulfil. It enables us to create a variety of classes to support use cases that differ in some respects, but share a significant amount of common functionality.

Hopefully, you can see from the above example that we could use EventInterface to derive classes that represent many different kinds of event - concerts, music festivals, sporting events, exhibitions - just about any kind of public event you can think of. The factors common to all of public events include a venue, a date and time at which they commence, the duration of the event, and the need for advance publication (otherwise no-one will turn up!).

For a class that implements an interface, we can implement a method declared in that interface in any way we choose. The only rule is that the function signature used must match that of the function declaration in the interface. Note that, while a class may use different parameter names to those used by the interface, this is not recommended because it will cause a runtime error if named arguments are used.

As mentioned previously, one way in which interfaces differ from abstract classes is that a class can implement more than one interface. The names of the interfaces to be implemented follow the implements operator, separated by a comma. For example:

<?php
interface A {
public function hello();
}

interface B {
public function niceDay();
}

class MyClass implements A, B {
public function hello() {
echo "Hello World!<br>";
}
public function niceDay() {
echo "Have a nice day!<br>";
}
}

$myObj = new MyClass;
$myObj->hello();
$myObj->niceDay();
// Hello World!
// Have a nice day!
?>

It is also possible for one interface to extend another, in the same way that one class can extend another. For example:

<?php
interface A {
public function hello();
}

interface B extends A {
public function niceDay();
}

class MyClass implements B {
public function hello() {
echo "Hello World!<br>";
}
public function niceDay() {
echo "Have a nice day!<br>";
}
}

$myObj = new MyClass;
$myObj->hello();
$myObj->niceDay();
// Hello World!
// Have a nice day!
?>

Anonymous classes

Anonymous classes were introduced in PHP 7.0. Like anonymous functions, anonymous classes are declared without a name, and are intended to be used once only during script execution. That said, an anonymous class can do pretty much anything a normal class can do, including having its own properties and methods, extending another class, or implementing an interface.

An instance of an anonymous class is created at the same time as the class itself, using the new keyword followed by the class keyword and then the body of the class inside curly brackets. For example:

<?php
$anonymous = new class {
public function __construct() {
echo "Im an anonymous class object!<br>";
}
};
var_dump($anonymous);
// Im an anonymous class object!
// object(class@anonymous)#1 (0) { }
?>

Note that, even though an anonymous class does not have a name, it will be assigned a randomly generated name internally by the PHP engine. If we use the previous example with the addition of a call to the get_class() function we’ll see something like the following:

<?php
$anonymous = new class {
public function __construct() {
echo "Im an anonymous class object!<br>";
}
};
var_dump(get_class($anonymous));
// Im an anonymous class object!
// string(48) "class@anonymousC:\vhosts\myserver\demo.php:2$15"
?>

So, as you can see, the anonymous class is not entirely anonymous. Having said that, the name generated by the engine has no special meaning and cannot be relied on, and will in any case be different each time the script runs. We should therefore proceed as if the anonymous class has no name.

We saw an example earlier of a class (Concert) which implements an interface (EventInterface). Suppose that we want our script to provide exactly the same output as that example, but that this will be a one-off occurrence. We can forget about the interface straight away. Even so, creating a class just for a one-off usage like this is overkill, because we’ll never use the class again. Let’s use an anonymous class instead:

<?php
$concert = new class {
public function publishEvent() {
echo "Coming soon: \"The Custers\" in concert - ";
echo "\"One More Last Stand\"<br>";
echo "Venue: Wembley Stadium, London<br>";
echo "Date: 30-06-2026<br>";
echo "Time: 20:00 (for 2 hours)";
}
};

$concert->publishEvent();

// Coming soon: "The Custers" in concert - "One More Last Stand"
// Venue: Wembley Stadium, London
// Date: 30-06-2026
// Time: 20:00 (for 2 hours)
?>

One of the good things about anonymous classes is that, once we have created an instance of that class, it is removed from memory. In this example, the object pointed to by $concert embodies the functionality defined in the anonymous class and, since we’re not going to use it again, the anonymous class itself is discarded.

We probably won’t need to use anonymous classes very often. Nevertheless - and despite the fact that we have not undertaken a particularly deep dive into the ways in which they can be used here - at some point we’ll probably encounter a situation where using an anonymous class makes more sense than using a named class.

The __destruct() method

As the last topic in this article, we will talk about destructors which, as you may already have guessed, is a method that destroys an object - the opposite, in fact, of a constructor method. An object’s destructor method - assuming there is one - is called when there are no further references to the object in our code, or when our script has finished executing, even if script execution occurs as a result of calling exit().

A PHP destructor is created using the __destruct() method, which does not accept any arguments and does not return a value. Destructors are essentially intended to perform cleanup operations when an object is no longer required. They carry out any outstanding tasks and ensure that any resources used by the object are released. For example:

<?php
class FileHandler {
private $file;

public function __construct(string $filename) {
$this->file = fopen($filename, 'w');
}
public function write($data) {
fwrite($this->file, $data);
}
public function __destruct() {
echo "Closing file . . .<br>";
fclose($this->file);
echo "File closed.";
}
}

$file = new FileHandler('mydata.txt');
$myData = "Chris Wells\nEmail: cwells@technologyuk.net";
$file->write($myData);
?>

We left the subject of destructors until last because, unlike constructors, they are required relatively infrequently. In most situations, you simply don’t need to write a destructor method, because the “garbage collection” undertaken automatically by PHP will usually clean up after objects that are no longer needed.

There are some situations, however, where they might be useful, such as when working with files. A typical situation is illustrated by the example above, in which the __destruct() method ensures that the file handle is properly closed before the FileHandler object $file is destroyed.

You should also consider implementing destructors when creating objects that handle network or database connections, or any other external resource. Such resources are often not dealt with automatically by PHP’s garbage collection. Making sure these resources are released properly will prevent potential problems such as memory leaks.

In most cases, it is not necessary to call a destructor. An object’s __destruct() function, if it exists, will be called automatically immediately before an object is destroyed, regardless of whether that event occurs as a result of the object going out of scope, the termination of a script, or the explicit use of the unset() function to remove an object variable.