EC-CUBE本体の3.0.18へのバージョンアップの互換性についての技術的注意点を記載します。
Edit me

背景

Symfonyフレームワークのセキュリティ強化のため、後方互換性のないアップデートが含まれます。 そのため、プラグインや独自カスタマイズしている部分のコードについて、修正が必要になる可能性があります。

影響内容

ログインに関する影響について

アップデートを適用すると、ログイン関連のセッションの仕様が変更されるため、管理画面やマイページでログインしているユーザは一度ログアウトされるため再度ログインが必要です。 RememberMeなどでログイン情報を保存している場合も、再度ログインが必要となります。
(こちらは仕様となるのでコード修正の必要はありません。必要に応じて利用者へ周知してください。)

非会員のセッション情報取得について

Symfonyの仕様変更に伴い、ShoppingControllerでの非会員情報をセッションに保存をする箇所に修正が入っています。

コードの修正が必要な場合があります。
詳しくはこちらをご確認ください。

Entityを含んだクラスのデータの扱い方について

Symfonyの仕様変更に伴い、Entityを含むオブジェクトをそのまま serialize unserialize をした際にエラーが発生する可能性があります。

コードの修正が必要な場合があります。
詳しくはこちらをご確認ください。

該当コードのサンプルと修正方法

非会員のセッション情報取得の修正方法

Symfonyの仕様変更に伴い、ShoppingControllerでの非会員情報をセッションに保存をする箇所に修正が入っています。 ShoppingControllerのカスタマイズやプラグインでオーバーライドを行なっている場合は修正が必要となります。

ShoppingControllerで非会員情報を扱う場合は、setNonMembergetNonMemberを使用してください。
プラグインやカスタマイズでgetNonMemberを使用せずに非会員情報を取得している場合は、セッションの非会員情報を正しく取得できません。

旧バージョンでの非会員用セッションの作成方法(修正が必要なコード)

 // 非会員用セッションを作成
 $nonMember = array();
 $nonMember['customer'] = $Customer;
 $nonMember['pref'] = $Customer->getPref()->getId();
 $app['session']->set($this->sessionKey, $nonMember);

3.0.18 以降での非会員用セッションの作成方法(修正後のコード)

 // 非会員用セッションを作成
 $app['eccube.service.shopping']->setNonMember($this->sessionKey, $Customer);

なおその他プラグインで考慮すべき項目 にも該当していないかご確認ください。

参考:EC-CUBE本体では今回の変更に伴い、下記のような変更を行っています。
https://github.com/EC-CUBE/ec-cube/pull/2865/files#diff-615c41c60c70bb3b6ddabc92fa58c67c

Entityを含んだクラスのデータの扱い方の修正

Symfonyの仕様変更に伴い、Entityを含むオブジェクトをそのまま serialize unserialize をした際にエラーが発生する可能性があります。
そのため、該当するコードがある場合は動作確認の上、下記修正方法に従って修正をする必要があります。

旧バージョンでオブジェクトをそのままシリアライズしてセッションに保持する実装方法(修正が必要なコード)

クラスオブジェクトであるCustomerAddressをそのままserialize/unserializeしているため、エラーが発生する可能性のあるコードです。

セッションへの保持

$CustomerAddress = new CustomerAddress();
$app['session']->set($this->sessionCustomerAddressKey, serialize($CustomerAddress));

セッションからの復元

$CustomerAddress = unserialize($CustomerAddress);

オブジェクトを配列化してjson形式でセッションに保持する実装方法(推奨する修正方法)

クラスオブジェクトであるCustomerAddressを配列化してjson形式でセッションに保持しています。
CustomerAddressに含まれるオブジェクトであるPrefも配列化しています。 復元時は逆にjson形式から配列に復元し、さらにそれをオブジェクトに復元しています。

セッションへの保持

// CustomerAddressオブジェクトを配列化する
$CustomerAddress = new CustomerAddress();
$CustomerAddressArray = $CustomerAddress->toArray();                    // CustomerAddressを配列化
$CustomerAddressArray['Pref'] = $CustomerAddress->getPref()->toArray(); // CustomerAddressに含まれるPrefオブジェクトを配列化

// json形式でセッションに保持する
$json = json_encode($CustomerAddressArray);
$app['session']->set($this->sessionCustomerAddressKey, $json);

セッションからの復元

// json形式でセッションから取得する
$json = $app['session']->get($this->sessionCustomerAddressKey);
$CustomerAddressArray = json_decode($json, true);

// CustomerAddressオブジェクトを復元する
$CustomerAddress = new CustomerAddress();
$CustomerAddress->setPropertiesFromArray($CustomerAddressArray); // 配列からCustomerAddressのデータを復元する
$CustomerAddress->setPref($app['eccube.repository.master.pref']->find($CustomerAddressArray['Pref']['id'])); // Prefオブジェクトの復元

なおその他プラグインで考慮すべき項目 にも該当していないかご確認ください。

参考:EC-CUBE本体では今回の変更に伴い、下記のような変更を行っています。
https://github.com/EC-CUBE/ec-cube/pull/4168/files

その他プラグインで考慮すべき項目

非会員のセッション情報取得時の注意点

同一コードで3.0.17以前で動作させる場合は、setNonMember関数が存在しないため、条件分岐等を行ってください。

if (method_exists($app['eccube.service.shopping'], 'setNonMember')) {
    // 3.0.18以降
    $app['eccube.service.shopping']->setNonMember($this->sessionKey, $Customer);
} else {
    // 既存の処理
}

プラグインアップデート時の注意点

すでにEntityを含んだクラスのデータをSerializeしてデータベースに保存している場合、プラグインアップデート時の互換性を考慮(*1)する必要があります。
*1 プラグインアップデート時のマイグレーション処理でデータを変換する等

マイグレーションのサンプルコード

参考:メールマガジンプラグインでは今回の変更に伴い、下記のようなマイグレーションを行っています。
https://github.com/EC-CUBE/mail-magazine-plugin/blob/3.0/Resource/doctrine/migration/Version201906031100.php


public function up(Schema $schema)
{
	// PDOインスタンスの取得
    $pdo = $this->connection->getWrappedConnection();
    // JSONに変換が必要なデータを取得
    $stmt = $pdo->prepare('SELECT send_id, search_data FROM plg_send_history;');
    $stmt->execute();
    foreach ($stmt as $row) {
        // 配信履歴テーブルのsearch_dataをserializeされたデータからjson形式に変換する
        $formData = $this->unserializeWrapper(base64_decode($row['search_data']));
        // unserializeしたデータから配列fに変換
        $formDataArray = $formData;
        $formDataArray['pref'] = ($formData['pref'] instanceof Pref) ? $formData['pref']->toArray() : null;
        $formDataArray['sex'] = array_filter(array_map(function ($entity) {
            if ($entity instanceof Sex) {
                return $entity->toArray();
            } else {
                return false;
            }
        }, $formData['sex']->toArray()));
        $formDataArray['customer_status'] = array_filter(array_map(function ($entity) {
            if ($entity instanceof CustomerStatus) {
                return $entity->toArray();
            } else {
                return false;
            }
        }, $formData['customer_status']->toArray()));
        // 不要なデータを削除
        unset($formDataArray['buy_category']);
        // 配列をJSONに変換
        $json = json_encode($formDataArray);
        // 変換したJSON形式のデータを配信履歴テーブルのsearch_dataに保存
        $stmt = $pdo->prepare('UPDATE plg_send_history SET search_data = :search_data WHERE send_id = :send_id;');
        $stmt->execute(array(':search_data' => $json, ':send_id' => $row['send_id']));
    }
}

/**
 * 互換性のないEntityを取り除いた状態でUnserialiseを実行する
 * Member,Customerは "__php_incomplete_class"となる.
 *
 * @param array $serializedData Base64でエンコードされたシリアライズデータ
 *
 * @return mixed unserializeしたデータ
 */
private function unserializeWrapper($serializedData)
{
    $serializedData = str_replace('DoctrineProxy\__CG__\Eccube\Entity\Member', '__Workaround_\__CG__\Eccube\Entity\Member', $serializedData);
    $serializedData = str_replace('DoctrineProxy\__CG__\Eccube\Entity\Customer', '__Workaround_\__CG__\Eccube\Entity\Customer', $serializedData);
    return unserialize($serializedData);
}

Tags: quickstart