Skip to content

Commit ed25c5b

Browse files
committed
Fixed phpstan issues
1 parent 961b06d commit ed25c5b

27 files changed

Lines changed: 140 additions & 414 deletions

bin/console

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
declare(strict_types=1);
5+
6+
use ITKDev\EntityBundle\Tests\App\Kernel;
7+
use Symfony\Bundle\FrameworkBundle\Console\Application;
8+
use Symfony\Component\Console\Input\ArgvInput;
9+
use Symfony\Component\Dotenv\Dotenv;
10+
use Symfony\Component\ErrorHandler\Debug;
11+
12+
if (!is_file(__DIR__.'/../vendor/autoload.php')) {
13+
fwrite(STDERR, "vendor/autoload.php not found. Run composer install.\n");
14+
exit(1);
15+
}
16+
17+
require __DIR__.'/../vendor/autoload.php';
18+
19+
if (is_file(__DIR__.'/../.env')) {
20+
(new Dotenv())->bootEnv(__DIR__.'/../.env');
21+
}
22+
23+
$input = new ArgvInput();
24+
$env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'test');
25+
$debug = ($_SERVER['APP_DEBUG'] ?? '1') && !$input->hasParameterOption('--no-debug', true);
26+
27+
if ($debug) {
28+
umask(0000);
29+
if (class_exists(Debug::class)) {
30+
Debug::enable();
31+
}
32+
}
33+
34+
$kernel = new Kernel($env, (bool) $debug);
35+
$application = new Application($kernel);
36+
$application->run($input);

phpstan-baseline.neon

Lines changed: 0 additions & 379 deletions
This file was deleted.

phpstan-bootstrap.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
require __DIR__.'/vendor/autoload.php';
6+
7+
$_ENV['APP_DEBUG'] = $_SERVER['APP_DEBUG'] = '1';
8+
$_ENV['APP_ENV'] = $_SERVER['APP_ENV'] = 'test';
9+
10+
$kernel = new ITKDev\EntityBundle\Tests\App\Kernel('test', true);
11+
$kernel->boot();

phpstan.neon.dist

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
includes:
2-
- phpstan-baseline.neon
3-
41
parameters:
52
level: 8
63
paths:
74
- src
85
- tests
96
excludePaths:
107
- tests/App/var/*
8+
bootstrapFiles:
9+
- phpstan-bootstrap.php
10+
symfony:
11+
containerXmlPath: var/cache/test/ITKDev_EntityBundle_Tests_App_KernelTestDebugContainer.xml

src/Command/PrivacyAnonymizeCommand.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323
)]
2424
final class PrivacyAnonymizeCommand extends Command
2525
{
26-
/**
27-
* @param class-string<UserInterface&IdentifiableInterface> $userClass
28-
*/
2926
public function __construct(
3027
private readonly EntityManagerInterface $em,
3128
private readonly SubjectAnonymizer $anonymizer,
@@ -44,7 +41,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
4441
{
4542
$io = new SymfonyStyle($input, $output);
4643

47-
if ('' === $this->userClass) {
44+
if ('' === $this->userClass || !class_exists($this->userClass)) {
4845
$io->error('privacy:anonymize requires itk_dev_entity.user_class to be configured.');
4946

5047
return Command::FAILURE;

src/DependencyInjection/ITKDevEntityExtension.php

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,33 +151,49 @@ public function prepend(ContainerBuilder $container): void
151151
* Merge config-supplied anonymization rules into the attribute-discovered set.
152152
* For the same property name, the config wins (it's the explicit override).
153153
*
154+
* $configRules comes from a variableNode in the config tree, so its shape is
155+
* not enforced upstream — every layer is validated here. Class keys are not
156+
* required to exist locally (the bundle supports overrides for third-party
157+
* entities that may live in unloaded namespaces).
158+
*
154159
* @param array<class-string, list<array{property: string, strategy: string, replacement: ?string}>> $discovered
155-
* @param array<string, array<string, array{strategy: string, replacement?: ?string}>> $configRules
160+
* @param array<string, mixed> $configRules
156161
*
157-
* @return array<class-string, list<array{property: string, strategy: string, replacement: ?string}>>
162+
* @return array<string, list<array{property: string, strategy: string, replacement: ?string}>>
158163
*/
159164
private function mergeAnonymizationRules(array $discovered, array $configRules): array
160165
{
166+
/** @var array<string, list<array{property: string, strategy: string, replacement: ?string}>> $merged */
167+
$merged = $discovered;
168+
161169
foreach ($configRules as $class => $propRules) {
170+
if (!\is_array($propRules)) {
171+
throw new InvalidConfigurationException(sprintf('itk_dev_entity.anonymization.rules[%s] must be a map of property => { strategy: ..., replacement?: ... }.', $class));
172+
}
173+
162174
/** @var array<string, array{property: string, strategy: string, replacement: ?string}> $byProp */
163175
$byProp = [];
164-
foreach ($discovered[$class] ?? [] as $rule) {
176+
foreach ($merged[$class] ?? [] as $rule) {
165177
$byProp[$rule['property']] = $rule;
166178
}
167179
foreach ($propRules as $property => $spec) {
168-
if (!\is_array($spec) || !isset($spec['strategy'])) {
169-
throw new InvalidConfigurationException(sprintf('itk_dev_entity.anonymization.rules[%s][%s] must be { strategy: ..., replacement?: ... }', $class, $property));
180+
if (!\is_string($property) || !\is_array($spec) || !isset($spec['strategy']) || !\is_string($spec['strategy'])) {
181+
throw new InvalidConfigurationException(sprintf('itk_dev_entity.anonymization.rules[%s][%s] must be { strategy: ..., replacement?: ... }', $class, (string) $property));
182+
}
183+
$replacement = $spec['replacement'] ?? null;
184+
if (null !== $replacement && !\is_string($replacement)) {
185+
throw new InvalidConfigurationException(sprintf('itk_dev_entity.anonymization.rules[%s][%s].replacement must be a string or null.', $class, $property));
170186
}
171187
$byProp[$property] = [
172188
'property' => $property,
173-
'strategy' => (string) $spec['strategy'],
174-
'replacement' => $spec['replacement'] ?? null,
189+
'strategy' => $spec['strategy'],
190+
'replacement' => $replacement,
175191
];
176192
}
177-
$discovered[$class] = array_values($byProp);
193+
$merged[$class] = array_values($byProp);
178194
}
179195

180-
return $discovered;
196+
return $merged;
181197
}
182198

183199
/**
@@ -209,7 +225,6 @@ private function discoverAuditIgnoredColumns(string $class): array
209225
private function discoverEntities(ContainerBuilder $container, array $paths): array
210226
{
211227
$projectDir = $container->getParameter('kernel.project_dir');
212-
\assert(\is_string($projectDir));
213228

214229
$entities = [];
215230
foreach ($paths as $path) {
@@ -241,6 +256,8 @@ private function discoverEntities(ContainerBuilder $container, array $paths): ar
241256
* Walk the class hierarchy looking for #[ITKDevEntity]. The attribute is non-inherited
242257
* at the language level, but the bundle treats inherited declarations as opting in —
243258
* so subclasses of AbstractITKDevEntity automatically count.
259+
*
260+
* @param \ReflectionClass<object> $ref
244261
*/
245262
private function hasITKDevEntityAttribute(\ReflectionClass $ref): bool
246263
{

src/Doctrine/Filter/ArchivableFilter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
final class ArchivableFilter extends SQLFilter
1212
{
13-
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
13+
public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string
1414
{
1515
if (!$targetEntity->reflClass?->implementsInterface(ArchivableInterface::class)) {
1616
return '';

src/Doctrine/Filter/SoftDeleteFilter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
final class SoftDeleteFilter extends SQLFilter
1212
{
13-
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
13+
public function addFilterConstraint(ClassMetadata $targetEntity, string $targetTableAlias): string
1414
{
1515
if (!$targetEntity->reflClass?->implementsInterface(SoftDeletableInterface::class)) {
1616
return '';

src/Entity/Trait/TimestampableTrait.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
trait TimestampableTrait
1010
{
1111
#[ORM\Column(type: 'datetime_immutable')]
12-
private ?\DateTimeImmutable $createdAt = null;
12+
private \DateTimeImmutable $createdAt;
1313

1414
#[ORM\Column(type: 'datetime_immutable')]
15-
private ?\DateTimeImmutable $updatedAt = null;
15+
private \DateTimeImmutable $updatedAt;
1616

1717
public function getCreatedAt(): ?\DateTimeImmutable
1818
{
19-
return $this->createdAt;
19+
return $this->createdAt ?? null;
2020
}
2121

2222
public function getUpdatedAt(): ?\DateTimeImmutable
2323
{
24-
return $this->updatedAt;
24+
return $this->updatedAt ?? null;
2525
}
2626

2727
public function setCreatedAt(\DateTimeImmutable $createdAt): void

src/Privacy/AuditScrubber.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace ITKDev\EntityBundle\Privacy;
66

7+
use DH\Auditor\Provider\Doctrine\Configuration as DoctrineAuditConfiguration;
78
use DH\Auditor\Provider\Doctrine\DoctrineProvider;
89
use Doctrine\ORM\EntityManagerInterface;
910

@@ -184,7 +185,9 @@ private function clearDiffValues(array $diffs): array
184185
private function discoverAuditTables(): array
185186
{
186187
$tables = [];
187-
foreach ($this->auditProvider->getConfiguration()->getEntities() as $entry) {
188+
$configuration = $this->auditProvider->getConfiguration();
189+
\assert($configuration instanceof DoctrineAuditConfiguration);
190+
foreach ($configuration->getEntities() as $entry) {
188191
if (!\is_array($entry) || !isset($entry['audit_table_name'])) {
189192
continue;
190193
}

0 commit comments

Comments
 (0)