Methods — instance, static, visibility (public/protected/private)
Concept
PHP methods have three visibility levels that control access: public, protected, and private. Understanding visibility is the first step to designing well-encapsulated classes.
public: Callable from anywhere — inside the class, from subclasses, from external code. The class's public interface.
protected: Callable from within the class and from any subclass. Not accessible from external code. Use for "internal machinery" that subclasses may need to override or extend, but external callers should not see.
private: Callable only from within the declaring class. Subclasses cannot call private methods or see private properties — not even through parent::. Use for implementation details that must not leak or be overridden.
Instance methods (public function doSomething()) are called on an object ($obj->doSomething()). They have access to $this.
Static methods (public static function create()) are called on the class (MyClass::create()). They have no access to $this — they operate at the class level, not the instance level. Static methods are often used for factory patterns, utility functions, and Laravel macros.
Abstract methods (covered in a later lesson) declare a method signature without a body — subclasses must implement them.
Final methods cannot be overridden in subclasses. Use final sparingly — it prevents extension and can make testing harder. Appropriate when an algorithm must remain exactly as implemented for correctness or security.
Code Example
<?php
declare(strict_types=1);
class BankAccount
{
private float $balance;
private array $transactions = [];
public function __construct(float $initialBalance)
{
$this->balance = $initialBalance;
}
// Public interface — what callers can do
public function deposit(float $amount): void
{
$this->validateAmount($amount); // calls private method
$this->balance += $amount;
$this->recordTransaction('deposit', $amount);
}
public function getBalance(): float
{
return $this->balance;
}
// Protected — subclasses can call/override this
protected function recordTransaction(string $type, float $amount): void
{
$this->transactions[] = ['type' => $type, 'amount' => $amount];
}
// Private — only this class can call this
private function validateAmount(float $amount): void
{
if ($amount <= 0) {
throw new \InvalidArgumentException("Amount must be positive");
}
}
}
// Subclass CAN access protected, CANNOT access private
class LoggedAccount extends BankAccount
{
protected function recordTransaction(string $type, float $amount): void
{
parent::recordTransaction($type, $amount);
error_log("[$type] $amount"); // adds logging
}
// Cannot call $this->validateAmount() — it's private to BankAccount
}
// Static factory method
class User
{
private function __construct(
public readonly string $email,
public readonly string $role,
) {}
public static function admin(string $email): self
{
return new self($email, 'admin');
}
public static function guest(string $email): self
{
return new self($email, 'guest');
}
}
$admin = User::admin('admin@example.com'); // cleaner than new User(..., 'admin')