objects namespaces and application architecture
Inheritance
Inheritance lets one class extend another class. The child class receives the parent's public and protected behaviour and can add or override behaviour.
Inheritance is useful when there is a true "is a kind of" relationship and the child can safely be used wherever the parent is expected. It is risky when it is used only to share code, because parent and child classes can become tightly coupled.
Extend A Parent Class
Use extends to create a child class.
<?php
declare(strict_types=1);
class Notification
{
public function summary(): string
{
return 'Notification ready';
}
}
class EmailNotification extends Notification
{
public function recipientType(): string
{
return 'email';
}
}
$notification = new EmailNotification();
echo $notification->summary() . PHP_EOL;
echo $notification->recipientType() . PHP_EOL;
// Prints:
// Notification ready
// email
EmailNotification can call the inherited summary() method because it extends Notification.
Override Behaviour
A child class can replace a parent method with its own implementation.
<?php
declare(strict_types=1);
class Report
{
public function title(): string
{
return 'Report';
}
}
class SalesReport extends Report
{
public function title(): string
{
return 'Sales Report';
}
}
$report = new SalesReport();
echo $report->title() . PHP_EOL;
// Prints:
// Sales Report
Overriding is powerful, but it means callers may get different behaviour depending on the concrete child class.
Call Parent Behaviour
Use parent:: when a child wants to reuse part of the parent's method.
<?php
declare(strict_types=1);
class BaseEmail
{
public function subject(): string
{
return 'Account update';
}
}
class SecurityEmail extends BaseEmail
{
public function subject(): string
{
return '[Security] ' . parent::subject();
}
}
$email = new SecurityEmail();
echo $email->subject() . PHP_EOL;
// Prints:
// [Security] Account update
This keeps common behaviour in one place while still allowing a child to specialise it.
Child Objects Can Be Used As Parent Objects
Code that accepts a parent type can receive a child object.
<?php
declare(strict_types=1);
class Message
{
public function body(): string
{
return 'Message body';
}
}
class WelcomeMessage extends Message
{
public function body(): string
{
return 'Welcome to the application';
}
}
function printMessage(Message $message): void
{
echo $message->body() . PHP_EOL;
}
printMessage(new WelcomeMessage());
// Prints:
// Welcome to the application
This only works well when the child honours the expectations of the parent type.
Avoid Inheritance For Simple Code Sharing
If two classes merely need the same helper, inheritance may be the wrong tool.
<?php
declare(strict_types=1);
class MoneyFormatter
{
public function formatPennies(int $pennies): string
{
return '£' . number_format($pennies / 100, 2);
}
}
$formatter = new MoneyFormatter();
echo $formatter->formatPennies(2499) . PHP_EOL;
// Prints:
// £24.99
Passing a helper object is often clearer than making unrelated classes extend a shared base class.
Watch Fragile Base Classes
Changes to a parent class can unexpectedly affect every child class. This is why deep inheritance trees are hard to maintain.
<?php
declare(strict_types=1);
class Importer
{
public function sourceType(): string
{
return 'generic';
}
}
class CsvImporter extends Importer
{
public function sourceType(): string
{
return 'csv';
}
}
echo (new CsvImporter())->sourceType() . PHP_EOL;
// Prints:
// csv
Keep inheritance shallow and purposeful.
What To Remember
Inheritance models "is a kind of" relationships. Use it when child objects can safely stand in for parent objects. Prefer composition when the goal is sharing services, helpers, or changeable behaviour.
Practice
Task: Specialise A Notification
Create a parent notification class and a specialised child class.
Requirements
- Use
declare(strict_types=1);. - Create a
Notificationparent class. - Give it a
channel()method that returnsgeneric. - Give it a
summary()method. - Create an
EmailNotificationchild class. - Override
channel()so it returnsemail. - Use a function that accepts
Notificationand prints its summary and channel. - Pass an
EmailNotificationinto that function. - Include the expected output as comments in the same PHP code block.
The example should show that a child object can be used where the parent type is expected.
Show solution
<?php
declare(strict_types=1);
class Notification
{
public function channel(): string
{
return 'generic';
}
public function summary(): string
{
return 'Notification ready';
}
}
class EmailNotification extends Notification
{
public function channel(): string
{
return 'email';
}
}
function printNotification(Notification $notification): void
{
echo $notification->summary() . ' via ' . $notification->channel() . PHP_EOL;
}
printNotification(new EmailNotification());
// Prints:
// Notification ready via email
EmailNotification extends Notification, so it can be passed to a function that asks for Notification. The child class specialises the channel without changing the function signature.