WooCommerceのマジックストリング廃止へ、エナムクラス導入で拡張開発が安全に

WooCommerceのマジックストリング廃止へ、エナムクラス導入で拡張開発が安全に

WooCommerceのマジックストリング廃止へ、エナムクラス導入で拡張開発が安全に

WooCommerceのコードベースで長年使われてきた「マジックストリング」が、人知れず問題を引き起こしていた。注文ステータス、商品タイプ、在庫状態、税金モード。これらの重要な値が、コード中で生の文字列として比較されていたのだ。タイプミス一つでバグになり、プレフィックスの有無で混乱し、コード検索でノイズが大量に混ざる。開発者にとって頭の痛い状況だった。

WooCommerceはこの問題に対処するため、Automattic\WooCommerce\Enumsという名前空間にエナムクラス群を導入した。注文ステータスや商品タイプを表す名前付き定数をパブリックAPIとして公開し、拡張機能開発者に採用を促している。本記事では、この変更の背景、利用可能なクラス、実際の導入方法を整理する。

マジックストリングがもたらす4つの問題

マジックストリングがもたらす4つの問題

WooCommerceは歴史が長い。そのため、注文ステータスや商品タイプといった重要な値が、PHPの近代的な慣習が生まれる前から存在している。コードベースの各所で、次のような生の文字列比較が行われてきた。

if ( 'completed' === $order->get_status() ) {
    // タイプミスがないことを祈るしかない
}

この書き方は一見シンプルに見えるが、WooCommerceのような大規模なコードベースでは深刻な代償を伴う。Developer WooCommerce Blogの記事では、その問題を4つの観点から説明している。

沈黙のエラー

文字列リテラルの最大のリスクは、タイプミスが静かに潜むことだ。'complete'と書くべきところを'completed'と書いても、リンターもオートローダーもテストもエラーを検出しない場合がある。コードは実行時に黙って失敗し、デバッグに時間を奪われる。

プレフィックスの有無による混乱

WordPressのデータベースでは、注文ステータスにwc-というプレフィックスが付く。データベースにはwc-completedとして保存されるが、WooCommerceの多くのAPIはプレフィックスなしのcompletedを期待する。この違いは、開発者が実際にコードを動かして初めて気づく類の落とし穴だ。

検索の非効率さとドキュメントの分散

エージェントやコード検索で'simple'という文字列を探すと、商品タイプのロジックを探しているのに無関係な箇所が大量にヒットする。一方、ProductType::SIMPLEのように名前付き定数で検索すれば、該当箇所を正確に絞り込める。さらに、on-holdのようなステータスの定義は、宣言箇所のdocblockにドキュメントとして残せるようになる。

マジックストリングの問題点(Before)
タイプミス検出困難 実行時まで気づけない
プレフィックス混乱 wc-completed と completed の区別が曖昧
検索ノイズ simple で検索すると無関係な箇所が大量ヒット
ドキュメント分散 値の意味がコード中に散在
エナムクラス導入後(After)
名前付き定数 タイプミスをコンパイル時に検出
意図の明確化 OrderStatus::COMPLETED と OrderInternalStatus::COMPLETED を区別
検索精度向上 ProductType::SIMPLE で該当箇所を正確に絞り込み
ドキュメント統合 docblockに意味を記載

この比較で示したように、エナムクラスは文字列リテラルが抱える問題を構造的に解決する。特に、プレフィックス有無の違いを別々のクラスに分けることで、意図を明確に伝えられる点が重要だ。

ネイティブPHPエナムを採用しなかった理由

ネイティブPHPエナムを採用しなかった理由

PHPにはバージョン8.1からネイティブのエナム型が導入されている。しかしWooCommerceはこの選択肢を取らなかった。主に2つの理由がある。

PHP 7.4のサポート

WooCommerceの最低サポートPHPバージョンは7.4だ。このバージョンにはネイティブエナムが存在しない。PHP 8.1を前提にすると、多くのユーザーを切り捨てることになる。クラス定数として文字列を定義する方式なら、PHP 7.4でも問題なく動作する。

文字列互換性という設計判断

もう一つの理由はアーキテクチャ上の判断だ。注文ステータスや商品タイプの値は、すでに数百万のデータベースにプレーンな文字列として保存されている。数千の拡張機能もその形式を前提にしている。ネイティブPHPエナムを使うと値がオブジェクトに変換され、既存のコードが壊れる可能性がある。

文字列定数を使う方式なら、OrderStatus::COMPLETEDは従来の'completed'と同じ文字列を生成する。開発者はより明確な名前を使いつつ、WooCommerceの動作を変えない。既存コードが動き続け、拡張機能は準備ができた時点で新しい定数に移行できる。

final class OrderStatus {
    /**
     * 注文が完了した状態
     */
    public const COMPLETED = 'completed';
    // ...
}

このコードが示すように、エナムクラスはfinalで宣言され、public constとして定数を公開する。docblockで各定数の意味をドキュメント化できるのが利点だ。クラスを継承させないことで、APIの一貫性を保つ。

利用可能なエナムクラス

利用可能なエナムクラス

現在WooCommerceのsrc/Enumsディレクトリには、4つのカテゴリにまたがるエナムクラスが用意されている。各クラスの一覧を確認しよう。

注文と商品のエナム

  • OrderStatus(プレフィックスなしの値。例としてcompleted
  • OrderInternalStatus(データベースに保存されるwc-プレフィックス付きの値)
  • OrderItemType(注文アイテムのタイプ)
  • ProductType(商品タイプ。例としてsimple
  • ProductStatus(商品ステータス)
  • ProductStockStatus(在庫状態)
  • ProductTaxStatus(税金状態)
  • CatalogVisibility(カタログの表示設定)

支払いと設定値のエナム

  • PaymentGatewayFeature(ゲートウェイがsupports配列で宣言する文字列)
  • WeightUnit(重量単位)
  • DimensionUnit(寸法単位)
  • CurrencyPosition(通貨位置)
  • TaxBasedOn(課税基準)
  • TaxDisplayMode(税表示モード)
  • DefaultCustomerAddress(デフォルト顧客住所)
  • StockDisplayFormat(在庫表示形式)
  • CatalogSortOrder(カタログ並び順)

各クラスの完全な一覧は、WooCommerceのGitHubリポジトリにあるsrc/EnumsディレクトリとREADMEで確認できる。このディレクトリが権威ある情報源として公開されている。

注文関連 OrderStatus / OrderInternalStatus / OrderItemType
商品関連 ProductType / ProductStatus / ProductStockStatus 他
支払い関連 PaymentGatewayFeature
設定値関連 WeightUnit / DimensionUnit / CurrencyPosition 他

以上の4カテゴリが、現在WooCommerceで利用可能なエナムクラスの全体像だ。注文関連と商品関連が特に充実している。

拡張機能での採用方法

拡張機能での採用方法

エナムクラスは公的に発見可能なAPIとして設計されており、publicの可視性、docblock、開発者向けドキュメントを備えている。拡張機能開発者は安心して利用できる。

基本的な使い方

使い方はシンプルだ。use文で必要なクラスをインポートし、定数を参照する。例えば注文ステータスをチェックする場合、次のように書ける。

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

if ( OrderStatus::COMPLETED === $order->get_status() ) {
    // 注文が完了した場合の処理
}

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

採用前に確認すべき2つのポイント

エナムクラスを拡張機能に導入する前に、確認しておくべきことが2つある。

1. 最小サポートWooCommerceバージョン。エナムクラスは段階的に追加されてきた。OrderStatusはWooCommerce 9.5で導入され、商品関連クラスは9.7〜9.8頃、設定値クラスは10.xシリーズで追加された。古いバージョンもサポートする場合は、文字列リテラルを使い続けるか、class_exists()でガードする必要がある。

2. WooCommerceが期待する文字列OrderStatus::COMPLETEDcompletedを返すが、OrderInternalStatus::COMPLETEDwc-completedを返す。ほとんどのWooCommerce関数はプレフィックスなしの形式を取るが、データベースレベルやpost_statusコンテキストではプレフィックス付きを使う必要がある。どちらの形式を使うべきかを意識するのが重要だ。

商品検索と注文検索の公式ドキュメントには、文字列リテラルとエナムクラスの両方の形式が併記されている。移行時の参考になる。

プレフィックスなし(API用)
OrderStatus::COMPLETED completed を返す
使用場面 wc_get_orders() などの API
プレフィックスあり(DB用)
OrderInternalStatus::COMPLETED wc-completed を返す
使用場面 post_status やデータベース操作

このデモで示したように、2つの定数は同じ注文完了状態を表すが、使用するコンテキストが異なる。混同すると期待通りに動作しない。

今後の展開

今後の展開

Developer WooCommerce Blogの記事によれば、コアに新しい語彙(文字列値の集合)を追加する場合、エナムクラスをデフォルトで付ける方針が示されている。WooCommerceコアにはまだ名前の付いていない文字列値が多数残っており、コミュニティからの貢献も歓迎している。

この変更は拡張機能開発者にとって歓迎すべき動きだ。タイプミスのリスクが減り、コードの意図が読みやすくなり、IDEの補完機能も効きやすくなる。長期的にはWooCommerceエコシステム全体のコード品質向上につながる。

一方で、移行には段階的な対応が必要だ。既存の拡張機能は古いWooCommerceバージョンとの互換性を維持しながら、徐々にエナムクラスへ移行していくことになる。一括で全置換するのではなく、新規コードから採用していくのが現実的なアプローチだろう。

この記事のポイント

  • WooCommerceがマジックストリング(生の文字列リテラル)をエナムクラスに置き換える動きを進めている
  • エナムクラスはタイプミス防止、プレフィックス区別、検索精度向上、ドキュメント統合の4つの利点を持つ
  • ネイティブPHPエナムではなく文字列定数クラスを採用した理由は、PHP 7.4互換性と既存データベースの文字列互換性
  • 注文・商品・支払い・設定値の4カテゴリにエナムクラスが用意されている
  • 採用時は最小サポートWooCommerceバージョンと、プレフィックス有無のどちらの定数を使うかを確認する
海田 洋祐

・ 複数業界における17年間のデジタルビジネス開発経験 ・ ウェブサイト開発のためのHTML、PHP、CSS、JavaScript等の実用的知識 ・ 15ヶ国語対応の多言語SaaSの開発経験 ・ 17年間にも及ぶ、Eコマース長期運営経験 ・ 幅広い業界でのSEO最適化の豊富な経験

メッセージを残す