Skip to content

feat: EC-CUBE 4.4への対応 - #67

Open
ttokoro20240902 wants to merge 17 commits into
4.4from
feat-4.4
Open

feat: EC-CUBE 4.4への対応#67
ttokoro20240902 wants to merge 17 commits into
4.4from
feat-4.4

Conversation

@ttokoro20240902

@ttokoro20240902 ttokoro20240902 commented Jun 24, 2026

Copy link
Copy Markdown

概要

おすすめ商品管理プラグインを EC-CUBE 4.4(Symfony 7.4 / Doctrine ORM 3.0 / PHP 8.2+) に対応させ、コードを Recommend42Recommend44 に改名します。4.3 とは非互換(属性必須・ORM 3・PHP 8.2+)のため、新規 4.4 ブランチへの取り込みです。

参考: 4.3→4.4 マイグレーション手順 (doc #346) / 先行対応 eccube-api4 #186 / sample-payment-plugin #53

変更内容

1. EC-CUBE 4.4 対応(コア移行)

  • Annotations → PHP 属性: Entity の @ORM\*#[ORM\*]、Controller の @Route/@Template#[Route]/#[Template]Sensio 依存除去)
  • 型宣言の明示: Entity プロパティの型付け(?int/?string/bool 等)、ゲッター/セッター・各メソッドの戻り値型
  • Doctrine ORM 3.0: flush($entity)flush()
  • Symfony 7: Assert\Length を名前付き引数形式へ
  • AbstractPluginManager: 全メソッドに : void、暗黙 nullable 引数の解消
  • PHPUnit 11: phpunit.xml.dist<source>/<extensions> 形式へ
  • コード名: namespace / composer code / version: 4.4.0 / テンプレート参照を Recommend44 に統一

2. Docker Compose によるテスト環境

  • docker-compose.yml + dev/mysql/pgsql オーバーレイ、dockerbuild/
  • EC-CUBE 4.4 イメージ(ghcr.io/ec-cube/ec-cube-php:8.2-apache-4.4)でプラグインを自動導入・有効化し PHPUnit を実行可能
  • 管理画面ログイン用に APP_ENV=dev 起動+dev 用 Cookie 設定(cookie_secure:false/cookie_samesite:lax)を上書き

3. 静的解析・整形ツール

  • phpstan.neon.dist(level 6)/ Resource/rector.php / Resource/.php-cs-fixer.dist.php を追加(.php 設定は本体の Plugin\: サービス検出で 500 を避けるため Resource/ 配下に配置)
  • rector / php-cs-fixer を適用し、phpstan level 6 を baseline なしでエラーゼロまでクリア(Repository に @extends AbstractRepository<RecommendProduct> を付与する等)
  • CLAUDE.md に開発・テスト手順、アーキテクチャ、移行・配置の注意を記載

4. CI(.github/workflows/main.yml ほか)

  • マトリクスを EC-CUBE 4.4 / PHP 8.2-8.4 / MySQL8・PostgreSQL に更新、checkout@v4$GITHUB_OUTPUT
  • --ignore-platform-req=ext-redis: Symfony 7.4 の symfony/cache が古い php-redis と衝突して本体 composer install が失敗するのを回避(redis は未使用)
  • phpunit 前に cache:warmup: 有効プラグインのルートはコンテナのコンパイル時に確定するため、phpunit プロセスの遅延コンパイルだと RouteNotFoundException が断続的に発生する問題を解消
  • release.yml: 配布パッケージから開発・テスト用ファイル(docker-compose / dockerbuild / CLAUDE.md / phpstan / rector / php-cs-fixer)を除外

テスト

  • EC-CUBE 4.4(8.2-apache-4.4)+ SQLite で PHPUnit 全 24 テスト パス(PHPUnit 11.5 / PHP 8.2)
  • CI 全マトリクス green(4.4 × PHP 8.2/8.3/8.4 × MySQL8/PostgreSQL)
  • phpstan level 6 エラーゼロ(baseline なし)/ php-cs-fixer 差分ゼロ
  • Playwright で管理画面ログイン・「おすすめ管理」画面の表示を確認
  • 配布パッケージ(release.yml 相当)が PharData で展開可能・code=Recommend44 を確認

🤖 Generated with Claude Code

ttokoro20240902 and others added 13 commits June 24, 2026 13:49
Symfony 7.4 / Doctrine ORM 3.0 / PHP 8.2+ へ対応し、コードを Recommend44 に改名。

- composer.json: name/code/version を Recommend44・4.4.0 へ
- 全 namespace と @Recommend42 エイリアスを Recommend44 へ改名
- Entity: アノテーション → PHP 属性、型付きプロパティ化、型宣言追加
- Controller: @Route/@template → 属性、戻り値型明示、Sensio 依存除去
- Form: Length 制約を名前付き引数化、戻り値型追加
- PluginManager: 全メソッドに : void、暗黙 nullable 廃止
- Doctrine ORM 3.0: flush($entity) → flush()
- phpunit.xml.dist: PHPUnit 11 形式(source/extensions)へ
- CI: EC-CUBE 4.4 / PHP 8.2-8.4 マトリクス、deprecated action 更新

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sample-payment-plugin に倣い、EC-CUBE 4.4 + 本プラグインを Docker Compose で
立ち上げて PHPUnit を実行できるようにする。

- docker-compose.yml: ベース(ghcr.io/ec-cube/ec-cube-php:8.2-apache-4.4 + mailcatcher、SQLite、APP_ENV=test)
- docker-compose.dev.yml: プラグインを tar 化し eccube:plugin:install で導入・有効化
  (PharData は先頭 "./" エントリで失敗するため ./* を対象に tar 化)
- docker-compose.mysql.yml / docker-compose.pgsql.yml: DB 切り替え用オーバーレイ
- dockerbuild/grant_to_dbuser.sql: MySQL 用権限付与
- README: 起動・テスト実行手順を追記

あわせて 4.4 移行に伴うテスト期待値を修正:
- Length バリデーションメッセージを Symfony 7.4 の文言へ更新
- getVisible() の boolean 型化に合わせ testRecommendDelete の期待値を false に

動作確認: EC-CUBE 4.4 + PHP 8.2 + SQLite で全 24 テスト パス(PHPUnit 11.5)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
APP_ENV=test ではセッションがモックストレージ(mock_file)になり、実ブラウザで
ログイン状態を保持できなかった。またEC-CUBE 4.4(Symfony 7)の既定 cookie_samesite: none
により HTTP では Cookie が拒否される。

- docker-compose.yml: APP_ENV を test → dev に変更(実セッションが必要なため)
- dockerbuild/dev-framework.yaml: dev 環境の Cookie を cookie_secure:false / cookie_samesite:lax に上書き
- docker-compose.dev.yml: 上記上書きファイルをマウント
- README: PHPUnit は test 環境のキャッシュをクリアしてから実行するよう手順を更新

動作確認: ログイン POST → 302 → ダッシュボード 200(admin/password、HTTP localhost:8080)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sample-payment-plugin (#53) / eccube-api4 (#186) のモデルケースに倣い、
4.4 対応の保守性向上のため開発ツール設定を追加する。

- phpstan.neon.dist: PHPStan level 6(ルート、neon のため配置可)
- Resource/rector.php: Symfony 7.4 / Doctrine ORM 3 への rector 設定
- Resource/.php-cs-fixer.dist.php: php-cs-fixer 設定
  ※ rector.php / .php-cs-fixer.dist.php は .php のためルート直下に置くと
    本体の Plugin\: サービス検出で 500 になる。Resource/ 配下が必須。
- .gitignore: /vendor/・composer.lock・.php-cs-fixer.cache を追加
- CLAUDE.md: 開発・テスト手順、アーキテクチャ、移行/配置の注意を記載

検証: 設定ファイル配置後も EC-CUBE 起動 OK(admin 302・ルート7件、500 なし)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
追加した静的解析ツールを実際に適用・対応した。

- rector process 適用: constructor promotion / readonly プロパティ / Elvis 演算子 /
  PHPDoc クラス名 import(TernaryToElvis / ClassPropertyAssignToConstructorPromotion / ReadOnlyProperty)
- php-cs-fixer fix 適用: 戻り値型補完・ライセンスヘッダ https 化など 11 ファイル整形
- テストクラスのプロパティを nullable 化(EccubeTestCase が tearDown で全プロパティに
  null 代入するため、非 null 型だと TypeError になる)
- phpstan-baseline.neon を追加し level 6 を green に(既存の型注釈不足/doctrine 偽陽性を
  grandfather、新規コードは level 6 で検査)

検証: PHPUnit 全 24 件パス / php-cs-fixer 差分ゼロ / phpstan [OK] No errors

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EC-CUBE 4.4 (Symfony 7.4) の symfony/cache が ext-redis <6.1 と衝突を宣言する一方、
CI ランナー(nanasess/setup-php) には古い php-redis 5.3.7 が apt で入るため本体の
composer install が失敗していた。本プラグイン・本体テストは redis を使用しないため
--ignore-platform-req=ext-redis で回避する。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
有効化したプラグインのルーティングはコンテナのコンパイル時に dtb_plugin から確定する。
phpunit プロセスでの遅延コンパイルに委ねると DB/タイミングにより有効プラグイン一覧を
取りこぼし、RouteNotFoundException で断続的に失敗していた(特に pgsql)。

- Run PHPUnit: cache:clear 後に cache:warmup を実行し、クリーンなプロセスで
  プラグイン込みのコンテナを確定的にビルドしてから phpunit を実行
- Setup Plugin: enable 後に cache:clear する順序へ(api4 #186 に合わせる)

検証: ローカル(EC-CUBE 4.4/test)で warmup 後にルート7件登録・phpunit 24件パス

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
baseline で grandfather していた 74 件の指摘を実際に解消し、phpstan-baseline.neon を撤廃。

- Repository に @extends AbstractRepository<RecommendProduct> を付与し、find()/findOneBy()/
  QueryBuilder の戻り値型を確定(method.notFound 7・doctrine.dql 5・argument.type を解消)
- 配列型に値型を明示(array<string, mixed> / RecommendProduct[] / int[] 等)
- Service の $data を RecommendProduct 型に、PluginManager の @param を整理(null/不明引数除去)
- Entity の visible を NOT NULL カラムに合わせ bool 型へ(doctrine.columnType 解消)
- テストメソッドに : void、ヘルパ引数に型、プロパティに @var を付与
  (EccubeTestCase の tearDown 対策で native 型化はせず @var + 無型に統一)
- php-cs-fixer: phpdoc_to_property_type を無効化(テストプロパティの非null native 型化を防ぐ)
- phpstan.neon.dist: includes(baseline) を削除

検証: phpstan No errors / php-cs-fixer 差分ゼロ / phpunit 24件パス(EC-CUBE 4.4・PHP 8.2)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
release.yml の packaging で、本番配布物に不要な開発用ファイルを削除してから tar 化する。

- docker-compose*.yml / dockerbuild/(Docker テスト環境)
- CLAUDE.md / phpstan.neon.dist / Resource/rector.php / Resource/.php-cs-fixer.dist.php(開発ツール)

検証: 上記除外後も PharData 展開 OK・composer code=Recommend44 を確認(インストール可能)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DB 種別に依存しない静的解析を、テストマトリクスとは別の static-analysis ジョブで
SQLite・PHP 8.3 の1構成・3ツール直列で実行する(重複実行を避けつつ Checks を分離)。

- php-cs-fixer --dry-run --diff / rector process --dry-run(差分で失敗)
- phpstan analyse(level 6)。phpstan は objectManagerLoader がカーネルを起動し
  EccubeExtension が dtb_plugin を読むため、本体インストール+DB+プラグイン有効化を前提とする

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- static-analysis: Setup EC-CUBE に eccube:fixtures:load を追加。プラグイン有効化時に
  PluginManager::createDataBlock() が DeviceType マスタを参照するため、未投入だと
  newBlock(null) で enable が TypeError になり失敗していた
- static-analysis / テストマトリクスの対象 PHP に 8.5 を追加(サポート範囲 8.2-8.5 の上端)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
コンストラクタでのみ代入される typed プロパティのため readonly が適切。
rector の ReadOnlyPropertyRector が検出する差分(過去の rector→php-cs-fixer の
適用順で型付与後に readonly 化されず残っていた)を解消し、CI の rector --dry-run を green にする。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
eccube:plugin:enable 直後の 1 回の cache:clear では、プラグインのルーティング
(おすすめ管理ページ plugin_recommend_list)と Nav メニューが確定せず、
docker compose up 直後の初回ロードで /admin/plugin/recommend が 404 になっていた。
enable とは別パスで cache:clear をもう一度実行すると確定するため、
docker-compose.dev.yml の entrypoint で cache:clear を 2 回流すようにした。

curl で docker compose up 直後(手動キャッシュ操作なし)から
/admin/plugin/recommend が 200・おすすめ管理ページ表示・Navメニュー表示に
なることを確認済み。CLAUDE.md にも挙動と対処を記載。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ttokoro20240902

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b605ad49-5a06-4ca1-ba46-8fe83462893f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-4.4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ttokoro20240902

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dotani1111 dotani1111 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4.4/Symfony7 対応をレビュー・実機検証しました。Recommend42→44 の移行(PluginManager シグネチャのコア親一致・flush() 引数除去・#[Route]/#[ORM]/#[Template] 属性化・namespace 全面統一)は正しく、CI 全マトリクス green。実機(install/enable・schema:validate 同期・管理画面描画)に加え、商品検索モーダルからおすすめ商品を登録→一覧表示まで確認しました。Blocker なし。LGTM 👍

任意の軽微点: Resource/rector.php のライセンスヘッダが http のまま/FormType の未使用 getName()。

@nanasess nanasess left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4.4 環境(PHP 8.5.4 / Symfony 7.4.13 / Doctrine ORM 3.6.2 / DBAL 4.4.3)へ本 PR のブランチをインストールし、実機で動作確認したうえでレビューしました。

一覧・新規登録・編集・削除・フロント表示のすべてが正常に動作しました。移行の品質は高く、fatal 化しうる箇所(AbstractPluginManager の新シグネチャ、EccubeNav::getNav(): array、ORM 3 の flush() 引数削除、Routing\Attribute\Route@ORM#[ORM] の options / JoinColumn)はすべて正しく追従できていることを本体 4.4 のソースと突き合わせて確認しています。

blocker はありません。

実機動作検証の結果

検証項目 結果
おすすめ商品一覧 ✅ 表示・件数カウント
新規登録(商品検索モーダル → 選択 → 保存) ✅ 「おすすめ商品を登録しました。」
編集 ✅ 「おすすめ商品を修正しました。」
削除(DELETE + CSRF) ✅ 「おすすめ商品を削除しました。」
フロント表示(ブロック) ✅ 下層ページ(レイアウト 2 / MAIN_BOTTOM)に描画
XSS 対策 ✅ 説明文は |raw|purify|nl2br を通す

別 PR での対応をご検討ください(差分範囲外)

Resource/template/admin/index.twig の冒頭で読み込んでいる assets/js/vendor/jquery.ui/*.min.js 4 本は、本体 4.2.0 の時点で削除済みで 4.4 のツリーに存在しません。実機で /admin/plugin/recommend を開くと 404 が 4 件出ます。

404 /html/template/admin/assets/js/vendor/jquery.ui/jquery.ui.core.min.js
404 /html/template/admin/assets/js/vendor/jquery.ui/jquery.ui.widget.min.js
404 /html/template/admin/assets/js/vendor/jquery.ui/jquery.ui.mouse.min.js
404 /html/template/admin/assets/js/vendor/jquery.ui/jquery.ui.sortable.min.js

機能退行はありません。 jQuery UI sortable は本体の admin.bundle.js が同梱しており、default_frame.twig<head> で読み込まれるため、読み込み順の面でも問題ありません。本 PR の差分には含まれない 4.2 からの持ち越しなので、別 PR での削除をご検討ください。

良い点

  • 削除が methods: ['DELETE'] + CSRF トークン検証で保護されている
  • 説明文の出力が |raw|purify|nl2br で XSS 対策済み
  • Resource/rector.php / Resource/.php-cs-fixer.dist.phpResource/ 配下に置く配置が、4.4 の app/config/eccube/services.php の exclude と整合している
  • RecommendServiceRecommendProduct $data に対して $data['id'] で配列アクセスしている点は、Eccube\Entity\AbstractEntity implements \ArrayAccess のため問題なし

Comment thread Controller/RecommendController.php Outdated
* @Route("/%eccube_admin_route%/plugin/recommend/sort_no/move", name="plugin_recommend_rank_move")
* @throws \Exception
*/
#[Route(path: '/%eccube_admin_route%/plugin/recommend/sort_no/move', name: 'plugin_recommend_rank_move')]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] methods 指定と CSRF トークン検証がありません

同じコントローラの delete()methods: ['DELETE'] + $this->isTokenValid() で保護されていますが、並び替えの moveRank() にはどちらもありません。

本体 4.4 の同型エンドポイントは 6 本すべてCategoryController / ClassCategoryController / ClassNameController / TagController / PaymentController / DeliveryController)が

  1. methods: ['POST']
  2. if (!$request->isXmlHttpRequest()) throw new BadRequestHttpException();
  3. if ($this->isTokenValid())

の 3 点セットになっており、本実装だけがこの規約から外れています。

実害の度合いは限定的です$request->isXmlHttpRequest() を通しており、X-Requested-With は CORS のセーフリストヘッダではないため、クロスサイトからの単純フォーム POST は成立しません)。ただし修正コストはほぼゼロで、テンプレート側の変更も不要です。

修正案: 本体 CategoryController::moveSortNo() と同じ形に揃える。

#[Route(path: '/%eccube_admin_route%/plugin/recommend/sort_no/move', name: 'plugin_recommend_rank_move', methods: ['POST'])]
public function moveRank(Request $request): Response
{
    if (!$request->isXmlHttpRequest()) {
        throw new BadRequestHttpException();
    }
    if ($this->isTokenValid()) {
        ...
    }
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘のとおり、本体の 3 点セットに揃えました(78dde65)。

#[Route(..., name: 'plugin_recommend_rank_move', methods: ['POST'])]
public function moveRank(Request $request): Response
{
    if (!$request->isXmlHttpRequest()) {
        throw new BadRequestHttpException();
    }
    if ($this->isTokenValid()) { ... return new Response('OK'); }
    throw new BadRequestHttpException();
}

ご指摘どおりテンプレートの変更は不要でした。本体 default_frame.twig$.ajaxSetupECCUBE-CSRF-TOKEN ヘッダを全 Ajax に付与し、isTokenValid() がそれを読むためです。実機で並び替えを行い、リクエストヘッダに同トークンが乗って 200 が返ること、リロード後も順序が保持されることを確認しました。

ガードの動作も確認済みです: トークンなし → 403 / GET → 405 / X-Requested-With なし → 400 / 正常 → 200

Comment thread .github/workflows/main.yml Outdated
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
mysql8:
image: mysql:8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] CI の DB イメージが、同じ PR で追加した docker-compose とも本体 4.4 とも食い違っています

場所 MySQL PostgreSQL
.github/workflows/main.yml mysql:8 postgres:14
docker-compose.mysql.yml(本 PR で追加) mysql:8.4
docker-compose.pgsql.yml(本 PR で追加) postgres:18
本体 unit-test.yml 8.4 18 / 13

本 PR 自身が追加した docker-compose は本体に揃えているのに、CI の service コンテナだけ 4.2 ブランチから持ち越した設定のままで、ローカル開発と CI でテストする DB バージョンが食い違っています。

なお database_server_version は本体 4.4 も MySQL 8.4 に対して 8 を指定しているので、現状のままで問題ありません。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mysql:8.4 / postgres:18 に更新し、pgsql の database_server_version18 に合わせました(92b0834)。ご指摘のとおり MySQL 側の 8 は本体と同じなので据え置いています。

開発環境が SQLite のためローカルでは検証できておらず、CI の結果で確認します。MySQL 8.4 は mysql_native_password が既定で無効なので、接続まわりで落ちるようなら追って調整します。

foreach ($arrRank as $recommendId => $rank) {
/* @var $Recommend RecommendProduct */
/** @var RecommendProduct $Recommend */
$Recommend = $this->find($recommendId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] find() の戻り値の null チェックがありません

$this->find($recommendId)RecommendProduct|null を返しますが、直後に $Recommend->getSortno() を呼んでいます。存在しない ID を含む配列を渡すと Error: Call to a member function getSortno() on null になり、catch (\Exception $e)\Error を捕捉しないため beginTransaction() したまま 500 になります。

ただし 本体 4.4 の CategoryController::moveSortNo() も同一パターンのため、移行 PR での退行でも規約違反でもありません。上記の moveRank() を修正される際に、ついでにガードを入れておくと堅くなる、という程度です。

$Recommend = $this->find($recommendId);
if (!$Recommend) {
    continue;
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

①と同じ経路なので、ついでに入れました(78dde65)。

1 点補足で、直前の /** @var RecommendProduct $Recommend */ も削除しています。@extends AbstractRepository<RecommendProduct>find()RecommendProduct|null に解決されますが、@var が残っていると非 null に絞り込まれ、追加したガードが booleanNot.alwaysFalse として phpstan に検出されるためです。level 6 エラーゼロは維持しています。

Comment thread Form/Type/RecommendProductType.php Outdated
* @return string
*/
public function getName()
public function getName(): string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] getName() は Symfony 3 以降どこからも呼ばれないデッドコードです

FormTypeInterface(Symfony 7.4)に getName() は存在せず、AbstractType にも実装がありません。フォーム名は AbstractType::getBlockPrefix() の既定実装が返す recommend_product で決まります。

実際、テンプレートの #recommend_product_ProductgetName() が返す admin_recommend ではなく recommend_product を前提にしており、admin_recommend という文字列はリポジトリ内でこの 1 箇所にしか現れません。

属性化・型付けを一通り行ったこのタイミングで削除してしまうのが良さそうです。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

削除しました(92b0834)。dotani1111 さんからも同じご指摘をいただいていました。

削除後に実機のフォームを確認したところ、フィールド名は recommend_product[_token] / recommend_product[Product] / recommend_product[comment] のままで、getBlockPrefix() 由来の名前で決まっていることが裏取りできました。テンプレートの #recommend_product_Product とも整合しています。

Comment thread Resource/rector.php
dirname(__DIR__).'/vendor',
dirname(__DIR__).'/node_modules',
])
->withSets([

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] rector の対象に Tests/ が入っておらず、PHPUnit 用のセットも未適用です

withPaths() は Controller / Entity / Form / Repository / Service / PluginManager.php / Nav.php のみで Tests/ が対象外、withSets() にも PHPUnitSetList::PHPUNIT_CODE_QUALITY / PHPUNIT_110 がありません(本体 4.4 の rector.php は両方入れています)。

本 PR のテストコード自体は静的データプロバイダも withConsecutive() も使っていないため現時点で問題はありませんが、Resource/.php-cs-fixer.dist.phpphpstan.neon.dist はいずれも Tests/ を見ているので、rector だけ穴が空いている状態です。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests/withPaths() に追加し、PHPUnitSetList::PHPUNIT_CODE_QUALITY / PHPUNIT_110 を有効にしました(92b0834)。

「現時点で問題はない」とのことでしたが、実際に有効化したところ rector が差分を出しました。CI が rector --dry-run をゲートにしているため適用しています:

  • declare(strict_types=1) の付与
  • テストクラスの final
  • 'POST'Request::METHOD_POST
  • assertInstanceOf() の追加

いずれも本体 4.4 の tests/Eccube/Tests/ が既に同じ形になっていることを確認したうえで取り込みました。PHPUnit は 24/24 のままパスしています。

Tests/bootstrap.php のみ withSkip() に追加しました。名前空間を持たないスクリプトのため、import 整理で use ...\Dotenv; がライセンスヘッダより上に移動し、その結果 php-cs-fixer の header_comment がヘッダを二重に付与する事象が実際に発生したためです。phpstan も同ファイルを除外しているので、扱いを揃えた形になります。

Comment thread Resource/rector.php
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] CLI 実行ガードがありません

本 PR で追加した Resource/.php-cs-fixer.dist.php にはガードがあり、本体 4.4 の rector.php にも同等のガードがありますが、このファイルにだけありません。

app/Plugin/ はドキュメントルート外で、かつ release.yml で配布物からも除去されるため実害はありませんが、多層防御として揃えておくのが無難です。

if (PHP_SAPI !== 'cli') {
    http_response_code(403);
    exit;
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本体 4.4 と同じガードを追加しました(92b0834)。多層防御として揃えておくべき、というご指摘に同意です。

@@ -13,6 +13,10 @@ jobs:
working-directory: ../
run: |
rm -rf $GITHUB_WORKSPACE/.github

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] このファイルだけ runs-on: ubuntu-22.04 のまま残っています(8 行目)

main.yml は 2 ジョブとも ubuntu-24.04 に更新されていますが、release.yml は 22.04 のままです。配布パッケージ生成は tar と rm しか使わないため今すぐ壊れはしませんが、ランナーイメージの世代を揃えておかないと将来 22.04 の提供終了時にリリースだけ落ちます。

(8 行目は差分範囲外のため、本 PR で触っているこの行にコメントしています。別 PR での対応でも構いません。)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

別 PR でも構わないとのことでしたが、main.yml を 24.04 にしたのが本 PR なので、こちらで ubuntu-24.04 に揃えました(92b0834)。

ttokoro20240902 and others added 2 commits August 4, 2026 09:51
並び替えの moveRank() には methods 指定と CSRF トークン検証がなく、本体 4.4 の
同型エンドポイント (CategoryController::moveSortNo() 等) が揃って備えている
3 点セットから外れていた。本体と同じ形に揃える。

- methods: ['POST'] を指定
- !isXmlHttpRequest() で BadRequestHttpException を送出
- isTokenValid() で CSRF トークンを検証

トークンは本体 default_frame.twig の $.ajaxSetup が ECCUBE-CSRF-TOKEN ヘッダとして
全 Ajax に付与するため、テンプレート側の変更は不要。

併せて moveRecommendRank() の find() に null ガードを追加した。存在しない ID を
渡すと getSortno() が \Error になり、catch (\Exception) では捕捉できず
beginTransaction() したまま 500 になっていた。null 非許容として扱っていた
@var アノテーションは、ガードが常時 false と解析されるため削除する。

edit() / delete() / moveRank() の戻り値型も明示した。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Form/Type/RecommendProductType.php: getName() を削除。Symfony 3 以降どこからも
  呼ばれず、FormTypeInterface にも存在しない。フォーム名は getBlockPrefix() 既定の
  recommend_product で決まっており、返り値の admin_recommend は未参照だった。

- Resource/rector.php:
  - ライセンスヘッダを https へ(プラグイン内の他ファイルと統一)
  - CLI 実行ガードを追加(.php-cs-fixer.dist.php・本体 4.4 と同様の多層防御)
  - Tests/ を対象に追加し PHPUnit セットを有効化。php-cs-fixer と phpstan は
    Tests/ を見ており rector だけ穴が空いていた。Tests/bootstrap.php のみ、
    import 整理でライセンスヘッダが二重化するため除外する。
  - 上記に伴い、テストへ declare(strict_types=1) / final / Request 定数化 /
    assertInstanceOf が適用された(本体 4.4 のテストと同じ形)

- .github/workflows/main.yml: CI の DB を mysql:8.4 / postgres:18 に更新。
  同 PR で追加した docker-compose および本体 4.4 と食い違っていた。
  postgres の database_server_version も 18 に合わせる。

- .github/workflows/release.yml: runs-on を ubuntu-24.04 に更新(main.yml と統一)

- Resource/template/admin/index.twig: jquery.ui の個別読み込み 4 本を削除。
  本体 4.2.0 で削除済みのファイルを参照しており 404 が 4 件出ていた。
  sortable は本体 admin.bundle.js に同梱され head で読まれるため機能退行はない。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ttokoro20240902

Copy link
Copy Markdown
Author

@nanasess 詳細な実機検証とレビューをありがとうございます。いただいた指摘 7 件はすべて対応し、各スレッドに個別に返信しました(78dde65 / 92b0834)。

jQuery UI の 404 について

別 PR をご提案いただきましたが、4 行の削除で本 PR の CHANGES_REQUESTED をまとめて解消できるため、こちらに含めさせていただきました(92b0834)。差分範囲外への越境になりますので、不適切であれば切り出します。

削除にあたって、ご指摘の裏取りをしています:

  • 本体の html/template/admin/assets/js/vendor/jquery.ui/ は 4.1 には存在しますが 4.4 のツリーには存在しない
  • admin.bundle.js のエントリ(html/template/admin/assets/js/bundle.js)が jquery-ui/ui/widgets/sortable / widget / mouse / position を require しており、default_frame.twig<head> で読み込まれる
  • 削除後の実機で jquery.ui へのリクエストが消え、typeof $.fn.sortable === 'function'、ドラッグ&ドロップでの並び替えも正常動作

なお 4.2 ブランチにも同じ 4 行が残っていますgit show origin/4.2:Resource/template/admin/index.twig)。4.2 は EC-CUBE 4.2/4.3 の両対応で、両バージョンでの bundle 同梱状況を別途確認する必要があるため、そちらは本 PR では触っていません。

検証結果

phpstan level 6 エラーゼロ(baseline なし)/ php-cs-fixer 差分ゼロ / rector dry-run クリーン / PHPUnit 24 テストパス。実機は EC-CUBE 4.4 + PHP 8.2 + SQLite で、一覧・登録・編集・削除・並び替えを確認しました。

CI の DB イメージ更新(mysql:8.4 / postgres:18)のみローカル未検証です。開発環境が SQLite のため、CI の結果で確認します。

@ttokoro20240902

Copy link
Copy Markdown
Author

@dotani1111 レビューと実機検証をありがとうございます。ご指摘の任意 2 点はどちらも対応しました。

  • Resource/rector.php のライセンスヘッダを https:// に修正(92b0834)。プラグイン内の他の PHP ファイルおよび Resource/.php-cs-fixer.dist.php$header が https なのに対し、このファイルだけ http でした。Resource/ 配下は cs-fixer の exclude に入っているため自動検出されない箇所でした。
  • RecommendProductType::getName() を削除(92b0834)。nanasess さんからも同じご指摘をいただいています。削除後、フォームのフィールド名が recommend_product[...] のままであることを実機で確認しています。

Static Analysis ジョブの "Setup EC-CUBE" が以下で失敗するようになっていた。

    Could not create database var/eccube.db for connection named default
    Operation "Doctrine\DBAL\Platforms\SQLitePlatform::getCreateDatabaseSQL"
    is not supported by platform.

本体 4.4 が doctrine/dbal 4 系を解決するようになったことによるもので、本 PR の
変更が原因ではない(同じステップは 2026-06-25 の実行では成功していた)。

SQLite では DB ファイルを doctrine:schema:create が生成するため
doctrine:database:create は不要。本体 4.4 の unit-test.yml も sqlite3 の場合は
同コマンドをスキップしており、その扱いに合わせる。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ttokoro20240902

Copy link
Copy Markdown
Author

CI 追加修正のご報告(5817ef4)

レビュー指摘対応(78dde65 / 92b0834)をプッシュしたところ、Static Analysis ジョブが失敗しました。調査の結果、本 PR の変更が原因ではなく、本体 4.4 側の依存解決の変化によるものでしたので、併せて修正しています。

失敗内容

Setup EC-CUBE ステップ(SQLite)で以下により中断していました。

Could not create database var/eccube.db for connection named default
Operation "Doctrine\DBAL\Platforms\SQLitePlatform::getCreateDatabaseSQL" is not supported by platform.

本 PR 起因でない根拠

  • 同じステップは 2026-06-25 の実行(db885ec)では success、ワークフローのコードもその後変更していません(本 PR の main.yml の差分は DB イメージのタグと database_server_version のみ)
  • 静的解析ジョブは SQLite 固定でサービスコンテナを使わないため、今回の mysql:8.4 / postgres:18 更新の影響を受けません
  • 原因は本体 4.4 が doctrine/dbal 4 系を解決するようになったことです。DBAL 4 の SQLitePlatformgetCreateDatabaseSQL() をサポートしません

修正内容

SQLite では DB ファイルを doctrine:schema:create が生成するため、doctrine:database:create を実行しないようにしました。本体 4.4 の unit-test.yml も sqlite3 の場合は同コマンドをスキップしており、その扱いに合わせています。MySQL / PostgreSQL のマトリクスジョブでは引き続き実行します。

レビュー範囲外の修正になりますので、別 PR に切り出すべきであればご指示ください(単一コミットなので分離は容易です)。

補足: 開発用 Docker イメージと CI で DBAL のバージョンが異なります

調査の過程で判明した点です。

開発用イメージ (8.2-apache-4.4) CI (fresh install)
doctrine/dbal 3.10.5 4.4.4
doctrine/orm 3.6.2 3.6.2
symfony/framework-bundle v7.4.8 v7.4.8

ORM と Symfony は一致していますが DBAL だけ差があり、ローカルでは doctrine:database:create が成功してしまうため CI の失敗を再現できませんでした。本 PR の範囲では対応していませんが、ローカル検証と CI で挙動が食い違う要因になり得るため共有します。

最終確認結果

CI 全 19 チェック green

  • PHPUnit マトリクス: 4.4 × PHP 8.2/8.3/8.4/8.5 × MySQL 8.4 / PostgreSQL 18 — 全 8 構成 pass
  • Static Analysis: php-cs-fixer 差分ゼロ / rector dry-run クリーン(Tests/ を対象に追加した状態)/ phpstan level 6 エラーゼロ(baseline なし)
  • CodeRabbit: pass

実機確認(EC-CUBE 4.4 + PHP 8.2 + SQLite): 一覧・登録・編集・削除・並び替え、および並び替えエンドポイントのガード(トークンなし 403 / GET 405 / X-Requested-With なし 400 / 正常 200)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants