Menu
Coddy logo textTech

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 - V6

The 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 icon

Challenge

Easy

Let'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 a Device class that represents electronic devices with varying levels of data sensitivity. Include:
    • A public property $brand for the manufacturer name
    • A protected property $modelNumber for internal identification
    • A private property $manufacturingCode for factory-only data
    The constructor should accept all three values. Add a private method getManufacturingCode() that returns the manufacturing code. Add a public method getModelInfo() that returns "Model: [modelNumber]". Finally, add a public method getFullCode() that returns "[brand]-[manufacturingCode]" — this demonstrates how the class itself can access its own private members.
  • Smartphone.php — Create a Smartphone class that extends Device. 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 method getDeviceDetails() that returns "[brand] [modelNumber] running [os]". Notice how you can access the public $brand directly and the protected $modelNumber, but you cannot access the private $manufacturingCode directly — you'd need to use the inherited public method getFullCode() 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 a Smartphone instance 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

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:

  • public properties and methods
  • protected properties and methods

Child classes cannot access:

  • private properties 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
?>
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming