PHP OOP Access Modifiers: A Comprehensive Guide
The concept of access modifiers in Object Oriented Programming (OOP) is fundamental to the development of robust and secure applications. In PHP, access
Access modifiers (also called visibility keywords) decide who is allowed to read, change, or call a class member. They are the mechanism behind encapsulation — one of the core ideas of Object-Oriented Programming: you expose a small, safe public surface and hide the internal details that callers should not touch.
This chapter covers the three PHP visibility keywords — public, protected, and private — what each one allows, what happens when you break the rules, how visibility behaves under inheritance, and the common patterns (getters, setters, and constructor promotion) that rely on them. If classes are new to you, read PHP Classes and Objects first.
The three access modifiers at a glance
| Modifier | Inside the same class | In a child class | From outside (e.g. $obj->member) |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
You can apply a modifier to properties, methods, and class constants. If you omit the modifier on a method, PHP defaults it to public; on a property you must write one explicitly (the old var keyword is an alias for public).
Public access modifier
public is the most permissive level. A public member can be accessed from anywhere — inside the class, from child classes, and from any code holding the object. Use it for the intended interface of your class: the methods callers are meant to use.
<?php
class User {
public $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$user = new User();
$user->setName("Ada"); // calling a public method
echo $user->getName(); // Ada
echo "\n";
$user->name = "Grace"; // touching a public property directly
echo $user->name; // GraceThe output is:
Ada
GraceBecause $name is public, callers can change it directly with $user->name = ..., bypassing setName(). That is exactly why public properties are often discouraged: you lose the chance to validate the value. Marking the property protected or private and exposing it through methods restores that control.
Protected access modifier
protected sits in the middle. The member is reachable from inside the class and from any subclass, but not from outside code. Use it when a subclass legitimately needs the data, but external callers should not.
<?php
class User {
protected $email;
protected function setEmail($email) {
$this->email = $email;
}
public function getEmail() {
return $this->email;
}
}
$user = new User();
echo $user->getEmail(); // works: getEmail() is public
$user->setEmail("[email protected]"); // Fatal error: Call to protected method User::setEmail()Running this prints nothing for the first line (the email is still null) and then stops with:
PHP Fatal error: Uncaught Error: Call to protected method User::setEmail() from global scopeThe error is the point: protected members protect the class from being misused by outside code while still cooperating with the inheritance chain shown below.
Private access modifier
private is the most restrictive level. The member is visible only inside the class that declares it — not even subclasses can see it. Reach for private when a detail is purely internal and might change at any time, like a password hash or a cached computation.
<?php
class User {
private $password;
public function setPassword($password) {
// hide the real value behind validation
$this->password = password_hash($password, PASSWORD_DEFAULT);
}
public function check($attempt) {
return password_verify($attempt, $this->password);
}
}
$user = new User();
$user->setPassword("s3cret");
var_dump($user->check("s3cret")); // bool(true)
var_dump($user->check("wrong")); // bool(false)The output is:
bool(true)
bool(false)Trying to read $user->password from outside the class raises Error: Cannot access private property User::$password. The caller can only interact through the public setPassword() and check() methods — the raw hash is sealed away.
Access modifiers and inheritance
When a child class extends a parent, it inherits the parent's members along with their visibility. public and protected members are usable inside the child; private ones are not visible to the child at all.
<?php
class User {
protected $email;
protected function setEmail($email) {
$this->email = $email;
}
public function getEmail() {
return $this->email;
}
}
class Admin extends User {
// Admin can call the protected setEmail() because it is a subclass
public function updateEmail($email) {
$this->setEmail($email);
}
}
$admin = new Admin();
$admin->updateEmail("[email protected]");
echo $admin->getEmail(); // [email protected]The output is:
A child may also widen visibility (protected → public) when overriding a member, but it may not narrow it — overriding a public method as private triggers a fatal error. For more on building class hierarchies, see PHP Inheritance.
Visibility on constants and constructor promotion
Class constants accept the same modifiers (since PHP 7.1). This lets you expose some constants while hiding tuning values:
<?php
class Order {
public const STATUS_PAID = "paid";
private const TAX_RATE = 0.2; // internal detail
public function total(float $net): float {
return $net * (1 + self::TAX_RATE);
}
}
echo Order::STATUS_PAID; // paid
echo "\n";
echo (new Order())->total(100); // 120The output is:
paid
120Since PHP 8.0, constructor property promotion lets you declare a property and its visibility right in the constructor signature, removing repetitive boilerplate:
<?php
class Point {
public function __construct(
public int $x = 0,
private int $y = 0, // declared, assigned, and made private in one line
) {}
public function describe(): string {
return "x={$this->x}, y={$this->y}";
}
}
echo (new Point(3, 4))->describe(); // x=3, y=4The output is:
x=3, y=4See PHP Class Constants and PHP Constructor for the full picture of these features.
Choosing the right modifier
- Start with the most restrictive level (
private) and loosen only when a real need appears. This keeps your public API small and easy to change later. - Make a member
publicwhen it is part of the interface callers depend on. - Make it
protectedwhen subclasses — but not outside code — need it. - Make it
privatewhen it is an implementation detail that could change without notice. - Prefer private/protected properties with public getter/setter methods over public properties, so you keep control over how values are read and written. This idea is part of what OOP is about.
Summary
Access modifiers control the visibility of class members: public allows access from anywhere, protected from the class and its subclasses, and private from the declaring class only. They apply to properties, methods, and constants, and they are how PHP enforces encapsulation. Used well — hiding details behind a small public surface — they make your code safer to change and easier to reason about.