From 43be2294887ea742e3eac8c97011ec30d0f4a4af Mon Sep 17 00:00:00 2001 From: Ken Tanaka Date: Mon, 13 Jul 2026 19:04:25 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Symfony7=E9=81=A9=E5=90=88=20-=20=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E3=81=A8=E3=81=AA=E3=82=8B=E6=94=B9=E4=BF=AE=E3=81=AF?= =?UTF-8?q?=20Claude=E3=81=A7=E5=AE=9F=E6=96=BD=20+=20=E3=83=AC=E3=83=93?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E6=B8=88=E3=81=BF(=E8=A9=A6=E9=A8=93?= =?UTF-8?q?=E3=81=AF=E6=89=8B=E5=8B=95=E5=AE=9F=E8=A1=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 170 +++++++++++++----- .github/workflows/release.yml | 4 +- .../TwoFactorAuthCustomerAppController.php | 34 ++-- Entity/CustomerTrait.php | 21 +-- Event.php | 17 +- .../TwoFactorAuthCustomerTypeExtension.php | 24 +-- Form/Type/TwoFactorAuthAppTypeCustomer.php | 7 +- PluginManager.php | 71 +++----- README.md | 12 ++ Resource/.php-cs-fixer.dist.php | 57 ++++++ Resource/rector.php | 59 ++++++ Tests/bootstrap.php | 18 ++ composer.json | 10 +- phpunit.xml.dist | 52 +++--- 14 files changed, 360 insertions(+), 196 deletions(-) create mode 100644 README.md create mode 100644 Resource/.php-cs-fixer.dist.php create mode 100644 Resource/rector.php create mode 100644 Tests/bootstrap.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b55a392..fac54b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: CI for TwoFactorAuthCustomerApp42 +name: CI for TwoFactorAuthCustomerApp44 on: push: branches: @@ -17,19 +17,15 @@ on: jobs: run-on-linux: name: Run on Linux - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: - eccube_version: [ '4.2','4.3' ] - php: [ '7.4', '8.0', '8.1','8.2', '8.3' ] - db: [ 'mysql', 'mysql8', 'pgsql' ] - plugin_code: [ 'TwoFactorAuthCustomerApp42' ] + eccube_version: [ '4.4' ] + php: [ '8.2', '8.3', '8.4', '8.5' ] + db: [ 'mysql8', 'pgsql' ] + plugin_code: [ 'TwoFactorAuthCustomerApp44' ] include: - - db: mysql - database_url: mysql://root:password@127.0.0.1:3306/eccube_db - database_server_version: 5.7 - database_charset: utf8mb4 - db: mysql8 database_url: mysql://root:password@127.0.0.1:3308/eccube_db database_server_version: 8 @@ -38,24 +34,7 @@ jobs: database_url: postgres://postgres:password@127.0.0.1:5432/eccube_db database_server_version: 14 database_charset: utf8 - exclude: - - eccube_version: 4.2 - php: 8.2 - - eccube_version: 4.2 - php: 8.3 - - eccube_version: 4.3 - php: 7.4 - - eccube_version: 4.3 - php: 8.0 services: - mysql: - image: mysql:5.7 - env: - MYSQL_ROOT_PASSWORD: password - MYSQL_DATABASE: ${{ matrix.dbname }} - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 mysql8: image: mysql:8 env: @@ -81,7 +60,7 @@ jobs: - 1025:1025 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: nanasess/setup-php@master @@ -97,26 +76,26 @@ jobs: - name: Checkout Base Plugin uses: actions/checkout@v4 with: - repository: 'EC-CUBE/TwoFactorAuthCustomer42' - path: 'TwoFactorAuthCustomer42' + repository: 'EC-CUBE/TwoFactorAuthCustomer44' + path: 'TwoFactorAuthCustomer44' - name: Archive Base Plugin - working-directory: 'TwoFactorAuthCustomer42' + working-directory: 'TwoFactorAuthCustomer44' run: | - tar cvzf ${GITHUB_WORKSPACE}/TwoFactorAuthCustomer42.tar.gz ./* + tar cvzf ${GITHUB_WORKSPACE}/TwoFactorAuthCustomer44.tar.gz ./* - name: Setup mock-package-api env: PLUGIN_CODE: ${{ matrix.plugin_code }} run: | mkdir -p /tmp/repos - for f in ${PLUGIN_CODE} TwoFactorAuthCustomer42; do + for f in ${PLUGIN_CODE} TwoFactorAuthCustomer44; do cp ${GITHUB_WORKSPACE}/${f}.tar.gz /tmp/repos/${f}.tgz done docker run --name package-api -d -v /tmp/repos:/repos -e MOCK_REPO_DIR=/repos -p 8080:8080 eccube/mock-package-api:composer2 - name: Checkout EC-CUBE - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: 'EC-CUBE/ec-cube' ref: ${{ matrix.eccube_version }} @@ -125,8 +104,8 @@ jobs: - name: Get Composer Cache Directory id: composer-cache run: | - echo "::set-output name=dir::$(composer config cache-files-dir)" - - uses: actions/cache@v1 + echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -134,7 +113,9 @@ jobs: ${{ runner.os }}-composer- - name: Install to composer working-directory: 'ec-cube' - run: composer install --no-interaction -o --apcu-autoloader + # Symfony 7.4 の symfony/cache が ext-redis <6.1 と衝突するが、ランナーには + # 古い php-redis(5.3.7) が入っている。本プラグイン/本体テストは redis 未使用のため無視する。 + run: composer install --no-interaction -o --apcu-autoloader --ignore-platform-req=ext-redis - name: Setup EC-CUBE env: @@ -160,10 +141,31 @@ jobs: ECCUBE_PACKAGE_API_URL: 'http://127.0.0.1:8080' working-directory: 'ec-cube' run: | - bin/console eccube:composer:require ec-cube/twofactorauthcustomer42 - bin/console eccube:plugin:enable --code=TwoFactorAuthCustomer42 - bin/console eccube:composer:require ec-cube/twofactorauthcustomerapp42 + bin/console eccube:composer:require ec-cube/twofactorauthcustomer44 + bin/console eccube:plugin:enable --code=TwoFactorAuthCustomer44 + bin/console eccube:composer:require ec-cube/twofactorauthcustomerapp44 bin/console eccube:plugin:enable --code=${PLUGIN_CODE} + bin/console cache:clear --no-warmup + + - name: Run PHPUnit + env: + APP_ENV: 'test' + APP_DEBUG: 0 + DATABASE_URL: ${{ matrix.database_url }} + DATABASE_SERVER_VERSION: ${{ matrix.database_server_version }} + DATABASE_CHARSET: ${{ matrix.database_charset }} + PLUGIN_CODE: ${{ matrix.plugin_code }} + ECCUBE_PACKAGE_API_URL: 'http://127.0.0.1:8080' + working-directory: 'ec-cube' + run: | + # 有効化したプラグインのルーティングはコンテナのコンパイル時に確定する。 + # phpunit プロセスでの遅延コンパイルに任せると DB/タイミングで有効プラグイン一覧を + # 取りこぼし RouteNotFound になることがあるため、クリーンなプロセスで warmup して確定させる。 + bin/console cache:clear --no-warmup + bin/console cache:warmup + if [ -d "app/Plugin/${PLUGIN_CODE}/Tests" ]; then + ./vendor/bin/phpunit -c app/Plugin/${PLUGIN_CODE}/phpunit.xml.dist app/Plugin/${PLUGIN_CODE}/Tests + fi - name: Disable Plugin working-directory: 'ec-cube' @@ -188,3 +190,91 @@ jobs: ECCUBE_PACKAGE_API_URL: 'http://127.0.0.1:8080' working-directory: 'ec-cube' run: bin/console eccube:plugin:uninstall --code=${PLUGIN_CODE} + + static-analysis: + name: Static Analysis + runs-on: ubuntu-24.04 + env: + PLUGIN_CODE: TwoFactorAuthCustomerApp44 + PARENT_PLUGIN_CODE: TwoFactorAuthCustomer44 + APP_ENV: 'test' + APP_DEBUG: 0 + DATABASE_URL: 'sqlite:///var/eccube.db' + DATABASE_SERVER_VERSION: 3 + DATABASE_CHARSET: 'utf8' + ECCUBE_PACKAGE_API_URL: 'http://127.0.0.1:8080' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: nanasess/setup-php@master + with: + php-version: '8.5' + + - name: Archive Plugin + run: | + tar cvzf ${GITHUB_WORKSPACE}/${PLUGIN_CODE}.tar.gz ./* + + - name: Checkout Base Plugin + uses: actions/checkout@v4 + with: + repository: 'EC-CUBE/TwoFactorAuthCustomer44' + path: 'TwoFactorAuthCustomer44' + + - name: Archive Base Plugin + working-directory: 'TwoFactorAuthCustomer44' + run: | + tar cvzf ${GITHUB_WORKSPACE}/TwoFactorAuthCustomer44.tar.gz ./* + + - name: Setup mock-package-api + run: | + mkdir -p /tmp/repos + for f in ${PLUGIN_CODE} ${PARENT_PLUGIN_CODE}; do + cp ${GITHUB_WORKSPACE}/${f}.tar.gz /tmp/repos/${f}.tgz + done + docker run --name package-api -d -v /tmp/repos:/repos -e MOCK_REPO_DIR=/repos -p 8080:8080 eccube/mock-package-api:composer2 + + - name: Checkout EC-CUBE + uses: actions/checkout@v4 + with: + repository: 'EC-CUBE/ec-cube' + ref: '4.4' + path: 'ec-cube' + + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" + - uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + - name: Install to composer + working-directory: 'ec-cube' + run: composer install --no-interaction -o --apcu-autoloader --ignore-platform-req=ext-redis + + - name: Setup EC-CUBE + working-directory: 'ec-cube' + run: | + bin/console doctrine:database:create + bin/console doctrine:schema:create + bin/console eccube:fixtures:load + - name: Setup Plugin + working-directory: 'ec-cube' + run: | + bin/console eccube:composer:require ec-cube/twofactorauthcustomer44 + bin/console eccube:plugin:enable --code=TwoFactorAuthCustomer44 + bin/console eccube:composer:require ec-cube/twofactorauthcustomerapp44 + bin/console eccube:plugin:enable --code=${PLUGIN_CODE} + bin/console cache:clear --no-warmup + + - name: Run php-cs-fixer + working-directory: 'ec-cube' + run: ./vendor/bin/php-cs-fixer fix --config=app/Plugin/${PLUGIN_CODE}/Resource/.php-cs-fixer.dist.php --dry-run --diff + + - name: Run Rector + working-directory: 'ec-cube' + run: ./vendor/bin/rector process --config=app/Plugin/${PLUGIN_CODE}/Resource/rector.php --dry-run diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d360e34..f402a65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,11 +8,13 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Packaging working-directory: ../ run: | rm -rf $GITHUB_WORKSPACE/.github + # 開発・テスト用ファイルは配布パッケージに含めない + rm -f $GITHUB_WORKSPACE/Resource/rector.php "$GITHUB_WORKSPACE/Resource/.php-cs-fixer.dist.php" find $GITHUB_WORKSPACE -name "dummy" -delete find $GITHUB_WORKSPACE -name ".git*" -and ! -name ".gitkeep" -print0 | xargs -0 rm -rf chmod -R o+w $GITHUB_WORKSPACE diff --git a/Controller/TwoFactorAuthCustomerAppController.php b/Controller/TwoFactorAuthCustomerAppController.php index 8915c24..51e8c01 100755 --- a/Controller/TwoFactorAuthCustomerAppController.php +++ b/Controller/TwoFactorAuthCustomerAppController.php @@ -11,16 +11,16 @@ * file that was distributed with this source code. */ -namespace Plugin\TwoFactorAuthCustomerApp42\Controller; +namespace Plugin\TwoFactorAuthCustomerApp44\Controller; use Eccube\Entity\Customer; -use Plugin\TwoFactorAuthCustomer42\Controller\TwoFactorAuthCustomerController; -use Plugin\TwoFactorAuthCustomerApp42\Form\Type\TwoFactorAuthAppTypeCustomer; +use Plugin\TwoFactorAuthCustomer44\Controller\TwoFactorAuthCustomerController; +use Plugin\TwoFactorAuthCustomerApp44\Form\Type\TwoFactorAuthAppTypeCustomer; use RobThree\Auth\TwoFactorAuth; use RobThree\Auth\TwoFactorAuthException; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; +use Symfony\Bridge\Twig\Attribute\Template; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class TwoFactorAuthCustomerAppController extends TwoFactorAuthCustomerController { @@ -36,10 +36,9 @@ class TwoFactorAuthCustomerAppController extends TwoFactorAuthCustomerController /** * 初回APP認証画面. - * - * @Route("/mypage/two_factor_auth/app/create", name="plg_customer_2fa_app_create", methods={"GET", "POST"}) - * @Template("TwoFactorAuthCustomerApp42/Resource/template/default/tfa/app/register.twig") */ + #[Route(path: '/mypage/two_factor_auth/app/create', name: 'plg_customer_2fa_app_create', methods: ['GET', 'POST'])] + #[Template('@TwoFactorAuthCustomerApp44/default/tfa/app/register.twig')] public function create(Request $request) { if ($this->isTwoFactorAuthed()) { @@ -107,10 +106,9 @@ public function create(Request $request) /** * APP認証画面. - * - * @Route("/mypage/two_factor_auth/app/challenge", name="plg_customer_2fa_app_challenge", methods={"GET", "POST"}) - * @Template("TwoFactorAuthCustomerApp42/Resource/template/default/tfa/app/challenge.twig") */ + #[Route(path: '/mypage/two_factor_auth/app/challenge', name: 'plg_customer_2fa_app_challenge', methods: ['GET', 'POST'])] + #[Template('@TwoFactorAuthCustomerApp44/default/tfa/app/challenge.twig')] public function challenge(Request $request) { if ($this->isTwoFactorAuthed()) { @@ -159,28 +157,20 @@ public function challenge(Request $request) ]; } - /** * 秘密鍵生成. * - * @return string - * * @throws TwoFactorAuthException */ - private function createSecret() + private function createSecret(): string { return $this->tfa->createSecret(); } /** - * 認証コードを取得. - * - * @param string $authKey - * @param string $token - * - * @return boolean + * 認証コードを検証. */ - private function verifyCode($authKey, $token) + private function verifyCode(string $authKey, string $token): bool { return $this->tfa->verifyCode($authKey, $token, 1); } diff --git a/Entity/CustomerTrait.php b/Entity/CustomerTrait.php index 8bcc126..40bf612 100644 --- a/Entity/CustomerTrait.php +++ b/Entity/CustomerTrait.php @@ -11,34 +11,23 @@ * file that was distributed with this source code. */ -namespace Plugin\TwoFactorAuthCustomerApp42\Entity; +namespace Plugin\TwoFactorAuthCustomerApp44\Entity; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; -use Eccube\Annotation\EntityExtension; +use Eccube\Attribute\EntityExtension; -/** - * @EntityExtension("Eccube\Entity\Customer") - */ +#[EntityExtension('Eccube\Entity\Customer')] trait CustomerTrait { - /** - * @var ?string - * - * @ORM\Column(name="two_factor_auth_secret", type="string", length=255, nullable=true) - */ + #[ORM\Column(name: 'two_factor_auth_secret', type: Types::STRING, length: 255, nullable: true)] private ?string $two_factor_auth_secret = null; - /** - * @return string - */ public function getTwoFactorAuthSecret(): ?string { return $this->two_factor_auth_secret; } - /** - * @param string|null $two_factor_auth_secret - */ public function setTwoFactorAuthSecret(?string $two_factor_auth_secret): void { $this->two_factor_auth_secret = $two_factor_auth_secret; diff --git a/Event.php b/Event.php index 0856be5..34ecede 100644 --- a/Event.php +++ b/Event.php @@ -11,7 +11,7 @@ * file that was distributed with this source code. */ -namespace Plugin\TwoFactorAuthCustomerApp42; +namespace Plugin\TwoFactorAuthCustomerApp44; use Eccube\Event\TemplateEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -21,15 +21,6 @@ */ class Event implements EventSubscriberInterface { - /** - * Event constructor. - * - */ - public function __construct() - { - - } - public static function getSubscribedEvents(): array { return [ @@ -40,13 +31,11 @@ public static function getSubscribedEvents(): array /** * [/admin/customer/edit]表示の時のEvent Hook. * 二段階認証関連項目を追加する. - * - * @param TemplateEvent $event */ - public function onRenderAdminCustomerEdit(TemplateEvent $event) + public function onRenderAdminCustomerEdit(TemplateEvent $event): void { // add twig - $twig = 'TwoFactorAuthCustomerApp42/Resource/template/admin/customer_edit.twig'; + $twig = 'TwoFactorAuthCustomerApp44/Resource/template/admin/customer_edit.twig'; $event->addSnippet($twig); } } diff --git a/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php b/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php index 7a30682..9e2c9a5 100644 --- a/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php +++ b/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php @@ -11,9 +11,8 @@ * file that was distributed with this source code. */ -namespace Plugin\TwoFactorAuthCustomerApp42\Form\Type\Extension\Admin; +namespace Plugin\TwoFactorAuthCustomerApp44\Form\Type\Extension\Admin; -use Doctrine\ORM\EntityManagerInterface; use Eccube\Form\Type\Admin\CustomerType; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -23,29 +22,12 @@ class TwoFactorAuthCustomerTypeExtension extends AbstractTypeExtension { - /** - * @var EntityManagerInterface - */ - protected EntityManagerInterface $entityManager; - - /** - * CouponDetailType constructor. - * - * @param EntityManagerInterface $entityManager - */ - public function __construct( - EntityManagerInterface $entityManager - ) { - $this->entityManager = $entityManager; - } - /** * buildForm. * - * @param FormBuilderInterface $builder - * @param array $options + * @param array $options */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { if (!empty($options['skip_add_form'])) { return; diff --git a/Form/Type/TwoFactorAuthAppTypeCustomer.php b/Form/Type/TwoFactorAuthAppTypeCustomer.php index 9adf0cd..1b42c9b 100644 --- a/Form/Type/TwoFactorAuthAppTypeCustomer.php +++ b/Form/Type/TwoFactorAuthAppTypeCustomer.php @@ -11,10 +11,9 @@ * file that was distributed with this source code. */ -namespace Plugin\TwoFactorAuthCustomerApp42\Form\Type; +namespace Plugin\TwoFactorAuthCustomerApp44\Form\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints as Assert; @@ -24,7 +23,7 @@ class TwoFactorAuthAppTypeCustomer extends AbstractType /** * {@inheritdoc} */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add( @@ -48,7 +47,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) /** * {@inheritdoc} */ - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'plg_customer_2fa'; } diff --git a/PluginManager.php b/PluginManager.php index 005afed..d5df6ae 100644 --- a/PluginManager.php +++ b/PluginManager.php @@ -11,16 +11,16 @@ * file that was distributed with this source code. */ -namespace Plugin\TwoFactorAuthCustomerApp42; +namespace Plugin\TwoFactorAuthCustomerApp44; use Doctrine\ORM\EntityManagerInterface; +use Eccube\Common\EccubeConfig; use Eccube\Entity\Layout; use Eccube\Entity\Page; use Eccube\Entity\PageLayout; use Eccube\Plugin\AbstractPluginManager; -use Eccube\Common\EccubeConfig; -use Plugin\TwoFactorAuthCustomer42\Entity\TwoFactorAuthConfig; -use Plugin\TwoFactorAuthCustomer42\Entity\TwoFactorAuthType; +use Plugin\TwoFactorAuthCustomer44\Entity\TwoFactorAuthConfig; +use Plugin\TwoFactorAuthCustomer44\Entity\TwoFactorAuthType; use Psr\Container\ContainerInterface; use Symfony\Component\Filesystem\Filesystem; @@ -30,16 +30,15 @@ class PluginManager extends AbstractPluginManager { // 設定対象ページ情報 - private $pages = [ - ['plg_customer_2fa_app_create', 'アプリ認証初期設定・トークン入力', 'TwoFactorAuthCustomer42/Resource/template/default/tfa/app/register'], - ['plg_customer_2fa_app_challenge', 'アプリ認証トークン入力', 'TwoFactorAuthCustomer42/Resource/template/default/tfa/app/challenge'], + private array $pages = [ + ['plg_customer_2fa_app_create', 'アプリ認証初期設定・トークン入力', 'TwoFactorAuthCustomerApp44/Resource/template/default/tfa/app/register'], + ['plg_customer_2fa_app_challenge', 'アプリ認証トークン入力', 'TwoFactorAuthCustomerApp44/Resource/template/default/tfa/app/challenge'], ]; /** - * @param array $meta - * @param ContainerInterface $container + * @param array $meta */ - public function enable(array $meta, ContainerInterface $container) + public function enable(array $meta, ContainerInterface $container): void { $em = $container->get('doctrine')->getManager(); @@ -53,10 +52,9 @@ public function enable(array $meta, ContainerInterface $container) } /** - * @param array $meta - * @param ContainerInterface $container + * @param array $meta */ - public function disable(array $meta, ContainerInterface $container) + public function disable(array $meta, ContainerInterface $container): void { $em = $container->get('doctrine')->getManager(); @@ -71,10 +69,9 @@ public function disable(array $meta, ContainerInterface $container) } /** - * @param array $meta - * @param ContainerInterface $container + * @param array $meta */ - public function uninstall(array $meta, ContainerInterface $container) + public function uninstall(array $meta, ContainerInterface $container): void { $em = $container->get('doctrine')->getManager(); @@ -90,14 +87,12 @@ public function uninstall(array $meta, ContainerInterface $container) /** * Twigファイルの登録 - * - * @param ContainerInterface $container */ - protected function copyTwigFiles(ContainerInterface $container) + protected function copyTwigFiles(ContainerInterface $container): void { // テンプレートファイルコピー $templatePath = $container->get(EccubeConfig::class)->get('eccube_theme_front_dir') - .'/TwoFactorAuthCustomerApp42/Resource/template/default'; + .'/TwoFactorAuthCustomerApp44/Resource/template/default'; $fs = new Filesystem(); if ($fs->exists($templatePath)) { return; @@ -108,15 +103,13 @@ protected function copyTwigFiles(ContainerInterface $container) /** * ページ情報の登録 - * - * @param EntityManagerInterface $em */ - protected function createPages(EntityManagerInterface $em) + protected function createPages(EntityManagerInterface $em): void { foreach ($this->pages as $p) { $hasPage = $em->getRepository(Page::class)->count(['url' => $p[0]]) > 0; if (!$hasPage) { - /** @var \Eccube\Entity\Page $Page */ + /** @var Page $Page */ $Page = $em->getRepository(Page::class)->newPage(); $Page->setEditType(Page::EDIT_TYPE_DEFAULT); $Page->setUrl($p[0]); @@ -142,27 +135,23 @@ protected function createPages(EntityManagerInterface $em) /** * Twigファイルの削除 - * - * @param ContainerInterface $container */ - protected function removeTwigFiles(ContainerInterface $container) + protected function removeTwigFiles(ContainerInterface $container): void { $templatePath = $container->get(EccubeConfig::class)->get('eccube_theme_front_dir') - .'/TwoFactorAuthCustomerApp42'; + .'/TwoFactorAuthCustomerApp44'; $fs = new Filesystem(); $fs->remove($templatePath); } /** * ページ情報の削除 - * - * @param EntityManagerInterface $em */ - protected function removePages(EntityManagerInterface $em) + protected function removePages(EntityManagerInterface $em): void { foreach ($this->pages as $p) { $Page = $em->getRepository(Page::class)->findOneBy(['url' => $p[0]]); - if (!$Page) { + if ($Page !== null) { $Layout = $em->getRepository(Layout::class)->find(Layout::DEFAULT_LAYOUT_UNDERLAYER_PAGE); $PageLayout = $em->getRepository(PageLayout::class)->findOneBy(['Page' => $Page, 'Layout' => $Layout]); @@ -175,12 +164,10 @@ protected function removePages(EntityManagerInterface $em) /** * 設定の登録. - * - * @param EntityManagerInterface $em */ - protected function createConfig(EntityManagerInterface $em) + protected function createConfig(EntityManagerInterface $em): void { - /** @var TwoFactorAuthType $TwoFactorAuthType */ + /** @var TwoFactorAuthType|null $TwoFactorAuthType */ $TwoFactorAuthType = $em->getRepository(TwoFactorAuthType::class)->findOneBy(['name' => 'APP']); if (!$TwoFactorAuthType) { // レコードを保存 @@ -197,20 +184,16 @@ protected function createConfig(EntityManagerInterface $em) // 除外ルートの登録 $TwoFactorAuthConfig = $em->find(TwoFactorAuthConfig::class, 1); - $em->persist($TwoFactorAuthConfig); + if ($TwoFactorAuthConfig !== null) { + $em->persist($TwoFactorAuthConfig); + } $em->flush(); - - return; } /** * 2段階認証設定を消す - * - * @param EntityManagerInterface $em - * - * @return void */ - protected function removeConfig(EntityManagerInterface $em) + protected function removeConfig(EntityManagerInterface $em): void { /** @var TwoFactorAuthType|null $TwoFactorAuthType */ $TwoFactorAuthType = $em->getRepository(TwoFactorAuthType::class)->findOneBy(['name' => 'APP']); diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2e4016 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# TwoFactorAuthCustomerApp44 + +EC-CUBE 4.4(Symfony 7.4)向けの会員向けアプリ認証(TOTP)プラグインです。親プラグイン `TwoFactorAuthCustomer44` が必要です。 + +- プラグインコード: `TwoFactorAuthCustomerApp44` +- Composer: `ec-cube/twofactorauthcustomerapp44` +- 依存: `ec-cube/twofactorauthcustomer44` + +## ブランチ運用 + +- `4.2` / `4.3`: `TwoFactorAuthCustomerApp42` +- `4.4`: `TwoFactorAuthCustomerApp44`(Attribute / ORM 3 / PHP 8.2+) diff --git a/Resource/.php-cs-fixer.dist.php b/Resource/.php-cs-fixer.dist.php new file mode 100644 index 0000000..79d83ee --- /dev/null +++ b/Resource/.php-cs-fixer.dist.php @@ -0,0 +1,57 @@ + true, + 'array_syntax' => ['syntax' => 'short'], + 'phpdoc_align' => false, + 'phpdoc_summary' => false, + 'phpdoc_annotation_without_dot' => false, + 'no_superfluous_phpdoc_tags' => false, + 'increment_style' => false, + 'yoda_style' => false, + 'header_comment' => ['header' => $header], + 'phpdoc_add_missing_param_annotation' => true, + 'phpdoc_param_order' => true, + 'phpdoc_to_comment' => false, // /** @var */ を変換してしまうため + 'phpdoc_trim' => true, + 'global_namespace_import' => [ + 'import_classes' => false, + 'import_constants' => false, + 'import_functions' => false, + ], + // PHPDocの型をネイティブ型へ + 'phpdoc_to_param_type' => true, + 'phpdoc_to_return_type' => true, + // プロパティのネイティブ型化は無効。EC-CUBE のテスト基盤が tearDown で全プロパティに + // null を代入するため、テストプロパティを非null native 型にすると TypeError になる。 + 'phpdoc_to_property_type' => false, +]; + +$finder = \PhpCsFixer\Finder::create() + ->in(dirname(__DIR__)) + ->exclude(['vendor', 'node_modules', 'Resource']) + ->name('*.php') +; +$config = new \PhpCsFixer\Config(); + +return $config + ->setRules($rules) + ->setFinder($finder) + ->setRiskyAllowed(true) + ->setUnsupportedPhpVersionAllowed(true) +; diff --git a/Resource/rector.php b/Resource/rector.php new file mode 100644 index 0000000..30c35df --- /dev/null +++ b/Resource/rector.php @@ -0,0 +1,59 @@ +withPhpVersion(PhpVersion::PHP_82) + // プラグインのソースディレクトリ + ->withPaths([ + dirname(__DIR__).'/Controller', + dirname(__DIR__).'/Entity', + dirname(__DIR__).'/Form', + dirname(__DIR__).'/Event.php', + dirname(__DIR__).'/PluginManager.php', + ]) + ->withSkip([ + dirname(__DIR__).'/vendor', + dirname(__DIR__).'/node_modules', + ]) + ->withSets([ + LevelSetList::UP_TO_PHP_82, + // Symfony 7.4 対応 (@Route → #[Route], @Template, buildForm(): void 等) + SymfonySetList::SYMFONY_74, + SymfonySetList::SYMFONY_CODE_QUALITY, + // Doctrine ORM 3.0 / DBAL 3.0 対応 (@ORM → #[ORM], 型付きプロパティ) + DoctrineSetList::DOCTRINE_CODE_QUALITY, + DoctrineSetList::DOCTRINE_DBAL_30, + DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES, + ]) + // Symfony/Doctrine 等のアノテーション → アトリビュート変換を有効化 + ->withAttributesSets() + // #[Route] は付与されるが use 文が旧 Annotation のまま残るため Attribute へ統一する + ->withConfiguredRule(RenameClassRector::class, [ + 'Symfony\Component\Routing\Annotation\Route' => 'Symfony\Component\Routing\Attribute\Route', + ]) + ->withImportNames( + importShortClasses: false, + importDocBlockNames: true, + importNames: true + ) + ->withParallel(); diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php new file mode 100644 index 0000000..1d625a1 --- /dev/null +++ b/Tests/bootstrap.php @@ -0,0 +1,18 @@ +bootEnv($envFile); +} diff --git a/composer.json b/composer.json index 10b8bd5..3cdf1f7 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,13 @@ { - "name": "ec-cube/twofactorauthcustomerapp42", - "version": "4.3.0", - "description": "2 factor authentication for Customers(APP) EC-CUBE42", + "name": "ec-cube/twofactorauthcustomerapp44", + "version": "4.4.0", + "description": "2 factor authentication for Customers(APP) EC-CUBE 4.4", "type": "eccube-plugin", "extra": { - "code": "TwoFactorAuthCustomerApp42" + "code": "TwoFactorAuthCustomerApp44" }, "require": { "ec-cube/plugin-installer": "^2.0", - "ec-cube/twofactorauthcustomer42": "*" + "ec-cube/twofactorauthcustomer44": "*" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index dd5996d..274abad 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,20 +1,20 @@ + - - - - - - - - + + + + + + + @@ -24,26 +24,20 @@ - - - - - + + + ./ - - ./Tests - ./Resource - ./PluginManager.php - - - + + + ./Tests + ./Resource + ./PluginManager.php + + - - - - + + + + From cec314802787e46e230eb58f1160e164e6adbe4f Mon Sep 17 00:00:00 2001 From: Ken Tanaka Date: Mon, 14 Sep 2026 16:45:50 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E9=9D=99=E7=9A=84=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=EF=BC=88php-cs-fixer=20/=20Rector=20/=20PHPStan=EF=BC=89?= =?UTF-8?q?=E3=82=92=E8=A6=AA=E3=83=97=E3=83=A9=E3=82=B0=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=81=A8=E5=90=8C=E6=A7=98=E3=81=AB=E9=80=9A=E3=81=97=E3=80=81?= =?UTF-8?q?CI=20=E3=82=92=204.4=20=E5=90=91=E3=81=91=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E3=81=99=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 21 ++++++- .../TwoFactorAuthCustomerAppController.php | 63 +++++++++++-------- Entity/CustomerTrait.php | 5 +- Event.php | 7 ++- .../TwoFactorAuthCustomerTypeExtension.php | 3 +- Form/Type/TwoFactorAuthAppTypeCustomer.php | 32 +++++----- PluginManager.php | 39 +++++++++--- Resource/rector.php | 8 +-- Tests/bootstrap.php | 4 +- phpstan.neon.dist | 25 ++++++++ 10 files changed, 144 insertions(+), 63 deletions(-) create mode 100644 phpstan.neon.dist diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fac54b6..ed39908 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,7 +76,8 @@ jobs: - name: Checkout Base Plugin uses: actions/checkout@v4 with: - repository: 'EC-CUBE/TwoFactorAuthCustomer44' + repository: 'EC-CUBE/TwoFactorAuthCustomer42' + ref: '4.4' path: 'TwoFactorAuthCustomer44' - name: Archive Base Plugin @@ -194,6 +195,9 @@ jobs: static-analysis: name: Static Analysis runs-on: ubuntu-24.04 + # 静的解析は DB 種別に依存しないため、SQLite 1 構成で一度だけ実行する。 + # (php-cs-fixer / rector は DB 不要だが、phpstan は objectManagerLoader が + # カーネルを起動し EccubeExtension が dtb_plugin を読むため本体+DBが必要) env: PLUGIN_CODE: TwoFactorAuthCustomerApp44 PARENT_PLUGIN_CODE: TwoFactorAuthCustomer44 @@ -219,7 +223,8 @@ jobs: - name: Checkout Base Plugin uses: actions/checkout@v4 with: - repository: 'EC-CUBE/TwoFactorAuthCustomer44' + repository: 'EC-CUBE/TwoFactorAuthCustomer42' + ref: '4.4' path: 'TwoFactorAuthCustomer44' - name: Archive Base Plugin @@ -259,7 +264,11 @@ jobs: - name: Setup EC-CUBE working-directory: 'ec-cube' run: | - bin/console doctrine:database:create + # DBAL 4 の SQLite は doctrine:database:create 非対応(getCreateDatabaseSQL が削除済み)。 + # 本体 InstallerCommand と同様、SQLite ではファイル作成をスキップし schema:create で用意する。 + if [[ "${DATABASE_URL}" != sqlite* ]]; then + bin/console doctrine:database:create + fi bin/console doctrine:schema:create bin/console eccube:fixtures:load - name: Setup Plugin @@ -278,3 +287,9 @@ jobs: - name: Run Rector working-directory: 'ec-cube' run: ./vendor/bin/rector process --config=app/Plugin/${PLUGIN_CODE}/Resource/rector.php --dry-run + + - name: Run PHPStan + working-directory: 'ec-cube' + run: | + bin/console cache:clear --no-warmup + ./vendor/bin/phpstan analyse -c app/Plugin/${PLUGIN_CODE}/phpstan.neon.dist --no-progress diff --git a/Controller/TwoFactorAuthCustomerAppController.php b/Controller/TwoFactorAuthCustomerAppController.php index 51e8c01..50c7182 100755 --- a/Controller/TwoFactorAuthCustomerAppController.php +++ b/Controller/TwoFactorAuthCustomerAppController.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,6 +19,7 @@ use RobThree\Auth\TwoFactorAuth; use RobThree\Auth\TwoFactorAuthException; use Symfony\Bridge\Twig\Attribute\Template; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; @@ -29,13 +30,10 @@ class TwoFactorAuthCustomerAppController extends TwoFactorAuthCustomerController */ protected const SESSION_APP_AUTH_KEY = 'plugin_eccube_customer_2fa_app_auth_key'; - /** - * @var TwoFactorAuth - */ - protected $tfa; - /** * 初回APP認証画面. + * + * @return array|RedirectResponse */ #[Route(path: '/mypage/two_factor_auth/app/create', name: 'plg_customer_2fa_app_create', methods: ['GET', 'POST'])] #[Template('@TwoFactorAuthCustomerApp44/default/tfa/app/register.twig')] @@ -45,34 +43,34 @@ public function create(Request $request) return $this->redirectToRoute($this->getCallbackRoute()); } - $this->tfa = new TwoFactorAuth(); + $tfa = new TwoFactorAuth(); $error = null; - /** @var Customer $Customer */ $Customer = $this->getUser(); + if (!$Customer instanceof Customer) { + return $this->redirectToRoute('mypage'); + } $builder = $this->formFactory->createBuilder(TwoFactorAuthAppTypeCustomer::class); - $form = null; + $form = $builder->getForm(); $auth_key = null; if ('GET' === $request->getMethod()) { if ($Customer->getTwoFactorAuthSecret()) { // 既に二段階認証設定済み + APP認証設定済み(二回目以降) return [ - 'form' => $builder->getForm(), + 'form' => $form->createView(), 'Customer' => $Customer, ]; } - $auth_key = $this->createSecret(); + $auth_key = $this->createSecret($tfa); $this->session->set(self::SESSION_APP_AUTH_KEY, $auth_key); - $form = $builder->getForm(); } elseif ('POST' === $request->getMethod()) { - $form = $builder->getForm(); $form->handleRequest($request); $auth_key = $this->session->get(self::SESSION_APP_AUTH_KEY); if ($form->isSubmitted() && $form->isValid()) { $token = $form->get('one_time_token')->getData(); - if ($this->verifyCode($auth_key, $token)) { + if (is_string($auth_key) && is_string($token) && $this->verifyCode($tfa, $auth_key, $token)) { // 秘密鍵更新 $Customer->setTwoFactorAuthSecret($auth_key); $this->entityManager->persist($Customer); @@ -85,12 +83,12 @@ public function create(Request $request) $this->customerTwoFactorAuthService->createAuthedCookie( $Customer, $this->getCallbackRoute() - )); + ) + ); return $response; - } else { - $error = trans('front.2fa.onetime.invalid_message__reinput'); } + $error = trans('front.2fa.onetime.invalid_message__reinput'); } else { $error = trans('front.2fa.onetime.invalid_message__reinput'); } @@ -106,6 +104,8 @@ public function create(Request $request) /** * APP認証画面. + * + * @return array|RedirectResponse */ #[Route(path: '/mypage/two_factor_auth/app/challenge', name: 'plg_customer_2fa_app_challenge', methods: ['GET', 'POST'])] #[Template('@TwoFactorAuthCustomerApp44/default/tfa/app/challenge.twig')] @@ -115,13 +115,15 @@ public function challenge(Request $request) return $this->redirectToRoute($this->getCallbackRoute()); } - $this->tfa = new TwoFactorAuth(); + $tfa = new TwoFactorAuth(); $error = null; - /** @var Customer $Customer */ $Customer = $this->getUser(); + if (!$Customer instanceof Customer) { + return $this->redirectToRoute('mypage'); + } - if ($Customer->getTwoFactorAuthSecret() == null) { + if ($Customer->getTwoFactorAuthSecret() === null) { // APP認証設定まだ return $this->redirectToRoute('plg_customer_2fa_app_create'); } @@ -133,7 +135,9 @@ public function challenge(Request $request) if ('POST' === $request->getMethod()) { $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - if ($this->verifyCode($Customer->getTwoFactorAuthSecret(), $form->get('one_time_token')->getData())) { + $secret = $Customer->getTwoFactorAuthSecret(); + $token = $form->get('one_time_token')->getData(); + if (is_string($secret) && is_string($token) && $this->verifyCode($tfa, $secret, $token)) { $response = $this->redirectToRoute($this->getCallbackRoute()); $response->headers->setCookie( $this->customerTwoFactorAuthService->createAuthedCookie( @@ -143,9 +147,8 @@ public function challenge(Request $request) ); return $response; - } else { - $error = trans('front.2fa.onetime.invalid_message__reinput'); } + $error = trans('front.2fa.onetime.invalid_message__reinput'); } else { $error = trans('front.2fa.onetime.invalid_message__reinput'); } @@ -160,18 +163,24 @@ public function challenge(Request $request) /** * 秘密鍵生成. * + * @param TwoFactorAuth $tfa + * * @throws TwoFactorAuthException */ - private function createSecret(): string + private function createSecret(TwoFactorAuth $tfa): string { - return $this->tfa->createSecret(); + return $tfa->createSecret(); } /** * 認証コードを検証. + * + * @param TwoFactorAuth $tfa + * @param string $authKey + * @param string $token */ - private function verifyCode(string $authKey, string $token): bool + private function verifyCode(TwoFactorAuth $tfa, string $authKey, string $token): bool { - return $this->tfa->verifyCode($authKey, $token, 1); + return $tfa->verifyCode($authKey, $token, 1); } } diff --git a/Entity/CustomerTrait.php b/Entity/CustomerTrait.php index 40bf612..4252064 100644 --- a/Entity/CustomerTrait.php +++ b/Entity/CustomerTrait.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,8 +16,9 @@ use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Eccube\Attribute\EntityExtension; +use Eccube\Entity\Customer; -#[EntityExtension('Eccube\Entity\Customer')] +#[EntityExtension(Customer::class)] trait CustomerTrait { #[ORM\Column(name: 'two_factor_auth_secret', type: Types::STRING, length: 255, nullable: true)] diff --git a/Event.php b/Event.php index 34ecede..71e2e2e 100644 --- a/Event.php +++ b/Event.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,6 +21,9 @@ */ class Event implements EventSubscriberInterface { + /** + * @return array + */ public static function getSubscribedEvents(): array { return [ @@ -31,6 +34,8 @@ public static function getSubscribedEvents(): array /** * [/admin/customer/edit]表示の時のEvent Hook. * 二段階認証関連項目を追加する. + * + * @param TemplateEvent $event */ public function onRenderAdminCustomerEdit(TemplateEvent $event): void { diff --git a/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php b/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php index 9e2c9a5..422f63d 100644 --- a/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php +++ b/Form/Type/Extension/Admin/TwoFactorAuthCustomerTypeExtension.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,6 +25,7 @@ class TwoFactorAuthCustomerTypeExtension extends AbstractTypeExtension /** * buildForm. * + * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/Form/Type/TwoFactorAuthAppTypeCustomer.php b/Form/Type/TwoFactorAuthAppTypeCustomer.php index 1b42c9b..2775b48 100644 --- a/Form/Type/TwoFactorAuthAppTypeCustomer.php +++ b/Form/Type/TwoFactorAuthAppTypeCustomer.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,27 +21,27 @@ class TwoFactorAuthAppTypeCustomer extends AbstractType { /** - * {@inheritdoc} + * buildForm. + * + * @param FormBuilderInterface $builder + * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add( 'one_time_token', TextType::class, [ - 'label' => 'front.setting.system.two_factor_auth.device_token', - 'required' => true, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Length([ - 'max' => 6, - 'min' => 6, - ]), - ], - 'attr' => [ - 'maxlength' => 6, - 'style' => 'width: 100px;', - ], - ]); + 'label' => 'front.setting.system.two_factor_auth.device_token', + 'required' => true, + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Length(max: 6, min: 6), + ], + 'attr' => [ + 'maxlength' => 6, + 'style' => 'width: 100px;', + ], + ]); } /** diff --git a/PluginManager.php b/PluginManager.php index d5df6ae..72a0068 100644 --- a/PluginManager.php +++ b/PluginManager.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -29,7 +29,11 @@ */ class PluginManager extends AbstractPluginManager { - // 設定対象ページ情報 + /** + * 設定対象ページ情報 + * + * @var list + */ private array $pages = [ ['plg_customer_2fa_app_create', 'アプリ認証初期設定・トークン入力', 'TwoFactorAuthCustomerApp44/Resource/template/default/tfa/app/register'], ['plg_customer_2fa_app_challenge', 'アプリ認証トークン入力', 'TwoFactorAuthCustomerApp44/Resource/template/default/tfa/app/challenge'], @@ -37,6 +41,7 @@ class PluginManager extends AbstractPluginManager /** * @param array $meta + * @param ContainerInterface $container */ public function enable(array $meta, ContainerInterface $container): void { @@ -53,6 +58,7 @@ public function enable(array $meta, ContainerInterface $container): void /** * @param array $meta + * @param ContainerInterface $container */ public function disable(array $meta, ContainerInterface $container): void { @@ -70,6 +76,7 @@ public function disable(array $meta, ContainerInterface $container): void /** * @param array $meta + * @param ContainerInterface $container */ public function uninstall(array $meta, ContainerInterface $container): void { @@ -87,6 +94,8 @@ public function uninstall(array $meta, ContainerInterface $container): void /** * Twigファイルの登録 + * + * @param ContainerInterface $container */ protected function copyTwigFiles(ContainerInterface $container): void { @@ -103,6 +112,8 @@ protected function copyTwigFiles(ContainerInterface $container): void /** * ページ情報の登録 + * + * @param EntityManagerInterface $em */ protected function createPages(EntityManagerInterface $em): void { @@ -121,11 +132,16 @@ protected function createPages(EntityManagerInterface $em): void $em->flush(); $Layout = $em->getRepository(Layout::class)->find(Layout::DEFAULT_LAYOUT_UNDERLAYER_PAGE); + $pageId = $Page->getId(); + $layoutId = $Layout?->getId(); + if ($Layout === null || $pageId === null || $layoutId === null) { + continue; + } $PageLayout = new PageLayout(); $PageLayout->setPage($Page) - ->setPageId($Page->getId()) + ->setPageId($pageId) ->setLayout($Layout) - ->setLayoutId($Layout->getId()) + ->setLayoutId($layoutId) ->setSortNo(0); $em->persist($PageLayout); $em->flush(); @@ -135,6 +151,8 @@ protected function createPages(EntityManagerInterface $em): void /** * Twigファイルの削除 + * + * @param ContainerInterface $container */ protected function removeTwigFiles(ContainerInterface $container): void { @@ -146,6 +164,8 @@ protected function removeTwigFiles(ContainerInterface $container): void /** * ページ情報の削除 + * + * @param EntityManagerInterface $em */ protected function removePages(EntityManagerInterface $em): void { @@ -154,8 +174,9 @@ protected function removePages(EntityManagerInterface $em): void if ($Page !== null) { $Layout = $em->getRepository(Layout::class)->find(Layout::DEFAULT_LAYOUT_UNDERLAYER_PAGE); $PageLayout = $em->getRepository(PageLayout::class)->findOneBy(['Page' => $Page, 'Layout' => $Layout]); - - $em->remove($PageLayout); + if ($PageLayout !== null) { + $em->remove($PageLayout); + } $em->remove($Page); $em->flush(); } @@ -164,6 +185,8 @@ protected function removePages(EntityManagerInterface $em): void /** * 設定の登録. + * + * @param EntityManagerInterface $em */ protected function createConfig(EntityManagerInterface $em): void { @@ -192,6 +215,8 @@ protected function createConfig(EntityManagerInterface $em): void /** * 2段階認証設定を消す + * + * @param EntityManagerInterface $em */ protected function removeConfig(EntityManagerInterface $em): void { @@ -199,7 +224,7 @@ protected function removeConfig(EntityManagerInterface $em): void $TwoFactorAuthType = $em->getRepository(TwoFactorAuthType::class)->findOneBy(['name' => 'APP']); // APPオプションがあれば、そのオプションを無効にする - if (!empty($TwoFactorAuthType)) { + if ($TwoFactorAuthType !== null) { $TwoFactorAuthType->setIsDisabled(true); $em->persist($TwoFactorAuthType); } diff --git a/Resource/rector.php b/Resource/rector.php index 30c35df..c8ee107 100644 --- a/Resource/rector.php +++ b/Resource/rector.php @@ -37,12 +37,12 @@ ]) ->withSets([ LevelSetList::UP_TO_PHP_82, - // Symfony 7.4 対応 (@Route → #[Route], @Template, buildForm(): void 等) - SymfonySetList::SYMFONY_74, + // rector 2.6.2 でバージョン別のセット定数 (SYMFONY_74 等) は撤去された。 + // 各ルールが composer.json を見て、インストール済みバージョンに合うものだけ実行する。 + SymfonySetList::COMPOSER_BASED, SymfonySetList::SYMFONY_CODE_QUALITY, - // Doctrine ORM 3.0 / DBAL 3.0 対応 (@ORM → #[ORM], 型付きプロパティ) DoctrineSetList::DOCTRINE_CODE_QUALITY, - DoctrineSetList::DOCTRINE_DBAL_30, + DoctrineSetList::COMPOSER_BASED, DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES, ]) // Symfony/Doctrine 等のアノテーション → アトリビュート変換を有効化 diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php index 1d625a1..45f40ee 100644 --- a/Tests/bootstrap.php +++ b/Tests/bootstrap.php @@ -5,7 +5,7 @@ * * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. * - * http://www.ec-cube.co.jp/ + * https://www.ec-cube.co.jp/ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,5 +14,5 @@ $loader = require __DIR__.'/../../../../vendor/autoload.php'; $envFile = __DIR__.'/../../../../.env'; if (file_exists($envFile)) { - (new \Symfony\Component\Dotenv\Dotenv())->bootEnv($envFile); + (new Symfony\Component\Dotenv\Dotenv())->bootEnv($envFile); } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..f699510 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,25 @@ +parameters: + level: 6 + paths: + - . + excludePaths: + - Resource/* + - Tests/bootstrap.php + doctrine: + objectManagerLoader: ../../../tests/object-manager.php + ormRepositoryClass: Eccube\Repository\AbstractRepository + # stubFiles は doctrine.objectManagerLoader が Customer/BaseInfo の + # リフレクションを上書きするため EntityExtension メソッドには効かない。 + ignoreErrors: + - + message: '#Call to an undefined method Eccube\\Entity\\Customer::(get|set|is)(DeviceAuth|DeviceAuthed|TwoFactorAuth).*#' + - + message: '#Call to an undefined method Eccube\\Entity\\BaseInfo::(is|set)(TwoFactorAuthUse|OptionActivateDevice)\(\)#' + # EntityExtension の trait は実行時 proxy に載るため、解析上は未使用になる + - + identifier: trait.unused + # EC-CUBE 本体と同様。new Entity() 時に nullable が必要なプロパティと DB 非 null の差 + - + message: "#^Property .+::\\$.+ type mapping mismatch: property can contain .+\\|null but database expects .+\\.$#" + identifier: doctrine.columnType + reportUnmatchedIgnoredErrors: false From 19cee78358888873d7a8af8cf7b5bd4b8a8c173e Mon Sep 17 00:00:00 2001 From: Ken Tanaka Date: Mon, 14 Sep 2026 17:05:02 +0900 Subject: [PATCH 3/4] =?UTF-8?q?composer:require=20=E3=81=AE=E7=9B=B4?= =?UTF-8?q?=E5=BE=8C=E3=81=AB=20cache:clear=20=E3=81=97=E3=80=81plugin:ena?= =?UTF-8?q?ble=20=E3=81=8C=E6=96=B0=E3=81=97=E3=81=84=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=82=92=E8=AA=AD=E3=82=81=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ed39908..471de4d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -143,8 +143,10 @@ jobs: working-directory: 'ec-cube' run: | bin/console eccube:composer:require ec-cube/twofactorauthcustomer44 + bin/console cache:clear --no-warmup bin/console eccube:plugin:enable --code=TwoFactorAuthCustomer44 bin/console eccube:composer:require ec-cube/twofactorauthcustomerapp44 + bin/console cache:clear --no-warmup bin/console eccube:plugin:enable --code=${PLUGIN_CODE} bin/console cache:clear --no-warmup @@ -275,8 +277,10 @@ jobs: working-directory: 'ec-cube' run: | bin/console eccube:composer:require ec-cube/twofactorauthcustomer44 + bin/console cache:clear --no-warmup bin/console eccube:plugin:enable --code=TwoFactorAuthCustomer44 bin/console eccube:composer:require ec-cube/twofactorauthcustomerapp44 + bin/console cache:clear --no-warmup bin/console eccube:plugin:enable --code=${PLUGIN_CODE} bin/console cache:clear --no-warmup From ffad99837f129e111f456d9012572be0efb1164a Mon Sep 17 00:00:00 2001 From: Ken Tanaka Date: Wed, 16 Sep 2026 17:58:28 +0900 Subject: [PATCH 4/4] =?UTF-8?q?CI=20=E3=81=AE=E8=A6=AA=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E5=8F=96=E5=BE=97=E5=85=88=E3=82=92?= =?UTF-8?q?=E3=80=81=E3=83=9E=E3=83=BC=E3=82=B8=E5=89=8D=E3=81=AE=20feat-4?= =?UTF-8?q?.4=20=E3=81=AB=E4=B8=80=E6=99=82=E7=9A=84=E3=81=AB=E5=88=87?= =?UTF-8?q?=E3=82=8A=E6=9B=BF=E3=81=88=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 471de4d..b848952 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -76,8 +76,10 @@ jobs: - name: Checkout Base Plugin uses: actions/checkout@v4 with: - repository: 'EC-CUBE/TwoFactorAuthCustomer42' - ref: '4.4' + # 親プラグイン PR が公式 4.4 にマージされるまで、パッケージ名 twofactorauthcustomer44 が入っている + # KenTanaka/TwoFactorAuthCustomer の feat-4.4 を参照する。マージ後は EC-CUBE/TwoFactorAuthCustomer42@4.4 に戻す。 + repository: 'KenTanaka/TwoFactorAuthCustomer' + ref: 'feat-4.4' path: 'TwoFactorAuthCustomer44' - name: Archive Base Plugin @@ -225,8 +227,10 @@ jobs: - name: Checkout Base Plugin uses: actions/checkout@v4 with: - repository: 'EC-CUBE/TwoFactorAuthCustomer42' - ref: '4.4' + # 親プラグイン PR が公式 4.4 にマージされるまで、パッケージ名 twofactorauthcustomer44 が入っている + # KenTanaka/TwoFactorAuthCustomer の feat-4.4 を参照する。マージ後は EC-CUBE/TwoFactorAuthCustomer42@4.4 に戻す。 + repository: 'KenTanaka/TwoFactorAuthCustomer' + ref: 'feat-4.4' path: 'TwoFactorAuthCustomer44' - name: Archive Base Plugin