Skip to main content

Course Progress

Loading...

Introduction to Object-Oriented PHP

Duration: 45 minutes
Module 2: 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.

Diagram
Class Diagram (Diagram converted to static representation) classDiagram class House { +int squareFeet +int nu...

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:

Diagram
Class Diagram (Diagram converted to static representation) classDiagram class WP_Post { +ID +post_author +pos...

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

Diagram
Sequence Diagram (Diagram converted to static representation) sequenceDiagram participant C as Class Code partic...

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.

Diagram
Class Diagram (Diagram converted to static representation) classDiagram class AccessModifiers { +public prope...

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."

Object: $post $this->title = "Hello World" $this->author = "Admin" $this->content = "Content..." $this->display() $this->save() $this->delete() $this (self-reference)
<?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 ""; foreach ($this->items as $index => $item) { $subtotal = $item['price'] * $item['quantity']; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; } echo ""; echo "
ItemPriceQuantitySubtotal
{$item['name']}$" . number_format($item['price'], 2) . "{$item['quantity']}$" . number_format($subtotal, 2) . "
Total$" . number_format($this->totalPrice, 2) . "
"; } } // 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.

Diagram
Class Diagram (Diagram converted to static representation) classDiagram class User { -userCount$ +username +e...

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 "Featured Image";
    }
}

// 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:

  1. At least 3 properties (public, protected, or private as appropriate)
  2. A constructor that sets initial values
  3. At least 3 methods that perform different operations
  4. At least one static property and one static method
  5. Proper use of the $this keyword
  6. 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

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.