Skip to content

Commit c969875

Browse files
committed
allow to switch to stack hydrator
1 parent 8bdaf37 commit c969875

9 files changed

Lines changed: 450 additions & 226 deletions

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
],
2020
"require": {
2121
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
22-
"patchlevel/event-sourcing": "^3.18.0",
23-
"patchlevel/hydrator": "^1.18.0",
22+
"patchlevel/event-sourcing": "^3.19.1",
23+
"patchlevel/hydrator": "^1.21.2",
2424
"symfony/cache": "^6.4.0 || ^7.0.0 || ^8.0.0",
2525
"symfony/config": "^6.4.0 || ^7.0.0 || ^8.0.0",
2626
"symfony/console": "^6.4.1 || ^7.0.1 || ^8.0.0",

composer.lock

Lines changed: 209 additions & 195 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/DependencyInjection/Configuration.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Patchlevel\EventSourcingBundle\DependencyInjection;
66

77
use Patchlevel\EventSourcing\Repository\AggregateOutdated;
8+
use Patchlevel\Hydrator\Extension\Cryptography\Cipher\OpensslCipherKeyFactory;
89
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
910
use Symfony\Component\Config\Definition\ConfigurationInterface;
1011
use Throwable;
@@ -87,6 +88,17 @@
8788
* },
8889
* clock: array{freeze: ?string, service: ?string},
8990
* aggregate_handlers: array{enabled: bool, bus: string|null},
91+
* hydrator: array{
92+
* enabled: bool,
93+
* default_lazy: bool,
94+
* cryptography: array{
95+
* enabled: bool,
96+
* algorithm: string,
97+
* },
98+
* lifecycle: array{
99+
* enabled: bool,
100+
* },
101+
* },
90102
* }
91103
*/
92104
final class Configuration implements ConfigurationInterface
@@ -361,6 +373,25 @@ public function getConfigTreeBuilder(): TreeBuilder
361373
)
362374
->end()
363375

376+
->arrayNode('hydrator')
377+
->canBeEnabled()
378+
->addDefaultsIfNotSet()
379+
->children()
380+
->booleanNode('default_lazy')->defaultFalse()->end()
381+
->arrayNode('cryptography')
382+
->canBeEnabled()
383+
->addDefaultsIfNotSet()
384+
->children()
385+
->scalarNode('algorithm')->defaultValue(OpensslCipherKeyFactory::DEFAULT_METHOD)->end()
386+
->end()
387+
->end()
388+
->arrayNode('lifecycle')
389+
->canBeEnabled()
390+
->addDefaultsIfNotSet()
391+
->end()
392+
->end()
393+
->end()
394+
364395
->end();
365396
// @codingStandardsIgnoreEnd
366397

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcingBundle\DependencyInjection;
6+
7+
use Patchlevel\Hydrator\StackHydratorBuilder;
8+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
9+
use Symfony\Component\DependencyInjection\ContainerBuilder;
10+
use Symfony\Component\DependencyInjection\Reference;
11+
12+
use function array_keys;
13+
14+
/** @internal */
15+
final class HydratorCompilerPass implements CompilerPassInterface
16+
{
17+
public function process(ContainerBuilder $container): void
18+
{
19+
if (!$container->has(StackHydratorBuilder::class)) {
20+
return;
21+
}
22+
23+
$builder = $container->getDefinition(StackHydratorBuilder::class);
24+
$extensions = $container->findTaggedServiceIds('event_sourcing.hydrator.extension');
25+
26+
foreach (array_keys($extensions) as $subscriberServiceName) {
27+
$builder->addMethodCall('useExtension', [new Reference($subscriberServiceName)]);
28+
}
29+
}
30+
}

src/DependencyInjection/PatchlevelEventSourcingExtension.php

Lines changed: 90 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
use Patchlevel\EventSourcing\Console\Command\WatchCommand;
4646
use Patchlevel\EventSourcing\Console\DoctrineHelper;
4747
use Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore;
48+
use Patchlevel\EventSourcing\Cryptography\ExtensionDoctrineCipherKeyStore;
4849
use Patchlevel\EventSourcing\EventBus\AttributeListenerProvider;
4950
use Patchlevel\EventSourcing\EventBus\Consumer;
5051
use Patchlevel\EventSourcing\EventBus\DefaultConsumer;
@@ -126,6 +127,7 @@
126127
use Patchlevel\EventSourcingBundle\DataCollector\MessageCollectorEventBus;
127128
use Patchlevel\EventSourcingBundle\Doctrine\DbalConnectionFactory;
128129
use Patchlevel\EventSourcingBundle\EventBus\SymfonyEventBus;
130+
use Patchlevel\EventSourcingBundle\Normalizer\SymfonyExtension;
129131
use Patchlevel\EventSourcingBundle\Normalizer\SymfonyGuesser;
130132
use Patchlevel\EventSourcingBundle\QueryBus\SymfonyQueryBus;
131133
use Patchlevel\EventSourcingBundle\RequestListener\AutoSetupListener;
@@ -134,20 +136,28 @@
134136
use Patchlevel\EventSourcingBundle\Subscription\ResetServicesListener;
135137
use Patchlevel\EventSourcingBundle\Subscription\StaticInMemorySubscriptionStoreFactory;
136138
use Patchlevel\EventSourcingBundle\ValueResolver\AggregateRootIdValueResolver;
139+
use Patchlevel\Hydrator\CoreExtension;
137140
use Patchlevel\Hydrator\Cryptography\Cipher\Cipher;
138141
use Patchlevel\Hydrator\Cryptography\Cipher\CipherKeyFactory;
139142
use Patchlevel\Hydrator\Cryptography\Cipher\OpensslCipher;
140143
use Patchlevel\Hydrator\Cryptography\Cipher\OpensslCipherKeyFactory;
141144
use Patchlevel\Hydrator\Cryptography\PayloadCryptographer;
142145
use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;
143146
use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore;
147+
use Patchlevel\Hydrator\Extension as HydratorExtension;
148+
use Patchlevel\Hydrator\Extension\Cryptography\BaseCryptographer;
149+
use Patchlevel\Hydrator\Extension\Cryptography\Cryptographer;
150+
use Patchlevel\Hydrator\Extension\Cryptography\CryptographyExtension;
151+
use Patchlevel\Hydrator\Extension\Lifecycle\LifecycleExtension;
144152
use Patchlevel\Hydrator\Guesser\BuiltInGuesser;
145153
use Patchlevel\Hydrator\Guesser\ChainGuesser;
146154
use Patchlevel\Hydrator\Guesser\Guesser;
147155
use Patchlevel\Hydrator\Hydrator;
148156
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
149157
use Patchlevel\Hydrator\Metadata\MetadataFactory;
150158
use Patchlevel\Hydrator\MetadataHydrator;
159+
use Patchlevel\Hydrator\StackHydrator;
160+
use Patchlevel\Hydrator\StackHydratorBuilder;
151161
use Patchlevel\Worker\Event\WorkerRunningEvent;
152162
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
153163
use Symfony\Component\DependencyInjection\ChildDefinition;
@@ -156,7 +166,6 @@
156166
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
157167
use Symfony\Component\DependencyInjection\Extension\Extension;
158168
use Symfony\Component\DependencyInjection\Reference;
159-
160169
use function class_exists;
161170
use function sprintf;
162171

@@ -177,7 +186,7 @@ public function load(array $configs, ContainerBuilder $container): void
177186
return;
178187
}
179188

180-
$this->configureHydrator($container);
189+
$this->configureHydrator($config, $container);
181190
$this->configureUpcaster($container);
182191
$this->configureSerializer($config, $container);
183192
$this->configureMessageDecorator($container);
@@ -622,38 +631,93 @@ static function (ChildDefinition $definition): void {
622631
]);
623632
}
624633

625-
private function configureHydrator(ContainerBuilder $container): void
634+
/** @param Config $config */
635+
private function configureHydrator(array $config, ContainerBuilder $container): void
626636
{
627-
$container->register(ChainGuesser::class)
628-
->setArguments([new TaggedIteratorArgument('event_sourcing.hydrator.guesser')]);
637+
if (!$config['hydrator']['enabled']) { // legacy MetadataHydrator
638+
$container->register(ChainGuesser::class)
639+
->setArguments([new TaggedIteratorArgument('event_sourcing.hydrator.guesser')]);
629640

630-
$container->register(BuiltInGuesser::class)
631-
->addTag('event_sourcing.hydrator.guesser', ['priority' => -100]);
641+
$container->register(BuiltInGuesser::class)
642+
->addTag('event_sourcing.hydrator.guesser', ['priority' => -64]);
632643

633-
$container->register(SymfonyGuesser::class)
634-
->addTag('event_sourcing.hydrator.guesser', ['priority' => -50]);
644+
$container->register(SymfonyGuesser::class)
645+
->addTag('event_sourcing.hydrator.guesser', ['priority' => -32]);
635646

636-
$container->registerForAutoconfiguration(Guesser::class)
637-
->addTag('event_sourcing.hydrator.guesser');
647+
$container->registerForAutoconfiguration(Guesser::class)
648+
->addTag('event_sourcing.hydrator.guesser');
638649

639-
$container->register(AttributeMetadataFactory::class)
640-
->setArguments([
641-
null,
642-
new Reference(ChainGuesser::class),
643-
]);
650+
$container->register(AttributeMetadataFactory::class)
651+
->setArguments([
652+
null,
653+
new Reference(ChainGuesser::class),
654+
]);
644655

645-
$container->setAlias(MetadataFactory::class, AttributeMetadataFactory::class);
656+
$container->setAlias(MetadataFactory::class, AttributeMetadataFactory::class);
646657

647-
$container->register(MetadataHydrator::class)
648-
->setArguments([
649-
new Reference(MetadataFactory::class),
650-
new Reference(
651-
PayloadCryptographer::class,
652-
ContainerInterface::IGNORE_ON_INVALID_REFERENCE,
653-
),
654-
]);
658+
$container->register(MetadataHydrator::class)
659+
->setArguments([
660+
new Reference(MetadataFactory::class),
661+
new Reference(
662+
PayloadCryptographer::class,
663+
ContainerInterface::IGNORE_ON_INVALID_REFERENCE,
664+
),
665+
]);
666+
667+
$container->setAlias(Hydrator::class, MetadataHydrator::class);
668+
669+
return;
670+
}
671+
672+
$container->registerForAutoconfiguration(HydratorExtension::class)
673+
->addTag('event_sourcing.hydrator.extension');
674+
675+
$container->register(CoreExtension::class)
676+
->addTag('event_sourcing.hydrator.extension');
677+
678+
$container->register(SymfonyExtension::class)
679+
->addTag('event_sourcing.hydrator.extension');
680+
681+
if ($config['hydrator']['cryptography']['enabled']) {
682+
$container->register(ExtensionDoctrineCipherKeyStore::class)
683+
->setArguments([new Reference('event_sourcing.dbal_connection')])
684+
->addTag('event_sourcing.doctrine_schema_configurator');
685+
686+
$container->setAlias(
687+
\Patchlevel\Hydrator\Extension\Cryptography\Store\CipherKeyStore::class,
688+
ExtensionDoctrineCipherKeyStore::class,
689+
);
690+
691+
$container->register(BaseCryptographer::class)
692+
->setFactory([BaseCryptographer::class, 'createWithOpenssl'])
693+
->setArguments([
694+
new Reference(\Patchlevel\Hydrator\Extension\Cryptography\Store\CipherKeyStore::class),
695+
$config['hydrator']['cryptography']['algorithm'],
696+
]);
697+
698+
$container->setAlias(Cryptographer::class, BaseCryptographer::class);
699+
700+
$container->register(CryptographyExtension::class)
701+
->setArguments([
702+
new Reference(Cryptographer::class),
703+
new Reference(PayloadCryptographer::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
704+
true,
705+
])
706+
->addTag('event_sourcing.hydrator.extension');
707+
}
708+
709+
if ($config['hydrator']['lifecycle']['enabled']) {
710+
$container->register(LifecycleExtension::class)
711+
->addTag('event_sourcing.hydrator.extension');
712+
}
713+
714+
$builder = $container->register(StackHydratorBuilder::class);
715+
$builder->addMethodCall('enableDefaultLazy', [$config['hydrator']['default_lazy']]);
716+
717+
$container->register(StackHydrator::class)
718+
->setFactory([new Reference(StackHydratorBuilder::class), 'build']);
655719

656-
$container->setAlias(Hydrator::class, MetadataHydrator::class);
720+
$container->setAlias(Hydrator::class, StackHydrator::class);
657721
}
658722

659723
private function configureUpcaster(ContainerBuilder $container): void
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcingBundle\Normalizer;
6+
7+
use Patchlevel\Hydrator\Extension;
8+
use Patchlevel\Hydrator\StackHydratorBuilder;
9+
10+
class SymfonyExtension implements Extension
11+
{
12+
public function configure(StackHydratorBuilder $builder): void
13+
{
14+
$builder->addGuesser(new SymfonyGuesser(), -32);
15+
}
16+
}

src/PatchlevelEventSourcingBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Patchlevel\EventSourcingBundle\DependencyInjection\CommandHandlerCompilerPass;
88
use Patchlevel\EventSourcingBundle\DependencyInjection\DoctrineCleanupCompilerPass;
99
use Patchlevel\EventSourcingBundle\DependencyInjection\HandlerServiceLocatorCompilerPass;
10+
use Patchlevel\EventSourcingBundle\DependencyInjection\HydratorCompilerPass;
1011
use Patchlevel\EventSourcingBundle\DependencyInjection\QueryHandlerCompilerPass;
1112
use Patchlevel\EventSourcingBundle\DependencyInjection\RepositoryCompilerPass;
1213
use Patchlevel\EventSourcingBundle\DependencyInjection\SubscriberGuardCompilePass;
@@ -25,5 +26,6 @@ public function build(ContainerBuilder $container): void
2526
$container->addCompilerPass(new HandlerServiceLocatorCompilerPass(), priority: -100);
2627
$container->addCompilerPass(new TranslatorCompilerPass());
2728
$container->addCompilerPass(new DoctrineCleanupCompilerPass());
29+
$container->addCompilerPass(new HydratorCompilerPass());
2830
}
2931
}

tests/Fixtures/DummyExtension.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fixtures;
6+
7+
use Patchlevel\Hydrator\Extension;
8+
use Patchlevel\Hydrator\StackHydratorBuilder;
9+
10+
final readonly class DummyExtension implements Extension
11+
{
12+
public function configure(StackHydratorBuilder $builder): void
13+
{
14+
// do nothing
15+
}
16+
}

0 commit comments

Comments
 (0)