Core PHP 11 programs with output 15 Q/A 5 MCQs

Learning Objectives – after this chapter you will be able to:

  • Create abstract classes and methods, interfaces and traits, and use final.
  • Use the overloading magic methods __get(), __set(), __isset(), __unset(), __call() and __callStatic().
  • Iterate over objects with foreach, Iterator and IteratorAggregate.
  • Implement the Factory and Singleton design patterns.
  • Use other magic methods such as __toString().
Class Abstraction

PHP has abstract classes and methods. It is not allowed to create an instance of a class that has been defined as abstract. Any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature – they cannot define the implementation.

When inheriting from an abstract class, all methods marked abstract in the parent must be defined by the child, with the same (or a less restricted) visibility. For example, if the abstract method is protected, the implementation must be protected or public, but not private.

File: htdocs/phpbook/ch18/abstract.php
<?php
abstract class AbstractClass
{
    // Force extending classes to define these methods
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // Common method
    public function printOut()
    {
        print $this->getValue() . "<br>";
    }
}
class ConcreteClass1 extends AbstractClass
{
    protected function getValue()
    {
        return "ConcreteClass1";
    }
    public function prefixValue($prefix)
    {
        return "{$prefix}ConcreteClass1";
    }
}
class ConcreteClass2 extends AbstractClass
{
    public function getValue()
    {
        return "ConcreteClass2";
    }
    public function prefixValue($prefix)
    {
        return "{$prefix}ConcreteClass2";
    }
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') . "<br>";
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') . "<br>";
?>
OUTPUT browser · http://localhost/phpbook/ch18/abstract.php
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

Trying new AbstractClass() would produce the fatal error "Cannot instantiate abstract class AbstractClass".

Interfaces

An interface specifies which methods a class must implement, without defining how. All methods in an interface are public and have no body. A class implements an interface with the keyword implements and may implement several interfaces – a way around single inheritance.

File: htdocs/phpbook/ch18/interface.php
<?php
interface Shape
{
    const UNITS = "cm";
    public function area(): float;
    public function name(): string;
}
interface Printable
{
    public function printDetails(): void;
}
class Circle implements Shape, Printable
{
    public function __construct(private float $r) {}
    public function area(): float { return round(M_PI * $this->r ** 2, 2); }
    public function name(): string { return "Circle"; }
    public function printDetails(): void
    {
        echo $this->name() . " area = " . $this->area() . " sq " . self::UNITS . "<br>";
    }
}
class Rectangle implements Shape, Printable
{
    public function __construct(private float $l, private float $w) {}
    public function area(): float { return $this->l * $this->w; }
    public function name(): string { return "Rectangle"; }
    public function printDetails(): void
    {
        echo $this->name() . " area = " . $this->area() . " sq " . Shape::UNITS . "<br>";
    }
}
$shapes = [new Circle(3), new Rectangle(4, 5)];
foreach ($shapes as $s) {
    $s->printDetails();
}
var_dump($shapes[0] instanceof Shape);
?>
OUTPUT browser · http://localhost/phpbook/ch18/interface.php
Circle area = 28.27 sq cm
Rectangle area = 20 sq cm
bool(true)
FeatureAbstract classInterface
Instantiationnot possiblenot possible
Method bodiesmay have normal (concrete) methodsno bodies (only signatures)
Propertiesyesno (constants only)
Inheritancea class extends onea class implements many
Keywordextendsimplements
Traits and final

A trait is a group of methods that can be copied into several unrelated classes with use. It provides code reuse without inheritance. The keyword final prevents a method from being overridden, or a class from being extended.

File: htdocs/phpbook/ch18/trait.php
<?php
trait Loggable
{
    public function log(string $msg): void
    {
        echo "[" . static::class . "] $msg<br>";
    }
}
final class Invoice
{
    use Loggable;
    final public function total(): int { return 1180; }
}
class User
{
    use Loggable;
}
(new Invoice())->log("Invoice created, total " . (new Invoice())->total());
(new User())->log("User logged in");
?>
OUTPUT browser · http://localhost/phpbook/ch18/trait.php
[Invoice] Invoice created, total 1180
[User] User logged in
Overloading

Overloading in PHP provides a means to dynamically "create" properties and methods. These dynamic entities are processed via magic methods that you define in the class. The overloading methods are invoked when interacting with properties or methods that have not been declared or are not visible in the current scope – called "inaccessible properties" and "inaccessible methods" below. All overloading methods must be defined as public.

PHP's meaning of "overloading" differs from most object-oriented languages, where overloading means several methods with the same name but different numbers or types of parameters. None of the arguments of these magic methods can be passed by reference.

Property overloading
Signatures
public __set(string $name, mixed $value): void
public __get(string $name): mixed
public __isset(string $name): bool
public __unset(string $name): void
  • __set() runs when writing data to inaccessible properties.
  • __get() is used for reading data from inaccessible properties.
  • __isset() is triggered by calling isset() or empty() on inaccessible properties.
  • __unset() is invoked when unset() is used on inaccessible properties.

The $name argument is the name of the property being accessed; the $value argument of __set() is the value being assigned. Property overloading works only in object context – these methods are not triggered in static context and must not be declared static.

File: htdocs/phpbook/ch18/propoverload.php
<?php
class PropertyTest
{
    /** Location for overloaded data. */
    private $data = array();
    /** Overloading not used on declared properties. */
    public $declared = 1;
    /** Overloading only used on this when accessed outside the class. */
    private $hidden = 2;

    public function __set($name, $value)
    {
        echo "Setting '$name' to '$value'\n";
        $this->data[$name] = $value;
    }
    public function __get($name)
    {
        echo "Getting '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        $trace = debug_backtrace();
        trigger_error('Undefined property via __get(): ' . $name .
            ' in ' . basename($trace[0]['file']) . ' on line ' . $trace[0]['line'], E_USER_NOTICE);
        return null;
    }
    public function __isset($name)
    {
        echo "Is '$name' set?\n";
        return isset($this->data[$name]);
    }
    public function __unset($name)
    {
        echo "Unsetting '$name'\n";
        unset($this->data[$name]);
    }
    /** Not a magic method, just here for example. */
    public function getHidden()
    {
        return $this->hidden;
    }
}
echo "<pre>\n";
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
echo "</pre>";
?>
OUTPUT browser · http://localhost/phpbook/ch18/propoverload.php
Setting 'a' to '1'
Getting 'a'
1

Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)

1

Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'


Notice:  Undefined property via __get(): hidden in propoverload.php on line 56 in /opt/lampp/htdocs/phpbook/ch18/propoverload.php on line 23
Method overloading
Signatures
public __call(string $name, array $arguments): mixed
public static __callStatic(string $name, array $arguments): mixed

__call() is triggered when invoking inaccessible methods in an object context; __callStatic() when invoking inaccessible methods in a static context. $name is the method name; $arguments is an array of the parameters passed.

File: htdocs/phpbook/ch18/methodoverload.php
<?php
class MethodTest
{
    public function __call($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' " . implode(', ', $arguments) . "<br>";
    }
    public static function __callStatic($name, $arguments)
    {
        echo "Calling static method '$name' " . implode(', ', $arguments) . "<br>";
    }
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');
?>
OUTPUT browser · http://localhost/phpbook/ch18/methodoverload.php
Calling object method 'runTest' in object context
Calling static method 'runTest' in static context
__toString()

__toString() decides what is printed when an object is used as a string:

File: htdocs/phpbook/ch18/tostring.php
<?php
class Money
{
    public function __construct(private float $amount) {}
    public function __toString(): string
    {
        return "Rs. " . number_format($this->amount, 2);
    }
}
$price = new Money(2499.5);
echo "The price is $price";
?>
OUTPUT browser · http://localhost/phpbook/ch18/tostring.php
The price is Rs. 2,499.50
Object Iteration

PHP provides a way for objects to be iterated through with, for example, a foreach statement. By default, all visible properties are used for the iteration.

File: htdocs/phpbook/ch18/iterate.php
<?php
class MyClass
{
    public $var1 = 'value 1';
    public $var2 = 'value 2';
    public $var3 = 'value 3';
    protected $protected = 'protected var';
    private $private = 'private var';

    function iterateVisible()
    {
        echo "MyClass::iterateVisible:<br>";
        foreach ($this as $key => $value) {
            print "$key => $value<br>";
        }
    }
}
$class = new MyClass();
foreach ($class as $key => $value) {
    print "$key => $value<br>";
}
echo "<br>";
$class->iterateVisible();
?>
OUTPUT browser · http://localhost/phpbook/ch18/iterate.php
var1 => value 1
var2 => value 2
var3 => value 3

MyClass::iterateVisible:
var1 => value 1
var2 => value 2
var3 => value 3
protected => protected var
private => private var

As the output shows, foreach iterates through all visible properties: from outside only the public ones, from inside also protected and private. To go a step further, implement PHP's internal Iterator interface; the object then decides what is iterated and how.

File: htdocs/phpbook/ch18/iterator.php
<?php
class MyIterator implements Iterator
{
    private $var = array();

    public function __construct($array)
    {
        if (is_array($array)) {
            $this->var = $array;
        }
    }
    public function rewind(): void
    {
        echo "rewinding<br>";
        reset($this->var);
    }
    public function current(): mixed
    {
        $var = current($this->var);
        echo "current: $var<br>";
        return $var;
    }
    public function key(): mixed
    {
        $var = key($this->var);
        echo "key: $var<br>";
        return $var;
    }
    public function next(): void
    {
        $var = next($this->var);
        echo "next: $var<br>";
    }
    public function valid(): bool
    {
        $key = key($this->var);
        $var = ($key !== NULL && $key !== FALSE);
        echo "valid: " . ($var ? "true" : "false") . "<br>";
        return $var;
    }
}
$values = array(1, 2, 3);
$it = new MyIterator($values);
foreach ($it as $a => $b) {
    print "$a: $b<br>";
}
?>
OUTPUT browser · http://localhost/phpbook/ch18/iterator.php
rewinding
valid: true
current: 1
key: 0
0: 1
next: 2
valid: true
current: 2
key: 1
1: 2
next: 3
valid: true
current: 3
key: 2
2: 3
next:
valid: false

PHP 8.1 change. The methods of PHP's built-in interfaces now declare return types (current(): mixed, next(): void, valid(): bool …). Implementations without these return types cause a Deprecated notice, so the old example has been updated with them.

You can also define your class so that it does not have to implement all Iterator methods, by implementing the IteratorAggregate interface. Its single method getIterator() returns an iterator – here the MyIterator class above, saved in the file iterator_class.php:

File: htdocs/phpbook/ch18/aggregate.php
<?php
require "iterator_class.php";

class MyCollection implements IteratorAggregate
{
    private $items = array();
    private $count = 0;

    // Required definition of interface IteratorAggregate
    public function getIterator(): Iterator
    {
        return new MyIterator($this->items);
    }
    public function add($value)
    {
        $this->items[$this->count++] = $value;
    }
}
$coll = new MyCollection();
$coll->add('value 1');
$coll->add('value 2');
$coll->add('value 3');
foreach ($coll as $key => $val) {
    echo "key/value: [$key -> $val]<br><br>";
}
?>
OUTPUT browser · http://localhost/phpbook/ch18/aggregate.php
rewinding
valid: true
current: value 1
key: 0
key/value: [0 -> value 1]

next: value 2
valid: true
current: value 2
key: 1
key/value: [1 -> value 2]

next: value 3
valid: true
current: value 3
key: 2
key/value: [2 -> value 3]

next:
valid: false
Design Patterns

Patterns are ways to describe best practices and good designs. They show flexible solutions to common programming problems.

Factory

The Factory pattern allows objects to be instantiated at run time. It is called a factory because it is responsible for "manufacturing" objects. A parameterised factory receives the name of the class to instantiate as an argument.

File: htdocs/phpbook/ch18/Drivers/MySQL.php
<?php
class Driver_MySQL
{
    public function name() { return "MySQL driver ready"; }
}
File: htdocs/phpbook/ch18/factory.php
<?php
class Example
{
    // The parameterized factory method
    public static function factory($type)
    {
        if (include_once 'Drivers/' . $type . '.php') {
            $classname = 'Driver_' . $type;
            return new $classname;
        } else {
            throw new Exception('Driver not found');
        }
    }
}
// Load a MySQL Driver
$mysql = Example::factory('MySQL');
// Load an SQLite Driver
$sqlite = Example::factory('SQLite');
echo $mysql->name() . "<br>";
echo $sqlite->name();
?>
OUTPUT browser · http://localhost/phpbook/ch18/factory.php
MySQL driver ready
SQLite driver ready

Defining this method in a class allows drivers to be loaded on the fly. If Example were a database abstraction class, loading a MySQL or an SQLite driver is a single call.

Singleton

The Singleton pattern applies when there must be exactly one instance of a class. The most common example is a database connection. Implementing this pattern makes the single instance easily accessible to many other objects.

File: htdocs/phpbook/ch18/singleton.php
<?php
class Example
{
    // Hold an instance of the class
    private static $instance;

    // A private constructor; prevents direct creation of object
    private function __construct()
    {
        echo 'I am constructed<br>';
    }
    // The singleton method
    public static function singleton()
    {
        if (!isset(self::$instance)) {
            $c = __CLASS__;
            self::$instance = new $c;
        }
        return self::$instance;
    }
    // Example method
    public function bark()
    {
        echo 'Woof!<br>';
    }
    // Prevent users from cloning the instance
    private function __clone() {}
}
$a = Example::singleton();
$b = Example::singleton();     // constructor is NOT called again
$a->bark();
var_dump($a === $b);
echo "<br>";
try {
    $c = new Example();        // not allowed
} catch (Error $e) {
    echo "Error: " . $e->getMessage();
}
?>
OUTPUT browser · http://localhost/phpbook/ch18/singleton.php
I am constructed
Woof!
bool(true)
Error: Call to private Example::__construct() from global scope

The constructor runs only once; every later call of singleton() returns the same object. Making __construct() and __clone() private prevents other code from creating or copying instances.

Key Takeaways
  • Abstract classes cannot be instantiated; abstract methods must be implemented by child classes.
  • Interfaces declare public methods only; a class can implement many interfaces.
  • Traits copy methods into classes with use; final blocks overriding/extending.
  • __get, __set, __isset, __unset, __call and __callStatic handle inaccessible members (PHP "overloading").
  • foreach iterates visible properties; Iterator and IteratorAggregate customise iteration (with PHP 8.1 return types).
  • Factory creates objects of a class chosen at run time; Singleton guarantees one instance.
Program File

Extract the ZIP into C:\xampp\htdocs. It creates the folder phpbook/ch18 used in the file labels above; open the programs at http://localhost/phpbook/… with Apache (and MySQL, where needed) started in the XAMPP Control Panel.

Question / Answer (15 questions)

Answer : A class declared with the abstract keyword that cannot be instantiated. It may contain abstract methods (signatures only) and normal methods, and serves as a base for child classes.

Answer : Every abstract method of the parent must be implemented in the child with a compatible signature and the same or less restrictive visibility (protected may become public, but not private).

Answer : A contract that lists public method signatures (and constants) without implementation. A class that implements it must define all its methods.

Answer : An abstract class can have properties and concrete methods and a class can extend only one; an interface has only method signatures and constants, and a class can implement many interfaces.

Answer : A reusable set of methods that can be inserted into any class with the use keyword, providing code reuse without inheritance.

Answer : A final method cannot be overridden in a child class; a final class cannot be extended.

Answer : Dynamically creating properties and methods through magic methods that are called when inaccessible (undeclared or invisible) properties or methods are used.

Answer : __set() when a value is written to an inaccessible property; __get() when an inaccessible property is read.

Answer : __isset() when isset() or empty() is used on an inaccessible property; __unset() when unset() is used on one.

Answer : __call() is triggered when an inaccessible method is called on an object; __callStatic() when an inaccessible method is called statically on the class.

Answer : All visible properties – from outside the class only public ones, from inside the class also protected and private ones.

Answer : current(), key(), next(), rewind() and valid().

Answer : An interface with a single method, getIterator(), which returns an iterator; it lets a class be used in foreach without implementing all Iterator methods.

Answer : A design pattern in which a method creates and returns objects of a class chosen at run time, e.g. Example::factory('MySQL') loads and instantiates Driver_MySQL.

Answer : It ensures that only one instance of a class exists. The constructor and __clone() are private, the instance is stored in a private static property, and a public static method creates it on first call and returns it afterwards.
MCQ Test : Check Your Understanding (5 questions)

Select an option and press Check Answer, or answer all questions and press Submit Test to see your score. Each answer comes with an explanation.

Q1. An abstract class:

Show Answer

Correct Answer : B. Cannot be instantiated
Abstract classes only serve as parents.

Q2. A class can implement:

Show Answer

Correct Answer : B. Many interfaces
implements accepts a comma-separated list.

Q3. Which magic method runs when a value is written to an inaccessible property?

Show Answer

Correct Answer : B. __set()
__set() handles writes.

Q4. Which interface needs only the getIterator() method?

Show Answer

Correct Answer : C. IteratorAggregate
IteratorAggregate returns an iterator from getIterator().

Q5. The Singleton pattern makes the constructor:

Show Answer

Correct Answer : C. private
A private constructor prevents creating instances with new.

Bibliography/References

The notes on this page are the author’s own compilation from the e-book PHP: Basics to Advance. Every program was run on Apache with PHP 8 and MariaDB, and the output shown is the real result of that run. The works listed below were consulted and are cited for attribution and further reading. Product names and trademarks belong to their respective owners.

  1. The PHP Group. (n.d.). PHP Manual – Class Abstraction. https://www.php.net/manual/en/language.oop5.abstract.php
  2. The PHP Group. (n.d.). PHP Manual – Object Interfaces. https://www.php.net/manual/en/language.oop5.interfaces.php
  3. The PHP Group. (n.d.). PHP Manual – Traits. https://www.php.net/manual/en/language.oop5.traits.php
  4. The PHP Group. (n.d.). PHP Manual – Overloading. https://www.php.net/manual/en/language.oop5.overloading.php
  5. The PHP Group. (n.d.). PHP Manual – Object Iteration. https://www.php.net/manual/en/language.oop5.iterations.php
  6. Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.