Visibility: public, private, protected

Learn PHP /Object-Oriented PHP /Lesson 5 of 11 /PHP 8.3 in your browser
In this lesson you’ll learn to
  • Choose the right visibility keyword for each part of a class
  • Protect a value with a private property, a public getter, and a validating setter

When you build a class, some of its parts are meant for the outside world to use, and some are private workings that nobody else should touch. Visibility is how you say which is which. It lets you keep the inside of a class safe while still offering a tidy way to use it from outside.

This builds on Inheritance, where you saw a child class reuse the properties and methods of its parent.

The Three Keywords

PHP gives every property and method a visibility, chosen from three keywords. Each one draws the line in a different place:

  • public: anyone can reach it, from anywhere.
  • private: only code inside this same class can reach it.
  • protected: this class and its children can reach it, but nobody outside.

Think of a class as a shop. The counter is public: any customer can walk up to it. The stockroom is protected: staff and trainees go in, customers do not. The safe is private: only this one shop opens it, not even the branch next door.

Public: Open to Everyone

A public property can be read and changed directly from outside the class. Here the name is on display for anyone:

<?php
class User {
    public string $name = "Billy";
}

$u = new User();
echo $u->name;
Billy

That is handy, but it comes with a cost. Because anything can change $name, anything can also set it to nonsense, and the class has no say in the matter. For values that need protecting, you want a gate in front of them.

Private: Locked Inside the Class

Mark a property private and only methods that belong to the same class can touch it. Outside code has to ask politely, through a method you provide. This is the classic pattern: a private property, a public getter to read it, and a public setter to change it after checking the value is sensible.

<?php
class Person {
    private int $age = 0;

    public function setAge(int $age): void {
        if ($age < 0) {
            echo "Age cannot be negative";
            return;
        }
        $this->age = $age;
    }

    public function getAge(): int {
        return $this->age;
    }
}

$p = new Person();
$p->setAge(30);
echo $p->getAge();
30

The outside world never touches $age directly. It calls setAge(), which guards the value first, and getAge(), which hands it back. A getter is just a method that returns a value; a setter is a method that changes one. The names getAge and setAge are a common convention, not a rule.

PHP 8.4 has two newer tools for exactly this. The getter/setter pattern above works everywhere and is worth knowing, but modern PHP gives you shorter options for guarding a property. Property hooks let you validate right on the assignment with a set { ... } block, so callers write $p->age = 30 and the check still runs. Asymmetric visibility lets a property be readable everywhere but writable only inside the class, with public private(set) int $age. Both keep the plain $p->age syntax while retaining control.

Why Hide the Internals?

Wrapping a value behind methods like this is called encapsulation, and it buys you two real advantages:

  • You can guard against bad values. The setter refused a negative age. A public property would have accepted it without complaint.
  • You can change the inside without breaking the outside. Suppose you later store the age differently, or calculate it from a birth date. As long as getAge() still returns an age, every piece of code that calls it keeps working. The internals are yours to change.
public: open access

Anything can overwrite it directly, even with a value that makes no sense.

$p->age = -5;
// nothing stops this
private: guarded access

Outside code must go through a method, which can check the value first.

$p->setAge(-5);
// the setter rejects it

What the Setter Actually Does

The guard only lets good values through, and it leaves the stored value untouched when it rejects one. That last part surprises people, so predict it before you run it:

What does this print?

<?php
class Person {
    private int $age = 0;

    public function setAge(int $age): void {
        if ($age < 0) {
            return;
        }
        $this->age = $age;
    }

    public function getAge(): int {
        return $this->age;
    }
}

$p = new Person();
$p->setAge(25);
$p->setAge(-5);
echo $p->getAge();
  • 25
  • -5
  • 0

The first call stores 25. The second call sees a negative number, hits return before the assignment line, and leaves the age alone. So 25 is still in the box.

Reaching for a private member throws an Error. If you write echo $p->age; from outside the class, PHP does not print a blank or a zero. It stops the script with a fatal Error: Cannot access private property Person::$age. That is the point: private really means private. Go through the getter instead.

Protected: Shared With Children

Sometimes you want to hide a value from the outside world but still let a child class use it. That is exactly what protected is for. Remember from the inheritance lesson that a child class extends its parent:

<?php
class Animal {
    protected string $name = "Rex";
}

class Dog extends Animal {
    public function speak(): string {
        return $this->name . " says woof";
    }
}

$d = new Dog();
echo $d->speak();
Rex says woof

Inside Dog, the method reaches $this->name freely because protected reaches down into children. Try echo $d->name; from outside, though, and you get the same Error a private property would give you. Protected is the middle setting: open to the family, closed to strangers.

Start closed, open only what you must. A good habit is to make properties private by default and expose just the methods people genuinely need. It is easy to open something up later; it is painful to lock down a property that other code has already been poking at directly.

Check Yourself

Which keyword lets a child class use a property while still hiding it from code outside the class?

  • protected
  • public
  • private

Protected reaches this class and its children. Public would expose it to everyone, and private would hide it even from the children.

Your Turn

The Counter class keeps a private property $count. Add a public getter called getCount() that returns it, then print the count. The output should be exactly 7. Then hit Check my answer.

<?php
class Counter {
    private int $count = 7;
    // add getCount() here
}

$c = new Counter();
// print the count here
<?php
class Counter {
    private int $count = 7;

    public function getCount(): int {
        return $this->count;
    }
}

$c = new Counter();
echo $c->getCount();

What You Learned

  • Visibility sets who can reach a property or method: public is anyone, private is this class only, protected is this class and its children
  • Encapsulation means hiding a private value behind a getter and a setter, so you can validate changes and rework the internals without breaking outside code
  • Reaching a private or protected member from outside throws a fatal Error, not a blank value

You can now:

  • Choose the right visibility keyword for each part of a class
  • Protect a value with a private property, a public getter, and a validating setter

You have controlled access within a single class and its family. Next you will meet interfaces, which let unrelated classes promise to provide the same public methods.

Comments

Leave a reply

You’ll learn to
  • Choose the right visibility keyword for each part of a class
  • Protect a value with a private property, a public getter, and a validating setter
Next: Interfaces →