intermediate Step 7 of 16

Object-Oriented PHP

PHP Programming

Object-Oriented PHP

Object-oriented programming in PHP has matured significantly, especially with PHP 8.x adding constructor promotion, named arguments, enums, readonly properties, and intersection types. Modern PHP OOP rivals the expressiveness of Java and TypeScript. Understanding OOP is essential because all major PHP frameworks (Laravel, Symfony, WordPress plugins) are built using object-oriented patterns. Classes, interfaces, traits, and abstract classes form the building blocks of well-structured PHP applications.

Classes and Constructor Promotion

<?php
declare(strict_types=1);

// Traditional class
class User {
    private string $name;
    private string $email;
    private string $role;

    public function __construct(string $name, string $email, string $role = 'user') {
        $this->name = $name;
        $this->email = $email;
        $this->role = $role;
    }

    public function getName(): string {
        return $this->name;
    }
}

// Constructor promotion (PHP 8.0+) — much cleaner!
class Product {
    public function __construct(
        private readonly string $name,      // readonly = cannot change after set
        private readonly float $price,
        private string $category = 'general',
        private bool $active = true
    ) {}

    public function getInfo(): string {
        return "{$this->name}: \${$this->price} ({$this->category})";
    }

    public function deactivate(): void {
        $this->active = false;
    }
}

$product = new Product(name: "Laptop", price: 999.99, category: "electronics");
echo $product->getInfo();  // "Laptop: $999.99 (electronics)"
?>

Inheritance and Interfaces

<?php
// Interface — defines a contract
interface Renderable {
    public function render(): string;
}

interface Validatable {
    public function validate(): bool;
    public function getErrors(): array;
}

// Abstract class — partial implementation
abstract class Shape implements Renderable {
    public function __construct(
        protected string $color = 'black'
    ) {}

    abstract public function area(): float;

    public function render(): string {
        return "A {$this->color} shape with area " . number_format($this->area(), 2);
    }
}

// Concrete classes
class Circle extends Shape {
    public function __construct(
        private float $radius,
        string $color = 'black'
    ) {
        parent::__construct($color);
    }

    public function area(): float {
        return M_PI * $this->radius ** 2;
    }
}

class Rectangle extends Shape {
    public function __construct(
        private float $width,
        private float $height,
        string $color = 'black'
    ) {
        parent::__construct($color);
    }

    public function area(): float {
        return $this->width * $this->height;
    }
}

// Polymorphism
$shapes = [
    new Circle(5, 'red'),
    new Rectangle(4, 6, 'blue'),
];
foreach ($shapes as $shape) {
    echo $shape->render() . "
";
}
?>

Traits

<?php
// Traits — reusable code blocks (like mixins)
trait HasTimestamps {
    private ?DateTime $createdAt = null;
    private ?DateTime $updatedAt = null;

    public function setCreatedAt(): void {
        $this->createdAt = new DateTime();
    }

    public function getCreatedAt(): ?DateTime {
        return $this->createdAt;
    }
}

trait SoftDeletes {
    private ?DateTime $deletedAt = null;

    public function softDelete(): void {
        $this->deletedAt = new DateTime();
    }

    public function isDeleted(): bool {
        return $this->deletedAt !== null;
    }

    public function restore(): void {
        $this->deletedAt = null;
    }
}

class Post {
    use HasTimestamps, SoftDeletes;

    public function __construct(
        private string $title,
        private string $body
    ) {
        $this->setCreatedAt();
    }
}

$post = new Post("Hello", "World");
$post->softDelete();
echo $post->isDeleted() ? "Deleted" : "Active";  // "Deleted"
$post->restore();
echo $post->isDeleted() ? "Deleted" : "Active";  // "Active"
?>

Enums (PHP 8.1+)

<?php
// Basic enum
enum Color {
    case Red;
    case Green;
    case Blue;
}

// Backed enum (with values)
enum Status: string {
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';

    public function label(): string {
        return match($this) {
            self::Draft => 'Draft',
            self::Published => 'Published',
            self::Archived => 'Archived',
        };
    }

    public function canTransitionTo(self $new): bool {
        return match($this) {
            self::Draft => $new === self::Published,
            self::Published => $new === self::Archived,
            self::Archived => false,
        };
    }
}

$status = Status::Draft;
echo $status->value;     // "draft"
echo $status->label();   // "Draft"
echo $status->canTransitionTo(Status::Published) ? "Yes" : "No";  // "Yes"

// Create from value
$fromDb = Status::from('published');
$maybe = Status::tryFrom('invalid');  // null (no exception)
?>
Pro tip: Use constructor promotion (PHP 8.0+) for clean, concise class definitions. Combined with readonly properties (PHP 8.1+), you can create immutable value objects in just a few lines. Prefer composition with traits over deep inheritance hierarchies — traits let you mix and match behavior without the limitations of single inheritance.

Key Takeaways

  • Constructor promotion (public function __construct(private string $name)) eliminates boilerplate property declarations.
  • Use interfaces for contracts, abstract classes for partial implementations, and traits for reusable code blocks.
  • readonly properties (PHP 8.1+) create immutable values that can only be set once in the constructor.
  • Enums (PHP 8.1+) replace magic strings and constants with type-safe, method-equipped values.
  • PHP supports single inheritance; use traits to compose multiple behaviors into a class.