What is a PHP Class?
May 14, 2020

What Is a PHP Class?

PHP Development

Learning About a PHP Class

As somebody new to programming, particularly in PHP, you may have heard the terms "classes" or "objects" tossed around at times. What is a PHP class?

First, we'll assume you know what a PHP function is; if you do not, go read our post "What is a PHP Function?".

PHP functions are the first form of reusability you will generally encounter in PHP. They allow you to encapsulate logic that you can then repeat over and over again. You can pass a function arguments that it can then act on or consume in order to perform its logic.

A PHP class, and more generally, object-oriented programming, provides additional approaches to reusability, and can be used for a variety of purposes:

  • They can describe entities that have known properties and behaviors.
  • They can be used as messages to functions and other objects.
  • They can be used to group related behaviors or state.
  • They provide a way to describe hierarchies and inheritance within an application.

A class is a blueprint, defining a mix of properties and behaviors, that acts as a template for what are called objects, or instances of the class.

Though they may sound daunting, at their most basic, syntactically they are only a form of declaration:

class WheeledVehicle
{
}

We can create an instance of a wheeled vehicle using the new keyword:

$car = new WheeledVehicle();

By itself, an empty class like this only gives us one thing: a type. You already know a number of built-in types: nulls, booleans, strings, integers, floats, and arrays. In PHP 7, we can typehint against any of these. As an example, if we want to only allow a string as an argument to a function, and only allow returning a string from it, we can write that signature as follows:

function transform(string $message): string

Classes let us define additional types for the PHP language. Returning to our example, $car is now an instance of WheeledVehicle. If we wanted our transform() function to only allow WheeledVehicle instances, we could write it as follows:

function transform(WheeledVehicle $message): string

If we tried to pass anything other than an instance of WheeledVehicle, the code would fail!

PHP Properties

There are many kinds of wheeled vehicles: unicycles, bicycles, motorcycles, tricycles, wagons, carts, automobiles, trucks, vans, lorries, and more. These are distinguished from one another by a variety of properties and behaviors. Let's talk about properties first.

A class property is part of object state and is an element that can be re-used and referred to within the class, and, depending on how it is defined, by consumers of class instances. A property might be:

  • Something that differentiates this instance from another, such as number of wheels, whether or not it uses fuel, whether or not it is self-propelled, or its size or weight.
  • Something that aids in its behavior, such as an internal combustion engine, the crank ratio, etc.

Let's expand our WheeledVehicle class to add some properties:

class WheeledVehicle
{
    int $wheelCount;
    bool $usesFuel;
    bool $selfPropelled;
    float $weight;
}

Here we have defined the number of wheels ($wheelCount), whether or not it uses fuel ($usesFuel), whether or not it is self-propelled ($selfPropelled), and its weight ($weight). With each, I have defined a valid type, so that we always know what we'll receive from the property, as well as what values will be accepted. 

Property type hints were only added in the PHP 7.4 release. Previous versions did not allow them. Instead, you used the var keyword (currently deprecated), or a visibility operator. To keep the examples simpler, we are demonstrating declarations with type only.

From here, we can define our $car instance a bit better:

$car->wheelCount    = 4;
$car->usesFuel      = true;
$car->selfPropelled = true;
$car->weight        = 2200;

What is that -> notation? And why are the properties not referred to using a $?

The syntax used to refer to instance properties is to use a -> operator between the instance variable ($car) and the name of the property. We can assign a value using the = assignment operator or retrieve the value by simply referring to it (e.g., $car->weight).

Properties can also be declared static, meaning the value is the same between instances, or given visibility, which defines where and when they may be accessed. Those concepts are beyond the scope of this article, however.

PHP Methods

It's nice that our WheeledVehicle now has properties, but what about behavior?

We can provide behavior by defining methods on the class. A method is a function scoped to the class instance. That means it can access the property values associated with the instance of the class; in our example above, $car is an instance of the class WheeledVehicle, and the values we have assigned can be accessed within methods it defines.

Just like normal functions, methods can accept arguments, and will have an associated return value. Generally speaking, you will want to write these such that they do not have side effects; that means they usually should not change state in the instance, or produce output (e.g., calling echo or header()).

The primary difference with a function is that it has access to the instance properties, which means it can use them either in its behavior, or to produce behavior (if the properties are themselves other object instances or functions).

Writing a PHP Method

Let's write a method that will compare the number of wheels in the instance against another instance; the idea will be that we could use the method to help sort WheeledVehicle instances by the number of wheels they have. Most sorting functions will return -1 if the left item is less than the right, 0 if they are equal, and 1 if the left item is greater than the right; as of PHP 7, there is a special operator for such comparisons, the spaceship operator, <=>. For our purposes, we'll consider the current instance the "left" side of an operation.

Inside our class, we will define the method:

class WheeledVehicle
{
    int $wheelCount;
    bool $usesFuel;
    bool $selfPropelled;
    float $weight;

    public function compareWheelCount(WheeledVehicle $comparison): int
    {
        return $this->wheelCount <=> $comparison->wheelCount;
    }
}

What is $this? In PHP syntax, $this is the current instance of the class, and is used internally to refer to instance properties, as well as to call its own methods. Here, we are comparing the $wheelCount of the current instance to that of another instance provided to us. $this->wheelCount will give us the value assigned to the current instance. For our $car definition earlier, that value will be 4.

The public keyword is used to define visibility of the method, which is essentially who can call it, and where it may be called. Methods have public visibility by default, but it is best to define the visibility explicitly. We will cover visibility in a later article, as it's a more advanced topic.

If we had several WheeledVehicle instances, we could sort an array of them as follows:

usort($wheeledVehicles, fn($a, $b) => $a->compareWheelCount($b));
Starting in PHP 7.4, you can define "arrow functions", which are a compact way of defining closures that return the result of exactly one expression. The above could also be written:
usort($wheeledVehicles, function ($a, $b) {
    return $a->compareWheelCount($b)
});

Methods can have as much or as little logic as you need in them in order to create the behavior required. The important thing to remember is that they are just PHP functions that have access to the current instance, which means they can access properties and call their own methods.

PHP Inheritance

It would be nice not to have to set all the properties of a car. What if we could define those and still have the concept of a "wheeled vehicle"?

Classes allow this through class extension. We are going to create a class Car that extends WheeledVehicle, and defines some of those properties up front:

class Car extends WheeledVehicle
{
    int $wheelCount     = 4;
    bool $usesFuel      = true;
    bool $selfPropelled = true;
}

Now we can define our car like this:

$sedan = new Car();
$sedan->weight = 2200;

If we were to access the other properties, we'd find they have sane defaults set:

if ($sedan->usesFuel && $sedan->selfPropelled) {
    printf("Vehicle has %d wheels.\n", $sedan->wheelCount); // "Vehicle has 4 wheels"
}

The extends keyword tells PHP that the new class we are declaring will inherit from the class specified. This means that, unless we override them, the new class will have the same properties and behaviors.

In our Car class, we overrode the definitions for $wheelCount, $usesFuel, and $selfPropelled in order to provide default values; this meant we only needed to provide a weight after we created the instance. We can also still call the compareWheelCount() method we defined in WheeledVehicle!

Something particularly interesting is that because Car extends WheeledVehicle, it fulfills the same typehint; you can pass your $car to a function or method that requires a WheeledVehicle! Essentially, inheritance in PHP is an "is-a" relationship: a Car is-a WheeledVehicle.

Conversely, if we typehint on Car, because it is more specific than WheeledVehicle, an instance of WheeledVehicle is not a Car, and would not fulfill the typehint.

$bike  = new WheeledVehicle();
$sedan = new Car();

echo $bike instanceof WheeledVehicle ? 'TRUE' : 'FALSE';  // TRUE
echo $bike instanceof Car ? 'TRUE' : 'FALSE';             // FALSE
echo $sedan instanceof WheeledVehicle ? 'TRUE' : 'FALSE'; // TRUE
echo $sedan instanceof Car  ? 'TRUE' : 'FALSE';           // TRUE

PHP Constructors

It might be nice to have a fully populated instance once we are done calling new. We can do that by defining a constructor.

A constructor is a method called when you create a new instance, and can accept arguments, but must not define a return value. Constructors are typically used to initialize properties based on the arguments provided to them. In PHP, constructors must be named __construct() (__ prefixes indicate reserved "magic" methods used by the language for certain behaviors).

We will now write a constructor for Car that accepts a $weight value.

class Car extends WheeledVehicle
{
    int $wheelCount     = 4;
    bool $usesFuel      = true;
    bool $selfPropelled = true;

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

The constructor accepts a $weight argument, and then assigns it to a property. But we didn't define that property! Why? Because it's defined already in the parent class, WheeledVehicle.

We can now create our sedan a bit more easily:

$sedan = new Car(2200);

Conclusion

Classes are a way to define new types for the language, providing a template for the data and behavior they encapsulate. Additionally, they provide functionality for creating taxonomies of types via inheritance.

Zend by Perforce has a number of resources to help you learn more about object-oriented programming in the PHP language. Check out our training options here.

FREE PHP TRAINING