The $this Keyword in PHP
Learning Objectives
- Master PHP programming concepts
- Write clean, maintainable code
- Apply best practices
- Build dynamic applications
Understanding Self-Reference: The $this Keyword
Welcome to our exploration of one of the most fundamental concepts in object-oriented PHP: the $this keyword. This powerful self-referential variable is at the heart of how objects interact with their own properties and methods. Mastering it is essential for effective WordPress development, as it appears throughout the WordPress codebase and is vital for building plugins and themes.
By the end of this session, you'll understand how $this works, how to use it correctly, and how to avoid common pitfalls. You'll also see real-world examples of how WordPress uses $this in its code.
What Exactly Is $this?
The $this keyword is a special variable that is available inside any class method. It refers to the current object instance - the specific object that the method was called on. Think of $this as the object's way of saying "me" or "myself."
The Concept of Self-Reference
To understand $this, let's use an analogy:
The House Analogy
Imagine a class as a blueprint for building houses. Each house built from that blueprint is an object instance. Now, when you're inside a specific house:
- Saying "this kitchen" refers to the kitchen in the specific house you're standing in - not any other house
- Similarly,
$this->kitchenrefers to thekitchenproperty of the specific object you're currently working with
Just as you would use "this" to differentiate between "this house" (the one you're in) and other houses, PHP uses $this to differentiate between the current object and other objects of the same class.
The key insight is that $this represents a dynamic connection to the current object, not a fixed value. Its meaning changes depending on which object instance is executing the method.
Using $this to Access Properties
The most common use of $this is to access the properties (variables) that belong to the current object.
<?php
class Person {
// Properties
public $name;
public $email;
public $age;
// Constructor
public function __construct($name, $email, $age) {
$this->name = $name;
$this->email = $email;
$this->age = $age;
}
// Method to display person info
public function displayInfo() {
echo "Name: {$this->name}
";
echo "Email: {$this->email}
";
echo "Age: {$this->age}
";
}
// Method to update age
public function celebrateBirthday() {
$this->age++;
echo "{$this->name} is now {$this->age} years old!
";
}
}
// Create two Person objects
$person1 = new Person("John Doe", "john@example.com", 30);
$person2 = new Person("Jane Smith", "jane@example.com", 25);
// Call methods on each object
$person1->displayInfo();
// Output:
// Name: John Doe
// Email: john@example.com
// Age: 30
$person2->displayInfo();
// Output:
// Name: Jane Smith
// Email: jane@example.com
// Age: 25
$person1->celebrateBirthday();
// Output: John Doe is now 31 years old!
$person2->celebrateBirthday();
// Output: Jane Smith is now 26 years old!
?>
In this example, $this refers to the specific Person object that calls the method. When $person1->celebrateBirthday() is called, $this refers to $person1. Similarly, when $person2->celebrateBirthday() is called, $this refers to $person2.
Why Not Just Use the Property Directly?
You might wonder why we use $this->property instead of just $property:
<?php
class BadExample {
public $counter = 0;
public function incorrectIncrement() {
// WRONG! This creates a local variable, not accessing the property
$counter++;
echo "Counter: $counter
";
}
public function correctIncrement() {
// CORRECT! This accesses the object's property
$this->counter++;
echo "Counter: {$this->counter}
";
}
}
$example = new BadExample();
$example->incorrectIncrement(); // Outputs: Counter: 1
$example->incorrectIncrement(); // Still outputs: Counter: 1 (not incrementing the property)
$example->correctIncrement(); // Outputs: Counter: 1
$example->correctIncrement(); // Outputs: Counter: 2 (properly incrementing)
?>
The key difference:
$counterrefers to a local variable inside the method, which is created and destroyed each time the method is called$this->counterrefers to the property of the object, which persists between method calls
Using $this to Call Other Methods
Just as $this lets you access properties, it also lets you call other methods within the same class:
<?php
class Calculator {
public $result = 0;
public function add($number) {
$this->result += $number;
return $this;
}
public function subtract($number) {
$this->result -= $number;
return $this;
}
public function multiply($number) {
$this->result *= $number;
return $this;
}
public function divide($number) {
if ($number == 0) {
echo "Error: Cannot divide by zero
";
return $this;
}
$this->result /= $number;
return $this;
}
public function getResult() {
return $this->result;
}
public function clear() {
$this->result = 0;
return $this;
}
public function calculate($num1, $num2, $operation) {
$this->clear(); // Using $this to call the clear() method
$this->add($num1); // Using $this to call the add() method
// Using $this to call different methods based on the operation
switch ($operation) {
case 'add':
$this->add($num2);
break;
case 'subtract':
$this->subtract($num2);
break;
case 'multiply':
$this->multiply($num2);
break;
case 'divide':
$this->divide($num2);
break;
}
return $this->getResult(); // Using $this to call the getResult() method
}
}
$calc = new Calculator();
echo $calc->calculate(10, 5, 'add'); // Output: 15
echo "
";
echo $calc->calculate(10, 5, 'subtract'); // Output: 5
echo "
";
echo $calc->calculate(10, 5, 'multiply'); // Output: 50
echo "
";
echo $calc->calculate(10, 5, 'divide'); // Output: 2
?>
In this example, the calculate() method uses $this to call other methods within the same class. This promotes code reuse and keeps the implementation DRY (Don't Repeat Yourself).
Why Not Just Call the Method Directly?
Similar to properties, you need to use $this to call methods within the same class:
<?php
class MethodExample {
public function sayHello() {
echo "Hello!
";
}
public function incorrectGreeting() {
// WRONG! This tries to call a global function, not a class method
sayHello(); // Error: Call to undefined function
}
public function correctGreeting() {
// CORRECT! This calls the method on the current object
$this->sayHello();
}
}
$example = new MethodExample();
$example->correctGreeting(); // Works properly: Outputs "Hello!"
// $example->incorrectGreeting(); // Would cause an error
?>
Method Chaining with $this
One powerful pattern in OOP is method chaining, which allows you to call multiple methods in sequence on the same object. This is achieved by returning $this from methods:
<?php
class QueryBuilder {
private $table;
private $where = [];
private $orderBy = [];
private $limit;
public function table($name) {
$this->table = $name;
return $this; // Return $this for method chaining
}
public function where($column, $value) {
$this->where[] = [$column, $value];
return $this; // Return $this for method chaining
}
public function orderBy($column, $direction = 'ASC') {
$this->orderBy[] = [$column, $direction];
return $this; // Return $this for method chaining
}
public function limit($limit) {
$this->limit = $limit;
return $this; // Return $this for method chaining
}
public function getSQL() {
// Build the SQL query
$sql = "SELECT * FROM {$this->table}";
// Add WHERE clauses
if (!empty($this->where)) {
$sql .= " WHERE ";
$conditions = [];
foreach ($this->where as $condition) {
list($column, $value) = $condition;
$conditions[] = "$column = '$value'";
}
$sql .= implode(' AND ', $conditions);
}
// Add ORDER BY clauses
if (!empty($this->orderBy)) {
$sql .= " ORDER BY ";
$orders = [];
foreach ($this->orderBy as $order) {
list($column, $direction) = $order;
$orders[] = "$column $direction";
}
$sql .= implode(', ', $orders);
}
// Add LIMIT clause
if ($this->limit) {
$sql .= " LIMIT {$this->limit}";
}
return $sql;
}
}
// Method chaining in action
$query = new QueryBuilder();
$sql = $query->table('users')
->where('status', 'active')
->where('role', 'admin')
->orderBy('created_at', 'DESC')
->limit(10)
->getSQL();
echo $sql;
// Output: SELECT * FROM users WHERE status = 'active' AND role = 'admin' ORDER BY created_at DESC LIMIT 10
?>
Method chaining creates fluent interfaces that are readable and expressive. Each method modifies the object's state and returns $this to allow the next method to be called immediately.
Method Chaining in WordPress
WordPress uses method chaining in many places, such as the WP_Query class:
<?php
// WordPress-style method chaining (simplified example)
class WP_Query_Example {
private $args = [];
public function posts_per_page($number) {
$this->args['posts_per_page'] = $number;
return $this;
}
public function post_type($type) {
$this->args['post_type'] = $type;
return $this;
}
public function category($category) {
$this->args['category_name'] = $category;
return $this;
}
public function tag($tag) {
$this->args['tag'] = $tag;
return $this;
}
public function get() {
// In a real implementation, this would query the database
return "Query with args: " . print_r($this->args, true);
}
}
// Using method chaining
$query = new WP_Query_Example();
$result = $query->posts_per_page(5)
->post_type('post')
->category('news')
->tag('featured')
->get();
echo $result;
?>
$this in WordPress Development
The $this keyword is used extensively throughout WordPress. Let's look at some real-world examples:
In WordPress Widgets
<?php
// Example based on WordPress widget implementation
class My_Custom_Widget extends WP_Widget {
// Constructor
public function __construct() {
parent::__construct(
'my_custom_widget',
'My Custom Widget',
['description' => 'A simple custom widget']
);
}
// Widget front-end display
public function widget($args, $instance) {
$title = apply_filters('widget_title', $instance['title']);
echo $args['before_widget'];
if (!empty($title)) {
echo $args['before_title'] . $title . $args['after_title'];
}
// Display widget content
echo '<div class="widget-content">';
echo $this->get_widget_content($instance);
echo '</div>';
echo $args['after_widget'];
}
// Widget backend form
public function form($instance) {
$title = isset($instance['title']) ? $instance['title'] : 'Default Title';
$items = isset($instance['items']) ? $instance['items'] : 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('items'); ?>">Number of items:</label>
<input class="tiny-text" id="<?php echo $this->get_field_id('items'); ?>"
name="<?php echo $this->get_field_name('items'); ?>" type="number"
step="1" min="1" value="<?php echo esc_attr($items); ?>" size="3">
</p>
In this WordPress widget example, $this is used to:
- Call the widget's own methods with
$this->get_widget_content() - Access WordPress Widget API methods inherited from the parent class with
$this->get_field_id()and$this->get_field_name()
In WordPress Plugins
<?php
// Example of $this in a WordPress plugin class
class My_Plugin {
// Plugin settings
private $options;
private $plugin_path;
private $plugin_url;
// Constructor
public function __construct() {
// Set up plugin paths
$this->plugin_path = plugin_dir_path(__FILE__);
$this->plugin_url = plugin_dir_url(__FILE__);
// Load plugin options
$this->options = get_option('my_plugin_options', []);
// Register hooks
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_init', [$this, 'register_settings']);
add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);
add_shortcode('my_plugin', [$this, 'handle_shortcode']);
// Initialize plugin components
$this->init_components();
}
// Create admin menu
public function add_admin_menu() {
add_options_page(
'My Plugin Settings',
'My Plugin',
'manage_options',
'my-plugin',
[$this, 'render_settings_page']
);
}
// Register plugin settings
public function register_settings() {
register_setting('my_plugin_options', 'my_plugin_options');
add_settings_section(
'my_plugin_section',
'General Settings',
[$this, 'render_section_description'],
'my-plugin'
);
add_settings_field(
'enabled',
'Enable Plugin',
[$this, 'render_enabled_field'],
'my-plugin',
'my_plugin_section'
);
}
// Enqueue frontend scripts and styles
public function enqueue_scripts() {
if ($this->is_plugin_enabled()) {
wp_enqueue_style(
'my-plugin-style',
$this->plugin_url . 'assets/css/style.css',
[],
'1.0.0'
);
wp_enqueue_script(
'my-plugin-script',
$this->plugin_url . 'assets/js/script.js',
['jquery'],
'1.0.0',
true
);
}
}
// Shortcode handler
public function handle_shortcode($atts) {
if (!$this->is_plugin_enabled()) {
return '';
}
$atts = shortcode_atts([
'title' => 'Default Title',
'type' => 'default'
], $atts, 'my_plugin');
return $this->generate_shortcode_output($atts);
}
// Render settings page
public function render_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('my_plugin_options');
do_settings_sections('my-plugin');
submit_button('Save Settings');
?>
</form>
</div>
options['enabled']) ? $this->options['enabled'] : 0;
?>
<input type="checkbox" id="enabled" name="my_plugin_options[enabled]" value="1" <?php checked(1, $value); ?>>
<label for="enabled">Enable plugin functionality</label>
plugin_path . 'includes/class-component.php';
new Plugin_Component($this);
}
// Check if plugin is enabled
private function is_plugin_enabled() {
return isset($this->options['enabled']) && $this->options['enabled'] == 1;
}
// Generate shortcode output
private function generate_shortcode_output($atts) {
$output = '<div class="my-plugin-output type-' . esc-attr($atts['type']) . '">';
$output .= '<h3>' . esc_html($atts['title']) . '</h3>';
$output .= '<div class="content">Plugin content here</div>';
$output .= '</div>';
return $output;
}
// Public getter for plugin path
public function get_plugin_path() {
return $this->plugin_path;
}
// Public getter for plugin URL
public function get_plugin_url() {
return $this->plugin_url;
}
}
?>
This plugin example demonstrates how $this is used extensively in WordPress plugin development to:
- Access plugin properties like paths and options
- Call internal methods from within other methods
- Reference the current object in action and filter callbacks
- Create a cohesive, self-contained plugin structure
$this Tips and Tricks
Method Chaining with Conditionals
<?php
class QueryBuilder {
private $conditions = [];
public function where($column, $operator, $value) {
$this->conditions[] = [$column, $operator, $value];
return $this;
}
public function whereIf($condition, $column, $operator, $value) {
if ($condition) {
return $this->where($column, $operator, $value);
}
return $this; // Return $this even when the condition is false
}
public function getConditions() {
return $this->conditions;
}
}
// Example usage
$hasRole = true;
$isAdmin = false;
$query = new QueryBuilder();
$query->where('status', '=', 'active')
->whereIf($hasRole, 'role', '!=', 'guest')
->whereIf($isAdmin, 'role', '=', 'admin'); // This condition won't be added
print_r($query->getConditions());
// Output: Array with two conditions, not three
?>
This pattern allows for more dynamic and conditional method chaining without breaking the chain.
Using $this in Closures
<?php
class EventManager {
private $events = [];
private $counter = 0;
public function addListener($event, $callback) {
if (!isset($this->events[$event])) {
$this->events[$event] = [];
}
$this->events[$event][] = $callback;
return $this;
}
public function trigger($event, $data = null) {
if (!isset($this->events[$event])) {
return false;
}
foreach ($this->events[$event] as $callback) {
call_user_func($callback, $data, $this);
}
return $this;
}
public function registerDefaultListeners() {
// Using $this inside a closure
$this->addListener('start', function($data, $manager) {
echo "Event started with data: $data
";
$manager->incrementCounter();
});
$this->addListener('complete', function($data, $manager) {
echo "Event completed with data: $data
";
$manager->incrementCounter();
});
return $this;
}
public function incrementCounter() {
$this->counter++;
echo "Counter incremented to: {$this->counter}
";
return $this;
}
public function getCounter() {
return $this->counter;
}
}
// Using the event manager
$manager = new EventManager();
$manager->registerDefaultListeners()
->trigger('start', 'Initial data')
->trigger('complete', 'Final data');
echo "Final counter value: " . $manager->getCounter() . "
";
?>
In this example, $this is passed to the closure as the second parameter ($manager), allowing the closure to call methods on the object.
Using $this with trait methods
<?php
// Define a trait with utility methods
trait Validatable {
private $errors = [];
public function validate($rules) {
$this->errors = []; // Reset errors
foreach ($rules as $field => $rule) {
if (isset($rule['required']) && $rule['required'] && empty($this->$field)) {
$this->addError($field, "{$field} is required");
}
if (isset($rule['min']) && strlen($this->$field) < $rule['min']) {
$this->addError($field, "{$field} must be at least {$rule['min']} characters");
}
if (isset($rule['max']) && strlen($this->$field) > $rule['max']) {
$this->addError($field, "{$field} must be at most {$rule['max']} characters");
}
}
return empty($this->errors);
}
protected function addError($field, $message) {
$this->errors[$field] = $message;
}
public function getErrors() {
return $this->errors;
}
public function getError($field) {
return isset($this->errors[$field]) ? $this->errors[$field] : null;
}
}
// Use the trait in a class
class User {
use Validatable;
public $username;
public $email;
public $password;
public function __construct($username, $email, $password) {
$this->username = $username;
$this->email = $email;
$this->password = $password;
}
public function save() {
// Define validation rules
$rules = [
'username' => [
'required' => true,
'min' => 3,
'max' => 20
],
'email' => [
'required' => true
],
'password' => [
'required' => true,
'min' => 8
]
];
// Validate using the trait method
if (!$this->validate($rules)) {
echo "Validation failed:
";
foreach ($this->getErrors() as $field => $error) {
echo "- $error
";
}
return false;
}
echo "User saved successfully!
";
return true;
}
}
// Test the validation
$validUser = new User('johndoe', 'john@example.com', 'password123');
$validUser->save();
$invalidUser = new User('jo', '', 'pass');
$invalidUser->save();
?>
In this example, $this is used within the trait methods to access properties of the class that uses the trait. This allows for code reuse across multiple classes.
Common Mistakes and Gotchas with $this
Forgetting $this When Accessing Properties
<?php
class Counter {
public $count = 0;
public function increment() {
// WRONG - This creates a local variable, not modifying the property
$count++;
// CORRECT - This modifies the object property
// $this->count++;
}
public function getCount() {
return $this->count;
}
}
$counter = new Counter();
$counter->increment();
echo $counter->getCount(); // Will output 0, not 1
?>
Losing $this in Callback Functions
<?php
class EventHandler {
private $message = "Event triggered!";
public function registerEvents() {
// WRONG - $this will not refer to the EventHandler instance inside the callback
add_action('my_event', function() {
echo $this->message; // Error: $this is undefined
});
// CORRECT - Use with a reference to the current object
$self = $this; // Store a reference to $this
add_action('my_event', function() use ($self) {
echo $self->message; // Works correctly
});
// ALTERNATIVE - Use array callback format
add_action('another_event', [$this, 'handleEvent']);
}
public function handleEvent() {
echo $this->message;
}
}
?>
$this Inside Static Methods
<?php
class StaticExample {
public $name = "Instance Property";
public static $static_name = "Static Property";
public function instanceMethod() {
echo $this->name; // Works fine
}
public static function staticMethod() {
// WRONG - $this doesn't exist in static context
// echo $this->name; // Fatal error
// CORRECT - Use self:: to access static properties
echo self::$static_name;
}
}
?>
Using $this Before Calling Parent Constructor
<?php
class ParentClass {
protected $data;
public function __construct() {
$this->data = [];
}
}
class ChildClass extends ParentClass {
public function __construct() {
// WRONG - Using $this->data before parent constructor initializes it
$this->data['key'] = 'value'; // $this->data might be undefined
// CORRECT - Call parent constructor first
parent::__construct();
// Now it's safe to use $this->data
$this->data['key'] = 'value';
}
}
?>
Best Practices for Using $this
Be Consistent with Method Chaining
<?php
class Post {
private $title;
private $content;
private $author;
private $tags = [];
// Consistent approach: All setter methods return $this
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setContent($content) {
$this->content = $content;
return $this;
}
public function setAuthor($author) {
$this->author = $author;
return $this;
}
public function addTag($tag) {
$this->tags[] = $tag;
return $this;
}
// Getter methods don't return $this
public function getTitle() {
return $this->title;
}
public function getContent() {
return $this->content;
}
}
// Consistent method chaining
$post = new Post();
$post->setTitle('Hello World')
->setContent('This is a blog post')
->setAuthor('John Doe')
->addTag('hello')
->addTag('world');
?>
Use $this to Promote Code Reuse
<?php
class EmailValidator {
private $email;
private $errors = [];
public function __construct($email) {
$this->email = $email;
}
public function validate() {
$this->errors = []; // Reset errors
$this->checkEmpty()
->checkFormat()
->checkDomain();
return empty($this->errors);
}
private function checkEmpty() {
if (empty($this->email)) {
$this->addError('Email cannot be empty');
}
return $this;
}
private function checkFormat() {
if (!empty($this->email) && !filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
$this->addError('Invalid email format');
}
return $this;
}
private function checkDomain() {
if (!empty($this->email) && strpos($this->email, '@example.com') === false) {
$this->addError('Email must be from example.com domain');
}
return $this;
}
private function addError($message) {
$this->errors[] = $message;
return $this;
}
public function getErrors() {
return $this->errors;
}
}
// Using the validator
$validator = new EmailValidator('test@gmail.com');
if (!$validator->validate()) {
foreach ($validator->getErrors() as $error) {
echo "Error: $error
";
}
}
?>
Use Type Hints for Method Chaining
<?php
class Product {
private $name;
private $price;
private $description;
/**
* Set the product name
*
* @param string $name Product name
* @return Product For method chaining
*/
public function setName($name) {
$this->name = $name;
return $this;
}
/**
* Set the product price
*
* @param float $price Product price
* @return Product For method chaining
*/
public function setPrice($price) {
$this->price = $price;
return $this;
}
/**
* Set the product description
*
* @param string $description Product description
* @return Product For method chaining
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get product information
*
* @return array Product data
*/
public function getData() {
return [
'name' => $this->name,
'price' => $this->price,
'description' => $this->description
];
}
}
?>
Using PHPDoc comments with return type hints helps IDEs provide better code completion for method chaining.
Homework: Create a WordPress Custom Post Type Helper Class
Now it's your turn to practice using $this by creating a helper class for registering WordPress custom post types:
Assignment
Create a Custom_Post_Type_Builder class that makes it easy to register custom post types in WordPress. Your class should:
- Use method chaining for setting post type options
- Use
$thisto access properties and methods within the class - Include methods for configuring labels, supports, taxonomies, etc.
- Include a
register()method that finalizes the registration
Here's a starter template to help you:
<?php
/**
* Custom Post Type Builder Class
*
* A fluent interface for registering WordPress custom post types.
*/
class Custom_Post_Type_Builder {
// Post type properties
private $post_type;
private $args = [];
private $labels = [];
/**
* Constructor
*
* @param string $post_type Post type name
* @param string $singular Singular label
* @param string $plural Plural label
*/
public function __construct($post_type, $singular, $plural) {
$this->post_type = $post_type;
// Set up basic labels
$this->labels = [
'name' => $plural,
'singular_name' => $singular
];
// Set up default arguments
$this->args = [
'labels' => $this->labels,
'public' => true,
'has_archive' => true,
'supports' => ['title', 'editor', 'thumbnail']
];
}
/**
* Set whether the post type is public
*
* @param bool $public Whether post type is public
* @return Custom_Post_Type_Builder
*/
public function setPublic($public) {
// TODO: Implement this method
}
/**
* Set the menu icon
*
* @param string $icon Dashicon name or URL
* @return Custom_Post_Type_Builder
*/
public function setIcon($icon) {
// TODO: Implement this method
}
/**
* Set supported features
*
* @param array $supports Features to support
* @return Custom_Post_Type_Builder
*/
public function setSupports($supports) {
// TODO: Implement this method
}
/**
* Add a taxonomy to the post type
*
* @param string $taxonomy Taxonomy name
* @return Custom_Post_Type_Builder
*/
public function addTaxonomy($taxonomy) {
// TODO: Implement this method
}
/**
* Set custom rewrite rules
*
* @param string $slug Custom slug for rewrites
* @return Custom_Post_Type_Builder
*/
public function setRewrite($slug) {
// TODO: Implement this method
}
/**
* Set additional labels
*
* @param array $labels Custom labels
* @return Custom_Post_Type_Builder
*/
public function setLabels($labels) {
// TODO: Implement this method
}
/**
* Register the custom post type
*
* @return WP_Post_Type|WP_Error
*/
public function register() {
// TODO: Implement this method
}
}
// Example usage
function register_my_custom_post_types() {
$portfolio = new Custom_Post_Type_Builder('portfolio', 'Portfolio Item', 'Portfolio');
$portfolio->setIcon('dashicons-images')
->setSupports(['title', 'editor', 'thumbnail', 'excerpt'])
->addTaxonomy('category')
->addTaxonomy('post_tag')
->setRewrite('works')
->register();
$testimonial = new Custom_Post_Type_Builder('testimonial', 'Testimonial', 'Testimonials');
$testimonial->setIcon('dashicons-format-quote')
->setSupports(['title', 'editor', 'thumbnail'])
->setPublic(true)
->register();
}
add_action('init', 'register_my_custom_post_types');
?>
Complete this class with your own implementation. Consider how you can use $this effectively to create a fluent, developer-friendly API.
Next Class Preview
In our next session, we'll explore static properties and methods in PHP, which provide a way to access class functionality without creating an instance. We'll see how they differ from instance properties and methods, and how they complement your understanding of $this.