Access modifiers control which code is allowed to read or call a class's properties and methods — the three-level model (public, protected, private) is essentially identical across PHP, Java, and most other mainstream object-oriented languages, even though the exact keyword syntax differs slightly.
public
class User
{
public string $name;
public function greet(): string
{
return "Hello, {$this->name}";
}
}
$user = new User();
$user->name = 'Alex'; // accessible from anywhere
$user->greet();
public members are accessible from anywhere — inside the class, from a subclass, and from outside code that holds an instance of the object.
protected
class Animal
{
protected string $sound = 'generic noise';
protected function makeSound(): string
{
return $this->sound;
}
}
class Dog extends Animal
{
protected string $sound = 'Woof';
public function bark(): string
{
return $this->makeSound(); // accessible: Dog extends Animal
}
}
$dog = new Dog();
$dog->bark(); // fine — bark() is public
// $dog->makeSound(); // error — makeSound() is protected, not accessible from outside
protected members are accessible within the defining class and any subclass, but not from outside code holding an instance directly — this is the level specifically meant for internal implementation details a subclass is expected to build on.
private
class BankAccount
{
private float $balance = 0;
public function deposit(float $amount): void
{
$this->balance += $amount;
}
public function getBalance(): float
{
return $this->balance;
}
}
$account = new BankAccount();
$account->deposit(100);
// $account->balance; // error — private, not accessible at all outside the class
echo $account->getBalance(); // the only sanctioned way to read it
private members are accessible only within the exact class that defines them — not even a subclass can access a parent's private property directly, which is the key distinction from protected.
Why this matters: encapsulation
Restricting direct access to a class's internal state (like BankAccount::$balance above) and exposing only controlled methods to modify it (deposit(), rather than letting outside code set $balance directly) is what encapsulation actually means in practice — it prevents external code from putting an object into an invalid state that the class's own logic wasn't designed to handle.
The same concept in Java
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Java's syntax and the getter/setter convention shown here are more rigidly enforced by convention than PHP's, but the underlying three-level access model (public/protected/private) is the same concept, just expressed with the same keywords in a different language.
Choosing the right modifier when designing a class
A reasonable default is starting with private and only widening to protected or public when a specific, deliberate reason exists to expose something — this default-to-restrictive approach keeps a class's public surface area intentional rather than accidentally exposing internal details that later become difficult to change without breaking other code that came to depend on them.