{"id":19737205,"url":"https://github.com/patchlevel/hydrator","last_synced_at":"2026-04-06T19:00:39.366Z","repository":{"id":66815497,"uuid":"588658691","full_name":"patchlevel/hydrator","owner":"patchlevel","description":"This library enables seamless hydration of objects to arrays—and back again. It’s optimized for both developer experience (DX) and performance.","archived":false,"fork":false,"pushed_at":"2026-04-03T15:45:06.000Z","size":1385,"stargazers_count":16,"open_issues_count":7,"forks_count":3,"subscribers_count":3,"default_branch":"1.21.x","last_synced_at":"2026-04-03T19:09:42.105Z","etag":null,"topics":["ddd","dto","serialization"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/patchlevel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-01-13T17:03:37.000Z","updated_at":"2026-04-03T15:42:27.000Z","dependencies_parsed_at":"2025-12-30T14:10:57.493Z","dependency_job_id":"48c2894e-cf1a-45c0-91eb-3f6838ffcc4c","html_url":"https://github.com/patchlevel/hydrator","commit_stats":{"total_commits":18,"total_committers":2,"mean_commits":9.0,"dds":0.2222222222222222,"last_synced_commit":"70fd201ddb96437b8140bae0f79d39b33fdb1843"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/patchlevel/hydrator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patchlevel%2Fhydrator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patchlevel%2Fhydrator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patchlevel%2Fhydrator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patchlevel%2Fhydrator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/patchlevel","download_url":"https://codeload.github.com/patchlevel/hydrator/tar.gz/refs/heads/1.21.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patchlevel%2Fhydrator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31485516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddd","dto","serialization"],"created_at":"2024-11-12T01:09:58.822Z","updated_at":"2026-04-06T19:00:39.359Z","avatar_url":"https://github.com/patchlevel.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Mutation testing badge](https://img.shields.io/endpoint?style=flat\u0026url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fpatchlevel%2Fhydrator%2F1.13.x)](https://dashboard.stryker-mutator.io/reports/github.com/patchlevel/hydrator/1.13.x)\n[![Latest Stable Version](https://poser.pugx.org/patchlevel/hydrator/v)](//packagist.org/packages/patchlevel/hydrator)\n[![License](https://poser.pugx.org/patchlevel/hydrator/license)](//packagist.org/packages/patchlevel/hydrator)\n\n# Hydrator\n\nThis library enables seamless hydration of objects to arrays—and back again.\nIt’s optimized for both developer experience (DX) and performance.\n\nThe library is a core component of [patchlevel/event-sourcing](ttps://github.com/patchlevel/event-sourcing),\nwhere it powers the storage and retrieval of thousands of objects.\n\nHydration is handled through normalizers, especially for complex data types.\nThe system can automatically determine the appropriate normalizer based on the data type and annotations.\n\nIn most cases, no manual configuration is needed.\nAnd if customization is required, it can be done easily using attributes.\n\n## Installation\n\n```bash\ncomposer require patchlevel/hydrator\n```\n\n## Usage\n\nTo use the hydrator you just have to create an instance of it.\n\n```php\nuse Patchlevel\\Hydrator\\MetadataHydrator;\n\n$hydrator = MetadataHydrator::create();\n```\n\nAfter that you can hydrate any classes or objects. Also `final`, `readonly` classes with `property promotion`.\nThese objects or classes can have complex structures in the form of value objects, DTOs or collections.\nOr all nested together. Here's an example:\n\n```php\nfinal readonly class ProfileCreated \n{\n    /**\n     * @param list\u003cSkill\u003e $skills\n     */\n    public function __construct(\n        public int $id,\n        public string $name,\n        public Role $role, // enum,\n        public array $skills, // array of objects\n        public DateTimeImmutable $createdAt,\n    ) {\n    }\n}\n```\n\n### Extract Data\n\nTo convert objects into serializable arrays, you can use the `extract` method of the hydrator.\n\n```php\n$event = new ProfileCreated(\n    1, \n    'patchlevel',\n    Role::Admin,\n    [new Skill('php', 10), new Skill('event-sourcing', 10)],\n    new DateTimeImmutable('2023-10-01 12:00:00'),\n);\n\n$data = $hydrator-\u003eextract($event);\n```\n\nThe result looks like this:\n\n```php\n[\n  'id' =\u003e 1,\n  'name' =\u003e 'patchlevel',\n  'role' =\u003e 'admin',\n  'skills' =\u003e [\n    [\n      'name' =\u003e 'php',\n      'level' =\u003e 10,\n    ],\n    [\n      'name' =\u003e 'event-sourcing',\n      'level' =\u003e 10,\n    ],\n  ],\n  'createdAt' =\u003e '2023-10-01T12:00:00+00:00',\n]\n```\n\nWe could now convert the whole thing into JSON using `json_encode`.\n\n### Hydrate Object\n\nThe process can also be reversed. Hydrate an array back into an object. \nTo do this, we need to specify the class that should be created \nand the data that should then be written into it.\n\n```php\n$event = $hydrator-\u003ehydrate(\n    ProfileCreated::class,\n    [\n      'id' =\u003e 1,\n      'name' =\u003e 'patchlevel',\n      'role' =\u003e 'admin',\n      'skills' =\u003e [\n        [\n          'name' =\u003e 'php',\n          'level' =\u003e 10,\n        ],\n        [\n          'name' =\u003e 'event-sourcing',\n          'level' =\u003e 10,\n        ],\n      ],\n      'createdAt' =\u003e '2023-10-01T12:00:00+00:00',\n    ]\n);\n\n$oldEvent == $event // true\n```\n\n\u003e [!WARNING]\n\u003e It is important to know that the constructor is not called!\n\n#### Object to populate\n\nIf you want to hydrate an object that already exists, you can specify the object to populate.\nThis is useful if you want to update an existing object.\n\n```php\n$dto = new Dto();\n\n$event = $hydrator-\u003ehydrate(\n    $dto::class,\n    [\n      'name' =\u003e 'patchlevel',\n    ], [\n        MetadataHydrator::OBJECT_TO_POPULATE =\u003e $dto,\n    ],\n);\n```\n\n### Normalizer\n\nFor more complex structures, i.e. non-scalar data types, we use normalizers.\nWe have some built-in normalizers for standard structures such as objects, arrays, enums, datetime etc. \nYou can find the full list below.\n\nThe library attempts to independently determine which normalizers should be used.\nFor this purpose, normalizers of this order are determined:\n\n1) Does the class property have a normalizer as an attribute? Use this.\n2) The data type of the property is determined.\n   1) If it is an array shape, use the ArrayShapeNormalizer (recursive).\n   2) If it is a collection, use the ArrayNormalizer (recursive).\n   3) If it is an object, then look for a normalizer as attribute on the class or interfaces and use this.\n   4) If it is an object, then guess the normalizer based on the object. Fallback to the object normalizer.\n\nThe normalizer is only determined once because it is cached in the metadata.\nBelow you will find the list of all normalizers and how to set them manually or explicitly.\n\n#### Array\n\nIf you have a collection (array, iterable, list) with a data type that needs to be normalized, \nyou can use the ArrayNormalizer and pass it the required normalizer.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\ArrayNormalizer;\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeImmutableNormalizer;\n\nfinal class DTO \n{\n    /**\n     * @var list\u003cDateTimeImmutable\u003e\n     */\n    #[ArrayNormalizer]\n    public array $dates;\n    \n    #[ArrayNormalizer(new DateTimeImmutableNormalizer())]\n    public array $explicitDates;\n}\n```\n\n\u003e [!NOTE]\n\u003e The keys from the arrays are taken over here.\n\n#### ArrayShape\n\nIf you have an array with a specific shape, you can use the `ArrayShapeNormalizer`.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\ArrayShapeNormalizer;\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeImmutableNormalizer;\n\nfinal class DTO \n{\n    /**\n     * @var array{\n     *     date: DateTimeImmutable,\n     *     otherField: string\n     * }\n     */\n    #[ArrayShapeNormalizer]\n    public array $meta;\n    \n    #[ArrayShapeNormalizer(['date' =\u003e new DateTimeImmutableNormalizer()])]\n    public array $explicitMeta;\n}\n```\n\n#### DateTimeImmutable\n\nWith the `DateTimeImmutable` Normalizer, as the name suggests,\nyou can convert DateTimeImmutable objects to a String and back again.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeImmutableNormalizer;\n\nfinal class DTO \n{\n    #[DateTimeImmutableNormalizer]\n    public DateTimeImmutable $date;\n}\n```\n\nYou can also define the format. Either describe it yourself as a string or use one of the existing constants.\nThe default is `DateTimeImmutable::ATOM`.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeImmutableNormalizer;\n\nfinal class DTO \n{\n    #[DateTimeImmutableNormalizer(format: DateTimeImmutable::RFC3339_EXTENDED)]\n    public DateTimeImmutable $date;\n}\n```\n\n\u003e [!NOTE]\n\u003e You can read about how the format is structured in the [php docs](https://www.php.net/manual/de/datetime.format.php).\n\n#### DateTime\n\nThe `DateTime` Normalizer works exactly like the DateTimeNormalizer. Only for DateTime objects.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeNormalizer;\n\nfinal class DTO \n{\n    #[DateTimeNormalizer]\n    public DateTime $date;\n}\n```\n\nYou can also specify the format here. The default is `DateTime::ATOM`.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeNormalizer;\n\nfinal class DTO \n{\n    #[DateTimeNormalizer(format: DateTime::RFC3339_EXTENDED)]\n    public DateTime $date;\n}\n```\n\n\u003e [!NOTE]\n\u003e You can read about how the format is structured in the [php docs](https://www.php.net/manual/de/datetime.format.php).\n\n#### DateTimeZone\n\nTo normalize a `DateTimeZone` one can use the `DateTimeZoneNormalizer`.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\DateTimeZoneNormalizer;\n\nfinal class DTO\n{\n    #[DateTimeZoneNormalizer]\n    public DateTimeZone $timeZone;\n}\n```\n\n#### Enum\n\nBacked enums can also be normalized.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\EnumNormalizer;\n\nfinal class DTO\n{\n    #[EnumNormalizer]\n    public Status $status;\n}\n```\n\n#### Object\n\nIf you have a complex object that you want to normalize, you can use the `ObjectNormalizer`.\nThis use the hydrator internally to normalize the object.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\ObjectNormalizer;\n\nfinal class DTO\n{\n    #[ObjectNormalizer]\n    public AnohterDto $anotherDto;\n    \n    #[ObjectNormalizer(AnohterDto::class)]\n    public object $object;\n}\n\nfinal class AnotherDto\n{\n    #[EnumNormalizer]\n    public Status $status;\n}\n```\n\n\u003e [!WARNING]\n\u003e Circular references are not supported and will result in an exception.\n\n#### ObjectMap\n\nYou can also use the `ObjectMapNormalizer` if you have either inheritance or a union type.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\ObjectMapNormalizer;\n\n// inheritance\n#[ObjectMapNormalizer([\n    ContentBlock::class =\u003e 'content',\n    CodeBlock::class =\u003e 'code'\n])]\ninterface Block\n{\n}\n\n// union type\nclass ContentBlock implements Block {\n    #[ObjectMapNormalizer([\n       ContentA::class =\u003e 'content',\n       ContentB::class =\u003e 'code'\n    ])]\n    public ContentA|ContentB $content;\n}\n```\n\n\u003e [!NOTE]\n\u003e Auto detection of the type is not possible. You have to specify the type yourself.\n\n#### Inline\n\nThe `InlineNormalizer` allows you to define normalization and denormalization logic directly via closures.\nThis is useful for simple value objects or when you don't want to create a separate normalizer class.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\InlineNormalizer;\n\n#[InlineNormalizer(\n    normalize: static function (self $object): string {\n        return $object-\u003etoString();\n    },\n    denormalize: static function (string $value): self {\n        return new self($value);\n    },\n)]\nfinal class Email\n{\n    public function __construct(\n        private string $value\n    ) {}\n\n    public function toString(): string\n    {\n        return $this-\u003evalue;\n    }\n}\n```\n\n\u003e [!NOTE] \n\u003e Closures in attributes can only be used since PHP 8.5, therefore this normalizer can only be used with PHP 8.5.\n\n\u003e [!TIP]\n\u003e If you want to handle `null` values within your closures, you can set the `passNull` option to `true`.\n\u003e By default, `null` values are not passed to the closures and are returned as `null` directly.\n\n### Custom Normalizer\n\nSince we only offer normalizers for PHP native things,\nyou have to write your own normalizers for your own structures, such as value objects.\n\nIn our example we have built a value object that should hold a name.\n\n```php\nfinal class Name\n{\n    private string $value;\n    \n    public function __construct(string $value) \n    {\n        if (strlen($value) \u003c 3) {\n            throw new NameIsToShortException($value);\n        }\n        \n        $this-\u003evalue = $value;\n    }\n    \n    public function toString(): string \n    {\n        return $this-\u003evalue;\n    }\n}\n```\n\nFor this we now need a custom normalizer.\nThis normalizer must implement the `Normalizer` interface.\nFinally, you have to allow the normalizer to be used as an attribute,\nbest to allow it for properties as well as classes.\n\n```php\nuse Patchlevel\\Hydrator\\Normalizer\\Normalizer;\nuse Patchlevel\\Hydrator\\Normalizer\\InvalidArgument;\n\n#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS)]\nclass NameNormalizer implements Normalizer\n{\n    public function normalize(mixed $value): string\n    {\n        if (!$value instanceof Name) {\n            throw InvalidArgument::withWrongType(Name::class, $value);\n        }\n\n        return $value-\u003etoString();\n    }\n\n    public function denormalize(mixed $value): ?Name\n    {\n        if ($value === null) {\n            return null;\n        }\n\n        if (!is_string($value)) {\n            throw InvalidArgument::withWrongType('string', $value);\n        }\n\n        return new Name($value);\n    }\n}\n```\n\nNow we can also use the normalizer directly.\n\n```php\nfinal class DTO\n{\n    #[NameNormalizer]\n    public Name $name\n}\n```\n\n### Define normalizer on class level\n\nInstead of specifying the normalizer on each property, you can also set the normalizer on the class or on an interface.\n\n```php\n#[NameNormalizer]\nfinal class Name\n{\n    // ... same as before\n}\n```\n\n### Guess normalizer\n\nIt's also possible to write your own guesser that finds the correct normalizer based on the object. \nThis is useful if, for example, setting the normalizer on the class or interface isn't possible.\n\n```php\nuse Patchlevel\\Hydrator\\Guesser\\Guesser;\nuse Symfony\\Component\\TypeInfo\\Type\\ObjectType;\n\nclass NameGuesser implements Guesser\n{\n    public function guess(ObjectType $object): Normalizer|null\n    {\n        return match($object-\u003egetClassName()) {\n            case Name::class =\u003e new NameNormalizer(),\n            default =\u003e null,\n        };\n    }\n}\n```\n\nTo use this Guesser, you must specify it when creating the Hydrator:\n\n```php\nuse Patchlevel\\Hydrator\\MetadataHydrator;\n\n$hydrator = MetadataHydrator::create([new NameGuesser()]);\n```\n\n\u003e [!NOTE]\n\u003e The guessers are queried in order, and the first match is returned. Finally, our built-in guesser is executed.\n\n### Normalized Name\n\nBy default, the property name is used to name the field in the normalized result.\nThis can be customized with the `NormalizedName` attribute.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\NormalizedName;\n\nfinal class DTO\n{\n    #[NormalizedName('profile_name')]\n    public string $name\n}\n```\n\nThe whole thing looks like this\n\n```php\n[\n  'profile_name' =\u003e 'David'\n]\n```\n\n\u003e [!TIP]\n\u003e You can also rename properties to events without having a backwards compatibility break by keeping the serialized name.\n\n### Ignore\n\nSometimes it is necessary to exclude properties. You can do that with the `Ignore` attribute. \nThe property is ignored both when extracting and when hydrating.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\Ignore;\n\nreadonly class ProfileCreated \n{\n    public function __construct(\n        public string $id,\n        public string $name,\n        #[Ignore]\n        public string $ignoreMe,\n    ) {\n    }\n}\n```\n\n### Lazy\n\nSince PHP 8.4, it's been possible to lazy-hydrate objects. \nThat is, the actual hydration process occurs when the object is accessed.\nYou can define for each class whether you want it to be lazy by using the `Lazy` attribute.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\Lazy;\n\n#[Lazy]\nreadonly class ProfileCreated \n{\n    public function __construct(\n        public string $id,\n        public string $name,\n    ) {\n    }\n}\n```\n\n\u003e [!NOTE]\n\u003e If you are using a PHP version older than 8.4, the attribute will be ignored.\n\n### Hooks\n\nSometimes you need to do something before extract or after hydrate process.\nFor this we have the `PreExtract` and `PostHydrate` attributes.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\PostHydrate;\nuse Patchlevel\\Hydrator\\Attribute\\PreExtract;\n\nreadonly class Dto \n{    \n    #[PostHydrate]\n    private function postHydrate(): void\n    {\n        // do something\n    }\n    \n    #[PreExtract]\n    private function preExtract(): void\n    {\n        // do something\n    }\n}\n```\n\n### Events\n\nAnother way to intervene in the extract and hydrate process is through events. \nThere are two events: `PostExtract` and `PreHydrate`.\nFor this functionality we use the [symfony/event-dispatcher](https://symfony.com/doc/current/components/event_dispatcher.html).\n\n```php\nuse Patchlevel\\Hydrator\\Cryptography\\PersonalDataPayloadCryptographer;\nuse Patchlevel\\Hydrator\\Cryptography\\Store\\CipherKeyStore;\nuse Patchlevel\\Hydrator\\Metadata\\Event\\EventMetadataFactory;\nuse Patchlevel\\Hydrator\\MetadataHydrator;\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Patchlevel\\Hydrator\\Event\\PostExtract;\nuse Patchlevel\\Hydrator\\Event\\PreHydrate;\n\n$eventDispatcher = new EventDispatcher();\n\n$eventDispatcher-\u003eaddListener(\n    PostExtract::class,\n    static function (PostExtract $event): void {\n        // do something\n    }\n);\n\n$eventDispatcher-\u003eaddListener(\n    PreHydrate::class,\n    static function (PreHydrate $event): void {\n        // do something\n    }\n);\n\n$hydrator = new MetadataHydrator(eventDispatcher: $eventDispatcher);\n```\n\n### Cryptography\n\nThe library also offers the possibility to encrypt and decrypt personal data.\nFor this purpose, a key is created for each subject ID, which is used to encrypt the personal data.\n\n#### DataSubjectId\n\nFirst we need to define what the subject id is.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\DataSubjectId;\n\nfinal class EmailChanged\n{\n    public function __construct(\n        #[DataSubjectId]\n        public readonly string $profileId,\n    ) {\n    }\n}\n```\n\n\u003e [!WARNING]\n\u003e The `DataSubjectId` must be a string. You can use a normalizer to convert it to a string.\n\u003e The Subject ID cannot be personal data.\n\n#### PersonalData\n\nNext, we need to specify which fields we want to encrypt.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\DataSubjectId;\nuse Patchlevel\\Hydrator\\Attribute\\PersonalData;\n\nfinal class DTO \n{\n    public function __construct(\n        #[DataSubjectId]\n        public readonly string $profileId,\n        #[PersonalData]\n        public readonly string|null $email,\n    ) {\n    }\n}\n```\n\nIf the information could not be decrypted, then a fallback value is inserted.\nThe default fallback value is `null`.\nYou can change this by setting the `fallback` parameter.\nIn this case `unknown` is added:\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\PersonalData;\n\nfinal class DTO\n{\n    public function __construct(\n        #[DataSubjectId]\n        public readonly string $profileId,\n        #[PersonalData(fallback: 'unknown')]\n        public readonly string $name,\n    ) {\n    }\n}\n```\n\nYou can also use a callable as a fallback.\n\n```php\nuse Patchlevel\\Hydrator\\Attribute\\DataSubjectId;\nuse Patchlevel\\Hydrator\\Attribute\\PersonalData;\n\nfinal class ProfileCreated\n{\n    public function __construct(\n        #[DataSubjectId]\n        public readonly string $profileId,\n        #[PersonalData(fallback: 'deleted profile')]\n        public readonly string $name,\n        #[PersonalData(fallbackCallable: [self::class, 'anonymizedEmail'])]\n        public readonly string $email,\n    ) {\n    }\n    \n    public static function anonymizedEmail(string $subjectId): string\n    {\n        return sprintf('%s@anno.com', $subjectId);\n    }\n}\n```\n\n\u003e [!TIP]\n\u003e Cryptography is very expensive in terms of performance, \n\u003e you can combine it with lazy to improve performance and only decrypt when you actually access the object.\n\n#### Configure Cryptography\n\nHere we show you how to configure the cryptography.\n\n```php\nuse Patchlevel\\Hydrator\\Cryptography\\PersonalDataPayloadCryptographer;\nuse Patchlevel\\Hydrator\\Cryptography\\Store\\CipherKeyStore;\nuse Patchlevel\\Hydrator\\Metadata\\Event\\EventMetadataFactory;\nuse Patchlevel\\Hydrator\\MetadataHydrator;\n\n$cipherKeyStore = new InMemoryCipherKeyStore();\n$cryptographer = PersonalDataPayloadCryptographer::createWithDefaultSettings($cipherKeyStore);\n$hydrator = new MetadataHydrator(cryptographer: $cryptographer);\n```\n\n\u003e [!WARNING]\n\u003e We recommend to use the `useEncryptedFieldName` option to recognize encrypted fields.\n\u003e This allows data to be encrypted later without big troubles.\n\n#### Cipher Key Store\n\nThe keys must be stored somewhere. For testing purposes, we offer an in-memory implementation.\n\n```php\nuse Patchlevel\\Hydrator\\Cryptography\\Cipher\\CipherKey;\nuse Patchlevel\\Hydrator\\Cryptography\\Store\\InMemoryCipherKeyStore;\n\n$cipherKeyStore = new InMemoryCipherKeyStore();\n\n/** @var CipherKey $cipherKey */\n$cipherKeyStore-\u003estore('foo-id', $cipherKey);\n$cipherKey = $cipherKeyStore-\u003eget('foo-id');\n$cipherKeyStore-\u003eremove('foo-id');\n```\n\nBecause we don't know where you want to store the keys, we don't offer any other implementations.\nYou should use a database or a key store for this. To do this, you have to implement the `CipherKeyStore` interface.\n\n#### Remove personal data\n\nTo remove personal data, you need only remove the key from the store.\n\n```php\n$cipherKeyStore-\u003eremove('foo-id');\n```\n\n\n## Contributing\n\nWe are open to contributions as long as they are in line with\nour [BC-Policy](https://event-sourcing.patchlevel.io/latest/our-backward-compatibility-promise/).\n\nAlso note that the `composer.lock` is always generated with the newest supported PHP version as this is the version our tools run in the CI.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatchlevel%2Fhydrator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpatchlevel%2Fhydrator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatchlevel%2Fhydrator/lists"}