Introduction to Object-Oriented PHP
Learning Objectives
- Understand OOP principles in PHP
- Create and use classes and objects
- Implement inheritance and polymorphism
- Apply OOP best practices
Welcome to Object-Oriented Programming in PHP
Today, we embark on an exciting journey into the world of Object-Oriented Programming (OOP) in PHP. If you've been writing procedural PHP code, think of this transition like upgrading from a bicycle to a car—both will get you to your destination, but OOP provides more power, organization, and features for the journey.
WordPress, our ultimate destination in this course, is built using object-oriented principles. Understanding OOP isn't just academic—it's essential for working with WordPress themes, plugins, and core functionality.
The Building Blocks of OOP
Classes and Objects: The Blueprint Analogy
Imagine you're an architect designing houses. A class is like your blueprint—it defines the structure but isn't a physical house itself. An object is an actual house built from that blueprint.
In PHP, we define a class and then create objects (also called instances) from it:
<?php
// Define the class (blueprint)
class Car {
// Properties will go here
// Methods will go here
}
// Create objects (actual cars) from the blueprint
$tesla = new Car();
$honda = new Car();
?>
Properties: The Characteristics
Properties (also called attributes or fields) are the characteristics of your objects. In our car example, properties might include color, model, year, and fuel level.
<?php
class Car {
// These are properties
public $color;
public $model;
public $year;
public $fuelLevel = 100; // With default value
}
$tesla = new Car();
$tesla->color = "Red"; // Setting a property
$tesla->model = "Model 3";
$tesla->year = 2023;
echo "My car is a {$tesla->color} {$tesla->model} from {$tesla->year}.";
// Outputs: My car is a Red Model 3 from 2023.
?>
Think of properties as the form fields in WordPress that store information about posts, pages, or users.
Methods: The Behaviors
Methods are functions defined within a class that describe what objects can do. They represent behaviors or actions.
<?php
class Car {
public $color;
public $fuelLevel = 100;
// This is a method
public function drive($distance) {
// Cars use fuel when driving
$this->fuelLevel -= ($distance / 10);
return "The {$this->color} car drove {$distance} miles. Fuel remaining: {$this->fuelLevel}%";
}
// Another method
public function refuel() {
$this->fuelLevel = 100;
return "The car has been refueled!";
}
}
$tesla = new Car();
$tesla->color = "Red";
echo $tesla->drive(50); // Outputs: The Red car drove 50 miles. Fuel remaining: 95%
echo $tesla->refuel(); // Outputs: The car has been refueled!
?>
In WordPress, methods might handle actions like displaying content, saving posts, or processing form submissions.
OOP in WordPress: Real-World Context
Before we dive deeper, let's see how these concepts connect to WordPress development:
When you work with WordPress, you're constantly interacting with objects. For example, $post is an object created from the WP_Post class, and the function get_the_title() is essentially accessing that object's post_title property.
Creating Classes and Instantiating Objects
Let's create a practical example related to a blog system (like WordPress) to demonstrate class creation and object instantiation.
<?php
class BlogPost {
// Properties
public $title;
public $content;
public $author;
public $publishDate;
public $commentCount = 0;
// Methods
public function display() {
$formattedDate = date("F j, Y", strtotime($this->publishDate));
echo "<article>";
echo "<h2>{$this->title}</h2>";
echo "<p class='meta'>By {$this->author} on {$formattedDate} | {$this->commentCount} comments</p>";
echo "<div class='content'>{$this->content}</div>";
echo "</article>";
}
public function addComment() {
$this->commentCount++;
return "Comment added! Total comments: {$this->commentCount}";
}
}
// Creating multiple blog post objects
$firstPost = new BlogPost();
$firstPost->title = "Getting Started with WordPress";
$firstPost->content = "WordPress is a powerful content management system...";
$firstPost->author = "Jane Doe";
$firstPost->publishDate = "2025-04-20";
$secondPost = new BlogPost();
$secondPost->title = "Understanding WordPress Hooks";
$secondPost->content = "Hooks are a way for one piece of code to interact with another...";
$secondPost->author = "John Smith";
$secondPost->publishDate = "2025-04-25";
// Using the objects
$firstPost->display();
echo $firstPost->addComment();
echo $firstPost->addComment();
$secondPost->display();
?>
The New Class Instance Pattern
Sometimes, you'll see a pattern where methods return a new instance of their class, allowing for method chaining:
<?php
class QueryBuilder {
private $table;
private $conditions = [];
public function table($tableName) {
$this->table = $tableName;
return $this; // Return the object itself
}
public function where($column, $value) {
$this->conditions[] = "$column = '$value'";
return $this; // Return the object itself
}
public function get() {
$whereClause = empty($this->conditions) ? '' : 'WHERE ' . implode(' AND ', $this->conditions);
$query = "SELECT * FROM {$this->table} $whereClause";
return $query;
}
}
// Method chaining
$query = new QueryBuilder();
$result = $query->table('users')
->where('status', 'active')
->where('role', 'admin')
->get();
echo $result; // Outputs: SELECT * FROM users WHERE status = 'active' AND role = 'admin'
?>
WordPress uses this pattern extensively, especially in the WP_Query class.
Constructor and Destructor Methods
Constructors: The Birth of an Object
A constructor is a special method that's automatically called when an object is created. It's perfect for setting up the initial state of your object.
<?php
class Product {
public $name;
public $price;
public $description;
public $inStock;
// Constructor - runs automatically when object is created
public function __construct($name, $price, $description = '', $inStock = true) {
$this->name = $name;
$this->price = $price;
$this->description = $description;
$this->inStock = $inStock;
echo "A new product '{$this->name}' has been created!<br>";
}
public function display() {
echo "<div class='product'>";
echo "<h3>{$this->name} - \${$this->price}</h3>";
if ($this->description) {
echo "<p>{$this->description}</p>";
}
echo $this->inStock ? "<span class='in-stock'>In Stock</span>" : "<span class='out-of-stock'>Out of Stock</span>";
echo "</div>";
}
}
// Creating objects with constructor parameters
$laptop = new Product("MacBook Pro", 1799, "Powerful laptop for developers");
$smartphone = new Product("iPhone 15", 999, "Latest smartphone with advanced features");
$headphones = new Product("AirPods Pro", 249, "", false); // Out of stock
// Display the products
$laptop->display();
$smartphone->display();
$headphones->display();
?>
In WordPress plugin development, constructors often handle hooks, filters, and initialization tasks.
Destructors: The End of an Object's Life
A destructor is called when an object is no longer needed or when the script ends. It's useful for cleanup tasks.
<?php
class DatabaseConnection {
private $connection;
public function __construct($host, $username, $password, $database) {
echo "Connecting to database...
";
// In a real application, this would be an actual database connection
$this->connection = "Connected to $database on $host";
echo "Connected!
";
}
public function query($sql) {
echo "Executing query: $sql
";
return "Query results for: $sql";
}
public function __destruct() {
echo "Closing database connection...
";
$this->connection = null;
echo "Connection closed!
";
}
}
// Create a scope block - when it ends, the object will be destroyed
{
$db = new DatabaseConnection("localhost", "root", "password", "my_wordpress_db");
$result = $db->query("SELECT * FROM wp_posts LIMIT 10");
echo $result . "
";
// $db will be destroyed when this block ends
}
echo "After the database operations.
";
?>
WordPress uses destructors in some classes to clean up resources, close file handles, or perform final logging.
Access Modifiers: Controlling Visibility
Access modifiers control how properties and methods can be accessed. Think of them as security permissions for your code.
Public: Open to Everyone
Public members can be accessed from anywhere—inside the class, in child classes, and from outside the class.
Protected: Family Access Only
Protected members can only be accessed from within the class itself and by child classes (inheritance).
Private: Personal Access Only
Private members can only be accessed from within the class that defined them.
<?php
class BankAccount {
public $accountHolder; // Anyone can see who owns the account
protected $accountNumber; // Only the bank and its branches can see this
private $pin; // Only this specific account logic can access the PIN
public function __construct($holder, $accountNum, $pin) {
$this->accountHolder = $holder;
$this->accountNumber = $accountNum;
$this->pin = $pin;
}
// Public method - can be called from anywhere
public function displayAccountSummary() {
// Last 4 digits only for security
$lastFour = substr($this->accountNumber, -4);
return "Account for {$this->accountHolder}, Account #: XXXX-XXXX-$lastFour";
}
// Protected method - only used by this class and child classes
protected function validateAccount() {
// Some validation logic
return true;
}
// Private method - only used internally
private function verifyPin($enteredPin) {
return $enteredPin === $this->pin;
}
// Public interface to access private functionality
public function withdrawMoney($amount, $enteredPin) {
if ($this->verifyPin($enteredPin)) {
return "Withdrawing \$$amount from account";
} else {
return "Incorrect PIN!";
}
}
}
// Create an account
$myAccount = new BankAccount("Jane Smith", "1234-5678-9012-3456", "1234");
// These work fine
echo $myAccount->accountHolder; // Works - public property
echo $myAccount->displayAccountSummary(); // Works - public method
// These would cause errors if uncommented
// echo $myAccount->accountNumber; // Error - protected property
// echo $myAccount->pin; // Error - private property
// $myAccount->validateAccount(); // Error - protected method
// $myAccount->verifyPin("1234"); // Error - private method
// Proper way to use private functionality
echo $myAccount->withdrawMoney(100, "1234"); // Works - using public interface
echo $myAccount->withdrawMoney(50, "wrong"); // Works but returns "Incorrect PIN!"
?>
Encapsulation: The Power of Access Modifiers
Using access modifiers properly gives you encapsulation—a core OOP principle that bundles data with the methods that operate on it and restricts direct access to an object's components.
WordPress uses these concepts extensively. For instance, post data might be public, while internal caching mechanisms might be protected or private.
The $this Keyword: Self-Reference
The $this keyword is a self-reference to the current object. Think of it as the object saying "me" or "myself."
<?php
class ShoppingCart {
private $items = [];
private $totalPrice = 0;
public function addItem($name, $price, $quantity = 1) {
$this->items[] = [
'name' => $name,
'price' => $price,
'quantity' => $quantity
];
$this->totalPrice += ($price * $quantity);
$this->updateCartSummary();
return $this; // Enabling method chaining
}
public function removeItem($index) {
if (isset($this->items[$index])) {
$item = $this->items[$index];
$this->totalPrice -= ($item['price'] * $item['quantity']);
unset($this->items[$index]);
$this->items = array_values($this->items); // Re-index array
$this->updateCartSummary();
}
return $this;
}
private function updateCartSummary() {
echo "Cart updated! Items: " . count($this->items) . ", Total: $" . number_format($this->totalPrice, 2) . "
";
}
public function displayCart() {
if (empty($this->items)) {
echo "Your cart is empty!";
return;
}
echo "";
echo "Item Price Quantity Subtotal ";
foreach ($this->items as $index => $item) {
$subtotal = $item['price'] * $item['quantity'];
echo "";
echo "{$item['name']} ";
echo "$" . number_format($item['price'], 2) . " ";
echo "{$item['quantity']} ";
echo "$" . number_format($subtotal, 2) . " ";
echo " ";
}
echo "Total $" . number_format($this->totalPrice, 2) . " ";
echo "
";
}
}
// Using $this for method chaining
$cart = new ShoppingCart();
$cart->addItem("WordPress Theme", 59.99)
->addItem("Custom Plugin", 29.99, 2)
->addItem("Support Package", 99.99)
->displayCart();
// Remove the second item
$cart->removeItem(1)->displayCart();
?>
Common $this Mistakes
New PHP developers often make these errors with $this:
<?php
class Common_Mistakes {
public $name = "WordPress";
public function wrong_example() {
// WRONG: $name doesn't refer to the property
$name = "Modified"; // This creates a local variable
echo $name; // Outputs: "Modified"
echo $this->name; // Still outputs: "WordPress"
// WRONG: Forgetting $this when calling internal methods
display_name(); // Error: Call to undefined function
// RIGHT: Using $this to access properties and methods
$this->name = "Modified WordPress";
$this->display_name();
}
public function display_name() {
echo "Name: " . $this->name;
}
}
?>
In WordPress development, $this is crucial when building classes for plugins and themes, especially when you need to reference properties or methods within the same class.
Static Properties and Methods: Class-Level Variables and Functions
Static members belong to the class itself, not to any specific instance. Think of them as shared resources among all objects of that class.
Static Properties: Shared Data
<?php
class User {
// Static property - shared among all User objects
private static $userCount = 0;
// Regular instance properties
public $username;
public $email;
private $password;
public function __construct($username, $email, $password) {
$this->username = $username;
$this->email = $email;
$this->password = password_hash($password, PASSWORD_DEFAULT);
// Increment the static user count
self::$userCount++;
}
// Static method to access static property
public static function getUserCount() {
return self::$userCount;
}
// Regular instance method
public function displayInfo() {
echo "Username: {$this->username}, Email: {$this->email}
";
echo "You are user #" . self::$userCount . "
";
}
}
// Static properties and methods can be accessed without creating an object
echo "Initial user count: " . User::getUserCount() . "
"; // Output: 0
// Create some users
$user1 = new User("john_doe", "john@example.com", "password123");
$user2 = new User("jane_smith", "jane@example.com", "secure456");
// Access static method again
echo "Current user count: " . User::getUserCount() . "
"; // Output: 2
// We can also access static properties/methods through an instance
// (though this is not recommended for clarity)
echo "User count via instance: " . $user1::getUserCount() . "
";
// Display individual user info
$user1->displayInfo();
$user2->displayInfo();
?>
Static vs. Non-Static: When to Use Each
<?php
class DatabaseConnection {
// Static property holding the single connection
private static $connection = null;
// Private constructor prevents direct instantiation
private function __construct() {
// This won't be called directly
}
// Static method to get the connection (Singleton pattern)
public static function getConnection() {
if (self::$connection === null) {
echo "Creating new database connection...
";
// In real code, this would connect to a database
self::$connection = "Database Connection Object";
} else {
echo "Reusing existing database connection...
";
}
return self::$connection;
}
}
// Using the static method - first time creates connection
$db1 = DatabaseConnection::getConnection();
echo "DB1: " . $db1 . "
";
// Second time reuses the same connection
$db2 = DatabaseConnection::getConnection();
echo "DB2: " . $db2 . "
";
// This won't work - constructor is private
// $dbDirect = new DatabaseConnection(); // Error!
?>
The code above demonstrates the Singleton pattern, commonly used in WordPress for things like the global $wpdb database connection.
Static Methods in WordPress
WordPress uses static methods frequently. For example, the helper class WP_Query has static methods like get_posts().
<?php
// Example similar to WordPress helper class
class WP_Theme_Helper {
// Static utility methods
public static function get_theme_directory() {
return '/wp-content/themes/current-theme';
}
public static function asset_url($file) {
return self::get_theme_directory() . '/assets/' . $file;
}
public static function featured_image_markup($post_id, $size = 'thumbnail') {
$image_url = "/path/to/image_{$size}.jpg"; // Simplified example
return "
";
}
}
// Using static methods without instantiation
echo WP_Theme_Helper::get_theme_directory() . "
";
echo "
";
echo WP_Theme_Helper::featured_image_markup(123, 'medium');
?>
OOP in WordPress: Practical Examples
Let's look at how a simple WordPress plugin might use OOP:
<?php
/**
* Plugin Name: Simple Contact Form
* Description: Adds a contact form to your WordPress site using OOP principles
* Version: 1.0
* Author: Your Name
*/
// Make sure this file is not directly accessed
if (!defined('ABSPATH')) {
exit;
}
class Simple_Contact_Form {
// Properties
private $form_submitted = false;
private $submission_errors = [];
// Constructor - hooks into WordPress
public function __construct() {
// Actions and filters
add_shortcode('contact_form', [$this, 'render_form']);
add_action('wp_enqueue_scripts', [$this, 'enqueue_styles']);
add_action('init', [$this, 'handle_form_submission']);
}
// Method to register and enqueue styles
public function enqueue_styles() {
wp_register_style(
'simple-contact-form',
plugin_dir_url(__FILE__) . 'css/contact-form.css',
[],
'1.0'
);
}
// Method to process form submission
public function handle_form_submission() {
if (isset($_POST['contact_form_submitted']) && $_POST['contact_form_submitted'] === 'true') {
$this->form_submitted = true;
// Validate name
if (empty($_POST['name'])) {
$this->submission_errors[] = 'Name is required.';
}
// Validate email
if (empty($_POST['email']) || !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$this->submission_errors[] = 'Valid email is required.';
}
// Validate message
if (empty($_POST['message'])) {
$this->submission_errors[] = 'Message is required.';
}
// If no errors, send email
if (empty($this->submission_errors)) {
$to = get_option('admin_email');
$subject = 'New Contact Form Submission';
$message = "Name: " . sanitize_text_field($_POST['name']) . "
";
$message .= "Email: " . sanitize_email($_POST['email']) . "
";
$message .= "Message:
" . sanitize_textarea_field($_POST['message']);
wp_mail($to, $subject, $message);
}
}
}
// Method to render the form (used by shortcode)
public function render_form() {
wp_enqueue_style('simple-contact-form');
ob_start();
// Show success message if form was submitted successfully
if ($this->form_submitted && empty($this->submission_errors)) {
echo '';
echo 'Thank you for your message! We will get back to you soon.';
echo '';
} else {
// Show errors if any
if (!empty($this->submission_errors)) {
echo '';
echo '';
foreach ($this->submission_errors as $error) {
echo '- ' . esc_html($error) . '
';
}
echo '
';
echo '';
}
// The form HTML
?>
This example demonstrates how a WordPress plugin can use OOP to organize code, handle hooks and filters, and manage form submissions—all within a clean, encapsulated class structure.
Homework Assignment
Create a simple class with properties and methods that could be used in a WordPress theme or plugin. Here's your assignment:
Product Catalog Class
Create a PHP class called Product_Catalog that could be used to manage products in a WordPress site. Your class should include:
- At least 3 properties (public, protected, or private as appropriate)
- A constructor that sets initial values
- At least 3 methods that perform different operations
- At least one static property and one static method
- Proper use of the
$thiskeyword - Comments explaining your code
Optional advanced features:
- Method chaining
- A destructor
- Integration with WordPress hooks (if you're familiar with them)
Submission Details
Create a PHP file with your class definition and include a short example of how to use it. You'll submit this file in the next class.
Example Solution Structure
<?php
/**
* Product Catalog class for managing products in a WordPress site
*/
class Product_Catalog {
// Properties
// Constructor
// Methods
// Static properties and methods
// Optional: Destructor
}
// Example usage
$catalog = new Product_Catalog();
// Code showing how to use your class
?>
Additional Resources
Further Reading
- PHP.net: Object Oriented PHP Documentation
- WordPress Plugin Developer Handbook
- PHP The Right Way: OOP Chapter
Next Steps
In our next class, we'll explore more advanced OOP concepts including inheritance, method overriding, abstract classes, and interfaces. These concepts will further enhance your ability to create robust WordPress themes and plugins.