From eae0957835e177d2ed1961552b51d1811fe95aea Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:00:56 +0000 Subject: [PATCH 1/9] chore: sync composer.json from infrastructure --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 748f386..a266283 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "license": "OSL-3.0", "type": "maho-module", "require": { - "php": ">=8.3" + "php": ">=8.5" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", @@ -20,7 +20,7 @@ "mahocommerce/maho-composer-plugin": true }, "platform": { - "php": "8.3" + "php": "8.5" } } } From 5166ec5c360b2d4c4e64b90d6028bf3935ee476a Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:00:57 +0000 Subject: [PATCH 2/9] chore: sync .github/workflows/phpstan.yml from infrastructure --- .github/workflows/phpstan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 7190103..25b276a 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - php-version: ['8.3', '8.4', '8.5'] + php-version: ['8.5', '8.6'] name: PHPStan (PHP ${{ matrix.php-version }}) From f9d234b43479bd80d4e0536a03cdfa09829e4228 Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:00:58 +0000 Subject: [PATCH 3/9] chore: sync .github/workflows/syntax-php.yml from infrastructure --- .github/workflows/syntax-php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/syntax-php.yml b/.github/workflows/syntax-php.yml index 0010e57..eea74bb 100644 --- a/.github/workflows/syntax-php.yml +++ b/.github/workflows/syntax-php.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['8.3', '8.4', '8.5'] + php-version: ['8.5', '8.6'] name: PHP Syntax (PHP ${{ matrix.php-version }}) From 0f0c1f0c4da108042c995162941e5e74b23a8ecd Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:00:59 +0000 Subject: [PATCH 4/9] chore: sync .github/workflows/lint.yml from infrastructure --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 11287a7..c37387c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 with: - php-version: '8.3' + php-version: '8.5' - name: Install dependencies run: composer install --no-interaction --no-progress @@ -24,4 +24,4 @@ jobs: run: php vendor/bin/php-cs-fixer fix --diff --dry-run - name: Rector - run: php vendor/bin/rector -c .rector.php --dry-run + run: php vendor/bin/rector --dry-run From bda8faba07cd4c2b41c96774829d6c671b199c74 Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:01:00 +0000 Subject: [PATCH 5/9] chore: sync .php-cs-fixer.php from infrastructure --- .php-cs-fixer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 53d5172..70dfa83 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -34,7 +34,8 @@ __DIR__ . '/src', ], 'is_dir'))) // Root-level entry points (e.g. the infra tool's sync.php / config.php). - // glob skips dotfiles, so these very config files aren't included. + // glob skips dotfiles, so .php-cs-fixer.php is left out; rector.php is + // a plain name now, so it is formatted like any other file. ->append(glob(__DIR__ . '/*.php') ?: []) ->name('*.php') ->ignoreDotFiles(true) From 7db2ab8614ea7c7e507688e776afa58a17b0f0f9 Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:01:01 +0000 Subject: [PATCH 6/9] chore: sync rector.php from infrastructure --- rector.php | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 rector.php diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..8b848cb --- /dev/null +++ b/rector.php @@ -0,0 +1,92 @@ +Maho migration) stays in maho and is not +// synced. Only the paths that exist in a given repo are scanned, so the one +// config works for app-only modules and the infra tool's src/ alike. +// +// Existence alone is not enough: in a module repo `composer install` lets the +// maho composer plugin materialize maho core files under public/ and lib/. +// Those files are never tracked by the module. Some modules git-ignore them, +// some ignore the whole directory, and some leave them untracked next to their +// own tracked skin or js files. Rector must not lint core files a module cannot +// change, so every path git does not track is skipped. Without +// --exclude-standard, ls-files lists ignored files as well as plain untracked +// ones, and --directory collapses a fully untracked directory to one entry. +function gitUntrackedPaths(): array +{ + exec( + 'git -C ' . escapeshellarg(__DIR__) . ' ls-files --others --directory 2>/dev/null', + $paths, + ); + + return array_map(static fn(string $path): string => __DIR__ . '/' . rtrim($path, '/'), $paths); +} + +return RectorConfig::configure() + ->withPaths(array_values(array_merge( + array_filter([ + __DIR__ . '/app', + __DIR__ . '/lib', + __DIR__ . '/public', + __DIR__ . '/src', + ], is_dir(...)), + // Root-level entry points (e.g. the infra tool's sync.php / config.php). + // glob skips dotfiles, so .php-cs-fixer.php is left out; rector.php is + // a plain name now, so this config lints itself. + glob(__DIR__ . '/*.php') ?: [], + ))) + // No argument: Rector reads the target PHP version from composer.json + // (require.php's floor, else config.platform.php), which the sync keeps in + // step with maho. + ->withPhpSets() + // The sets above are taken wholesale, unlike maho's own config, which pins + // them to an old target and hand-picks the newer rules by name. These three + // are what that policy guards against, and they are wrong for a published + // package: + // + // - AddTypeToConst emits `const string FOO`. That is new syntax, not a + // rewrite: it adds nothing the code needs, and it turns a floor bump into + // a hard parse error for anyone who installs with platform requirements + // ignored. A composer plugin also runs on the user's PHP, not on the + // platform the project resolved against. + // - ReadOnlyClass / ReadOnlyProperty change the contract, not the code: a + // readonly class cannot be extended by a normal child, and a readonly + // property cannot be written from one. Maho modules exist to be extended. + // + // Everything else in the sets is a safe rewrite, so keep the derivation. + ->withSkip([ + ...gitUntrackedPaths(), + Rector\Php81\Rector\Property\ReadOnlyPropertyRector::class, + Rector\Php82\Rector\Class_\ReadOnlyClassRector::class, + Rector\Php83\Rector\ClassConst\AddTypeToConstRector::class, + ]) + ->withRules([ + CodeQuality\BooleanNot\ReplaceMultipleBooleanNotRector::class, + CodeQuality\FuncCall\ChangeArrayPushToArrayAssignRector::class, + CodeQuality\FuncCall\CompactToVariablesRector::class, + CodeQuality\Identical\SimplifyArraySearchRector::class, + CodeQuality\Identical\SimplifyConditionsRector::class, + CodeQuality\Identical\StrlenZeroToIdenticalEmptyStringRector::class, + CodeQuality\LogicalAnd\LogicalToBooleanRector::class, + CodeQuality\NotEqual\CommonNotEqualRector::class, + CodeQuality\Ternary\SimplifyTautologyTernaryRector::class, + CodingStyle\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector::class, + DeadCode\ClassMethod\RemoveUselessParamTagRector::class, + DeadCode\ClassMethod\RemoveUselessReturnTagRector::class, + DeadCode\MethodCall\RemoveNullArgOnNullDefaultParamRector::class, + DeadCode\Property\RemoveUselessVarTagRector::class, + EarlyReturn\If_\RemoveAlwaysElseRector::class, + Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector::class, + TypeDeclaration\StmtsAwareInterface\SafeDeclareStrictTypesRector::class, + ]); From 015e8944d18e44f2ba00e93429a51286fe46b76d Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:01:02 +0000 Subject: [PATCH 7/9] chore: sync .gitattributes from infrastructure --- .gitattributes | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/.gitattributes b/.gitattributes index 04c6eb9..8ddbd04 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,25 @@ -# Keep packaged tarballs (Packagist downloads) small by stripping dev-only files -/.github export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/.php-cs-fixer.php export-ignore -/.phpstan.dist.neon export-ignore -/.rector.php export-ignore +# Keep Packagist/Composer downloads small: strip every file a consumer of the +# package never runs. `git archive` (which is what GitHub and Packagist serve) +# ignores a path that does not exist, so one canonical list works for every +# module and library. +/.claude export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/.idea export-ignore +/.junie export-ignore +/.mcp.json export-ignore +/.php-cs-fixer.php export-ignore +/.phpstan.dist.baseline.neon export-ignore +/.phpstan.dist.neon export-ignore +/AGENTS.md export-ignore +/CLAUDE.md export-ignore +/composer.lock export-ignore +/phpstan-baseline.neon export-ignore +/phpunit.xml export-ignore +/rector.php export-ignore +/tests export-ignore # Enforce LF line endings on checkout so the line-endings CI gate never fails *.css text eol=lf From 3868267c3267d5fc42b029710cb6b47ceaaf0927 Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:01:03 +0000 Subject: [PATCH 8/9] chore: sync .editorconfig from infrastructure --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..779f99a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false From 2a7338c7d4057b1498d25a2259c1fec974ab6ffe Mon Sep 17 00:00:00 2001 From: "maho-organization-controller[bot]" <289500297+maho-organization-controller[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:01:04 +0000 Subject: [PATCH 9/9] chore: remove .rector.php (superseded) from infrastructure --- .rector.php | 91 ----------------------------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 .rector.php diff --git a/.rector.php b/.rector.php deleted file mode 100644 index 02c776c..0000000 --- a/.rector.php +++ /dev/null @@ -1,91 +0,0 @@ -Maho migration) stays in maho and is not -// synced. Only the paths that exist in a given repo are scanned, so the one -// config works for app-only modules and the infra tool's src/ alike. -// -// Existence alone is not enough: in a module repo `composer install` lets the -// maho composer plugin materialize maho core files under public/ and lib/. -// Those files are never tracked by the module. Some modules git-ignore them, -// some ignore the whole directory, and some leave them untracked next to their -// own tracked skin or js files. Rector must not lint core files a module cannot -// change, so every path git does not track is skipped. Without -// --exclude-standard, ls-files lists ignored files as well as plain untracked -// ones, and --directory collapses a fully untracked directory to one entry. -function gitUntrackedPaths(): array -{ - exec( - 'git -C ' . escapeshellarg(__DIR__) . ' ls-files --others --directory 2>/dev/null', - $paths, - ); - - return array_map(static fn(string $path): string => __DIR__ . '/' . rtrim($path, '/'), $paths); -} - -return RectorConfig::configure() - ->withPaths(array_values(array_merge( - array_filter([ - __DIR__ . '/app', - __DIR__ . '/lib', - __DIR__ . '/public', - __DIR__ . '/src', - ], 'is_dir'), - // Root-level entry points (e.g. the infra tool's sync.php / config.php). - // glob skips dotfiles, so this very config file isn't included. - glob(__DIR__ . '/*.php') ?: [], - ))) - // No argument: Rector reads the target PHP version from composer.json - // (require.php's floor, else config.platform.php), which the sync keeps in - // step with maho. - ->withPhpSets() - // The sets above are taken wholesale, unlike maho's own config, which pins - // them to an old target and hand-picks the newer rules by name. These three - // are what that policy guards against, and they are wrong for a published - // package: - // - // - AddTypeToConst emits `const string FOO`. That is new syntax, not a - // rewrite, so a repo that declares no require.php floor (such as - // maho-composer-plugin) would ship code its own metadata never promised. - // A composer plugin also runs on the user's PHP, not on the platform the - // project resolved against. - // - ReadOnlyClass / ReadOnlyProperty change the contract, not the code: a - // readonly class cannot be extended by a normal child, and a readonly - // property cannot be written from one. Maho modules exist to be extended. - // - // Everything else in the sets is a safe rewrite, so keep the derivation. - ->withSkip([ - ...gitUntrackedPaths(), - Rector\Php81\Rector\Property\ReadOnlyPropertyRector::class, - Rector\Php82\Rector\Class_\ReadOnlyClassRector::class, - Rector\Php83\Rector\ClassConst\AddTypeToConstRector::class, - ]) - ->withRules([ - CodeQuality\BooleanNot\ReplaceMultipleBooleanNotRector::class, - CodeQuality\FuncCall\ChangeArrayPushToArrayAssignRector::class, - CodeQuality\FuncCall\CompactToVariablesRector::class, - CodeQuality\Identical\SimplifyArraySearchRector::class, - CodeQuality\Identical\SimplifyConditionsRector::class, - CodeQuality\Identical\StrlenZeroToIdenticalEmptyStringRector::class, - CodeQuality\LogicalAnd\LogicalToBooleanRector::class, - CodeQuality\NotEqual\CommonNotEqualRector::class, - CodeQuality\Ternary\SimplifyTautologyTernaryRector::class, - CodingStyle\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector::class, - DeadCode\ClassMethod\RemoveUselessParamTagRector::class, - DeadCode\ClassMethod\RemoveUselessReturnTagRector::class, - DeadCode\MethodCall\RemoveNullArgOnNullDefaultParamRector::class, - DeadCode\Property\RemoveUselessVarTagRector::class, - EarlyReturn\If_\RemoveAlwaysElseRector::class, - Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector::class, - TypeDeclaration\StmtsAwareInterface\SafeDeclareStrictTypesRector::class, - ]);