Skip to content

Give anonymizer metadata responsibility to the AnonymizerRegistry class #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ parameters:
checkMissingOverrideMethodAttribute: true
ignoreErrors:
- '#Instantiated class Seld\\PharUtils\\Timestamps not found.#'
- '#on an unknown class Seld\\PharUtils\\Timestamps.#'
- '#on an unknown class Seld\\PharUtils\\Timestamps.#'
11 changes: 4 additions & 7 deletions src/Anonymization/Anonymizator.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,11 @@ protected function getSalt(): string
*/
protected function createAnonymizer(AnonymizerConfig $config): AbstractAnonymizer
{
$className = $this->anonymizerRegistry->get($config->anonymizer);
\assert(\is_subclass_of($className, AbstractAnonymizer::class));

return new $className(
$config->table,
$config->targetName,
$this->databaseSession,
return $this->anonymizerRegistry->createAnonymizer(
$config->anonymizer,
$config,
$config->options->with(['salt' => $this->getSalt()]),
$this->databaseSession
);
}

Expand Down
15 changes: 0 additions & 15 deletions src/Anonymization/Anonymizer/AbstractAnonymizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer;

use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizator;
use MakinaCorpus\DbToolsBundle\Attribute\AsAnonymizer;
use MakinaCorpus\QueryBuilder\DatabaseSession;
use MakinaCorpus\QueryBuilder\Expression;
use MakinaCorpus\QueryBuilder\ExpressionFactory;
Expand All @@ -28,20 +27,6 @@ final public function __construct(
$this->validateOptions();
}

final public static function id(): string
{
return self::getMetadata()->id();
}

final public static function getMetadata(): AsAnonymizer
{
if ($attributes = (new \ReflectionClass(static::class))->getAttributes(AsAnonymizer::class)) {
return $attributes[0]->newInstance();
}

throw new \LogicException(\sprintf("Class '%s' should have an '%s' attribute.", static::class, AsAnonymizer::class));
}

/**
* Get table name.
*
Expand Down
77 changes: 60 additions & 17 deletions src/Anonymization/Anonymizer/AnonymizerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
namespace MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer;

use Composer\InstalledVersions;
use MakinaCorpus\DbToolsBundle\Anonymization\Config\AnonymizerConfig;
use MakinaCorpus\DbToolsBundle\Attribute\AsAnonymizer;
use MakinaCorpus\QueryBuilder\DatabaseSession;

class AnonymizerRegistry
{
Expand All @@ -26,7 +29,10 @@ class AnonymizerRegistry
Core\StringAnonymizer::class,
];

private ?array $anonymizers = null;
/** @var array<string, string> */
private ?array $classes = null;
/** @var array<string, AsAnonymizer> */
private ?array $metadata = null;
private array $paths = [];

public function __construct(?array $paths = null)
Expand All @@ -45,42 +51,70 @@ public function addPath(array $paths): void
/**
* Get all registered anonymizers classe names.
*
* @return array<string,string>
* Keys are names, values are class names.
* @return array<string,AsAnonymizer>
*/
public function getAnonymizers(): array
public function getAllAnonymizerMetadata(): array
{
$this->initialize();

return $this->anonymizers;
return $this->metadata;
}

/**
* Get anonymizer class name.
*
* @param string $name
* Anonymizer name.
*
* @return string
* Anonymizer class name.
* Create anonymizer instance.
*/
public function createAnonymizer(
string $name,
AnonymizerConfig $config,
Options $options,
DatabaseSession $databaseSession,
): AbstractAnonymizer {
$className = $this->getAnonymizerClass($name);

return new $className($config->table, $config->targetName, $databaseSession, $options);
}

/**
* Get anonymizer metadata.
*/
public function getAnonymizerMetadata(string $name): AsAnonymizer
{
$this->initialize();

return $this->metadata[$name] ?? $this->throwAnonymizerDoesNotExist($name);
}

/**
* @internal
* For unit tests only, please do not use.
*/
public function get(string $name): string
public function getAnonymizerClass(string $name): string
{
$this->initialize();

return $this->anonymizers[$name] ?? throw new \InvalidArgumentException(\sprintf("Can't find Anonymizer with name : %s, check your configuration.", $name));
return $this->classes[$name] ?? $this->throwAnonymizerDoesNotExist($name);
}

private function getAnonymizatorClassMetadata(string $className): AsAnonymizer
{
if ($attributes = (new \ReflectionClass($className))->getAttributes(AsAnonymizer::class)) {
return $attributes[0]->newInstance();
}

throw new \LogicException(\sprintf("Class '%s' should have an '%s' attribute.", $className, AsAnonymizer::class));
}

/**
* Lazy initialization.
*/
private function initialize(): void
{
if (null !== $this->anonymizers) {
if (null !== $this->classes) {
return;
}

$this->anonymizers = [];
$this->classes = [];
$this->metadata = [];

foreach (self::$coreAnonymizers as $className) {
$this->addAnonymizer($className, true);
Expand Down Expand Up @@ -152,7 +186,11 @@ private function addAnonymizer(string $className, bool $failOnError = false): vo
return;
}

$this->anonymizers[$className::id()] = $className;
$metadata = $this->getAnonymizatorClassMetadata($className);
$id = $metadata->id();

$this->classes[$id] = $className;
$this->metadata[$id] = $metadata;
}

/**
Expand All @@ -172,4 +210,9 @@ private function locatePacks(): void
}
}
}

private function throwAnonymizerDoesNotExist(string $name): never
{
throw new \InvalidArgumentException(\sprintf("Can't find Anonymizer with name : %s, check your configuration.", $name));
}
}
5 changes: 2 additions & 3 deletions src/Command/Anonymization/AnonymizerListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$io = new SymfonyStyle($input, $output);

$rawList = $this->anonymizerRegistry->getAnonymizers();
$rawList = $this->anonymizerRegistry->getAllAnonymizerMetadata();

$list = [];
foreach ($rawList as $anonymizer) {
$metadata = $anonymizer::getMetadata();
foreach ($rawList as $metadata) {
\assert($metadata instanceof AsAnonymizer);

if (!\array_key_exists($metadata->pack, $list)) {
Expand Down
13 changes: 8 additions & 5 deletions tests/Unit/Anonymization/Anonymizer/AnonymizerRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ public function testAnonymizerRegistryWithTestPack(): void

$anonymizerRegistry = new AnonymizerRegistry();

$anonymizers = $anonymizerRegistry->getAnonymizers();
$anonymizers = $anonymizerRegistry->getAllAnonymizerMetadata();

self::assertNotEmpty($anonymizers);
self::assertArrayHasKey('string', $anonymizers);
self::assertArrayHasKey('test.my-anonymizer', $anonymizers);

self::assertEquals('MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Core\FloatAnonymizer', $anonymizerRegistry->get('float'));
self::assertEquals('DbToolsBundle\PackTest\Anonymizer\MyAnonymizer', $anonymizerRegistry->get('test.my-anonymizer'));
self::assertEquals('MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Core\FloatAnonymizer', $anonymizerRegistry->getAnonymizerClass('float'));
self::assertEquals('float', $anonymizerRegistry->getAnonymizerMetadata('float')->id());

self::assertEquals('DbToolsBundle\PackTest\Anonymizer\MyAnonymizer', $anonymizerRegistry->getAnonymizerClass('test.my-anonymizer'));
self::assertEquals('test.my-anonymizer', $anonymizerRegistry->getAnonymizerMetadata('test.my-anonymizer')->id());
} finally {
$this->composerProjectRemove();
}
Expand All @@ -34,10 +37,10 @@ public function testAnonymizerRegistryWithoutTestPack(): void
{
$anonymizerRegistry = new AnonymizerRegistry();

self::assertEquals('MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Core\FloatAnonymizer', $anonymizerRegistry->get('float'));
self::assertEquals('MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Core\FloatAnonymizer', $anonymizerRegistry->getAnonymizerClass('float'));

self::expectExceptionMessageMatches("@Can't find Anonymizer@");
$anonymizerRegistry->get('test.my_anonymizer');
$anonymizerRegistry->getAnonymizerClass('test.my_anonymizer');
}

private function composerProjectRemove(): void
Expand Down