Creating Classes and Instantiating Objects in PHP
Learning Objectives
- Understand OOP principles in PHP
- Create and use classes and objects
- Implement inheritance and polymorphism
- Apply OOP best practices
From Blueprint to Building: Classes and Objects
Welcome to our exploration of creating classes and instantiating objects in PHP! Today's session forms a critical foundation for your journey into object-oriented programming and WordPress development.
Classes and objects are the building blocks of modern PHP development, especially in the WordPress ecosystem. Understanding how to create well-designed classes and work with objects will transform how you approach development challenges.
The Anatomy of a PHP Class
A PHP class is like an architectural blueprint. It defines the structure, behavior, and characteristics of objects that will be created from it. Think of it as a template or mold from which objects (or instances) are cast.
Basic Class Structure
The syntax for creating a class in PHP is straightforward:
<?php
// The simplest possible class
class SimpleClass {
// Class body goes here
}
?>
While a class can be empty, it's not very useful. Let's explore what goes inside a class:
<?php
class BlogPost {
// Properties (variables)
public $title;
public $content;
public $author;
public $publishDate;
// Constants
const STATUS_DRAFT = 'draft';
const STATUS_PUBLISHED = 'published';
// Methods (functions)
public function displayTitle() {
return "<h2>{$this->title}</h2>";
}
public function getExcerpt($length = 100) {
return substr($this->content, 0, $length) . '...';
}
}
?>
Class Naming Conventions
PHP classes should follow these naming conventions:
- PascalCase: Each word starts with a capital letter (e.g.,
BlogPost,WP_Query) - Meaningful names: The name should describe what the class represents
- WordPress-specific: WordPress often uses underscores for class names (e.g.,
WP_User)
Best Practice
In modern WordPress development, classes are typically placed in separate files named after the class. For example, a BlogPost class would be defined in class-blog-post.php.
Bringing Classes to Life: Instantiation
Instantiation is the process of creating an object from a class. Think of it like using a cookie cutter (the class) to create individual cookies (objects). Each cookie has the same shape but can have different decorations.
The "new" Keyword
To create an object in PHP, we use the new keyword followed by the class name:
<?php
// Define our class
class Car {
public $color;
public $brand;
public $year;
}
// Instantiate an object
$myCar = new Car();
// Now we can set properties
$myCar->color = "Red";
$myCar->brand = "Toyota";
$myCar->year = 2025;
// We can create multiple objects from the same class
$friendsCar = new Car();
$friendsCar->color = "Blue";
$friendsCar->brand = "Honda";
$friendsCar->year = 2024;
// Each object is independent
echo "My car is a {$myCar->color} {$myCar->brand} from {$myCar->year}.";
echo "My friend's car is a {$friendsCar->color} {$friendsCar->brand} from {$friendsCar->year}.";
?>
Objects in Memory
Each time you use new, PHP allocates a new chunk of memory for the object. This is why each object is independent—they each have their own copy of properties with their own values.
Working with Properties and Methods
Setting and Getting Properties
After instantiating an object, you can set and retrieve its properties using the arrow operator (->):
<?php
class Product {
public $name;
public $price;
public $description;
}
$newProduct = new Product();
// Setting properties
$newProduct->name = "WordPress Theme";
$newProduct->price = 59.99;
$newProduct->description = "A beautiful and responsive theme";
// Getting properties
echo "Product: {$newProduct->name}
";
echo "Price: \${$newProduct->price}
";
echo "Description: {$newProduct->description}
";
?>
Calling Methods
Methods are called in a similar way, also using the arrow operator:
<?php
class Product {
public $name;
public $price;
public $description;
public function calculateTax($rate = 0.1) {
return $this->price * $rate;
}
public function getFormattedPrice() {
return "$" . number_format($this->price, 2);
}
public function getProductDetails() {
return [
'name' => $this->name,
'price' => $this->price,
'description' => $this->description
];
}
}
$newProduct = new Product();
$newProduct->name = "WordPress Plugin";
$newProduct->price = 29.99;
$newProduct->description = "Extends WordPress functionality";
// Calling methods
echo "Product: {$newProduct->name}
";
echo "Price: {$newProduct->getFormattedPrice()}
";
echo "Tax: $" . number_format($newProduct->calculateTax(), 2) . "
";
// Getting detailed information
$details = $newProduct->getProductDetails();
echo "Product details for {$details['name']} retrieved!
";
?>
Best Practice
In well-designed classes, you often won't access properties directly. Instead, you'll use "getter" and "setter" methods (more on these in the next class). For example, getPrice() instead of directly accessing $price.
Object Initialization with Constructors
Setting properties one by one can be tedious. The constructor allows us to initialize an object when it's created.
<?php
class BlogPost {
public $title;
public $content;
public $author;
public $publishDate;
// Constructor method
public function __construct($title, $content, $author = 'Anonymous', $publishDate = null) {
$this->title = $title;
$this->content = $content;
$this->author = $author;
$this->publishDate = $publishDate ?: date('Y-m-d H:i:s');
}
public function displayInfo() {
echo "<h2>{$this->title}</h2>";
echo "<p>By {$this->author} on {$this->publishDate}</p>";
echo "<div>{$this->content}</div>";
}
}
// Creating an object with the constructor
$post = new BlogPost(
"Learning OOP in PHP",
"Object-oriented programming is a powerful paradigm...",
"Jane Developer"
);
// No need to set properties, they're already initialized
$post->displayInfo();
// Another example with default values
$quickNote = new BlogPost(
"Quick Note",
"Just a reminder about our meeting tomorrow."
);
$quickNote->displayInfo(); // Uses "Anonymous" as author and current date
?>
Constructor Parameters
Constructors can have:
- Required parameters: Must be provided when creating the object
- Optional parameters: Have default values, so they don't need to be provided
This makes object creation more flexible and prevents invalid states.
Real-World WordPress Examples
Let's see how classes and objects are used in WordPress development.
Custom Post Type Registration
<?php
class Product_CPT {
private $post_type = 'product';
private $labels;
private $args;
public function __construct() {
// Set up labels for the custom post type
$this->labels = [
'name' => 'Products',
'singular_name' => 'Product',
'add_new' => 'Add New Product',
'add_new_item' => 'Add New Product',
'edit_item' => 'Edit Product',
'new_item' => 'New Product',
'view_item' => 'View Product',
'search_items' => 'Search Products',
'not_found' => 'No products found',
'not_found_in_trash' => 'No products found in trash',
'menu_name' => 'Products'
];
// Set up arguments for the custom post type
$this->args = [
'labels' => $this->labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => ['slug' => 'product'],
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 5,
'menu_icon' => 'dashicons-cart',
'supports' => ['title', 'editor', 'thumbnail', 'excerpt', 'custom-fields']
];
}
// Method to register the custom post type
public function register() {
register_post_type($this->post_type, $this->args);
}
// Method to add a hook to WordPress
public function init() {
add_action('init', [$this, 'register']);
}
}
// Usage:
$products = new Product_CPT();
$products->init();
?>
This pattern is common in WordPress plugins. The class encapsulates all the logic for a custom post type, making it easy to maintain and extend.
WordPress Shortcode Class
<?php
class Contact_Form_Shortcode {
private $shortcode = 'contact_form';
public function __construct() {
add_shortcode($this->shortcode, [$this, 'render_form']);
}
public function render_form($atts) {
// Extract attributes
$atts = shortcode_atts([
'title' => 'Contact Us',
'submit_text' => 'Send Message',
'success_message' => 'Thank you for your message!'
], $atts, $this->shortcode);
// Start output buffering to capture HTML
ob_start();
?>
<div class="contact-form-container">
<h2><?php echo esc_html($atts['title']); ?></h2>
<form class="contact-form" method="post">
<div class="form-group">
<label for="name">Your Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Your Email</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<div class="form-group">
<button type="submit" name="contact_submit"><?php echo esc_html($atts['submit_text']); ?></button>
</div>
</form>
</div>
This class creates a [contact_form] shortcode that can be used in WordPress posts or pages.
Common Class and Object Patterns
Fluent Interface (Method Chaining)
A design pattern that allows multiple methods to be called in a single statement:
<?php
class Query_Builder {
private $table;
private $select = '*';
private $where = [];
private $limit;
public function select($columns) {
$this->select = is_array($columns) ? implode(', ', $columns) : $columns;
return $this; // Returns $this for chaining
}
public function from($table) {
$this->table = $table;
return $this;
}
public function where($column, $operator, $value) {
$this->where[] = "$column $operator '$value'";
return $this;
}
public function limit($limit) {
$this->limit = $limit;
return $this;
}
public function build() {
$query = "SELECT {$this->select} FROM {$this->table}";
if (!empty($this->where)) {
$query .= " WHERE " . implode(' AND ', $this->where);
}
if ($this->limit) {
$query .= " LIMIT {$this->limit}";
}
return $query;
}
}
// Usage with method chaining
$query = new Query_Builder();
$sql = $query->select(['id', 'title', 'content'])
->from('wp_posts')
->where('post_status', '=', 'publish')
->where('post_type', '=', 'post')
->limit(10)
->build();
echo $sql;
// Output: SELECT id, title, content FROM wp_posts WHERE post_status = 'publish' AND post_type = 'post' LIMIT 10
?>
WordPress uses fluent interfaces in many places, such as WP_Query and the REST API.
Factory Pattern
A pattern where a factory class creates objects, abstracting the instantiation logic:
<?php
// The product interface
interface FileExporter {
public function export($data);
}
// Concrete product classes
class CSVExporter implements FileExporter {
public function export($data) {
return "Exporting as CSV: " . json_encode($data);
}
}
class JSONExporter implements FileExporter {
public function export($data) {
return "Exporting as JSON: " . json_encode($data);
}
}
class XMLExporter implements FileExporter {
public function export($data) {
return "Exporting as XML: " . json_encode($data);
}
}
// The factory class
class ExporterFactory {
public static function createExporter($type) {
switch ($type) {
case 'csv':
return new CSVExporter();
case 'json':
return new JSONExporter();
case 'xml':
return new XMLExporter();
default:
throw new Exception("Unsupported exporter type: $type");
}
}
}
// Usage
try {
$data = ['title' => 'My Post', 'content' => 'Post content...'];
// Create the right exporter based on type
$exporter = ExporterFactory::createExporter('json');
echo $exporter->export($data);
// Use a different exporter
$csvExporter = ExporterFactory::createExporter('csv');
echo $csvExporter->export($data);
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>
WordPress uses factory patterns in various places, like post type creation.
Common Mistakes and Troubleshooting
Common Errors When Creating Objects
<?php
// ERROR 1: Forgetting the 'new' keyword
$post = BlogPost(); // Error: Function 'BlogPost' not found
// CORRECT
$post = new BlogPost();
// ERROR 2: Case sensitivity matters
$post = new blogpost(); // Error if class is defined as 'BlogPost'
// CORRECT
$post = new BlogPost(); // Match the case exactly as defined
// ERROR 3: Missing required constructor parameters
class Product {
public function __construct($name, $price) {
$this->name = $name;
$this->price = $price;
}
}
$product = new Product(); // Error: Too few arguments to constructor
// CORRECT
$product = new Product("WordPress Theme", 49.99);
// ERROR 4: Trying to access undefined properties
$post = new BlogPost();
echo $post->author_email; // Notice: Undefined property
// CORRECT
$post = new BlogPost();
$post->author_email = "author@example.com"; // Define it first
echo $post->author_email; // Now it works
?>
Debugging Objects
Here are some helpful functions for debugging objects:
<?php
$post = new BlogPost("Title", "Content", "Author");
// Print object structure and values
var_dump($post);
// Print human-readable information about a variable
print_r($post);
// Check if variable is an object
if (is_object($post)) {
echo "Yes, \$post is an object
";
}
// Get the class name of an object
echo "Class name: " . get_class($post) . "
";
// Check if object is an instance of a class
if ($post instanceof BlogPost) {
echo "Yes, \$post is an instance of BlogPost
";
}
// List available methods
$methods = get_class_methods($post);
echo "Available methods: " . implode(", ", $methods) . "
";
// List available properties
$properties = get_object_vars($post);
echo "Properties: " . print_r($properties, true) . "
";
?>
Working with WordPress Class Instances
WordPress provides many built-in classes that you'll work with daily:
WP_Query
<?php
// Creating a new WP_Query object
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 5,
'category_name' => 'news'
]);
// Using the object
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
echo '<h2>' . get_the_title() . '</h2>';
echo get_the_excerpt();
}
wp_reset_postdata();
}
?>
WP_User
<?php
// Get current user as an object
$current_user = wp_get_current_user();
// Or create a specific user object
$user = new WP_User(1); // User with ID 1
// Access properties and methods
echo "Username: " . $user->user_login . "
";
echo "Display name: " . $user->display_name . "
";
echo "User email: " . $user->user_email . "
";
// Check capabilities
if ($user->has_cap('edit_posts')) {
echo "This user can edit posts
";
}
?>
WP_Error
<?php
// Create a new error object
$error = new WP_Error('upload_error', 'The file failed to upload', ['file' => 'example.jpg']);
// Check if something is an error
if (is_wp_error($error)) {
// Get the error code
$code = $error->get_error_code();
// Get the error message
$message = $error->get_error_message();
// Get additional data
$data = $error->get_error_data();
echo "Error ($code): $message
";
echo "File: " . $data['file'] . "
";
}
?>
Understanding Object Lifecycle
Objects in PHP have a lifecycle, from creation to destruction:
<?php
class DatabaseConnection {
private $connection;
public function __construct() {
echo "1. Object created
";
$this->connection = "Database connection established";
echo "2. Object initialized
";
}
public function query($sql) {
echo "3. Object being used
";
return "Query result for: $sql";
}
public function __destruct() {
echo "4. Object being destroyed
";
$this->connection = null;
echo "5. Resources released
";
}
}
// Create a block scope
{
echo "Creating object...
";
$db = new DatabaseConnection();
$result = $db->query("SELECT * FROM users");
echo "Query complete. Result: $result
";
echo "End of block scope.
";
} // $db goes out of scope here
echo "After the block, the object is destroyed.
";
?>
Understanding the object lifecycle is important for managing resources properly, especially in WordPress plugins where you might be working with file handles, database connections, or API resources.
Class Autoloading: Professional Development Approach
In professional WordPress development, you won't manually include every class file. Instead, you'll use autoloading:
<?php
/**
* Simple autoloader function
*/
function my_plugin_autoloader($class_name) {
// Base directory for classes
$base_dir = plugin_dir_path(__FILE__) . 'includes/';
// Convert class name to file path
// Example: Convert 'My_Plugin_Admin' to 'includes/class-my-plugin-admin.php'
$file = $base_dir . 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
// Check if file exists
if (file_exists($file)) {
require_once $file;
}
}
// Register the autoloader
spl_autoload_register('my_plugin_autoloader');
// Now we can use classes without requiring their files manually
$admin = new My_Plugin_Admin();
$settings = new My_Plugin_Settings();
$api = new My_Plugin_API();
?>
WordPress itself doesn't use a standard autoloader, but modern WordPress development (especially with plugins) often uses PSR-4 autoloading via Composer:
// composer.json example
{
"name": "your-vendor/your-plugin",
"description": "Your WordPress Plugin",
"type": "wordpress-plugin",
"require": {
"php": ">=7.4"
},
"autoload": {
"psr-4": {
"Your_Plugin\": "includes/"
}
}
}
Practical Exercise: Creating Your First WordPress Class
Let's put what we've learned into practice:
Exercise: Custom WordPress Widget Class
Create a simple WordPress widget class that displays recent posts with thumbnails.
<?php
/**
* Widget class for displaying recent posts with thumbnails
*/
class Recent_Posts_With_Thumbnails_Widget extends WP_Widget {
/**
* Constructor
*/
public function __construct() {
// Parent constructor
parent::__construct(
'recent_posts_thumbnails', // Base ID
'Recent Posts with Thumbnails', // Name
['description' => 'Displays recent posts with featured images'] // Args
);
}
/**
* Widget front-end display
*/
public function widget($args, $instance) {
$title = !empty($instance['title']) ? $instance['title'] : 'Recent Posts';
$num_posts = !empty($instance['num_posts']) ? $instance['num_posts'] : 5;
echo $args['before_widget'];
echo $args['before_title'] . esc_html($title) . $args['after_title'];
// Query recent posts
$recent_posts = new WP_Query([
'posts_per_page' => $num_posts,
'post_status' => 'publish'
]);
if ($recent_posts->have_posts()) {
echo '<ul class="recent-posts-widget">';
while ($recent_posts->have_posts()) {
$recent_posts->the_post();
echo '<li class="recent-post-item">';
// Display thumbnail if available
if (has_post_thumbnail()) {
echo '<div class="post-thumbnail">';
echo the_post_thumbnail('thumbnail');
echo '</div>';
}
echo '<div class="post-details">';
echo '<h4><a href="' . get_permalink() . '">' . get_the_title() . '</a></h4>';
echo '<span class="post-date">' . get_the_date() . '</span>';
echo '</div>';
echo '</li>';
}
echo '</ul>';
// Reset post data
wp_reset_postdata();
} else {
echo '<p>No recent posts found.</p>';
}
echo $args['after_widget'];
}
/**
* Widget settings form
*/
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : 'Recent Posts';
$num_posts = !empty($instance['num_posts']) ? $instance['num_posts'] : 5;
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>">Title:</label>
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
name="<?php echo $this->get_field_name('title'); ?>" type="text"
value="<?php echo esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('num_posts'); ?>">Number of posts to show:</label>
<input class="tiny-text" id="<?php echo $this->get_field_id('num_posts'); ?>"
name="<?php echo $this->get_field_name('num_posts'); ?>" type="number"
step="1" min="1" value="<?php echo esc_attr($num_posts); ?>" size="3">
</p>
This example shows a complete widget class that extends WordPress's WP_Widget class. It demonstrates:
- Class creation and instantiation
- Inheritance (extending an existing class)
- Constructor for initialization
- Multiple methods with different purposes
- Creating other objects (
WP_Query) within our class
Homework: Create a Simple Class with Properties and Methods
Now it's your turn to apply what you've learned:
Assignment
Create a WP_Newsletter class that could be used to handle newsletter subscriptions in WordPress. The class should:
- Have properties for subscriber details (email, name, subscription date, status)
- Include methods for subscribing, unsubscribing, and sending test emails
- Have a constructor that initializes the object with required values
- Include at least one static property to track the total number of subscribers
Here's a starter template to help you:
<?php
/**
* Simple Newsletter Subscription Class
*/
class WP_Newsletter {
// Static property to track subscribers
private static $total_subscribers = 0;
// Properties
private $email;
private $name;
private $subscription_date;
private $status; // 'active', 'unsubscribed', 'pending'
// Constructor
public function __construct($email, $name = '') {
// Initialize properties
// Increment static counter
}
// Method to subscribe a user
public function subscribe() {
// Logic to subscribe
}
// Method to unsubscribe a user
public function unsubscribe() {
// Logic to unsubscribe
}
// Method to send a test email
public function send_test_email() {
// Logic to send test email
}
// Static method to get total subscribers
public static function get_total_subscribers() {
// Return count
}
// Any other methods you think would be useful
}
// Example usage
$subscriber = new WP_Newsletter('user@example.com', 'John Doe');
$subscriber->subscribe();
// Get total subscribers
echo WP_Newsletter::get_total_subscribers();
?>
Complete this class with your own implementation. Be creative and consider how this might actually work in a real WordPress environment.
Additional Resources
Further Reading
- PHP Manual: Classes and Objects Basics
- WordPress Plugin Developer Handbook: Best Practices
- Object-Oriented Programming for WordPress
Next Class Preview
In our next session, we'll explore more advanced OOP concepts including inheritance, method overriding, abstract classes, interfaces, and namespaces. These concepts will further enhance your ability to create robust WordPress themes and plugins.