What Is The Difference Between Public, Private, And Protected In PHP?

Published October 9, 2024

Problem: Understanding Access Modifiers in PHP

PHP uses access modifiers to control the visibility of class properties and methods. Developers often find it hard to tell the difference between public, private, and protected modifiers. This can cause problems with code organization and security. Choosing the right access modifier is important for creating well-structured and maintainable object-oriented PHP code.

Public Access Modifier in PHP

The public access modifier in PHP makes class properties and methods accessible from anywhere in the code. This includes access from outside the class, within the class, and in child classes that inherit from it.

Public properties and methods are useful in these cases:

  1. Interfaces: To define methods that should be available to other parts of your application or external code.

  2. Getters and Setters: Public methods that allow controlled access to private properties.

  3. Utility Functions: Methods that perform general tasks and can be used by various parts of your application.

  4. API Design: When creating classes for use by other developers or in different parts of a large project.

  5. Configuration: Public properties can be used for settings that need to be easily accessible and changeable.

Here's an example of using public access:

class User {
    public $username;

    public function getUsername() {
        return $this->username;
    }

    public function setUsername($username) {
        $this->username = $username;
    }
}

$user = new User();
$user->username = "JohnDoe";  // Direct access to public property
echo $user->getUsername();    // Calling public method

In this example, the $username property and the getUsername() and setUsername() methods are public, allowing them to be accessed and used from outside the class.

Tip: Use Public Methods for Encapsulation

While public properties offer direct access, it's often better to use public methods to access and modify private properties. This approach, known as encapsulation, allows you to add validation or additional logic when getting or setting values. For example:

class User {
    private $email;

    public function setEmail($email) {
        if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->email = $email;
        } else {
            throw new InvalidArgumentException("Invalid email format");
        }
    }

    public function getEmail() {
        return $this->email;
    }
}

This way, you can ensure data integrity and add checks before allowing changes to the object's state.

Private Access Modifier in PHP

The private access modifier in PHP limits the visibility of properties and methods to the class where they are defined. Private elements cannot be accessed or changed from outside the class, including child classes.

Private properties and methods are useful in these cases:

  1. Internal Implementation: For properties and methods that are part of the class's internal workings and should not be accessed from outside.

  2. Data Encapsulation: To hide an object's internal state and control access through public methods.

  3. Preventing Inheritance: When you want to make sure certain properties or methods are not available to child classes.

  4. Helper Functions: For methods that are only used within the class and don't need to be accessed externally.

Here's an example of using private access:

class BankAccount {
    private $balance = 0;

    public function deposit($amount) {
        if ($amount > 0) {
            $this->balance += $amount;
        }
    }

    public function getBalance() {
        return $this->balance;
    }

    private function validateTransaction($amount) {
        return $amount > 0 && $this->balance >= $amount;
    }

    public function withdraw($amount) {
        if ($this->validateTransaction($amount)) {
            $this->balance -= $amount;
            return true;
        }
        return false;
    }
}

$account = new BankAccount();
$account->deposit(100);
echo $account->getBalance(); // Outputs: 100

// The following would result in an error:
// $account->balance = 1000; // Cannot access private property
// $account->validateTransaction(50); // Cannot access private method

In this example, the $balance property and validateTransaction() method are private. You can only access them within the BankAccount class. The public methods deposit(), getBalance(), and withdraw() provide controlled access to the private property and use the private method internally.

Tip: Use Private Methods for Internal Logic

When designing classes, use private methods for internal logic that shouldn't be accessed outside the class. This helps maintain encapsulation and makes your code more maintainable. For example, in the BankAccount class, the validateTransaction() method is private because it's only used internally to check if a withdrawal is valid. By keeping it private, you prevent external code from bypassing the withdraw() method and potentially causing inconsistencies in the account balance.

Protected Access Modifier in PHP

The protected access modifier in PHP allows properties and methods to be accessed within the defining class and by classes that inherit from it. Protected members are available to child classes, but they remain inaccessible from outside the class hierarchy.

Protected properties and methods are useful in these cases:

  • Inheritance: When you want to share properties or methods with child classes, but not make them publicly accessible.

  • Template Method Pattern: For defining a skeleton of an algorithm in a method, allowing subclasses to override specific steps.

  • Base Class Functionality: To provide common functionality that derived classes can use and extend.

  • Partial Encapsulation: When you want to restrict access to certain elements but still allow flexibility for subclasses.

Here's an example of using protected access:

class Vehicle {
    protected $fuelLevel = 0;

    protected function startEngine() {
        if ($this->fuelLevel > 0) {
            return "Engine started";
        }
        return "Not enough fuel";
    }

    public function refuel($amount) {
        $this->fuelLevel += $amount;
    }
}

class Car extends Vehicle {
    public function drive() {
        $status = $this->startEngine();
        if ($status === "Engine started") {
            return "Car is driving";
        }
        return $status;
    }
}

$car = new Car();
$car->refuel(10);
echo $car->drive(); // Outputs: Car is driving

// The following would result in an error:
// echo $car->fuelLevel; // Cannot access protected property
// $car->startEngine(); // Cannot access protected method

In this example, the $fuelLevel property and startEngine() method are protected. You can access them within the Vehicle class and its child class Car, but not from outside these classes. The Car class can use the protected members inherited from Vehicle, allowing it to check the fuel level and start the engine in its drive() method.

Tip: Use Protected for Extensibility

Protected members are useful when you want to create a base class that can be extended by other classes. By using protected, you allow child classes to access and modify certain properties or methods while still maintaining some level of encapsulation. This approach is often used in frameworks and libraries to provide components that developers can customize for their specific needs.

Example: Using Protected in a Logger Class

class BaseLogger {
    protected $logFile;

    protected function writeLog($message) {
        file_put_contents($this->logFile, $message . PHP_EOL, FILE_APPEND);
    }

    public function setLogFile($file) {
        $this->logFile = $file;
    }
}

class ErrorLogger extends BaseLogger {
    public function logError($errorMessage) {
        $formattedMessage = "[ERROR] " . date('Y-m-d H:i:s') . ": " . $errorMessage;
        $this->writeLog($formattedMessage);
    }
}

$logger = new ErrorLogger();
$logger->setLogFile('error.log');
$logger->logError('An error occurred');

In this example, the BaseLogger class uses a protected $logFile property and a protected writeLog() method. The ErrorLogger class extends BaseLogger and can access these protected members to implement its specific logging functionality.

Comparing Access Modifiers

Public vs Private

Public and private access modifiers have differences in their usage and scope:

  1. Accessibility:

    • Public: Accessible from anywhere, including outside the class.
    • Private: Only accessible within the defining class.
  2. Inheritance:

    • Public: Inherited by child classes and remain public.
    • Private: Not inherited by child classes.
  3. Object instances:

    • Public: Can be accessed directly through object instances.
    • Private: Cannot be accessed directly through object instances.
  4. Usage:

    • Public: Use for methods and properties that need to be accessed externally.
    • Private: Use for internal implementation details and to enforce encapsulation.

Example: Public vs Private Access

class User {
    public $username;
    private $password;

    public function setPassword($newPassword) {
        $this->password = $newPassword;
    }
}

$user = new User();
$user->username = "JohnDoe"; // Works fine
$user->password = "secret"; // Throws an error
$user->setPassword("secret"); // Works fine

Private vs Protected

Private and protected access modifiers differ in their behavior with inheritance:

  1. Class access:

    • Private: Only accessible within the defining class.
    • Protected: Accessible within the defining class and its child classes.
  2. Inheritance:

    • Private: Not inherited by child classes.
    • Protected: Inherited by child classes.
  3. Visibility in child classes:

    • Private: Invisible to child classes.
    • Protected: Visible and usable in child classes.
  4. Usage:

    • Private: Use for strictly internal class members.
    • Protected: Use when you want to allow access in child classes but restrict public access.

Tip: Choosing Between Private and Protected

Use private when you want to completely hide a member from outside the class, including child classes. Use protected when you want to allow child classes to access and potentially override the member while still keeping it hidden from external code.

Protected vs Public

Protected and public access modifiers have different levels of accessibility:

  1. External access:

    • Protected: Not accessible from outside the class hierarchy.
    • Public: Accessible from anywhere.
  2. Inheritance:

    • Protected: Inherited by child classes but remain protected.
    • Public: Inherited by child classes and remain public.
  3. Encapsulation:

    • Protected: Provides a level of encapsulation within the class hierarchy.
    • Public: Offers no encapsulation.
  4. Usage:

    • Protected: Use for members that should be available to child classes but not publicly.
    • Public: Use for members that need to be accessible from anywhere in the code.

Example: Protected vs Public Inheritance

class ParentClass {
    protected $protectedVar = "Protected";
    public $publicVar = "Public";
}

class ChildClass extends ParentClass {
    public function accessVars() {
        echo $this->protectedVar; // Works fine
        echo $this->publicVar; // Works fine
    }
}

$child = new ChildClass();
echo $child->publicVar; // Works fine
echo $child->protectedVar; // Throws an error