Lab 07: OOP — Classes & Encapsulation

Objective

Design PHP classes with properties, constructors, methods, access modifiers, readonly properties, and enums. Apply encapsulation, constructor promotion, and static members.

Background

PHP has first-class OOP support since PHP 5, with major modernization in PHP 7.4 (typed properties), PHP 8.0 (constructor promotion, match, attributes), and PHP 8.1 (enums, readonly, intersection types). Modern PHP OOP is clean and expressive — Laravel, Symfony, and WordPress are all built on it.

Time

40 minutes

Prerequisites

  • Lab 05 (Functions)

Tools

  • PHP 8.3 CLI

  • Docker image: zchencow/innozverse-php:latest


Lab Instructions

Step 1: Classes & Constructor Promotion

💡 Constructor promotion (PHP 8.0) eliminates boilerplate: private string $name in the constructor parameter list simultaneously declares the property and assigns the argument. readonly (PHP 8.1) makes a property writable only in the constructor — perfect for value objects.

📸 Verified Output:


Step 2: Enums (PHP 8.1)

💡 PHP Enums (8.1) are first-class types — not just constants. Pure enums are for semantic labels; backed enums carry a string or int value for serialization. They can implement interfaces, have methods, and work with match exhaustiveness checks. Use them instead of const PENDING = 1 patterns.

📸 Verified Output:


Step 3: Readonly Properties & Value Objects

💡 Value objects are immutable — operations return new instances instead of modifying state. Money::add() returns a new Money, leaving the originals unchanged. This prevents subtle bugs where shared money objects are accidentally modified. Readonly properties enforce immutability at the language level.

📸 Verified Output:


Step 4: Static Members & Singleton

💡 Static factory methods (named constructors) are more expressive than constructors: Color::fromHex('#ff0000') and Color::fromRgb(255,0,0) both create the same type but communicate intent. They also allow caching — Color::fromHex could return a cached instance. Prefer them over multiple constructors.

📸 Verified Output:


Step 5: Magic Methods

💡 Magic methods start with __ and are called implicitly by PHP. __get/__set intercept property access, __call intercepts method calls on undefined methods. They power Laravel's Eloquent ORM (model attributes), PHP's proxy patterns, and dynamic API clients. Use them thoughtfully — they can hide bugs.

📸 Verified Output:


Step 6: Traits

💡 Traits are "horizontal code reuse" — they inject methods and properties into classes without inheritance. A class can use multiple traits (unlike extends which is single). Use traits for cross-cutting concerns: logging, timestamps, soft delete, caching. Prefer interfaces + traits over deep inheritance hierarchies.

📸 Verified Output:


Step 7: Interfaces & Abstract Classes in OOP

💡 Abstract classes provide a template — they can have concrete methods (shared implementation) and abstract methods (required overrides). Use abstract classes when subclasses share code; use interfaces when you only need to enforce a contract. A class can implement multiple interfaces but extend only one abstract class.

📸 Verified Output:


Step 8: Complete Example — Product Catalog

💡 self::$nextId++ in the constructor auto-increments a class-level counter — each new Product gets a unique ID without a database. This is the in-memory auto-increment pattern. In production, IDs come from a database sequence, but this pattern is useful for tests and prototypes.

📸 Verified Output:


Verification

Summary

PHP 8 OOP is modern and expressive. You've covered constructor promotion, readonly properties, enums, value objects, static members, magic methods, traits, and a full product catalog. These patterns are exactly what you'll find in Laravel, Symfony, and every professional PHP codebase.

Further Reading

Last updated