6 - OOP in PHP: Practical guide with real-world business examples
Short description: Learn classes, inheritance, constructors, visibility (public/protected/private), static properties/methods, and how to apply these concepts to real business systems (users, players, customers, CRM).
Why OOP matters for real business apps (SEO: PHP OOP, PHP classes, PHP inheritance)
Object-Oriented Programming (OOP) helps you write clear, maintainable, and reusable code — essential for business apps such as CRMs, e-commerce platforms, learning management systems and SaaS products. Instead of scattered procedural functions, OOP organizes code into objects (users, orders, products) that mirror real business entities. This improves developer collaboration, speeds up feature delivery, and simplifies long-term maintenance.
Lesson goals
- Understand PHP classes, properties and methods.
- Learn differences between
public,protected, andprivate. - See how
staticproperties and methods work and when to use them. - Understand constructors and
$thisvsself::. - Walk through the provided
MemberandPlayerexample and map it to business scenarios. - Follow best practices and avoid common pitfalls.
Provided example — files and code
Two files are used in the example: member.php (a base class) and Player.php (extends Member).
member.php (base class)
<?php
class Member {
public static $member = "Ali";
public $memberName = "Ahmed";
public static $memberSelected = "no";
// Autoload with the class
function __construct($age) {
// this: related to the closest object
self::$memberSelected = "yes";
echo $age;
}
public static function getMemberPublic() {
// return $this->member['name'];
echo "hello from function static <br/>";
// echo $this->getMemberName();
// echo $this->member;
}
// can't access this function everywhere except the class itself or an extends class
protected function getMemberName() {
// return $this->member['name'];
echo "function getMemberName";
}
// private method can be called only within the class
private function calcAgeInDays() {
echo "<br/> calc age in days <br/> ";
}
}
Player.php (child class)
<?php
include_once('member.php');
class Player extends Member {
public function getPlayerName() {
// echo $this->getMemberName(); // protected only from another class
// echo $this->calcAgeInDays(); // error, no call for private
}
}
echo Player::$memberSelected;
echo "<br/>";
new Player(32);
echo Player::$memberSelected;
Line-by-line explanation (key concepts)
1. Class and instance properties
public $memberName is an instance property — every object (instance of Member) gets its own copy. Use instance properties for data specific to an individual entity (a particular user, order, or product).
2. Static properties
public static $member and public static $memberSelected belong to the class itself (not to individual objects). They are shared across the entire class and its children. Use static properties for values that are global to a class (feature flags, counters, shared configuration).
3. The constructor __construct($age)
When you create a new Member (or Player, because it inherits the constructor), the constructor runs and receives $age. In the example it sets the static $memberSelected to "yes" and echoes the age. In business code, constructors typically initialize required properties or inject dependencies (database, logger).
4. $this vs self::
$this refers to the current object instance; you use it to access instance properties and methods. self:: refers to the class where the code is written (static context) — use it to access static properties/methods. In the example, the constructor uses self::$memberSelected to modify the static property.
5. Visibility: public, protected, private
- public — accessible from anywhere (instances, other classes, global scope).
- protected — accessible inside the class and by child classes (inheritance), but not from outside.
- private — accessible only inside the class where declared (not even child classes).
In the example, getMemberName() is protected, so Player can call it. calcAgeInDays() is private so it's not available in Player (commented out to show an error if called).
6. Static methods
public static function getMemberPublic() is callable without creating an instance: Member::getMemberPublic(). Static methods are useful for utility functions that don't require object state.
7. Instantiation and static behavior in the example
The example prints Player::$memberSelected before and after creating a new Player(32). Because the constructor sets self::$memberSelected = "yes", the value changes after instantiation — illustrating how static class state can be updated at runtime.
Practical business examples
Example A — CRM: Member and Customer
Map Member to a generic User or Customer class. A Customer class might extend User adding purchase history and loyalty points. Use protected methods for internal calculations (e.g., computing tier level) and private methods for confidential logic (e.g., decrypting a value).
Example B — E-commerce: Product & DigitalProduct
A base Product class has shared fields (price, SKU, name). DigitalProduct extends it (adds download link). Static properties can store global tax rates or SKU prefixes.
Example C — Gaming / LMS: Player & Member (your example)
Your Member / Player example matches a gaming or LMS scenario: Member stores common attributes; Player adds gameplay-specific logic. The constructor could load profile data; protected methods manage private profile workflows; statics can track global settings like "maintenance mode".
Best practices & patterns
- Prefer composition to excessive inheritance. Use traits or dependency injection when appropriate.
- Minimize use of public properties. Use getters/setters to validate access — or declare properties protected/private and expose controlled accessors.
- Limit static state. Static properties are global and can introduce hard-to-track side effects; prefer dependency injection or singletons only when justified.
- Keep constructors lightweight. Avoid heavy logic in constructors; prefer factory methods for complex initialization.
- Use protected for extensibility. Make helper methods protected if subclasses should reuse them, but keep sensitive logic private.
- Document methods and visibility. Use PHPDoc for methods and properties so IDEs and documentation tools help other developers.
Common pitfalls (and how to avoid them)
- Calling private methods from child classes: will error. Use protected if child classes need access.
- Overusing static state: leads to hidden dependencies across requests. Use small, controlled static values or dependency injection.
- Echoing inside models: In the example, the constructor echoes
$age. For clean separation, models should not echo or print — controllers or views should handle output. - Shadowing properties: Avoid redeclaring same-named properties in child classes unless intentionally overriding (and document it).
Refactored example (cleaner, business-ready)
Below is a cleaner version that avoids echoing inside the class and uses getters:
<?php
class Member {
protected string $memberName = "Ahmed";
protected static string $memberSelected = "no";
public function __construct(protected int $age) {
self::$memberSelected = "yes";
}
public static function isMemberSelected(): bool {
return self::$memberSelected === "yes";
}
protected function getMemberName(): string {
return $this->memberName;
}
private function calcAgeInDays(): int {
return $this->age * 365;
}
}
class Player extends Member {
public function getPlayerName(): string {
return $this->getMemberName(); // allowed: protected
}
}
$player = new Player(32);
// presentation layer prints values:
echo $player->getPlayerName();
echo Member::isMemberSelected() ? 'selected' : 'not selected';
Mini exercises (practice)
- Modify
Memberto accept name and age. Add a public methodgetProfile()that returns an array with name and age. - Create a
Coachclass that extendsMemberand adds a methodassignPlayer(Player $p). - Replace the static
$memberSelectedwith a dependency-injectedFeatureToggleservice and show how to instantiate with a factory.
SEO-friendly FAQ (short answers, good for snippets)
- What is the difference between public, protected and private in PHP?
- Public is accessible from everywhere. Protected is accessible inside the class and by subclasses. Private is accessible only within the declaring class.
- When should I use static properties?
- Use static for truly class-wide state or constants. For shared services or state that evolves per request, prefer dependency injection to avoid side effects.
- Why avoid echoing from a constructor?
- Constructors should prepare an object's state. Output belongs in the view/presentation layer; mixing them makes testing and reuse harder.
Conclusion & next steps
This lesson explained core OOP concepts using the Member / Player example. You can immediately apply these ideas to user models, product models, and domain objects in business applications.
Next lessons could cover: interfaces & abstract classes, dependency injection, design patterns (Repository, Service, Factory), and unit testing OOP code.
