Blog/ Object Oriented PHP

By neerav.mehta Wed, 08/19/2015 - 03:53 2 Comments

I am sure that by now you must have heard that Drupal 8 is using Symfony components and is based on object-oriented programming in PHP. If you are a Drupal 7 developer, then you may not know what is object-oriented programming or fail to understand the benefits it offers. In this post, you will learn the basics of object-oriented PHP programming so that you can start developing for Drupal 8.

What is Object Oriented Programming?

The central concept in the undestanding of Object Oriented Programming is the concept of Object. An Object is a group of data (called properties) and functions (called methods) that go together. Let's consider a real-life example. A vehicle is an object. It can be defined to have three properties: color, miles driven and warranty (number of miles). It can be defined to have one method: getWarrantyLeft(). This method returns the number of miles of warranty left on the vehicle. Let's create a template for such a Vehicle object. Open a file Vehicle.php and write the following:

<?php

/**
 * Vehicle class.
 */
class Vehicle {

  // Color of the vehicle.
  var $color = 'red'

  // Miles driven.
  var $milesDriven = 0;

  // Warranty available.
  var $warranty = 100000;

  /**
   * Returns miles of warranty left on the vehicle.
   *
   * @return int
   *   Miles of warrnty left on the vehicle.
   */
  function getWarrantyLeft() {
    if ($this->warranty - $this->milesDriven > 0) {
      return ($this->warranty - $this->milesDriven);
    }

    return 0;
  }
}

?>

Ignore the declaration $this in the code above for now. We'll explain it later. What we have defined above is a template for a vehicle. Such a template is called Class. It's not an object. The difference between Class and Object is similar to the difference between a person and you. Person is a common noun and there are billions of people roaming around in the world. On the other hand, there is only one of you. You have all the features of a person, but you are unique from every other person out there. If God were real and were to create another person, he'll use the same Person template and create one more. That new person will still be different from you. In the same way, there will be multiple objects created from a class template. Your car and my truck, both are vehicles but they are different from each other. To create (or instantiate) an object from a class, you use new operator.

$yourCar = new Vehicle();
$myTruck = new Vehicle();

If you compare the variables $yourCar and $myTruck, you will get FALSE. Notice the operator ===. If you use == operator, you will see TRUE since == checks whether both the objects belong to the same class (which they do), but === checks whether the objects are identical or not.

// This will return FALSE.
echo ($yourCar === $myTruck);

Now let's check what the method getWarrantyLeft() returns. When you are writing procedural code, you can write the following code at the end of Vehicle.php and it will work:

echo getWarrantyLeft();

But if you add the above code at the end of Vehicle.php and execute the file, you will get an error saying that getWarrantyLeft() is not defined. But you have defined this method then why is PHP complaining? The reason is that you have defined it inside the class Vehicle. For it to run, it needs to know which object it needs to run on because the number of miles of warranty left on my truck can be different from the number of miles of warranty left on your car. To get the number for each vehicle, we need to invoke it with the following syntax:

echo 'Warranty left on your car: ' . $yourCar->getWarrantyLeft() . "\n";
echo 'Warranty left on my truck: ' . $myTruck->getWarrantyLeft() . "\n";

Copy the above code and paste it at the end of Vehicle.php file. At this point, the file Vehicle.php looks as follows:

<?php

/**
 * Vehicle class.
 */
class Vehicle {

  // Color of the vehicle.
  var $color = 'red'

  // Miles driven.
  var $milesDriven = 0;

  // Warranty available.
  var $warranty = 100000;

  /**
   * Returns miles of warranty left on the vehicle.
   *
   * @return int
   *   Miles of warrnty left on the vehicle.
   */
  function getWarrantyLeft() {
    if ($this->warranty - $this->milesDriven > 0) {
      return ($this->warranty - $this->milesDriven);
    }

    return 0;
  }
}

$yourCar = new Vehicle();
$myTruck = new Vehicle();

echo 'Warranty left on your car: ' . $yourCar->getWarrantyLeft() . "\n";
echo 'Warranty left on my truck: ' . $myTruck->getWarrantyLeft() . "\n";

?>

Now run this file using the command: php Vehicle.php

You will see the output:

$ php Vehicle.php
Warranty left on your car: 100000
Warranty left on my truck: 100000

You are seeing the value 100000 because we have defined $warranty to be 100000 in the declaration on line 15 of Vehicle.php and $milesDriven to be 0 on line 12. As a result, 100000 miles of warranty is still left on both the vehicles.

Constructor

In the above example, what if my truck has manufacturer's warranty of 200000? How will I tell PHP about it? That's where the constructor comes into play. It's a function which gets executed when you create a new object from a class using the new operator. Constructor can take any number of arguments and that's where you define the initial properties of your object. Let's modify the Vehicle class to add a constructor.

/**
 * Class Vehicle.
 */
class Vehicle {

  // Color of the vehicle.
  var $color = 'red'

  // Miles driven.
  var $milesDriven = 0;

  // Warranty available.
  var $warranty;

  /**
   * Default constructor.
   *
   * @param int $warrantyProvided
   *   Number of miles of warranty provided with the vehicle.
   */
  function __construct($warrantyProvided = 100000) {
    $this->warranty = $warrantyProvided;
  }

  /**
   * Returns miles of warranty left on the vehicle.
   *
   * @return int
   *   Miles of warrnty left on the vehicle.
   */
  function getWarrantyLeft() {
    if ($this->warranty - $this->milesDriven > 0) {
      return ($this->warranty - $this->milesDriven);
    }

    return 0;
  }
}

You might have noticed that I used a variable $this->warranry, although I never defined $this anywhere. The $this pseudo-variable is automatically defined by PHP inside a method in a class and it refers to the object being called. If you want to refer to any object property from within a method, you will need to refer to it as $this->{property_name}. If you just write ${property_name}, PHP will assume it's a new variable that's distinct from the property of the class. That's the reason we are using $this->warranty and $this->milesDriven even in getWarrantyLeft() function. If you had used $warranty and $milesDriven within getWarrantyLeft() function, then their values would not have been defined and the function would have returned 0. Similarly to call any method of the class from within the object, you will need to call $this->{method_name}(). This concept may take a while to understand so read this paragraph again. If you still don't understand it, please leave a comment below and I'll try to reword it to make it simpler to understand.

You might have also noticed that since I am setting the value of $this->warranty variable inside the constructor on line 22, I have removed its initial value from line 13. This is perfectly acceptable. Before you can call any method, such as getWarrantyLeft(), you need to initialize the object using the new operator. Initializing the object will automatically call the constructor. The constructor sets the value of $this->warranty variable so any method called on this object will have the value of the variable defined.

Since $this->warranty and $warranty are different variables inside the method of a class, we can rename $warrantyProvided variable to $warranty and the code will run perfectly fine.

/**
 * Class Vehicle.
 */
class Vehicle {

  // Color of the vehicle.
  var $color = 'red'

  // Miles driven.
  var $milesDriven = 0;

  // Warranty available.
  var $warranty;

  /**
   * Default constructor.
   *
   * @param int $warrantyProvided
   *   Number of miles of warranty provided with the vehicle.
   */
  function __construct($warranty = 100000) {
    $this->warranty = $warranty;
  }

  /**
   * Returns miles of warranty left on the vehicle.
   *
   * @return int
   *   Miles of warrnty left on the vehicle.
   */
  function getWarrantyLeft() {
    if ($this->warranty - $this->milesDriven > 0) {
      return ($this->warranty - $this->milesDriven);
    }

    return 0;
  }
}

What we are doing in the __construct() method is that we are initializing the object's $warranty variable to the one provided when initializing the object. As an example, if I were to initialize my truck as:

$myTruck = new Vehicle(200000);

then $warranty property inside $myTruck object will be set to 200000. After making the above change, if you execute Vehicle.php file, then you will see the output:

$ php Vehicle.php
Warranty left on your car: 100000
Warranty left on my truck: 200000

You can see that the warranty left on my truck is 200000 miles while that left on your car is 100000 miles. This data is stored in the object itself so in future, if you do any calculation based on warranty left on the vehicle, my truck will provide a different answer than your car. The constructor can take multiple arguments as inputs so we can intialize the Vehicle with different color and miles driven as well. Here is the completed code with those changes:

<?php

/**
 * Vehicle class.
 */
class Vehicle {

  // Color of the vehicle.
  var $color;

  // Miles driven.
  var $milesDriven;

  // Warranty available.
  var $warranty;

  /**
   * Default constructor.
   *
   * @param string $color
   *  Color of the vehicle.
   * @param int $milesDriven
   *  Number of miles driven already.
   * @param int $warrantyProvided
   *   Number of miles of warranty provided with the vehicle.
   */
  function __construct($color, $milesDriven = 0, $warranty = 100000) {
    $this->color = $color;
    $this->milesDriven = $milesDriven;
    $this->warranty = $warranty;
  }

  /**
   * Returns miles of warranty left on the vehicle.
   *
   * @return int
   *   Miles of warrnty left on the vehicle.
   */
  function getWarrantyLeft() {
    if ($this->warranty - $this->milesDriven > 0) {
      return ($this->warranty - $this->milesDriven);
    }

    return 0;
  }
}

$yourCar = new Vehicle('white');
$myTruck = new Vehicle('red', 25000, 200000);

echo 'Warranty left on your car: ' . $yourCar->getWarrantyLeft() . "\n";
echo 'Warranty left on my truck: ' . $myTruck->getWarrantyLeft() . "\n";

?>

We are defining your car to be white in color. Since we are not providing the miles driven by your car or the warranty, it will take the default values of 0 and 100000 respectively. On the other hand, my truck is defined to be red in color with 200000 miles of warranty out of which I have already driven 25000 miles. As a result, warranty left on your car should be full 100000 miles while that left on my truck should be 175000 miles. That's what we get when we execute the file above using the command: php Vehicle.php

$ php Vehicle.php
Warranty left on your car: 100000
Warranty left on my truck: 175000

Congratulations!! If you understood this tutorial, then you understand the basics of Object Oriented Programming, not just in PHP but in any language including C++, Java, C#, etc. You now know what are classes and objects and you understand the difference between them. You know how to define a class and instantiate objects of that class. You are knowledgeable about creating a constructor function within a class so that multiple objects can be initialized from the same class using different initial values of its properties.

In the next post, you'll learn about inheritance in PHP using which you can nest classes one below the other. This technique is very handy for code re-use as well making your code more secure.

By Manu Adam (not verified) Thursday, November 12, 2015 - 20:28 Permalink

Hello Neerav, Thanks a lot for taking your precious time to clear OOP concept for the beginners in the best and easiest to understand way. I am much obliged to you for clearing my confusion as I could never grasp the crux of it. Hope you will carry it on further and provide some more advanced tutorials too for the beginners. Best wishes.

Ready to get Started?