Home › Block and Plugin Tutorials › Plugin Development › What Are PHP Traits and How to Use Them In Plugin Development
What Are PHP Traits and How to Use Them In Plugin Development
When you’re building WordPress plugins, chances are you’ll end up repeating yourself. You may need multiple tables, so there will be typical create, drop, and insert features. Maybe you need logging across multiple classes, a helper method for sanitization, or for storing and retrieving options. At that point, you’ve got a choice: duplicate code, refactor into a base class or static class, or reach for PHP Traits.
Classes Can use PHP Traits For Method Access to Helper Functions
A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way.
An Introduction to PHP Traits
PHP Traits sit somewhere between copy-pasting and inheritance. Let’s begin with a simple example of how traits work.
You should place one Trait per file, but in this example, I’ll show you how to use Traits to duplicate methods. We’ll start with the trait keyword, followed by the class name, and then some simple arithmetic functions.
trait MyCalculator {
public function add( $num_a, $num_b ) {
return $num_a + $num_b;
}
public function subtract( $num_a, $num_b ) {
return $num_a - $num_b;
}
}Code language: PHP (php)I’ve declared a new Trait called MyCalculator. It has two public methods, add and subtract. I can now reuse these functions in a new class.
Using the Trait, I can create a new class that’ll assume the Trait’s methods as part of its own.
trait MyCalculator {
public function add( $num_a, $num_b ) {
return $num_a + $num_b;
}
public function subtract( $num_a, $num_b ) {
return $num_a - $num_b;
}
}
class MyPurpleCalculator {
use MyCalculator;
}Code language: PHP (php)I can then instantiate the class and output as if the methods were part of my class.
trait MyCalculator {
public function add( $num_a, $num_b ) {
return $num_a + $num_b;
}
public function subtract( $num_a, $num_b ) {
return $num_a - $num_b;
}
}
class MyPurpleCalculator {
use MyCalculator;
}
$my_purple_calculator = new MyPurpleCalculator();
echo $my_purple_calculator->add( 1, 2 ); // 3
echo $my_purple_calculator->subtract( 1, 2 ); // -1Code language: PHP (php)To demonstrate this further, let me break up the Traits into multiples.
trait MyCalculatorAdd {
public function add( $num_a, $num_b ) {
return $num_a + $num_b;
}
}
trait MyCalculatorSubtract {
public function subtract( $num_a, $num_b ) {
return $num_a - $num_b;
}
}
class MyPurpleCalculator {
use MyCalculatorAdd;
use MyCalculatorSubtract;
}
$my_purple_calculator = new MyPurpleCalculator();
echo $my_purple_calculator->add( 1, 2 ); // 3
echo $my_purple_calculator->subtract( 1, 2 ); // -1Code language: PHP (php)As shown in the example above, you can use multiple Traits to make up your code.
Using Traits as abstract shortcuts
Traits are basically a shortcut to Abstract classes. Let’s look at an example of using Traits to lay out a table class.
<?php
/**
* Tables Trait
*
* @package DLXPlugins
* @author DLXPlugins
* @since 1.0.0
*/
trait Tables_Trait {
/**
* Get the tables
*
* @return array
*/
protected $tablename = '';
/**
* Get the columns
*
* @return array
*/
protected $columns = '';
/**
* Get the table version
*
* @return string
*/
protected $table_version = '';
public function drop_table() {
global $wpdb;
$wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %s', $this->tablename ) );
}
public function create_table() {
global $wpdb;
$table_version = get_option( 'dlx_traits_table_version_' . sanitize_key( $this->tablename ), '1.0.0' );
if ( $table_version !== $this->table_version ) {
$this->update_table();
}
}
protected function update_table() {
global $wpdb;
// Include delta.
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$schema = "CREATE TABLE $this->tablename (";
$charset_collate = $wpdb->get_charset_collate();
// Add column definitions.
$schema .= $this->columns;
$schema .= "\n) $charset_collate;";
$result = dbDelta( $schema );
if ( is_wp_error( $result ) ) {
// todo - handle error.
} else {
update_option( 'dlx_traits_table_version_' . sanitize_key( $this->tablename ), sanitize_text_field( $this->table_version ) );
}
}
/**
* Get the table name.
*
* @return string
*/
public function get_table_name() {
return $this->tablename;
}
/**
* Set the table name
*
* @param string $name
*/
protected function set_table_name( $name ) {
$this->tablename = $name;
}
/**
* Set the table version
*
* @param string $version
*/
protected function set_table_version( $version ) {
$this->table_version = $version;
}
/**
* Set the columns
*
* @param string $columns
*/
protected function set_columns( $columns ) {
$this->columns = $columns;
}
}
Code language: PHP (php)Here’s an example Trait I’ve created that houses table logic in WordPress for creating a custom database table.
Most of the methods are protected, so only the user of the Trait can access them. It houses logic that’ll drop, create, and update the tables for any class that uses the Trait.
Let’s look at an example class that uses the tables Trait.
<?php
/**
* Tables Color
*
* @package DLXPlugins
* @author DLXPlugins
* @since 1.0.0
*/
/**
* Tables Color
*
* @package DLXPlugins
* @author DLXPlugins
* @since 1.0.0
*/
class Tables_Color {
use Tables_Trait;
public function __construct() {
$this->set_table_name( 'dlx_string_finder_colors' );
$this->set_columns(
'`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`hex_color` varchar(7) NOT NULL,
PRIMARY KEY (`id`)
'
);
$this->set_table_version( '1.0.0' );
}
}Code language: PHP (php)The constructor sets all the necessary class variables. Note that these variables are from the Trait, since Tables_Trait is being used.
Finally, I can instantiate the class and create the table.
// Create the tables.
$colors = new Tables_Color();
$colors->create_table();Code language: PHP (php)
Trait and Auto-Completion of Code
Not all IDEs support PHP Traits, and you may run into problems where code completion or method documentation that doesn’t display.
Traits rewritten as an Abstract Class
I mentioned earlier that Traits are basically a shortcut version of Abstract classes. We could write the above as an Abstract Class instead. It’s a bit cleaner, more predictable, and works with code completion.
<?php
/**
* Tables Trait
*
* @package DLXPlugins
* @author DLXPlugins
* @since 1.0.0
*/
abstract class Tables_Abstract {
/**
* Get the tables
*
* @return array
*/
protected $tablename = '';
/**
* Get the columns
*
* @return array
*/
protected $columns = '';
/**
* Get the table version
*
* @return string
*/
protected $table_version = '';
public function drop_table() {
global $wpdb;
$wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %s', $this->tablename ) );
}
public function create_table() {
global $wpdb;
$table_version = get_option( 'dlx_traits_table_version_' . sanitize_key( $this->tablename ), '1.0.0' );
if ( $table_version !== $this->table_version ) {
$this->update_table();
}
}
protected function update_table() {
global $wpdb;
// Include delta.
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$schema = "CREATE TABLE $this->tablename (";
$charset_collate = $wpdb->get_charset_collate();
// Add column definitions.
$schema .= $this->columns;
$schema .= "\n) $charset_collate;";
$result = dbDelta( $schema );
if ( is_wp_error( $result ) ) {
// todo - handle error.
} else {
update_option( 'dlx_traits_table_version_' . sanitize_key( $this->tablename ), sanitize_text_field( $this->table_version ) );
}
}
/**
* Get the table name.
*
* @return string
*/
public function get_table_name() {
return $this->tablename;
}
/**
* Set the table name
*
* @param string $name
*/
protected function set_table_name( $name ) {
$this->tablename = $name;
}
/**
* Set the table version
*
* @param string $version
*/
protected function set_table_version( $version ) {
$this->table_version = $version;
}
/**
* Set the columns
*
* @param string $columns
*/
protected function set_columns( $columns ) {
$this->columns = $columns;
}
}Code language: PHP (php)And lastly, the table that extends the Abstract Class.
<?php
/**
* Tables Color
*
* @package DLXPlugins
* @author DLXPlugins
* @since 1.0.0
*/
class Tables_Color extends Tables_Abstract {
public function __construct() {
$this->set_table_name( 'dlx_string_finder_colors' );
$this->set_columns(
'`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`hex_color` varchar(7) NOT NULL,
PRIMARY KEY (`id`)
'
);
$this->set_table_version( '1.0.0' );
}
}Code language: PHP (php)The usefulness of Traits
Traits aren’t a silver bullet, and they can cause more problems than they solve if you lean on them too heavily. If you find yourself stuffing dozens of unrelated methods into a single trait, or if traits start feeling like a workaround for bad architecture, that’s a sign to step back and rethink your class design. In those cases, a dedicated class would work better.
That said, traits shine in WordPress plugin development when you need to share small, focused bits of functionality across multiple classes. They let you keep your code DRY without forcing rigid inheritance chains, and they give you flexibility as your plugin grows.
Used with care, traits are a handy tool in your toolbox. Not every problem calls for them, but when you have common behavior to reuse and don’t want the overhead of base classes, traits can be the cleanest solution. Please leave a comment if you have any questions. For further reading, please visit a tutorial on Traits with more examples.
Like the tutorial you just read? There's more like it.
There's more where that came from. Enter your email and I'll send you more like it.





