hank21014 / synth
A lightweight PHP utility for data dehydration and hydration.
Requires
- php: >=8.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- pestphp/pest: ^1.23
README
💧 PHP Synth: Seamless Data Hydration
Synth is a lightweight PHP utility designed to bridge the gap between complex objects and serializable arrays. By using custom "Synth," you can dehydrate objects into structured arrays and hydrate them back into their original state with type safety and zero fuss.
🌟 Why Synth?
-
Architecture Friendly: Keep your Entities/DTOs "lean" by moving transformation logic into dedicated Synth classes.
-
Custom Mapping: Decouple your internal object structure from your external data schema (API, Cache, or Queue).
🛠️ Installation
Install the package via Composer:
composer require hank21014/synth
🚀 Quick Start
1. Define your Entity
A plain PHP class. No traits or inheritance required.
namespace App; class User { public function __construct(public string $name, public array $friends = []) {} }
2. Create a Synth
Implement the Synth interface to define how your object should be transformed.
namespace App\Synths; use App\User; use Hank21014\Synth\Contracts\Synth; use Hank21014\Synth\SynthManager; /** * @implements Synth<User> */ class UserSynth implements Synth { #[\Override] public static function synthesizedClass(): string { return User::class; } #[\Override] public function dehydrate(SynthManager $synth, $subject): array { return [ '<name>' => $subject->name, '<friends>' => $synth->dehydrate($subject->friends) ]; } #[\Override] public function hydrate(SynthManager $synth, array $payload) { /** * The $payload matches the array structure returned by your dehydrate method. */ return new User( name: $payload['<name>'], friends: $synth->hydrate($payload['<friends>']) ); } }
3. Usage
Register your Synth and start transforming data.
use App\User; use App\Synths\UserSynth; use Hank21014\Synth\SynthManager; $manager = new SynthManager(); // Register the synth with a unique type identifier $manager->add('user', UserSynth::class); $user = new User('hank'); // --- Dehydrate --- $dehydrated = $manager->dehydrate($user); /** * Result $dehydrated: * Notice how each User object is recursively wrapped with the [SYNTH] flag. * This metadata allows the manager to know exactly which Synth to use for each level. */ /* [ "[SYNTH]" => true, "type" => "user", "payload" => [ "<name>" => "john", "<friends>" => [ [ "[SYNTH]" => true, "type" => "user", "payload" => [ "<name>" => "hank", "<friends>" => [ [ "[SYNTH]" => true, "type" => "user", "payload" => [ "<name>" => "joy", "<friends>" => [] ] ] ] ] ] ] ] ] */ // --- Hydrate --- $hydrated = $manager->hydrate($dehydrated); // $hydrated is now a fresh instance of App\User
💡 Key Concepts
Dehydration
Taking a "living" object and drying it out into a portable array format (perfect for JSON or Database storage).
Hydration
Taking that array data and "watering" it back into a PHP object.
The Metadata Wrapper
Synth automatically wraps your payload with a [SYNTH] flag and a type key. This allows the SynthManager to automatically know which class to instantiate during hydration.
Tip
Balance Uniqueness vs. Length
When customizing the identity key, choose a value that is unique enough to avoid data collisions but short enough to keep your dehydrated payloads efficient.
Primitive Handling
Synth handles non-object types via a built-in PrimitiveSynth. This ensures that your basic data structures remain intact while objects are being transformed.
| Type | Strategy | Detail |
|---|---|---|
| Scalar/Null | Pass-through | Values like int, float, string, bool or null are returned without modification. |
| Standard Array | Recursive | Every element inside the array is checked and processed by the SynthManager. |
| Synthesized Array | Delegated | Arrays containing the [SYNTH] flag are passed to their respective specialized Synths. |
⚙️ Advanced Configuration
Customizing the Identity Key
use Hank21014\Synth\SynthesizableArray; // Must be called before any hydration/dehydration occurs SynthesizableArray::identityBy('<FOO>');
Configuring Global Synths
Synth comes with built-in support for common PHP types:
| Synth Class | Target Type |
|---|---|
| \Hank21014\Synth\Synths\DateTimeSynth | \DateTime |
| \Hank21014\Synth\Synths\DateTimeImmutableSynth | \DateTimeImmutable |
To disable all global synths:
use Hank21014\Synth\Synths; Synths::withoutGlobalSynths();
To customize global synths:
use Hank21014\Synth\Synths; Synths::useGlobalSynths([...]);
Important
Global configuration must be performed before the SynthManager is initialized. Calling these methods after instantiation will have no effect on existing manager instances.