Retiring the magic strings: enum classes in WooCommerce core

WooCommerce is old enough that its most important string values (order statuses, product types, stock states, tax modes) predate almost every modern PHP convention. Across the WooCommerce codebase, and in many extensions, every one of those comparisons was written against a raw string literal:

if ( 'completed' === $order->get_status() ) { // hope you spelled it right!

Over the last few years, WooCommerce has begun shipping a family of enum classes under Automattic\WooCommerce\Enums. These are named, documented constants for order statuses, product types, stock states, settings values, and more.

The enum classes are considered a public API, and extension developers are encouraged to use them. This post covers what’s available, why we built classes of string constants instead of native PHP enums, and what shipping them taught us about load order and backward compatibility.

The price of a “magic string”

String literals might feel easier to write or more simple than classes, but the come with tradeoffs. Spread across a codebase the size of WooCommerce and the many extensions, magic strings tax you four ways:

  • Silent errors. Linters, autoloaders, and tests may not catch an error like typing 'complete' instead of 'completed'.
  • Abiguity. WordPress stores post-prefixed order statuses as wc-completed, while most WooCommerce APIs expect the un-prefixed completed, something a developer may only discover the hard way.
  • Discoverability. An agent searching 'simple' to find product-type logic returns half the codebase. Grepping for ProductType::SIMPLE should return a much narrower, accurate set of results.
  • Documentation. The definition for a status like on-hold will now live in a docblock next to its declaration.

Enum classes have the added benefit of clarifying empty strings based on intent. For example, woocommerce_default_customer_address treats '' as “no default”, which is unguessable without a named constant.

Why not native PHP enums

PHP has had enum support since 8.1, but for a few reasons, it wasn’t a viable solution for WooCommerce. Primarily, WooCommerce’s minimum supported PHP version is 7.4 which doesn’t include support for native enums.

There’s also an architectural difference. These values are already stored as plain strings in millions of databases, and thousands of extensions expect them to stay that way. Native PHP enums would turn those strings into objects, which could break existing code. String constants avoid this problem. OrderStatus::COMPLETED still produces the same 'completed' string, so developers can use the clearer name without changing how WooCommerce works. Existing code continues to work, and extensions can adopt the new constants when they are ready.

final class OrderStatus {
	/**
	 * Order fulfilled and complete.
	 */
	public const COMPLETED = 'completed';
	// ...
}

Enum classes currently available

The src/Enums directory (and its README) is an authoritative list of what’s currently available. The highlights:

  • Orders: OrderStatus (unprefixed values like completed), OrderInternalStatus (the wc--prefixed variants stored in the database), OrderItemType
  • Products: ProductType, ProductStatus, ProductStockStatus, ProductTaxStatus, CatalogVisibility
  • Payments: PaymentGatewayFeature, the strings gateways declare in their supports arrays
  • Settings values: WeightUnit, DimensionUnit, CurrencyPosition, TaxBasedOn, TaxDisplayMode, DefaultCustomerAddress, StockDisplayFormat, CatalogSortOrder

Using the constants in your extension

These constants are intentionally a publicly discoverable API, with explicit public visibility, docblocks, and developer docs. Extension developers are welcome to rely on them.

use Automattic\WooCommerce\Enums\OrderStatus;
use Automattic\WooCommerce\Enums\ProductType;

if ( OrderStatus::COMPLETED === $order->get_status() ) {
	// ...
}

$products = wc_get_products( array( 'type' => ProductType::SIMPLE ) );

Two things to check before adopting them:

  1. Your minimum supported WooCommerce version. The classes landed incrementally: OrderStatus in WooCommerce 9.5, the product classes around 9.7 and 9.8, the settings-value classes across the 10.x releases. If you support older versions, keep the literal or guard usage with class_exists().
  2. Which string WooCommerce expects. OrderStatus::COMPLETED is completed; OrderInternalStatus::COMPLETED is wc-completed. Most WooCommerce functions take the un-prefixed form; database-level and post_status contexts use the prefixed one.

The product querying and order querying docs show the constants in use alongside the literal forms.

What’s next

New vocabularies in core should now get an enum class by default. WooCommerce still contains many string values that deserve names, and contributions are welcome.


Leave a Reply

Your email address will not be published. Required fields are marked *