EC-CUBE
EC-CUBEDay 19

EC-CUBE、サービスロケーターやめるってよ

はじめに、DIとかサービスロケーターについてはこちらのエントリが詳しいので読んでおきましょう。

現在EC-CUBEの次期メジャーバージョンを絶賛開発中です。EC-CUBEの次期バージョンはEC-CUBE3.0系をベースに開発していますが、EC-CUBEをさらに良くするための大きめの変更がいくつかあります。その中の一つとしてサービスロケーターをやめてDI(Dependency Injection)をきちんとやるようにしました。

例えば、次のようなコントローラーが、

namespace Eccube\Controller\Block;

use Eccube\Application;

class CartController
{
    public function index(Application $app)
    {
        $Cart = $app['eccube.service.cart']->getCart();
        return $app->render('Block/cart.twig', array(
            'Cart' => $Cart,
        ));
    }
}

こんな感じになります。

namespace Eccube\Controller\Block;

use Eccube\Annotation\Inject;
use Eccube\Application;
use Eccube\Service\CartService;

class CartController
{
    /**
     * @Inject(CartService::class)
     * @var CartService
     */
    protected $cartService;

    public function index(Application $app)
    {
        $Cart = $this->cartService->getCart();
        return $app->render('Block/cart.twig', array(
            'Cart' => $Cart,
        ));
    }
}

CartServiceの参照の仕方が変わっています。これだけ?という感じかもしれませんが、実際のコードには$appからオブジェクトをルックアップしているコードが大量にあります。実際に対応した適用したプルリクが、これです。すべてのController/FormType/Repository/Serviceとそれ以外のいくつかのクラスにも手を入れたので、なかなかの変更量(211 files changed, 5596 insertions, 1858 deletions)になっています。

どうやって対応したのか?

これらの対応は、1ファイルづつEC-CUBE開発チームメンバーが心を込めて人の手で修正した温もりのあるコミットになっているのではなく、機械的にやりました。機械的といっても正規表現で一括置換できるものでもなく、いくつかのパターンがあったりして意外と面倒なので、こんなプログラムを書きました。詳しくは説明しませんが、ざっくりいうと PHP CS Fixer の仕組みを使ってコードを書き換えるやつです。

サービスロケーターやめてどうなったの?

IDEの補完がちゃんと効くようになったとか、今までコンテナにコンポーネントを登録する処理をServiceProviderに書いていたのがなくなったので記述量が減ったとか色々ありますが、一番良かったのが依存が見えるようになったことです。
冒頭のエントリにも書かれていた通り、サービスロケーターで記述されたコードは、いつの間にか依存関係が多くなってしまっています。今回Fixerを使って変換してみるとルックアップしていたオブジェクトがフィールドに宣言されるので、あるクラスがどのくらい依存関係を持っていたのかが一目瞭然となります。今までなんとなく複雑になっているなと思っていたクラスは、フィールドが大量に宣言されていたりするので、早くなんとかしないといけないと気づけるようになります。

また、コンテナをルックアップしていたコードは、依存オブジェクトの初期化が遅延されていましたが、DIによって先にオブジェクトの初期化が行われるようになったため、相互参照や循環参照しているコードは正しく動作しなくなりました。

このように、きちんとDIしてあげることで依存関係にまつわる問題を表面化することができ、これらの問題に対処することができました。また、今後コードを変更していく上でも、依存関係をひとつの指標としてクリーンなコードを保てるようになります。

最後に

ここまではPimpleをDIコンテナにして、独自でInjectionの仕組みを作っていたのですが、現在DIコンテナを別のものに変更しているところです。なので、このエントリで出てきたコードは最終的には少し違う形にはなりますが、サービスロケーターをやめるってのは変わりません。ですので、どこかでEC-CUBEの話が出てきたら「EC-CUBE、サービスロケーターやめるってよ」って噂を流しておいてください。