TABLE OF CONTENTS (HIDE)

Object-Oriented Programming (OOP) in PHP

(not for the dummies)

I shall assume that you are familiar with Object-oriented Programming (OOP) concepts and terminologies. Otherwise, read "Java OOP" or "C++ OOP".

Getting started by Examples

Let's get started with examples on:

  • Defining a class with private member variables, constructor, destructor, public getters/setters and public methods;
  • Allocating instances instances of the class;
  • Accessing instance's variable/functions.

Example: MyCircle Class

We shall begin with a class called MyCircle, which models a circle with a specific radius, as shown in the following class diagram.

Circle Class
MyCircle.php

The definition of the MyCircle class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php    // MyCircle.php
/**
 * The MyCircle class models a Circle with a specific radius.
 */
class MyCircle {
   // private variable
   private $radius;   // radius of the circle
 
   /**
    * Constructor to allocate an instance and initialize its private variables.
    * @param number $radius: radius of the circle, with default value of 1
    */
   public function __construct($radius = 1) {
      $this->radius = $radius;
         // "$this" refers to the current instance.
      echo 'Constructed an instance of ', __CLASS__, ' with radius=', $this->radius, ".\n";
         // "__CLASS__" is a special magic constant that stores the class name.
   }
 
   /**
    * Destructor - invoked just before the instance is deallocated.
    */
   public function __destruct() {
      echo 'Destructed instance ', $this, ' of ', __CLASS__, ".\n";
   }
 
   /**
    * Getter for radius.
    * @return number: radius of the circle.
    */
   public function getRadius() { return $this->radius; }
         // You cannot omit $this-> (unlike Java).
 
   /**
    * Setter for radius.
    * @param number $radius: radius of the circle.
    */
   public function setRadius($radius) { $this->radius = $radius; }
 
   /**
    * Return a string representation of this instance.
    * @return string: in the form of 'MyCircle[radius=r]'.
    */
   public function __toString() {
      return __CLASS__ . '[radius=' . $this->radius . ']';
   }
 
   /**
    * Return the area of this circle.
    * @return number: area of the circle.
    */
   public function getArea() {
      return $this->radius * $this->radius * pi();
   }
}
MyCircleTest.php

A test driver for the MyCircle class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php  // MyCircleTest.php
require_once 'MyCircle.php';
 
// Allocate an instance of class MyCircle
$c1 = new MyCircle(1.1);  // Test constructor
 
// Try different ways of printing an object
var_dump($c1);
print_r($c1);
var_export($c1);
echo "\n";
 
echo "Radius is: {$c1->getRadius()}\n"; // Test getter
$c1->setRadius(5);           // Test setter
echo "$c1\n";                // Call __toString() implicitly
echo "{$c1->getArea()}\n";   // Test getArea()
 
$c2 = new MyCircle();   // Test constructor with default argument
echo "$c2\n";
 
echo "Done.\n";
// Run the destructor before deallocating the instances at end of file.
?>

You can run the program via the PHP CLI (Command-Line Interface):

$ php MyCircleTest.php

The expected outputs are:

Constructed an instance of MyCircle with radius=1.1.
object(MyCircle)#1 (1) {   // var_dump()
  ["radius":"MyCircle":private]=>
  float(1.1)
}
MyCircle Object   // print_r() (for human-readable output)
(
    [radius:MyCircle:private] => 1.1
)
MyCircle::__set_state(array(  // var_export(), which can be re-imported in a PHP program
   'radius' => 1.1000000000000001,
))
Radius is: 1.1
MyCircle[radius=5]
78.539816339745
Constructed an instance of MyCircle with radius=1.
MyCircle[radius=1]
Done.
Destructed instance MyCircle[radius=1] of MyCircle.
Destructed instance MyCircle[radius=5] of MyCircle.

Example: MyTime Class

The MyTime class models a time instance of hour, minute and second, as shown in the following class diagram.

MyTime class diagram
MyTime.php

The class definition for the MyTime class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?php      // MyTime.php
/**
 * MyTime class models an instance of time.
 */
class MyTime {
   // private properties
   private $hour, $minute, $second;
 
   /**
    * Constructor to allocate a new instance and initialize all private variables.
    * @param int $hour: hour in 0-23
    * @param int $minute: minute in 0-59
    * @param int $second: second in 0-59
    */
   public function __construct($hour = 0, $minute = 0, $second = 0) {
      $this->setTime($hour, $minute, $second);
      echo 'Constructed an instance of ', __CLASS__, ' with value ', $this, ".\n";
         // "__CLASS__" is a special magic constant that stores the class name.
         // "$this" refers to the current instance.
   }
 
   /**
    * Destructor - invoked just before the instance is deallocated. 
    */
   public function __destruct() {
      echo 'Destructed instance ', $this, ' of ', __CLASS__, ".\n";
   }
 
   /**
    * Getter for hour
    * @return int hour
    */
   public function getHour()   { return $this->hour;   }
         // You cannot omit $this-> (unlike Java).
   /**
    * Getter for minute
    * @return int minute
    */
   public function getMinute() { return $this->minute; }
   /**
    * Getter for second
    * @return int second
    */
   public function getSecond() { return $this->second; }
 
   /**
    * Setter for hour
    * @param int $hour: hour in 0-23
    */
   public function setHour($hour) {
      if ($hour < 0 or $hour > 23) {
         throw new InvalidArgumentException("Invalid hour $hour. Hour shall be 0-23.\n");
      }
      $this->hour = $hour;
   }
   /**
    * Setter for minute
    * @param int $minute: minute in 0-59
    */
   public function setMinute($minute) {
      if ($minute < 0 or $minute > 59) {
         throw new InvalidArgumentException("Invalid minute $minute. Minute shall be 0-59.\n");
      }
      $this->minute = $minute;
   }
   /**
    * Setter for second
    * @param int $second: second in 0-59
    */
   public function setSecond($second) {
      if ($second < 0 or $second > 59) {
         throw new InvalidArgumentException("Invalid second $second. Second shall be 0-59.\n");
      }
      $this->second = $second;
   }
 
   /**
    * Set the hour, minute and second.
    * @param int $hour: hour in 0-23
    * @param int $minute: minute in 0-59
    * @param int $second: second in 0-59
    */
   public function setTime($hour = 0, $minute = 0, $second = 0) {
      $this->setHour($hour);
      $this->setMinute($minute);
      $this->setSecond($second);
   }
 
   /**
    * Return a string representation of this instance.
    * @return string: in the form of "HH:MM:SS".
    */
   public function __toString() {
      return sprintf("%02d:%02d:%02d", $this->hour, $this->minute, $this->second);
   }
 
   /**
    * Increment one second.
    * @return MyTime $this to support chain operations.
    */
   public function nextSecond() {
      ++$this->second;
      if ($this->second > 59) {
         $this->second = 0;
         ++$this->minute;
      }
      if ($this->minute > 59) {
         $this->minute = 0;
         ++$this->hour;
      }
      if ($this->hour > 23) {
         $this->hour = 0;
      }
      return $this;
   }
}
?>
MyTimeTest.php

A test driver for the MyTime class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php  // MyTimeTest.php
require_once 'MyTime.php';
 
// Allocate an instance of class MyTime
$t1 = new MyTime(23, 59, 59);  // Call constructor
// Try different ways of printing an object
var_dump($t1);
print_r($t1);
var_export($t1);
echo "\n";
 
echo "Hour is: {$t1->getHour()}\n"; // Call getter
$t1->setHour(12);          // Call setter
echo "$t1\n";              // Call __toString() implicitly
 
// Increment 3 second and invoke __toString()
echo $t1->nextSecond()->nextSecond()->nextSecond(), "\n";
 
// Allocate more instances
$t2 = new MyTime(12, 34);  // default second
echo "$t2\n";
 
$t3 = new MyTime;  // default hour, minute and second
echo "$t3\n";
 
// Exception handling with try-catch
try {
   $t4 = new MyTime(25);
   echo "This statement would not run, if an exception was thrown.";
} catch (InvalidArgumentException $e) {
   echo 'Caught InvalidArgumentException: ', $e->getMessage(), "\n";
}
 
echo "Done.\n";
// Run the destructor before deallocating the instances at end of file.
?>

The output is:

Constructed an instance of MyTime with value 23:59:59.
object(MyTime)#1 (3) {   //var_dump()
  ["hour":"MyTime":private]=>
  int(23)
  ["minute":"MyTime":private]=>
  int(59)
  ["second":"MyTime":private]=>
  int(59)
}
MyTime Object  //print_r()
(
    [hour:MyTime:private] => 23
    [minute:MyTime:private] => 59
    [second:MyTime:private] => 59
)
MyTime::__set_state(array(  //var_export() - in a format can be used imported
   'hour' => 23,
   'minute' => 59,
   'second' => 59,
))
Hour is: 23
12:59:59
13:00:02
Constructed an instance of MyTime with value 12:34:00.
12:34:00
Constructed an instance of MyTime with value 00:00:00.
00:00:00
Caught InvalidArgumentException: Invalid hour 25. Hour shall be 0-23.
Done.
Destructed instance 00:00:00 of MyTime.
Destructed instance 12:34:00 of MyTime.
Destructed instance 13:00:02 of MyTime.

How OOP works in OOP

Defining a Class

A class consists of member variables and functions. To define a class, use keyword class, as follows:

class {
   memberVariables;
   memberFunctions;
}
Constructor __construct()

Constructor is a special function called __construct(). Constructor is used to construct an instance and initialize the instance variables. To construct an instance of a class, you need to invoke the constructor via the new operator (as illustrated in the test driver). Constructor does not return anything, hence, there shall not be a return statement inside a constructor.

public function __construct( arguments ) {
   body;
}

PHP does NOT support function overloading (unlike C++ and Java). In other words, there is only one version of the constructor. However, in PHP, you can provide default value to function parameters, including constructors. In this way, the constructor can be invoked with different set of arguments. For example,

// Constructor of MyTime class
public function __construct($hour = 0, $minute = 0, $second = 0) { ...... }

// Invoke the constructor
$t1 = new MyTime(23, 59, 59);
$t2 = new MyTime(23, 59); // default second
$t3 = new MyTime(23);     // default minute and second
$t3 = new MyTime;         // default hour, minute and second

PHP does not provide a default no-arg (no-argument) constructor (unlike Java). You need to explicitly define the constructor, if required.

Destructor __destruct()

Similarly, you can define a destructor called __destruct() for a class. Destructor shall not carry any parameter. It will be run before the instance is destructed (or deallocated). Destructors are not commonly-used.

From the outputs of the above examples, it can be seen that the destructor is called on all objects when end-of-file is reached, before the program exits. To explicitly trigger the destructor, you can invoke unset() on an object. For example,

$t1 = new MyTime;
unset($t1); // Invoke destructor
Allocating an Instance - the new Operator

To allocate an instance of a class, use the new operator to invoke a constructor, in the form of new ClassName(parameters).

// Examples
$t1 = new MyTime(23, 59, 59);
$t2 = new MyTime;  // using default value for arguments
Accessing Members - the Member-of Operator (->)

To access member variables/functions, use member-of operator (->), in the form of $instanceName->memberName. For example,

$t1 = new MyTime(23, 59, 59);
echo $t1->getHour();  // Invoke getter
$t1->setHour(12);     // Invoke setter
$this

$this refers to the current instance. To reference a member variable/function WITHIN the class definition, you MUST precede with $this, e.g., $this->hour, $this->getHour(). Take note that you cannot omit $this (unlike Java).

Access Control Modifiers - public, private and protected

We can use access control modifier to restrict access to a member variable/function. PHP supports these access control modifiers for member variables and functions:

  • public: Accessible by all. This is the default.
  • private: Accessible within the class only.
  • protected: Accessible by subclasses.
Getters and Setters

Getters are setters are used to access and mutate private member variables, as shown in the above examples.

It is a common PHP practice to use the same function for getter cum setter, differentiated by its argument. For example,

// Getter cum Setter
public function hour ($hour = NULL) {
   if ($hour !== NULL) {
      $this->hour = $hour;
   }
   return $this->hour;
}

// Test
echo $t1->hour(), "\n";     // Getter
echo $t1->hour(18), "\n";   // Setter
__toString()

__toString() is one of the so-called magic methods, which shall return a string representation of the current instance. It is implicitly invoked when the object is to be treated as a string. For example,

$t1 = new MyTime(12, 34, 56);
echo $t1;  // Invoke __toString() implicitly.

Take note that if __toString() is not defined, "echo $t1" will cause a runtime error.

__toString() is similar to toString() in Java. However, unlike Java classes which always inherit a toString() from the root superclass java.lang.Object, __toString() in PHP must be explicitly defined for each class.

__CLASS__

__CLASS__ is one of the so-called magic constants in PHP, which keeps the class name in string.

DocBlock (or PhpDoc) Comment

Comments enclosed with /** .... */ are known as "DocBlock" or "PhpDoc", which can be extracted to produce documentation (like JavaDoc). You can use markups like params and return to target the descriptions.

Exception Handling and try-catch-finally

The statements in the try block are executed in sequence. If one of the statements throws an exception, the rest of the statements will not be executed. Instead, execution switches to the catch block. If all the statements in try block do not throw any exception, then try block completes, and catch block is not executed. Take note that execution always resumes after the try-catch block, regardless of whether there is any exception.

The try-catch construct supports graceful handling of errors/exception. Without try-catch, the program will be abruptly terminated when an exception is encountered. The try-catch construct also segregates the main logic and the exception handling logic, for better software engineering.

More OOP

static

A static member (variable or function) belongs to the class, not to the instances. In other words, all the instances share the same copy of a static member.

For example, let rewrite our MyCircle class to include a static variable $count to maintain the count of circles created, as shown in the following class diagram. Note that static members are underlined in the UML notation.

MyCircleWithCount.png
MyCircleWithCount.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php    // MyCircleWithCount.php
/**
 * The MyCircleWithCount class models a Circle with a specific radius.
 * It also maintains the number of circle instances created, via a static variable.
 */
class MyCircleWithCount {
   // static variable
   private static $count = 0;  // count of instances
 
   // private variable
   private $radius;   // radius of the circle
 
   /**
    * Constructor to allocate an instance and initialize its private variables.
    * @param number $radius: radius of the circle, with default value of 1
    */
   public function __construct($radius = 1) {
      $this->radius = $radius;
      echo 'Constructed a ', __CLASS__, '. Number of circles is: ', ++self::$count, ".\n";
            // You MUST use :: (scope resolution operator) instead of -> (member-of operator) to
            //   access static members.
            // Within class definition, SELF refers to this class.
            // Pre-increment $count before echo.
   }
 
   /**
    * Destructor - invoked just before the instance is deallocated.
    */
   public function __destruct() {
      echo 'Destructed a ', __CLASS__, '. Number of circles is: ', --self::$count, ".\n";
   }
 
   public function getRadius() { return $this->radius; }
   public function setRadius($radius) { $this->radius = $radius; }
   public function __toString() {
     return __CLASS__ . '[radius=' . $this->radius . ']';
   }
   public function getArea() {
      return $this->radius * $this->radius * pi();
   }
}
?>
MyCircleWithCountTest.php
1
2
3
4
5
6
7
8
9
10
11
<?php    // MyCircleWithCountTest.php
require_once 'MyCircleWithCount.php';
 
$c1 = new MyCircleWithCount(1.1);
$c2 = new MyCircleWithCount(2.2);
unset($c2);   // deallocate - run the destructor
$c3 = new MyCircleWithCount(3.3);
echo 'Number of circles is: ', MyCircleWithCount::$count, "\n.";
     // You can access public static variables via ClassName::memberName
echo "Done.\n";
?>

The expected outputs are:

Constructed a MyCircleWithCount. Number of circles is: 1.
Constructed a MyCircleWithCount. Number of circles is: 2.
Destructed a MyCircleWithCount. Number of circles is: 1.
Constructed a MyCircleWithCount. Number of circles is: 2.
Number of circles is: 2
Done.
Destructed a MyCircleWithCount. Number of circles is: 1.
Destructed a MyCircleWithCount. Number of circles is: 0.

Can you do the count without static?

Constants - const

A constant member variable is identified by keyword const. Constants are always public and static, their content cannot be modified. Unlike normal variables, constants do not begin with a leading $ sign. Within the class definition, constants can be referenced via self::constantName, just like any static member. Outside the class definition, they are referenced via className::constantName. By convention, use uppercase for constant names, e.g., ERROR_CODE, ERROR_MESSAGE.

Magic Methods and Constants

Magic methods are special methods, beginning with double underscore, that can be defined in any class and are executed via the built-in PHP functionality. For examples,

  • __construct(): constructor.
  • __destruct(): destructor.
  • __toString(): returns a string representation of this object. Called implicitly if a string is expected.
  • __clone(): for cloning a copy.
  • __sleep() and __wakeup(): for serialization.
  • __unset(), __isset(), __get() and __set(): Called when trying to access a member variable which is not available. __unset() is triggered by unset(); __isset() is triggered by isset().
  • __call() and __callStatic(): Called when trying to call a function (or static function) which is not available.
  • __set_state(): via var_export().
  • __invoke():

[TODO] Examples

Magic constants (public and static), named in uppercase and beginning and ending with double underscore (without a leading $), contains special read-only properties. For examples,

  • __CLASS__: the current classname in string.
  • __FILE__: the current filename.
  • __LINE__: the line number, used in conjunction with __FILE__.
  • __DIR__: absolute directory, same as dir(__FILE__).
  • __FUNCTION__ and __METHOD__: the function or method name.
  • __NAMESPACE__: the current namespace in string.

[TODO] Examples

self::, parent:: and static::

self::, parent:: and static:: allow you to access static members and const (public and static) WITHIN the class definition. self:: refer to the current class, as illustrated in the above example. parent:: refer to the superclass and static:: refer to the current class but with late binding - which will be discussed later in inheritance.

Recall that outside the class definition, you could access public static member and const via ClassName::memberName.

self vs. $this

Use $this to refer to the current object. Use self to refer to the current class. In other words, within a class definition, use $this->member to reference non-static members, use self::$member for static members.

Type Hinting for Function Parameters

In function definition, you can declare the TYPE of the parameters, such as array, a class or interface name, or callable. However, type hinting are NOT applicable for scalar types, such as int or string. For example,

function fun (array $a, Circle $b, $c) { ...... }
   // $a is an array; $b is an object of class Circle; $c unknown

Inheritance

Example: MyCircle and MyCylinder classes

Let's illustrate superclass-subclass inheritance with the following example of MyCircle and MyCylinder classes.

Inheritance
MyCircle.php

Same as the previous example.

MyCylinder.php

The MyCylinder class inherits from the MyCircle class, with more member variables/functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php   // MyCylinder.php
require_once 'MyCircle.php';
/**
 * The MyCylinder class models a cylinder with base radius and height.
 * It inherits from the MyCircle class.
 */
class MyCylinder extends MyCircle {   // keyword "extends" denotes inheritance
   // private variable
   private $height;  // height of the cylinder
 
   /**
    * Constructor to allocate an instance and initialize its private variables.
    * @param number $radius: radius of the base circle, with default value of 1
    * @param number $height: height of the cylinder, with default value of 1
    */
   public function __construct($radius = 1, $height = 1) {
      parent::__construct($radius);
            // Invoke the MyCircle's constructor to construct the base circle
      $this->height = $height;
      echo 'Constructed an instance of ', __CLASS__, ': ', self::__toString(), "\n.";
   }
 
   // Destructor
   public function __destruct() {
      echo 'Destructed instance of ', self::__toString(), "\n.";
   }
 
   // Getter and Setter
   public function getHeight() { return $this->height; }
   public function setHeight($height) { $this->height = $height; }
 
   // Return a string representation of this instance.
   public function __toString() {
      return __CLASS__ . '[height=' . $this->height . ', ' . parent::__toString() . ']';
   }
 
   // Public method
   public function getVolume() {
      return $this->getArea() * $this->height;
         // $this->getArea() invoke superclass method
   }
}
?>
MyCylinderTest.php

A test driver for the MyCylinder class.

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php    // MyCylinderTest.php
require_once 'MyCylinder.php';
// Allocate an instance of subclass MyCylinder.
$cy1 = new MyCylinder(5.6, 7.8);
print_r($cy1);
echo "$cy1\n";   // __toString()
echo "Radius is: {$cy1->getRadius()}\n";   // Call superclass function
echo "Base area is: {$cy1->getArea()}\n";  // Call superclass function
echo "Volume is: {$cy1->getVolume()}\n";   // Call subclass function
$cy1->setHeight(9.9);
echo "New height is: {$cy1->getHeight()}\n";
echo "Done.\n"
?>

The expected outputs are:

Constructed an instance of MyCircle with radius=5.6.
Constructed an instance of MyCylinder: MyCylinder[height=7.8, MyCircle[radius=5.6]]
.MyCylinder Object
(
    [height:MyCylinder:private] => 7.8
    [radius:MyCircle:private] => 5.6
)
MyCylinder[height=7.8, MyCircle[radius=5.6]]
Radius is: 5.6
Base area is: 98.520345616576
Volume is: 768.45869580929
New height is: 9.9
Done.
Destructed instance of MyCylinder[height=9.9, MyCircle[radius=5.6]]

How Inheritance works

extends

You can define a subclass inheriting a superclass via keyword extends.

self and parent

Within the subclass definition, you can use the keyword parent in the form of parent::memberName to refer to a member of the superclass; and self in the form of self::memberName to refer to a member of the subclass. The :: is known as scope resolution operator.

Method Overriding

The subclass can override the inherited method from the superclass by providing its own definition. For example, the MyCylinder class can override the getArea() method inherited from superclass MyCircle as follows:

class MyCylinder extends MyCircle {
   ......
   ......
   
   // Override inherited superclass' getArea() to return the surface area
   public function getArea() {
      return 2 * pi() * parent::getRadius() * $this->height;  
         // Cannot use parent::$radius as it is private, not public nor protected
   }
   
   // Need to rewrite to invoke superclass' getArea()
   public function getVolume() {
      return parent::getArea() * $this->height;
   }
}
 
$c1 = new MyCircle(1.0);
echo "Area is: {$c1->getArea()}\n";
 
$cy1 = new MyCylinder(2.0);
echo "Surface area is: {$cy1->getArea()}\n";
final

A final variable/method cannot be overridden in the subclass.

Polymorphism

abstract method and abstract class

An abstract method is a method with only the signature, without implementation. A class containing one or more abstract methods is an abstract class. An abstract class cannot be instantiated, as it's definition is incomplete. To use an abstract class, derive a subclass and override the abstract methods. The subclass is now complete and can be instantiated.

For example,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php    // TestAbstractClass.php
// The MyShape class contains an abstract method called getArea();
// Hence, the MyShape class must be declared abstract
abstract class MyShape {
   abstract public function getArea();
      // An abstract method only have signature, without implementation body
}
 
// The MyRectangle class extends MyShape, and overrides the getArea()
//  to provide its implementation.
// The MyRectangle class is concrete, and can be instantiated.
class MyRectangle extends MyShape {
   private $width, $length;
 
   // Constructor
   public function __construct($width, $length) {
      $this->width = $width;
      $this->length = $length;
   }
 
   // Override
   public function getArea() { return $this->width * $this->length; }
}
 
// Test
$r = new MyRectangle(3, 4);         // Can create instance of concrete class
echo "Area is: {$r->getArea()}\n";
$a = new MyShape();  // error:  Cannot instantiate abstract class MyShape

Interface

An interface is a pure abstract class, which contains only abstract methods. An interface is defined using keyword interface (instead of class). To derive a subclass from an interface, use keyword implements (instead of extends).

For examples,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php    // TestInterface.php
// Define an interface called MyShape which contains only abstract methods.
interface MyShape {
   public function getArea();
   public function getPerimeter();
         // No need to include the abstract keyword
}
 
// The MyRectangle class implements MyShape, and overrides all the abstract methods
class MyRectangle implements MyShape {
   private $width, $length;
 
   // Constructor
   public function __construct($width, $length) {
      $this->width = $width;
      $this->length = $length;
   }
 
   // Override
   public function getArea() { return $this->width * $this->length; }
   public function getPerimeter() { return 2 * ($this->width + $this->length); }
}
 
// Test
$r = new MyRectangle(3, 4);         // Can create instance of concrete class
echo "Area is: {$r->getArea()}\n";
echo "Perimeter is: {$r->getPerimeter()}\n";
 
$s = new MyShape();  // error:  Cannot instantiate interface MyShape

Take note that PHP's implementations of abstract class and interface are exactly the same as Java.

trait

See:

  1. "Using Traits in PHP 5.4" @ http://www.sitepoint.com/using-traits-in-php-5-4.
  2. PHP Manual "Traits" @ http://php.net/manual/en/language.oop5.traits.php.

More OOP

Cloning Object

PHP provides a keyword clone to copy an object, which performs a shadow-copy. If __clone() magic method is defined in the class, it will be run after the shadow-copy.

__autoload()

[TODO]

Namespace

Can you have two versions of Circle class in your program? Yes, by placing them into different namespaces.

A namespace (called package in Java) groups the related classes, interfaces, functions and constants. It tries to:

  • resolve naming conflict;
  • provide aliases for long names.

These keywords are used in namespace management:

  • namespace ...
  • use ... [as ...]
Defining a Namespace

To define a namespace, write "namespace identifier" as the FIRST statement in your PHP file, which sets the "current" namespace. For example,

<?php
namespace MyProject;   // FIRST statement
      // Set the current namespace.

// A namespace can contain classes, interfaces, constants, and functions.
class MyClass { .... }
const MY_CONSTANT = 2.17;
function myFunction() { ..... } 
?>

You can define more than one namespaces in one file. But don't do it for best practice.

Global Space

Any entity defined under no namespace belongs to global space.

Sub-namespace

PHP supports so-called sub-namespace, with names separated by back-slash, e.g., "namespace MyProject\ZBox\ModSec". Take note that there is no leading back-slash.

It is a best practice to keep your source in directory that matches the namespace. For example, namespace MyProject\ZBox\ModSec shall be kept in Path/To/MyProject/ZBox/ModSec.

Again, take note that PHP uses back-slash in namespace.

Referencing Entities in Namespace

To reference an entity in a namespace, you could:

  1. Use the fully-qualified name: e.g., $a = new \MyProject\ZBox\ModSec\Circle. Take note that it begins with a back-slash.
  2. Use the unqualified name: e.g., $a = Circle. This will be resolved to CurrentNamespace\Circle.
  3. Use a partially-qualified name: e.g., $a = ModSec\Circle (without a leading back-slash). This will be resolved to CurrentNamespace\ModSec\Circle. Partially-qualified name is messy, and should be avoided.

To access an entity in another namespace outside the current namespace, use the fully-qualified name.

To access an entity in global space outside the current namespace, prefix the entity with a leading backslash. For example, \strlen('hello').

[TODO] Example

Magic Constant __NAMESPACE__

The magic constant __NAMESPACE__ contains the name of the current namespace (in string). In global space, __NAMESPACE__ contains an empty string.

Importing with "use ..."

You can invoke the use operator to import a class, an interface or a namespace. Once an entity is imported, you can access it using its entity name, instead of the fully-qualified name. (This is similar to the import statement in Java; or using in C#.) PHP 5.6 supports the import of functions and constants as well. For example,

namespace MyProject\ZBox;   // Set the current namespace

use JoeProject\YBox\Circle;
   // Now you can simply refer to it as Circle
   // Take note that there is no leading back-slash

use AnotherNamespace\Subnamespace\SubSubSpace;
   // Now you can simply refer to it as SubSubSpace
   
use strlen;   // A function in the global space
   // Now you can refer to it as strlen, instead of fully-qualified name \strlen
Aliasing with "use ... as ..."

Furthermore, you can define an alias in the form of "use FullyQualifiedName as alias". For example,

namespace MyProject\ZBox;

use JoeProject\ABox\CircleWithASuperLongName as Circle;
   // Now you can refer to it as Circle
Functions/Constants fall back to Global Space

An unqualified class name is resolved to the current namespace. To use a class outside the current namespace, you need to use the fully-qualified name.

An unqualified function name and constant is similarly resolved to the current namespace. However, if it cannot be resolved, function/constant will fall back to the global space.

REFERENCES & RESOURCES

  1. PHP mother site @ http://www.php.net; "PHP Manual" @ http://php.net/manual/en/index.php; "PHP Language Reference" @ http://www.php.net/manual/en/langref.php.