Skip to content

Commit c83965e

Browse files
committed
Updated ECS to commit 9a00ea6cc6ed3b6db115f7e864a45bfa7ef2eb1f
1 parent 924ffba commit c83965e

242 files changed

Lines changed: 887 additions & 9535 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bin/ecs.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,6 @@ public function loadIfNotLoadedYet(string $file): void
132132
exit(Command::FAILURE);
133133
}
134134
/** @var EasyCodingStandardConsoleApplication $application */
135-
$application = $container->get(EasyCodingStandardConsoleApplication::class);
135+
$application = $container->make(EasyCodingStandardConsoleApplication::class);
136136
$statusCode = $application->run();
137137
exit($statusCode);

src/Application/Version/StaticVersionResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ final class StaticVersionResolver
1515
* @api
1616
* @var string
1717
*/
18-
public const PACKAGE_VERSION = '59e67d8f8e604e651ad8abef5e1534ca1ff2482d';
18+
public const PACKAGE_VERSION = '9a00ea6cc6ed3b6db115f7e864a45bfa7ef2eb1f';
1919
/**
2020
* @api
2121
* @var string
2222
*/
23-
public const RELEASE_DATE = '2026-06-10 09:32:54';
23+
public const RELEASE_DATE = '2026-06-10 15:17:29';
2424
/**
2525
* @var int
2626
*/

src/Caching/FileHashComputer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public function computeConfig(string $filePath): string
2020
Assert::isCallable($callable);
2121
$ecsConfig = new ECSConfig();
2222
$callable($ecsConfig);
23-
// hash the container setup
24-
$fileHash = sha1(Json::encode($ecsConfig->getBindings()));
23+
// hash the registered checkers and their configuration
24+
$fileHash = sha1(Json::encode($ecsConfig->getCheckerConfiguration()));
2525
return sha1($fileHash . SimpleParameterProvider::hash() . StaticVersionResolver::PACKAGE_VERSION);
2626
}
2727
public function compute(string $filePath): string

src/Config/ECSConfig.php

Lines changed: 149 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
declare (strict_types=1);
44
namespace Symplify\EasyCodingStandard\Config;
55

6-
use ECSPrefix202606\Illuminate\Container\Container;
6+
use ECSPrefix202606\Entropy\Container\Container;
77
use Override;
88
use PHP_CodeSniffer\Sniffs\Sniff;
99
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
@@ -13,7 +13,6 @@
1313
use PhpCsFixer\RuleSet\RuleSet;
1414
use PhpCsFixer\WhitespacesFixerConfig;
1515
use Symplify\EasyCodingStandard\Configuration\ECSConfigBuilder;
16-
use Symplify\EasyCodingStandard\Contract\Console\Output\OutputFormatterInterface;
1716
use Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\ConflictingCheckersCompilerPass;
1817
use Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\RemoveExcludedCheckersCompilerPass;
1918
use Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\RemoveMutualCheckersCompilerPass;
@@ -27,9 +26,31 @@
2726
final class ECSConfig extends Container
2827
{
2928
/**
30-
* @var string[]
29+
* Registered checkers, mapped to their configuration (empty array = no configuration).
30+
*
31+
* @var array<class-string<Sniff|FixerInterface>, mixed[]>
3132
*/
32-
private const AUTOTAG_INTERFACES = [Sniff::class, FixerInterface::class, OutputFormatterInterface::class];
33+
private $checkerConfiguration = [];
34+
/**
35+
* Checkers removed by compiler passes (skip, mutual exclusion, …).
36+
*
37+
* @var array<class-string<Sniff|FixerInterface>, true>
38+
*/
39+
private $removedCheckers = [];
40+
/**
41+
* Configured checker instances, built exactly once and shared, even when a
42+
* class is declared in both a set and explicitly.
43+
*
44+
* @var array<class-string<Sniff|FixerInterface>, Sniff|FixerInterface>
45+
*/
46+
private $builtCheckers = [];
47+
/**
48+
* Registration order, with duplicates preserved: a checker declared both in a
49+
* set and explicitly is listed twice (both share the last-wins configuration).
50+
*
51+
* @var list<class-string<Sniff|FixerInterface>>
52+
*/
53+
private $checkerRegistrationOrder = [];
3354
public static function configure(): ECSConfigBuilder
3455
{
3556
return new ECSConfigBuilder();
@@ -66,14 +87,7 @@ public function sets(array $sets): void
6687
public function rule(string $checkerClass): void
6788
{
6889
$this->assertCheckerClass($checkerClass);
69-
$this->singleton($checkerClass);
70-
$this->autowireWhitespaceAwareFixer($checkerClass);
71-
if (is_a($checkerClass, ConfigurableFixerInterface::class, \true)) {
72-
$this->extend($checkerClass, static function (ConfigurableFixerInterface $configurableFixer): ConfigurableFixerInterface {
73-
$configurableFixer->configure([]);
74-
return $configurableFixer;
75-
});
76-
}
90+
$this->registerChecker($checkerClass, []);
7791
}
7892
/**
7993
* @param array<class-string<Sniff|FixerInterface>> $checkerClasses
@@ -92,24 +106,10 @@ public function rules(array $checkerClasses): void
92106
public function ruleWithConfiguration(string $checkerClass, array $configuration): void
93107
{
94108
$this->assertCheckerClass($checkerClass);
95-
$this->singleton($checkerClass);
96-
$this->autowireWhitespaceAwareFixer($checkerClass);
97109
if (is_a($checkerClass, FixerInterface::class, \true)) {
98110
Assert::isAOf($checkerClass, ConfigurableFixerInterface::class);
99-
$this->extend($checkerClass, static function (ConfigurableFixerInterface $configurableFixer) use ($configuration): ConfigurableFixerInterface {
100-
$configurableFixer->configure($configuration);
101-
return $configurableFixer;
102-
});
103-
}
104-
if (is_a($checkerClass, Sniff::class, \true)) {
105-
$this->extend($checkerClass, static function (Sniff $sniff) use ($configuration): Sniff {
106-
foreach ($configuration as $propertyName => $value) {
107-
Assert::propertyExists($sniff, $propertyName);
108-
$sniff->{$propertyName} = $value;
109-
}
110-
return $sniff;
111-
});
112111
}
112+
$this->registerChecker($checkerClass, $configuration);
113113
}
114114
/**
115115
* @param array<class-string<Sniff|FixerInterface>, mixed[]> $rulesWithConfiguration
@@ -206,19 +206,134 @@ public function boot(): void
206206
$conflictingCheckersCompilerPass->process($this);
207207
}
208208
/**
209-
* @param string $abstract
210-
* @param mixed $concrete
209+
* Checkers are returned fully configured; every other class is built by the parent container.
210+
*
211+
* @template TType as object
212+
*
213+
* @param class-string<TType> $class
214+
* @return TType
211215
*/
212216
#[Override]
213-
public function singleton($abstract, $concrete = null): void
217+
public function make(string $class): object
218+
{
219+
if (isset($this->checkerConfiguration[$class])) {
220+
// a configured-checker key is always a Sniff|FixerInterface class-string
221+
/** @var class-string<Sniff|FixerInterface> $checkerClass */
222+
$checkerClass = $class;
223+
/** @var TType $checker */
224+
$checker = $this->buildConfiguredChecker($checkerClass);
225+
return $checker;
226+
}
227+
return parent::make($class);
228+
}
229+
/**
230+
* Checkers are returned in registration order with duplicates preserved (a class
231+
* declared both in a set and explicitly appears twice, sharing one instance);
232+
* every other service is resolved by the parent container.
233+
*
234+
* @template TType as object
235+
*
236+
* @param class-string<TType> $contractClass
237+
* @return array<TType>
238+
*/
239+
#[Override]
240+
public function findByContract(string $contractClass): array
241+
{
242+
// genuine (non-checker) services from the parent container
243+
$instances = [];
244+
foreach (parent::findByContract($contractClass) as $class => $instance) {
245+
if (isset($this->checkerConfiguration[$class])) {
246+
// checkers are handled below, in registration order with duplicates
247+
continue;
248+
}
249+
$instances[] = $instance;
250+
}
251+
// checkers, in registration order, keeping duplicates and honouring removals
252+
$checkerInstances = [];
253+
foreach ($this->checkerRegistrationOrder as $checkerClass) {
254+
if (isset($this->removedCheckers[$checkerClass])) {
255+
continue;
256+
}
257+
// avoid building checkers that cannot match the requested contract
258+
if (!is_a($checkerClass, $contractClass, \true)) {
259+
continue;
260+
}
261+
$checkerInstances[] = $this->buildConfiguredChecker($checkerClass);
262+
}
263+
$matchingCheckers = array_filter($checkerInstances, static function (object $instance) use ($contractClass): bool {
264+
return $instance instanceof $contractClass;
265+
});
266+
return array_merge($instances, array_values($matchingCheckers));
267+
}
268+
/**
269+
* Registered checker classes, minus those removed by compiler passes.
270+
*
271+
* @return array<class-string<Sniff|FixerInterface>>
272+
*/
273+
public function getCheckerClasses(): array
274+
{
275+
return array_values(array_filter(array_keys($this->checkerConfiguration), function (string $checkerClass): bool {
276+
return !isset($this->removedCheckers[$checkerClass]);
277+
}));
278+
}
279+
/**
280+
* Registered checkers and their configuration, used for cache invalidation.
281+
*
282+
* @return array<class-string<Sniff|FixerInterface>, mixed[]>
283+
*/
284+
public function getCheckerConfiguration(): array
214285
{
215-
parent::singleton($abstract, $concrete);
216-
foreach (self::AUTOTAG_INTERFACES as $autotagInterface) {
217-
if (!is_a($abstract, $autotagInterface, \true)) {
286+
$checkerConfiguration = [];
287+
foreach ($this->checkerConfiguration as $checkerClass => $configuration) {
288+
if (isset($this->removedCheckers[$checkerClass])) {
218289
continue;
219290
}
220-
$this->tag($abstract, $autotagInterface);
291+
$checkerConfiguration[$checkerClass] = $configuration;
221292
}
293+
return $checkerConfiguration;
294+
}
295+
/**
296+
* @param class-string<Sniff|FixerInterface> $checkerClass
297+
*/
298+
public function removeChecker(string $checkerClass): void
299+
{
300+
$this->removedCheckers[$checkerClass] = \true;
301+
}
302+
/**
303+
* @param class-string<Sniff|FixerInterface> $checkerClass
304+
* @param mixed[] $configuration
305+
*/
306+
private function registerChecker(string $checkerClass, array $configuration): void
307+
{
308+
// last registration wins for configuration, mirroring the previous container override behaviour
309+
$this->checkerConfiguration[$checkerClass] = $configuration;
310+
$this->checkerRegistrationOrder[] = $checkerClass;
311+
unset($this->removedCheckers[$checkerClass]);
312+
}
313+
/**
314+
* @param class-string<Sniff|FixerInterface> $checkerClass
315+
*/
316+
private function buildConfiguredChecker(string $checkerClass): object
317+
{
318+
if (isset($this->builtCheckers[$checkerClass])) {
319+
return $this->builtCheckers[$checkerClass];
320+
}
321+
// parent::make() autowires the raw checker by reflection; this class' make() override would recurse here
322+
$checker = parent::make($checkerClass);
323+
if ($checker instanceof WhitespacesAwareFixerInterface) {
324+
$checker->setWhitespacesConfig($this->make(WhitespacesFixerConfig::class));
325+
}
326+
$configuration = $this->checkerConfiguration[$checkerClass];
327+
if ($checker instanceof ConfigurableFixerInterface) {
328+
$checker->configure($configuration);
329+
} elseif ($checker instanceof Sniff) {
330+
foreach ($configuration as $propertyName => $value) {
331+
Assert::propertyExists($checker, $propertyName);
332+
$checker->{$propertyName} = $value;
333+
}
334+
}
335+
$this->builtCheckers[$checkerClass] = $checker;
336+
return $checker;
222337
}
223338
/**
224339
* @param class-string $checkerClass
@@ -245,18 +360,4 @@ private function ensureCheckerClassesAreUnique(array $checkerClasses): void
245360
$errorMessage = sprintf('There are duplicated classes in $rectorConfig->rules(): "%s". Make them unique to avoid unexpected behavior.', implode('", "', $duplicatedCheckerClasses));
246361
throw new InvalidArgumentException($errorMessage);
247362
}
248-
/**
249-
* @param class-string<FixerInterface|Sniff> $checkerClass
250-
*/
251-
private function autowireWhitespaceAwareFixer(string $checkerClass): void
252-
{
253-
if (!is_a($checkerClass, WhitespacesAwareFixerInterface::class, \true)) {
254-
return;
255-
}
256-
$this->extend($checkerClass, static function (WhitespacesAwareFixerInterface $whitespacesAwareFixer, Container $container): WhitespacesAwareFixerInterface {
257-
$whitespacesFixerConfig = $container->make(WhitespacesFixerConfig::class);
258-
$whitespacesAwareFixer->setWhitespacesConfig($whitespacesFixerConfig);
259-
return $whitespacesAwareFixer;
260-
});
261-
}
262363
}

src/DependencyInjection/CompilerPass/CompilerPassHelper.php

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,23 @@
33
declare (strict_types=1);
44
namespace Symplify\EasyCodingStandard\DependencyInjection\CompilerPass;
55

6-
use ECSPrefix202606\Illuminate\Container\Container;
76
use PHP_CodeSniffer\Sniffs\Sniff;
87
use PhpCsFixer\Fixer\FixerInterface;
9-
use Symplify\EasyCodingStandard\Utils\PrivatesAccessorHelper;
8+
use Symplify\EasyCodingStandard\Config\ECSConfig;
109
final class CompilerPassHelper
1110
{
1211
/**
13-
* @return string[]
12+
* @return array<class-string<Sniff|FixerInterface>>
1413
*/
15-
public static function resolveCheckerClasses(Container $container): array
14+
public static function resolveCheckerClasses(ECSConfig $ecsConfig): array
1615
{
17-
$serviceTypes = array_keys($container->getBindings());
18-
return array_filter($serviceTypes, static function (string $serviceType): bool {
19-
if (is_a($serviceType, FixerInterface::class, \true)) {
20-
return \true;
21-
}
22-
return is_a($serviceType, Sniff::class, \true);
23-
});
16+
return $ecsConfig->getCheckerClasses();
2417
}
25-
public static function removeCheckerFromContainer(Container $container, string $checkerClass): void
18+
/**
19+
* @param class-string<Sniff|FixerInterface> $checkerClass
20+
*/
21+
public static function removeCheckerFromContainer(ECSConfig $ecsConfig, string $checkerClass): void
2622
{
27-
// remove instance
28-
$container->offsetUnset($checkerClass);
29-
$tags = PrivatesAccessorHelper::getPropertyValue($container, 'tags');
30-
// nothing to remove
31-
if ($tags === []) {
32-
return;
33-
}
34-
// remove from tags
35-
$checkerTagClasses = [FixerInterface::class, Sniff::class];
36-
foreach ($checkerTagClasses as $checkerTagClass) {
37-
foreach ($tags[$checkerTagClass] ?? [] as $key => $class) {
38-
if ($class !== $checkerClass) {
39-
continue;
40-
}
41-
unset($tags[$checkerTagClass][$key]);
42-
}
43-
}
44-
// update value
45-
PrivatesAccessorHelper::setPropertyValue($container, 'tags', $tags);
23+
$ecsConfig->removeChecker($checkerClass);
4624
}
4725
}

src/DependencyInjection/CompilerPass/ConflictingCheckersCompilerPass.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
declare (strict_types=1);
44
namespace Symplify\EasyCodingStandard\DependencyInjection\CompilerPass;
55

6-
use ECSPrefix202606\Illuminate\Container\Container;
76
use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\EndFileNewlineSniff as GenericEndFileNewlineSniff;
87
use PHP_CodeSniffer\Standards\Generic\Sniffs\Files\EndFileNoNewlineSniff;
98
use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\LowerCaseConstantSniff;
@@ -19,6 +18,7 @@
1918
use PhpCsFixer\Fixer\PhpTag\BlankLineAfterOpeningTagFixer;
2019
use Symplify\CodingStandard\Fixer\Spacing\StandaloneLineConstructorParamFixer;
2120
use Symplify\CodingStandard\Fixer\Spacing\StandaloneLinePromotedPropertyFixer;
21+
use Symplify\EasyCodingStandard\Config\ECSConfig;
2222
use Symplify\EasyCodingStandard\Exception\Configuration\ConflictingCheckersLoadedException;
2323
final class ConflictingCheckersCompilerPass
2424
{
@@ -28,9 +28,9 @@ final class ConflictingCheckersCompilerPass
2828
* @var string[][]
2929
*/
3030
private const CONFLICTING_CHECKER_GROUPS = [[StandaloneLineConstructorParamFixer::class, StandaloneLinePromotedPropertyFixer::class], ['ECSPrefix202606\SlevomatCodingStandard\Sniffs\ControlStructures\DisallowYodaComparisonSniff', YodaStyleFixer::class], [LowerCaseConstantSniff::class, UpperCaseConstantSniff::class], [ConstantCaseFixer::class, UpperCaseConstantSniff::class], ['ECSPrefix202606\SlevomatCodingStandard\Sniffs\TypeHints\DeclareStrictTypesSniff', DeclareEqualNormalizeFixer::class], ['ECSPrefix202606\SlevomatCodingStandard\Sniffs\TypeHints\DeclareStrictTypesSniff', BlankLineAfterOpeningTagFixer::class], [FileHeaderSniff::class, NoBlankLinesAfterPhpdocFixer::class], [EndFileNewlineSniff::class, EndFileNoNewlineSniff::class], [GenericEndFileNewlineSniff::class, EndFileNoNewlineSniff::class], [DisallowTabIndentSniff::class, DisallowSpaceIndentSniff::class]];
31-
public function process(Container $container): void
31+
public function process(ECSConfig $ecsConfig): void
3232
{
33-
$checkerTypes = \Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\CompilerPassHelper::resolveCheckerClasses($container);
33+
$checkerTypes = \Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\CompilerPassHelper::resolveCheckerClasses($ecsConfig);
3434
if ($checkerTypes === []) {
3535
return;
3636
}

src/DependencyInjection/CompilerPass/RemoveExcludedCheckersCompilerPass.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
declare (strict_types=1);
44
namespace Symplify\EasyCodingStandard\DependencyInjection\CompilerPass;
55

6-
use ECSPrefix202606\Illuminate\Container\Container;
6+
use Symplify\EasyCodingStandard\Config\ECSConfig;
77
use Symplify\EasyCodingStandard\DependencyInjection\SimpleParameterProvider;
88
use Symplify\EasyCodingStandard\ValueObject\Option;
99
final class RemoveExcludedCheckersCompilerPass
1010
{
11-
public function process(Container $container): void
11+
public function process(ECSConfig $ecsConfig): void
1212
{
1313
$excludedCheckers = $this->getExcludedCheckersFromSkipParameter();
14-
foreach (array_keys($container->getBindings()) as $classType) {
14+
foreach ($ecsConfig->getCheckerClasses() as $classType) {
1515
if (!in_array($classType, $excludedCheckers, \true)) {
1616
continue;
1717
}
18-
// remove service from container completely
19-
\Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\CompilerPassHelper::removeCheckerFromContainer($container, $classType);
18+
// remove checker from container completely
19+
\Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\CompilerPassHelper::removeCheckerFromContainer($ecsConfig, $classType);
2020
}
2121
}
2222
/**

0 commit comments

Comments
 (0)