PHP Inheritance and Access Modifiers
Introduction to PHP Inheritance
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a new class (known as the derived or child class) to inherit attributes and methods from an existing class (known as the parent or base class). By using inheritance, PHP developers can avoid redundancy and promote code reuse, making it easier to maintain and extend their applications.
The basic syntax to declare a class that inherits from another is:
class ParentClass {
// Parent class properties and methods
}
class ChildClass extends ParentClass {
// Additional properties and methods
}
Example:
class Animal {
public $name;
public function __construct($name) {
$this->name = $name;
}
public function speak() {
echo "This animal makes a noise.";
}
}
class Dog extends Animal {
public function speak() {
echo $this->name . " barks.";
}
}
$dog = new Dog("Buddy");
$dog->speak(); // Output: Buddy barks.
In this example, Dog
class inherits the name
property and the __construct()
method from the Animal
class and overrides the speak()
method to provide specific behavior for dogs.
Types of Inheritance in PHP
Single Inheritance
A single class inherits from one parent class.
class Vehicle {
public function drive() {
echo "Vehicle is driving.";
}
}
class Car extends Vehicle {
public function honk() {
echo "Car is honking.";
}
}
Here, Car
is a single inheritor of Vehicle
.
Multi-level Inheritance
A derived class is created from another derived class. Although PHP does not support multiple inheritance directly, multi-level inheritance is allowed.
class Person {
public function eat() {
echo "Person is eating.";
}
}
class Employee extends Person {
public function work() {
echo "Employee is working.";
}
}
class Manager extends Employee {
public function manage() {
echo "Manager is managing.";
}
}
Employee
inherits from Person
, and Manager
extends Employee
.
Hierarchical Inheritance
Multiple classes inherit from a single base class.
class Shape {
public function draw() {
echo "Shape is being drawn.";
}
}
class Circle extends Shape { }
class Square extends Shape { }
Both Circle
and Square
inherit from Shape
.
Hybrid Inheritance
It's a combination of two or more types of inheritance, such as single, multiple, multi-level, and hierarchical.
Implementation of Hybrid Inheritance in PHP:
PHP supports hybrid inheritance through interfaces and trait composition.
Access Modifiers in PHP
Access modifiers control the visibility and accessibility of classes, methods, and properties. PHP provides three main access modifiers:
Public
When declared as public, the item is accessible from anywhere the object is instantiated, including outside the class definition and by inheritance.
class MyClass {
public $publicVar = 'I am public';
public function publicMethod() {
echo 'Hello from a public method!';
}
}
$obj = new MyClass();
echo $obj->publicVar; // Outputs: I am public
$obj->publicMethod(); // Outputs: Hello from a public method!
Protected
Protected items are accessible within the same class, its subclasses, but not from instances of the class.
class ParentClass {
protected $protectedVar = 'I am protected';
protected function protectedMethod() {
return 'Hello from a protected method!';
}
}
class ChildClass extends ParentClass {
public function accessProtected() {
echo $this->protectedVar; // Outputs: I am protected
echo $this->protectedMethod(); // Outputs: Hello from a protected method!
}
}
$obj = new ParentClass();
// echo $obj->protectedVar; // Fatal error: Cannot access protected property ParentClass::$protectedVar
// echo $obj->protectedMethod(); // Fatal error: Call to protected method ParentClass::protectedMethod() from context ''
Private
Private items are accessible only from within the class they are defined in.
class MyClass {
private $privateVar = 'I am private';
private function privateMethod() {
return 'Hello from a private method!';
}
public function getPrivateVar() {
return $this->privateVar;
}
public function callPrivateMethod() {
echo $this->privateMethod();
}
}
$obj = new MyClass();
// echo $obj->privateVar; // Fatal error: Cannot access private property MyClass::$privateVar
// echo $obj->privateMethod(); // Fatal error: Call to private method MyClass::privateMethod() from context ''
echo $obj->getPrivateVar(); // Outputs: I am private
$obj->callPrivateMethod(); // Outputs: Hello from a private method!
Practical Use Cases of Inheritance and Access Modifiers
Code Reusability: Inheritance enables you to reuse code efficiently. Instead of duplicating code across different classes, you can place common logic in a parent class and extend it.
Polymorphism: Methods in the derived classes can be overridden to change the behavior based on the specific implementation.
Encapsulation: Using different access modifiers helps in keeping certain parts of your application hidden from others, which increases the security and robustness of your codebase.
Organizing Code: Grouping related classes together helps in structuring your code better.
Advanced Concepts
- Final Keyword: The
final
keyword prevents a class from being extended or a method from being overridden.
final class BaseClass {
final public function myMethod() {
echo "This method cannot be overridden.";
}
}
// class ChildClass extends BaseClass {} // Fatal error: Class ChildClass may not inherit from final class (BaseClass)
- Abstract Classes: These classes cannot be instantiated and may include abstract methods that must be implemented by derived classes.
abstract class Shape {
abstract public function area();
public function perimeter() {
echo "Calculating perimeter...";
}
}
class Square extends Shape {
public function area() {
return "Area of square is side squared.";
}
}
$square = new Square();
echo $square->area();
- Interfaces: Interfaces allow you to specify methods that must be implemented by a class without defining how these methods should work. A single class can implement multiple interfaces.
interface Drawable {
public function draw();
}
interface Scalable {
public function scale(float $scale);
}
class Image implements Drawable, Scalable {
public function draw() {
echo "Drawing image...";
}
public function scale(float $scale) {
echo "Scaling image by {$scale}.";
}
}
Conclusion
Inheritance and access modifiers are crucial tools in PHP for building scalable, maintainable, and efficient applications. Understanding how to effectively use these concepts will greatly enhance your OOP skills, allowing you to create complex systems with less code repetition and improved security. Mastery of these advanced concepts such as final classes, abstract classes, and interfaces will further improve your ability to design robust and flexible software architectures.
Examples, Set Route, Run Application, and Data Flow: PHP Inheritance and Access Modifiers
Starting with PHP inheritance and access modifiers can be a bit challenging if you're new to object-oriented programming (OOP). However, once you grasp these concepts, they are instrumental in creating scalable and maintainable code. In this guide, we'll go through examples, setting up routes, running an application, and understanding how data flows within this OOP context.
Part 1: Understanding PHP Inheritance
Inheritance is a fundamental concept in OOP that allows a class (child or derived) to inherit properties and methods from another class (parent or base). This reduces code duplication and enhances the structure of your application.
Example 1 - Basic Inheritance
Let's create two classes, Vehicle
(the parent) and Car
(the child). The Car
class will inherit properties and methods from Vehicle
.
<?php
class Vehicle {
public $color; // Public property
protected $type; // Protected property - accessible within this class and subclasses
function __construct($color, $type) {
$this->color = $color;
$this->type = $type;
}
function drive() {
echo "The {$this->type} is driving.";
}
}
class Car extends Vehicle {
public $model;
function __construct($color, $type, $model) {
parent::__construct($color, $type); // Call parent constructor
$this->model = $model;
}
function start() {
echo "Starting the {$this->color} {$this->model}.";
}
}
$myCar = new Car('red', 'car', 'Toyota Camry');
$myCar->drive(); // Output: The car is driving.
$myCar->start(); // Output: Starting the red Toyota Camry.
Part 2: Understanding Access Modifiers
Access modifiers control the visibility of properties and methods from outside the class or within derived classes.
- Public: Accessible everywhere.
- Protected: Accessible only within the class and any derived classes.
- Private: Accessible only within the class.
Example 2 - Using Access Modifiers
Here, we adjust our Vehicle
and Car
classes to demonstrate the use of different access modifiers.
<?php
class Vehicle {
public $color = 'black';
protected $type = 'unknown';
private $engine = 'basic';
function __construct($color, $type) {
$this->color = $color;
$this->type = $type;
}
protected function drive() {
echo "The {$this->type} is driving.";
}
private function startEngine() {
echo "{$this->engine} engine started.";
}
}
class Car extends Vehicle {
public $model;
function __construct($color, $type, $model) {
parent::__construct($color, $type);
$this->model = $model;
}
function start() {
echo "Starting the {$this->color} {$this->model}. ";
$this->drive(); // Accessible as it's protected
// $this->startEngine(); // Error: Cannot access private method
}
}
$myCar = new Car('green', 'sedan', 'Ford Mustang');
$myCar->start(); // Outputs: Starting the green Ford Mustang. The sedan is driving.
// echo $myCar->engine; // Error: Cannot access private property
Part 3: Setting Up Routes and Running an Application
To fully understand these concepts in a real-world scenario, let's create a simple PHP application using a routing library like AltoRouter
. We will demonstrate how to use inheritance and access modifiers within an MVC framework-like structure.
Step-by-step guide:
Set Up Project Structure
- Create a new folder named
carApp
. - Inside
carApp
, create three subfolders:config
,controllers
,models
.
- Create a new folder named
Install AltoRouter via Composer
First, make sure you have composer installed. If not, download it from getcomposer.org.
Navigate to the
carApp
directory and run:composer require altorouter/altorouter
Create Basic Routing Configuration
Open the
config
folder and create a file namedroutes.php
.<?php require __DIR__ . '/../vendor/autoload.php'; $router = new AltoRouter(); $router->map('GET', '/', 'homeController#index', 'home'); $router->map('GET', '/vehicle', 'vehicleController@index', 'vehicle'); $router->map('GET', '/car', 'carController@index', 'car'); $match = $router->match(); if ($match && is_callable($match['target'])) { call_user_func_array($match['target'], $match['params']); } else { header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found"); echo "<h1>404 Not Found<h1>"; exit(); } ?>
Create Models
Inside the
models
folder, create a file namedVehicleModel.php
.<?php namespace models; class VehicleModel { public $color; protected $type; private $engine; public function __construct($color, $type, $engine) { $this->color = $color; $this->type = $type; $this->engine = $engine; } protected function displayDetails() { echo "Color: {$this->color}, Type: {$this->type}"; } }
Now, create a
CarModel.php
which inherits fromVehicleModel
.<?php namespace models; use models\VehicleModel; class CarModel extends VehicleModel { public $model; public function __construct($color, $type, $engine, $model) { parent::__construct($color, $type, $engine); $this->model = $model; } public function startCar() { echo "Starting {$this->model}. "; $this->displayDetails(); } }
Create Controllers
Inside the
controllers
folder, createHomeController.php
.<?php namespace controllers; use models\CarModel; class HomeController { public function index() { echo "<h1>Welcome to the Car App</h1>; $car = new CarModel('blue', 'sports', 'v8', 'Ferrari 488'); $car->startCar(); } }
Create
VehicleController.php
.<?php namespace controllers; use models\VehicleModel; class VehicleController { protected $vehicle; public function __construct() { $this->vehicle = new VehicleModel('grey', 'suv', 'diesel'); } public function index() { echo "<h2>Vehicle Details:</h2>"; echo $this->vehicle->color; // Color: grey echo $this->vehicle->type; // Error: Cannot access protected property } }
Create
CarController.php
.<?php namespace controllers; use models\CarModel; class CarController { public function index() { echo "<h2>Car Details:</h2>"; $car = new CarModel('red', 'coupe', 'turbocharged', 'Porsche 911'); $car->startCar(); // Outputs: Starting Porsche 911. Color: red, Type: coupe } }
Running the Application
Finally, in the root of your
carApp
directory, create a file namedindex.php
.<?php include 'config/routes.php'; ?>
To run the application, point your browser to
http://localhost/carApp/
,http://localhost/carApp/vehicle
, orhttp://localhost/carApp/car
, depending on the route you want to test.
Part 4: How Data Flows within Inheritance and Access Modifiers
When you run http://localhost/carApp/car
, index.php
kicks off the process by including the routes.php
file. AltoRouter
is used here to match the URL to the correct controller action.
The /car
URL matches the /car
route, which directs to the CarController::index()
action. Inside the index()
method of CarController
, we instantiate CarModel
and call its startCar()
method.
- The
CarModel::startCar()
method displays the model details and also calls a protected methoddisplayDetails()
from its parentVehicleModel
. - The
public
access modifier allows$car->startCar()
to be called from anywhere, even outside the class. - The
protected
access modifier keeps$this->displayDetails()
accessible only withinCarModel
and other classes that extendVehicleModel
. This means you can't call it directly from an instance ofCarModel
(you would get an error), but it can be used internally to share functionality between related classes. - The
private
access modifier restricts$engine
property to only be changed or accessed from withinVehicleModel
, ensuring it remains hidden fromCarModel
and elsewhere.
This demonstrates how inheritance allows a child class to reuse the properties and methods of a parent class, while access modifiers dictate where those properties and methods can be accessed and modified, thus maintaining encapsulation and preventing improper manipulation of object data.
By practicing with these examples, configuring routes, and running your application, you will solidify your understanding of PHP inheritance and access modifiers, preparing you to write more robust and maintainable object-oriented code.
Top 10 Questions and Answers on PHP Inheritance and Access Modifiers
PHP, as an object-oriented programming language, uses inheritance and access modifiers extensively to manage class relationships and control object behavior. Mastering these concepts will make your code more structured, reusable, and maintainable. Here are the top ten frequently asked questions (FAQs) about PHP inheritance and access modifiers:
1. What is Inheritance in PHP?
Inheritance in PHP allows a new class, called a derived or child class, to inherit properties and methods from another class, referred to as a base or parent class. This feature promotes code reusability and establishes a hierarchical relationship between classes.
Example:
class Vehicle {
function startEngine() {
echo "Engine started.\n";
}
}
class Car extends Vehicle { // Car inherits from Vehicle
function driveCar() {
echo "Driving car.\n";
}
}
$myCar = new Car();
$myCar->startEngine(); // Engine started.
$myCar->driveCar(); // Driving car.
Here, the Car
class inherits the startEngine()
method from the Vehicle
class.
2. How does method overriding work in PHP?
Method overriding occurs when a child class provides a specific implementation for a method that already exists in its parent class. The child class’s method definition replaces the parent class’s method definition.
Example:
class Animal {
function speak() {
echo "Animal speaks\n";
}
}
class Dog extends Animal {
function speak() {
echo "Dog barks\n"; // Overridden the speak method from Animal
}
}
$buddy = new Dog();
$buddy->speak(); // Outputs: Dog barks
Note that the original Animal
method is not accessible directly through an instance of Dog
.
3. Can a child class have its own methods and properties apart from those inherited from its parent?
Absolutely! A child class can define its own properties and methods independent of the properties and methods it inherits from its parent class.
Example:
class Shape {
protected $color;
function setColor($color) {
$this->color = $color;
}
}
class Square extends Shape {
protected $sideLength;
function setSideLength($sideLength) {
$this->sideLength = $sideLength;
}
function area() {
return $this->sideLength * $this->sideLength;
}
}
$square = new Square();
$square->setColor("Blue"); // Method defined in parent class
$square->setSideLength(5); // Method defined in child class
echo $square->area(); // Method defined in child class
// Output: 25
4. What are the differences between public, protected, and private access modifiers in PHP?
Access modifiers in PHP determine the visibility and accessibility of class properties and methods.
- Public: Accessible by any code within the script, regardless of context.
- Protected: Accessible only within the defining class and its subclasses.
- Private: Accessible only within the defining class; it cannot be accessed from outside, nor from subclasses.
Example:
class Example {
public $publicVar = "I am public";
protected $protectedVar = "I am protected";
private $privateVar = "I am private";
public function displayAllVars() {
echo $this->publicVar . "\n"; // Accessible
echo $this->protectedVar . "\n"; // Accessible
echo $this->privateVar . "\n"; // Accessible
}
}
$testObj = new Example();
echo $testObj->publicVar; // Accessible
// echo $testObj->protectedVar; // Error accessing protected property
// echo $testObj->privateVar; // Error accessing private property
5. What is a final keyword in PHP and how is it used?
The final
keyword in PHP can be used in two ways:
- To prevent a method from being overridden in a subclass.
- To prevent a class from being extended.
Examples:
class ParentClass {
final function iCannotBeOverridden() {}
}
class ChildClass extends ParentClass {
// function iCannotBeOverridden() {} // Error: Cannot override final method from parent.
}
And to prevent a class from being extended:
final class ParentClass {}
// class ChildClass extends ParentClass {} // Error: Cannot extend final class.
6. Is it possible to call a parent class’s constructor in PHP?
Yes, you can explicitly call the parent class's constructor by using the parent::__construct()
method within the child class’s constructor.
Example:
class Vehicle {
protected $brand;
public function __construct($brand) {
$this->brand = $brand;
}
}
class Car extends Vehicle {
protected $model;
public function __construct($brand, $model) {
parent::__construct($brand); // Calls the parent constructor with $brand argument
$this->model = $model;
}
public function getDetails() {
return $this->brand . ' ' . $this->model;
}
}
$myCar = new Car("Toyota", "Corolla");
echo $myCar->getDetails();
// Output: Toyota Corolla
7. Describe static methods and properties in PHP, particularly their implications in inheritance.
Static methods and properties belong to the class, rather than instances of the class. They can be accessed directly on the class without creating an instance.
- Static Methods: Not bound to instances of the class and thus do not use
$this
. Inherited and accessible via the child class name. - Static Properties: Shared among all instances of the class. Accessed with the class name and double colon
::
.
Example:
class Vehicle {
static $count = 0;
public function __construct() {
Vehicle::$count++;
}
static function getTotalVehicles() {
return self::$count;
}
}
class Car extends Vehicle {
// Static methods and properties are inherited but must be accessed via class name.
static $carTypes = ["Sedan", "SUV"];
static function getCarTypes() {
return parent::$carTypes; // Accessing inherited static property
}
}
new Car();
new Vehicle();
echo Car::getTotalVehicles(); // Outputs: 2
print_r(Car::getCarTypes()); // Outputs: Array ( [0] => Sedan [1] => SUV )
8. When might you need to use static method overriding in PHP?
You would override a static method in a child class when you want to provide a specific implementation for a method available in the parent class. Although overridden, it still belongs to the class itself.
Example:
class Animal {
static function classification() {
echo "Vertebrate\n";
}
}
class Bird extends Animal {
static function classification() {
echo "Bird - Specific Vertebrate\n";
}
}
Animal::classification(); // Outputs: Vertebrate
Bird::classification(); // Outputs: Bird - Specific Vertebrate
Static method calls are resolved at compile-time, meaning the method version invoked is determined based on the class name used at the point of the call.
9. How can you access a parent class’s member within a child class?
You can access a parent class’s protected and public members within the child class using the parent::
keyword.
Example:
class Vehicle {
protected $brand;
public function __construct($brand) {
$this->brand = $brand;
}
protected function getBrand() {
return $this->brand;
}
}
class Car extends Vehicle {
protected $model;
public function __construct($brand, $model) {
parent::__construct($brand);
$this->model = $model;
}
public function getDetails() {
return parent::getBrand() . " " . $this->model;
}
}
$myCar = new Car("Honda", "Civic");
echo $myCar->getDetails();
// Output: Honda Civic
In this example, parent::getBrand()
accesses the getBrand()
method from the Vehicle
class.
10. What is the difference between method shadowing and overriding in PHP, and why should I prefer overriding?
- Method Shadowing: When a method in a child class has the same name as a method in the parent class, but the visibility or signature differs. Typically, method shadowing is not desired because it can lead to confusion and bugs.
- Method Overriding: Happens when a child class implements a method that exists in the parent class with the same name and signature, providing a different behavior.
Prefer Overriding:
- Ensures that the base functionality can be extended or modified without breaking encapsulation.
- Facilitates better code organization and readability.
- Avoid ambiguity since both methods have identical signatures.
Example illustrating method shadowing vs. overriding:
class Base {
protected function performTask() {
echo "Executing task in Base\n";
}
}
class Derived extends Base {
public function performTask() { // Same method name but public instead of protected.
echo "Executing task in Derived\n";
}
}
$baseObj = new Base();
$derivedObj = new Derived();
// $baseObj->performTask(); // Error due to protected visibility.
$derivedObj->performTask(); // Outputs: Executing task in Derived
In the above example, Derived::performTask()
shadows the Base::performTask()
because the visibility was changed. If you intended Derived::performTask()
to override Base::performTask()
both should have compatible visibilities (i.e., if Base::performTask()
is protected, Derived::performTask()
should also be protected or less restrictive).
Understanding PHP inheritance and access modifiers can significantly improve the flexibility and security of your applications. By adhering to these principles, you create robust and maintainable code that can be easily extended or adapted in the future.