Search by

gaya / typo3-coder

gaya

Checks TYPO3 code against multiple tools.

Package info

github.com/agence-gaya/typo3-coder

Type:composer-plugin

pkg:composer/gaya/typo3-coder

Statistics

Installs: 124

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

14.2.0 2026-10-05 23:06 UTC

This package is auto-updated.

Last update: 2026-10-05 23:08:01 UTC


README

TYPO3 14 ci License

TYPO3 coder

Shared Rector, Fractor, PHP-CS-Fixer, PHPLint, TypoScript lint, YAML lint, PHPStan and PHPUnit configuration for TYPO3 14 projects and standalone extensions.

Installation

composer require --dev gaya/typo3-coder:^14.1
composer config allow-plugins.gaya/typo3-coder true
composer config allow-plugins.a9f/fractor-extension-installer true

Allow the plugins when prompted during installation. Shared configuration stays in this package. Optional project customizations and a deliberately generated PHPStan baseline belong in the project's build/ directory and can be committed. Tool caches may still need to be ignored.

Commands

Command Purpose
composer coder:rector Apply Rector refactorings.
composer coder:fractor Apply Fractor refactorings.
composer coder:php-cs-fixer Fix PHP code style.
composer coder:phplint Check PHP syntax.
composer coder:typoscript-lint Lint TypoScript files.
composer coder:yaml-lint Validate YAML files.
composer coder:phpstan Run PHPStan static analysis.
composer coder:tests:unit Run unit tests.
composer coder:tests:functional Run TYPO3 functional tests.
composer coder:all Run the nine tools and test suites above in order.
composer coder:phpstan:baseline Generate or update the project's PHPStan baseline.
composer coder:migrate Migrate legacy coder configuration.

Use --continuous-integration (or its alias --ci) to check without applying Rector, Fractor or PHP-CS-Fixer corrections. This option does not prevent an explicitly requested migration or PHPStan baseline generation, including within a custom group. Arguments after -- go to the individual tool, for example:

composer coder:rector --continuous-integration -- --no-progress-bar
composer coder:tests:unit --continuous-integration -- --filter MyTest --log-junit .build/logs/unit.xml
XDEBUG_MODE=coverage composer coder:tests:unit -- --coverage-clover .build/logs/clover.xml
composer --working-dir=path/to/extension coder:phplint

Run all tools and both test suites with composer coder:all, or use composer coder:all --ci (equivalently --continuous-integration) to check without applying corrections. The execution order is Rector, Fractor, PHP-CS-Fixer, PHPLint, TypoScript lint, YAML lint, PHPStan, unit tests, then functional tests. Migration and PHPStan baseline generation are excluded. Failures do not stop the remaining commands: a final summary reports each result, and the command returns 1 if any command failed, otherwise 0. Tool-specific arguments after -- are not supported by coder:all; use the individual commands for those arguments.

A coverage driver (Xdebug or PCOV) must be enabled to collect coverage. Tool exit codes are preserved. Missing conventional test directories and empty test suites are successful, including in CI. Explicitly configured missing directories, bootstrap errors and failing tests remain errors.

Custom command groups

Declare groups in the consuming project's root composer.json:

{
  "extra": {
    "gaya/typo3-coder": {
      "command": {
        "run": ["rector", "fractor"],
        "checks": ["phplint", "phpstan", "tests:unit"]
      }
    }
  }
}
composer coder:run
composer coder:checks --ci
composer coder:run --continuous-integration

Groups run commands in the declared order, apply corrections by default, and forward either CI option to every command. As with coder:all, execution continues after failures, prints a final summary, and returns 0 on success or 1 if any command failed. Global Composer options are supported; tool-specific arguments after -- are not.

Group names must match [a-z][a-z0-9-]* and cannot replace built-in commands, including all. Each group must contain a non-empty list of distinct built-in tool names without the coder: prefix. migrate and phpstan:baseline may be included explicitly. References to other groups, all, external Composer commands, and shell commands are not supported. Invalid configuration produces an error identifying the configuration key. Omitting command or setting it to {} adds no groups.

Project context

The root Composer package determines the profile: typo3-cms-extension selects extension, other types select project. Dependencies' require-dev entries do not install coder: require it in the Composer root where commands run.

Setting Project default Extension default
Analysis paths packages/ .
Unit tests packages/site*/Tests/Unit Tests/Unit
Functional tests packages/site*/Tests/Functional Tests/Functional
Vendor/binaries Composer vendor-dir / bin-dir Same, including .build/vendor / .build/bin
Web root extra.typo3/cms.web-dir, otherwise public Same
Rector PHP target Current PHP major/minor Current PHP major/minor

Dependencies, public assets, build directories, caches, Git metadata and node_modules are excluded from analysis. Explicit analysis/test paths must exist. Paths and exclusions are relative to the root manifest. Test paths accept glob patterns.

Optional overrides in composer.json:

{
  "extra": {
    "gaya/typo3-coder": {
      "profile": "extension",
      "paths": ["Classes", "Configuration", "Tests"],
      "exclude": ["Tests/Fixtures"],
      "phpstan-level": 10,
      "tests": {
        "unit": ["Tests/Unit"],
        "functional": ["Tests/Functional"]
      }
    }
  }
}

Only include settings you need. Rector always targets the running PHP major/minor version. For a reusable extension, run Rector only in the CI job using its minimum supported PHP version; keep tests and lint on the full PHP matrix. For example, the Rector step is restricted to PHP 8.2:

- name: Rector
  if: matrix.php == '8.2'
  run: composer coder:rector --continuous-integration

Run Rector locally with PHP 8.2. Unsupported runtime versions produce an actionable error asking you to update Rector.

Tool customizations

Version optional files in build/ for both TYPO3 projects and standalone extensions. These consumer files customize the shared configurations in vendor/gaya/typo3-coder/build/. PHP callbacks receive the default configuration and a GAYA\Typo3Coder\Configuration\ProjectContext with absolute rootDir, vendorDir, binDir and webDir. Mutate the configuration and return nothing, or return a replacement of the same configuration type. Defaults, including the PHP target, are applied first.

build/rector.php:

<?php
use GAYA\Typo3Coder\Configuration\ProjectContext;
use Rector\Configuration\RectorConfigBuilder;
use Rector\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector;

return static function (RectorConfigBuilder $config, ProjectContext $context): void {
    $config->withSkip([OptionalParametersAfterRequiredRector::class]);
};

Rector's builder merges sets and skips. To replace defaults completely, return a new RectorConfig::configure() builder and configure its paths, PHP target and rules explicitly.

build/fractor.php follows the same contract with a9f\Fractor\Configuration\FractorConfigurationBuilder.

build/php-cs-fixer.php:

<?php
use PhpCsFixer\Config;

return static function (Config $config): void {
    $config->setRules(array_replace($config->getRules(), ['declare_strict_types' => false]));
};

For IDEs, run the Composer commands with the consumer as working directory. Rector, Fractor and PHP-CS-Fixer can also use the files in this package's build/ with their --config option, provided the consumer autoloader is available and the working directory is the consumer root. Use coder:phplint and coder:tests:* to resolve their dynamic paths.

TypoScript lint

composer coder:typoscript-lint uses helmich/typo3-typoscript-lint with the shared build/tslint.yaml shipped in this package. It scans the configured analysis paths with the same dependency/build exclusions as Rector. Supported files are *.typoscript, *.tsconfig, setup.txt, constants.txt, ext_typoscript_setup.txt and ext_typoscript_constants.txt. Legacy *.ts files are included only under Configuration/TypoScript/ or Configuration/TSconfig/ to avoid treating TypeScript as TypoScript.

The common rules use two spaces per indentation level, indent conditions, and disable RepeatingRValue. Errors fail the command. --ci and --continuous-integration do not change the TypoScript linter's warning handling; use composer coder:typoscript-lint -- --fail-on-warnings to fail on warnings as well. Projects without TypoScript files succeed with an informational message.

Additional native options can be passed after --, for example --format xml --output build/typoscript-report.xml. An explicit --config build/tslint.yaml replaces the shared YAML with a consumer-owned native configuration; file selection still uses coder's analysis paths. No configuration is copied into the consumer.

YAML lint

composer coder:yaml-lint uses Symfony's YAML linter to validate *.yaml and *.yml in the configured analysis paths, with the common dependency/build exclusions. Invalid YAML fails both locally and in CI; no YAML files is a success. No configuration or launcher is copied into the consumer.

Use composer coder:yaml-lint --continuous-integration in CI. Native options can be passed after --, for example --format=json or --parse-tags for custom YAML tags.

PHPStan

composer coder:phpstan uses the shared configuration and scans the consumer's PHP source files, using the configured analysis paths and exclusions. PHPStan defaults to level 10. Set extra.gaya/typo3-coder.phpstan-level in composer.json to select another level, as shown in the project context example above.

Optional build/phpstan.neon is included automatically for additional PHPStan configuration. The generated configuration sets the level and analysis paths from coder's project context; configure these through composer.json.

composer coder:phpstan
composer coder:phpstan -- --error-format=table
composer coder:phpstan:baseline

composer coder:phpstan:baseline generates or updates build/phpstan.baseline.neon in the consuming project. When present, this baseline is included automatically in subsequent PHPStan runs. Commit it if the project uses a baseline to track existing issues. Baseline generation is an explicit operation and still writes the file when --ci or --continuous-integration is supplied; it is excluded from coder:all.

PHPUnit and TYPO3 functional tests

The package builds a temporary XML with absolute paths, validates it against the installed PHPUnit schema and removes it after execution. It supports the PHPUnit versions allowed by the installed TYPO3 testing framework. No XML is written into your repository or vendor directory.

build/phpunit.php returns a callback accepting DOMDocument $document, ProjectContext $context and string $suite (unit or functional). It can adjust PHPUnit attributes, coverage sources/exclusions, suites and environment variables. Relative paths added here must be converted with $context->absolute() because the XML lives in the system temporary directory.

<?php
use GAYA\Typo3Coder\Configuration\ProjectContext;

return static function (DOMDocument $document, ProjectContext $context, string $suite): void {
    $document->documentElement->setAttribute('requireCoverageMetadata', 'true');
};

Functional tests bootstrap TYPO3's testing framework, use the Composer web root and default to pdo_sqlite. Install pdo_sqlite in CI (for shivammathur/setup-php, use extensions: pdo_sqlite). Environment variables already set by the caller take precedence over XML defaults; other database drivers must use a dedicated test database and testing-framework credentials. Never point test database settings at production data.

Minimal Tests/Functional/PageTest.php:

<?php
namespace Vendor\Extension\Tests\Functional;

use PHPUnit\Framework\Attributes\Test;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;

final class PageTest extends FunctionalTestCase
{
    #[Test]
    public function importsPage(): void
    {
        $this->importCSVDataSet(__DIR__ . '/Fixtures/pages.csv');
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
        self::assertSame('Test page', $connection->select(['title'], 'pages', ['uid' => 1])->fetchOne());
    }
}

Tests/Functional/Fixtures/pages.csv:

"pages"
,"uid","pid","title"
,1,0,"Test page"

Configure Composer autoload-dev for your Tests/ namespace. The framework creates an isolated TYPO3 instance and imports fixtures into its test database. Extension-specific packages, fixtures and HTTP mocks remain in the extension's tests. The gaya/typo3-hcaptcha integration demonstrates loading an extension and testing form requests.

Migration from 14.0

On Composer install/update, coder automatically:

  • Removes the unmodified copies formerly generated as rector.php, fractor.php, .php-cs-fixer.php, .phplint.yml and build/phpunit/PhpUnit.xml.
  • Moves rector.project.php, fractor.project.php and .php-cs-fixer.project.php to their corresponding build/*.php paths. Legacy variable scope is supported, including replacing the builder. __DIR__ and __FILE__ retain their old meaning after relocation. The migrated files should be committed.
  • Removes exact ignore entries for the generated files, preserving cache patterns and unrelated entries.

Run composer coder:migrate explicitly if needed (for example after an install with scripts disabled). It is safe to run repeatedly. Conflicting customizations are reported without overwriting the destination or deleting the original. Resolve the conflict and rerun migration before using the new commands.

The previously documented .rector.php name was a documentation error. It is neither loaded nor migrated.

Development and release

composer install
vendor/bin/phpunit -c Tests/phpunit.xml

Publish coder package first on GitHub/Packagist, then update the consuming projects' lock files. Local verification can use a temporary Composer manifest with a path repository; do not commit that repository to consumers.