NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #757 most downloaded on Packagist
Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer
Last release today
07 Oct 2026
Release timing varies
gaps range from 8 days to 4 months
Rarely documented
notes for 11 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
776 releases · first in 2017
Deprecated withPhpCsFixerSets() no longer crashes with legacy arguments
--dirty optionCheck only the files with uncommitted git changes - handy for a quick local run before a commit.
vendor/bin/ecs check --dirtyRemoveParamDescriptionDuplicateNameFixerRemoves redundant @param descriptions that only repeat the parameter name.
withPhpCsFixerSets() no longer crashes with legacy argumentsCalling the deprecated method with its former named arguments, e.g. ->withPhpCsFixerSets(perCS30: true), threw an "unknown named parameter" error instead of printing the deprecation warning. It now accepts legacy arguments and shows the warning as intended.
One column per quarter.
Native ECS support for the PER Coding Style 3.0. Before this, PER-CS was only reachable through the deprecated withPhpCsFixerSets() , now a no-op - so…
Native ECS support for the PER Coding Style 3.0. Before this, PER-CS was only reachable through the deprecated withPhpCsFixerSets(), now a no-op - so there was no way to apply PER-CS through ECS.
Reachable both ways:
$ecsConfig->withPreparedSets(perCs: true);Non-risky rules only, matching how the other prepared sets ship. Faithful to php-cs-fixer v3.95 @PER-CS3x0 resolution.
Nothing published for this version
🧹 Fixer-first: deprecate the CI report formats
NoBlankLineBetweenImportsFixer to the spaces setRemoves blank lines between consecutive import use statements, while keeping the blank line after the namespace intact. Ported from a custom fixer used in the Mautic codebase.
use App\First;
-
use App\Second;JsonOutputFormatter now always reports absolute paths - the format machines consume (IDE links, CI ingestion) - so no toggle is needed.
ECS is a fixer by default, not a static analyzer. The report formats that target CI static-analysis pipelines now emit a warning and fall back to console:
junit — ecsphp/ecs-src#69gitlab — ecsphp/ecs-src#71checkstyle — ecsphp/ecs-src#74Remaining formatters: console and json.
withRealPathReporting()Kept as a deprecation warning pointing to --output-format json, now that JSON output always reports absolute paths.
withEditorConfig().editorconfig discovery silently leaked project-wide settings into ECS, overriding the rules configured through sets. Coding standard should be driven by the ECS config. withEditorConfig() is kept as a deprecation warning; configure the matching PHP-CS-Fixer rules explicitly instead.
withPhpCsFixerSets()Prepared sets cover the same ground with a simpler API. withPhpCsFixerSets() is reduced to a deprecation no-op pointing to ->withPreparedSets() / ->withSets().
symplify setThe symplify set was already a no-op loading zero rules; its rules live in the common sets. The symplify arg of withPreparedSets() and SetList::SYMPLIFY are removed - switch to ->withPreparedSets(common: true) or the matching with*Level() methods.
phpunit and strict set stub files - ecsphp/ecs-src#72Option::ONLY constant - ecsphp/ecs-src#73Configuring a php-cs-fixer fixer with a deprecated option ran ConfigurableFixerTrait::configure() , which calls PhpCsFixer\Console\Application::getMaj…
ECS replaces symfony/console, so Symfony\Component\Console\Application is never installed. Configuring a php-cs-fixer fixer with a deprecated option ran ConfigurableFixerTrait::configure(), which calls PhpCsFixer\Console\Application::getMajorVersion() — a class that extends the missing Symfony Application, so PHP fatalled resolving the parent:
PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Application" not found
Now a declaration-only stub ships in /stubs (prefixed by php-scoper in the release build), so the run proceeds instead of fatalling:
-<?php $x=1;
+<?php
+
+$x = 1;A Configured Fixer CI reproducer guards against regressions.
Nothing published for this version
Nothing published for this version
Nothing published for this version
🐛 Explicit Symfony attribute allowlist in StandaloneLineSymfonyAttributeParamFixer
StandaloneLineSymfonyAttributeParamFixerReplaces the str_contains($name, 'Symfony') heuristic with an explicit allowlist of 17 Symfony attribute classes (SYMFONY_ATTRIBUTE_CLASSES). Third-party attributes whose FQCN merely contains Symfony (e.g. App\Symfony\...) are no longer reformatted.
-#[\Symfony\Component\Console\Attribute\AsCommand(name: 'app:some', description: 'Some description')]
+#[\Symfony\Component\Console\Attribute\AsCommand(
+ name: 'app:some',
+ description: 'Some description'
+)]pull_request trigger from version_command workflowThe job smoke-tests the published Packagist artifact, so on PRs it installed the already-published package, never the PR code — pure slow noise. push (main + tags) and weekly schedule already cover it.
✨ New StandaloneLineSymfonyAttributeParamFixer + standaloneLine set
StandaloneLineSymfonyAttributeParamFixer + standaloneLine setBreaks each argument of a Symfony attribute onto its own line. New opt-in standaloneLine set groups the standalone-line rules.
-#[\Symfony\Component\Console\Attribute\AsCommand(name: 'mautic:entity:import', description: 'Import entity data from a ZIP file.')]
+#[\Symfony\Component\Console\Attribute\AsCommand(
+ name: 'mautic:entity:import',
+ description: 'Import entity data from a ZIP file.'
+)]$ecsConfig->withPreparedSets(standaloneLine: true);
// or
$ecsConfig->withSets([SetList::STANDALONE_LINE]);Now resolves short names against use imports, not just fully-qualified names.
use Symfony\Component\Console\Attribute\AsCommand;
-#[AsCommand(name: 'app:some', description: 'Some description')]
+#[AsCommand(
+ name: 'app:some',
+ description: 'Some description'
+)]🐛 Drop 3rd-person verb s in method-name duplicate description
s in method-name duplicate description-/**
- * Contacts a user.
*
* @return object
*/
public function contactUser()scoper.php for FinalInternalClassFixer (#24)Prefixed build no longer mangles the exclude entity names:
-'exclude' => ['final', 'Entity', 'ECSPrefix202607\ORM\Entity', ...]
+'exclude' => ['final', 'Entity', 'ORM\Entity', ...]Locks StandaloneLinePromotedPropertyFixer splitting private readonly promoted props.
🐛 StandaloneLinePlainConstructorParamFixer skips ≤3 params
StandaloneLinePlainConstructorParamFixer skips ≤3 paramsOnly splits constructors with 4+ params. Short lists stay on one line. Default-value tokens ([1, 2], new Foo(1)) don't inflate the count.
✨ New StandaloneLinePlainConstructorParamFixer (spaces set)
StandaloneLinePlainConstructorParamFixer (spaces set)Covers the gap: constructors with plain params and no promoted properties.
-public function __construct(CorePermissions $security, Translator $translator, RouterInterface $router, FormFactoryInterface $formFactory)
+public function __construct(
+ CorePermissions $security,
+ Translator $translator,
+ RouterInterface $router,
+ FormFactoryInterface $formFactory
+)✅ Regression fixture: single promoted property among plain params
Locks StandaloneLinePromotedPropertyFixer splitting all params even when only one is promoted.
public function __construct(
- CorePermissions $security, Translator $translator, RouterInterface $router,
+ CorePermissions $security,
+ Translator $translator,
+ RouterInterface $router,
private LeadModel $leadModel,
) {
}✨ New StandaloneLineRequiredParamFixer (spaces set)
StandaloneLineRequiredParamFixer (spaces set)Public methods marked #[Required] / @required are DI points; one param per line keeps diffs to the single changed dependency.
#[Required]
-public function autowireDependencies(FormModel $formModel, SubmissionModel $submissionModel): void
+public function autowireDependencies(
+ FormModel $formModel,
+ SubmissionModel $submissionModel
+): voidNothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →