🇹🇷 Türkçe: Bu yazının Türkçesini oku →

Every mature codebase eventually stops trusting strings. Order statuses spelled seven different ways across a plugin. A settings value that used to be yes becoming 1 in a minor release nobody read the changelog for. A typo in complete where completed was expected, silently sending an order to a state that does not exist. I have shipped my share of these bugs and I have grepped my way out of even more of them. The fix is boring and it is old: give the vocabulary a name and put it somewhere the linter can see.

WooCommerce just made that move in public. On September 2 the developer blog announced a set of enum classes for the core order, product, payment and settings vocabularies. It is the kind of housekeeping post you can miss on a busy Wednesday and it is the kind of change that quietly cleans up a decade of accumulated string literals across every WooCommerce site you maintain.

The choice they made about how to ship it is interesting on its own. Read past the surface and there is a small lesson in it about backwards compatibility that is worth pulling out.

What is actually new

The announcement by WesleyRosa on the WooCommerce developer blog introduces a full Automattic\WooCommerce\Enums namespace covering the most-touched string vocabularies in the platform. The Enums directory in the WooCommerce monorepo lists them explicitly: OrderStatus, OrderInternalStatus and OrderItemType for orders; ProductType, ProductStatus, ProductStockStatus, ProductTaxStatus and CatalogVisibility for products; PaymentGatewayFeature for payments; WeightUnit, DimensionUnit, CurrencyPosition, TaxBasedOn, TaxDisplayMode, DefaultCustomerAddress, StockDisplayFormat and CatalogSortOrder for settings.

The interesting choice is what they are not. PHP has had native enums since 8.1, but WooCommerce still supports PHP 7.4 and would rather not ship an object where a string used to be. So the classes are string-constant holders, not real enums. OrderStatus::COMPLETED returns the literal 'completed'. You can use it anywhere the old string was valid, on the day you upgrade, without touching runtime behaviour anywhere else.

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

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

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

That last line matters because it lines up with the two query functions wc_get_products() and wc_get_orders() that most non-trivial extensions already lean on. Nothing about how those functions accept arguments changed. The strings they always wanted are now available with a compiler-friendly name and a namespace the IDE can autocomplete. That is the whole point.

The blog post is explicit that the enum classes are considered a public API, and that extension developers are encouraged to adopt them. It is also explicit about the sharp edge: OrderStatus::COMPLETED gives you 'completed', but OrderInternalStatus::COMPLETED gives you 'wc-completed'. WooCommerce has always had both flavours in circulation and the enum set surfaces that split instead of hiding it. Pick the wrong one and your WP_Query comes back empty.

Why it matters for WordPress and WooCommerce people

Magic strings are the kind of debt that never files a bug report. They just sit in the codebase making refactors expensive. When a client says “we need to add a new order status for warehouse hold,” what actually happens is somebody greps for 'processing' in twelve custom plugins and hopes nobody spelled it Processing or process along the way. I have watched that grep session eat a whole afternoon on projects that should have been half-hour tickets.

An enum class fixes three problems at once. It gives the string a canonical spelling that a typo cannot beat. It gives every editor and every static analyser a fixed symbol to follow, so “find usages” actually works. And it makes deprecations honest: when a value is retired, the constant gets a @deprecated tag and every call site lights up in review, instead of hiding as a lowercase word inside quotes.

The old craft parallel is exact. QuarkXPress in the early nineties trained a whole generation of designers to stop typing font, size and leading into every paragraph and to reach for a paragraph style instead. Same content, same visible output, but change the style once and the document caught up. Enum classes are paragraph styles for your PHP vocabulary. The runtime value never changes. The name changes, and with it the ability to move it.

There is also a quiet compatibility lesson in the choice not to use native PHP 8.1 enums. WooCommerce could have shipped the modern shape and told PHP 7.4 users to catch up. They did not, because breaking the identity 'completed' === $order->get_status() in a million existing themes and plugins would have been the opposite of a housekeeping release. A string that is also a class constant is the honest bridge between where PHP is and where WooCommerce’s install base actually lives.

What I would do (or not do) about it

Three moves and one line I would not cross.

  • Grep the estate before the next sprint. On every WooCommerce site you own, run rg -n "'(completed|processing|on-hold|pending|cancelled|refunded|failed)'" across custom plugins, must-use plugins and child themes. Every hit is a candidate for an OrderStatus constant. Do the same for 'simple', 'variable', 'grouped', 'external' against ProductType. You are not rewriting the world, you are building a punch list.
  • Adopt in new code first, migrate old code opportunistically. New extensions and new tickets should use the enum classes from day one. For legacy code, wait until you are already in that file for another reason and migrate as you pass through. That keeps diffs reviewable and does not burn a sprint on a change that has no user-visible effect.
  • Gate adoption behind class_exists() when you support older WooCommerce. The blog post is clear that classes arrived in different versions. If your plugin claims compatibility back to WooCommerce 9.x, wrap the import in a guard or bump your minimum. Do not assume.

The line I would not cross: do not treat this as an excuse to rewrite half of a working plugin. The enum classes make your code more legible; they do not make a fragile module more reliable. If a legacy custom order-status plugin has been quietly earning its keep for four years, migrating its string literals to constants is a fifteen-minute pull request, not a redesign. Ship the small change, tag the release, move on.

The best refactors are the ones nobody notices at runtime. This one qualifies. Adopt it in the places you touch this week and let the rest catch up over the next few months of normal work.

Leave a Reply

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

Close Search Window