Access Modifiers in Depth
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 39 of 91.
Now that you understand the three access modifiers, let's explore how they behave in inheritance scenarios. The key insight is that protected members become accessible to child classes, while private members remain hidden even from children.
<?php
class Vehicle {
public $brand;
protected $engineType;
private $serialNumber;
public function __construct($brand, $engine, $serial) {
$this->brand = $brand;
$this->engineType = $engine;
$this->serialNumber = $serial;
}
private function getSerial() {
return $this->serialNumber;
}
protected function getEngineInfo() {
return $this->engineType;
}
}
class Car extends Vehicle {
public function getDetails() {
return $this->brand . " - " . $this->getEngineInfo();
// $this->serialNumber would cause an error - private!
// $this->getSerial() would also fail - private method!
}
}
$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails();
Output:
Toyota - V6The Car class can access $brand (public) and call getEngineInfo() (protected), but has no access to $serialNumber or getSerial() because they're private to Vehicle. This distinction lets you share some internal details with child classes while keeping others completely hidden.
Key Point: Use protected when child classes need access to internal data or methods, and private when even child classes shouldn't touch certain implementation details.
Challenge
EasyLet's build a device management system that explores how access modifiers behave across inheritance. You'll create a base Device class with properties at different visibility levels, then extend it with a Smartphone class that demonstrates which members child classes can and cannot access.
You'll organize your code across three files:
Device.php— Create aDeviceclass that represents electronic devices with varying levels of data sensitivity. Include:- A
publicproperty$brandfor the manufacturer name - A
protectedproperty$modelNumberfor internal identification - A
privateproperty$manufacturingCodefor factory-only data
privatemethodgetManufacturingCode()that returns the manufacturing code. Add apublicmethodgetModelInfo()that returns"Model: [modelNumber]". Finally, add apublicmethodgetFullCode()that returns"[brand]-[manufacturingCode]"— this demonstrates how the class itself can access its own private members.- A
Smartphone.php— Create aSmartphoneclass that extendsDevice. Include the Device file. The constructor should accept brand, modelNumber, manufacturingCode, and an operating system name. Call the parent constructor with the first three values, then store the OS in a private property. Add a public methodgetDeviceDetails()that returns"[brand] [modelNumber] running [os]". Notice how you can access the public$branddirectly and the protected$modelNumber, but you cannot access the private$manufacturingCodedirectly — you'd need to use the inherited public methodgetFullCode()instead.main.php— Include the Smartphone file. You'll receive four inputs: a brand name, a model number, a manufacturing code, and an operating system. Create aSmartphoneinstance with these values. Print three lines:- The result of calling
getDeviceDetails() - The result of calling the inherited
getModelInfo()method - The result of calling the inherited
getFullCode()method
- The result of calling
This challenge highlights the key distinction: your Smartphone class can freely use the protected $modelNumber inherited from Device, but the private $manufacturingCode and getManufacturingCode() remain completely hidden — accessible only through the public interface that Device chooses to expose.
Cheat sheet
In inheritance, protected members are accessible to child classes, while private members remain hidden even from children.
<?php
class Vehicle {
public $brand;
protected $engineType;
private $serialNumber;
public function __construct($brand, $engine, $serial) {
$this->brand = $brand;
$this->engineType = $engine;
$this->serialNumber = $serial;
}
private function getSerial() {
return $this->serialNumber;
}
protected function getEngineInfo() {
return $this->engineType;
}
}
class Car extends Vehicle {
public function getDetails() {
return $this->brand . " - " . $this->getEngineInfo();
// $this->serialNumber would cause an error - private!
// $this->getSerial() would also fail - private method!
}
}
$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails(); // Toyota - V6
Child classes can access:
publicproperties and methodsprotectedproperties and methods
Child classes cannot access:
privateproperties and methods (these remain exclusive to the parent class)
Best Practice: Use protected when child classes need access to internal data or methods, and private when even child classes shouldn't touch certain implementation details.
Try it yourself
<?php
require_once 'Smartphone.php';
// Read inputs
$brand = trim(fgets(STDIN));
$modelNumber = trim(fgets(STDIN));
$manufacturingCode = trim(fgets(STDIN));
$os = trim(fgets(STDIN));
// TODO: Create a Smartphone instance with the input values
// TODO: Print the result of getDeviceDetails()
// TODO: Print the result of the inherited getModelInfo() method
// TODO: Print the result of the inherited getFullCode() method
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern