@matarld
@matarld
mtarld
@bakslahHQ
@matarld
@matarld
Representing data structures in a format that can be sent or persisted in order to be reconstructed later
Binary, textual
Construction pattern
Databases, flat files, APIs
Anywhere, interoperable
@matarld
GET /api/cats/1
Accept: application/json
{ "name": "Undercover", "aggressivity": "high" }
serialization
@matarld
POST /api/cats
Content-Type: application/json
{ "name": "Black mustache", "aggressivity": "low" }
deserialization
@matarld
SomeProvider
AnotherProvider
...
GET /api/cats/1
Accept: application/json
Response
@matarld
SomeProvider
DeserializeProvider
...
POST /api/cats
Content-Type: application/json
{ "name": "Black mustache", "aggressivity": "low" }
@matarld
@matarld
Just count mustaches
Capitalize the name
Evaluate aggressivity
Don't keep the id
@matarld
Create "n" default mustaches
Set claws size by aggressivity
Instantiate a new cat
@matarld
class Cat
{
#[Ignore]
public int $id;
#[SerializedName('name')]
private string $nickname;
/** @var Mustache[] */
public array $mustaches;
public function getNickname(): string
{
return $this->name;
}
}
@matarld
class Cat
{
#[Ignore]
public int $id;
#[SerializedName('name')]
private string $nickname;
/** @var Mustache[] */
public array $mustaches;
public function getNickname(): string
{
return $this->nickname;
}
}
Can I read $id?
No, you can ignore it.
@matarld
class Cat
{
#[Ignore]
public int $id;
#[SerializedName('name')]
private string $nickname;
/** @var Mustache[] */
public array $mustaches;
public function getNickname(): string
{
return $this->nickname;
}
}
Can I read $nickname?
Yep, but use the getter to read it.
Change "nickname" to "name".
Do anything with the key?
Nothing in particular.
Do anything with the value?
@matarld
class Cat
{
#[Ignore]
public int $id;
#[SerializedName('name')]
private string $nickname;
/** @var Mustache[] */
public array $mustaches;
public function getNickname(): string
{
return $this->nickname;
}
}
Can I read $mustaches?
Yes, you can.
Nothing in particular.
Do anything with the key?
Check if it's an object collection. If so, serialize objects of that collection as well.
Do anything with the value?
@matarld
class CatMustacheNormalizer implements NormalizerInterface
{
public function normalize($object, $format, $context): array
{
$normalized = $this->normalizer->normalize($object, $format, $context);
$normalized['mustaches'] = count($object->mustaches);
return $normalized;
}
// ...
}
@matarld
Cache
@matarld
( )
@matarld
Metadata is data
data, but about data
@matarld
@matarld
@matarld
@matarld
@matarld
data
/** @temlate T of string */
final class Rain {
/** @return list<T> */
public function content(): array {...}
}
/** @var Rain<'cat'|'dog'> $*/
$englishRain = new Rain();
$fallingStuff = $englishRain->content();
type
no idea
array of something
list of something
list of cats and dogs
Possible JSON
[
[0.7]
]
{
"c": 1
}
true
{
"a": 1
}
[
3.14
]
[
"pi"
]
[ true ]
[
3.14
]
[
"dog"
]
[ "cat" ]
[
"dog"
]
[
"dog"
]
@matarld
interface PropertyTypeExtractorInterface
{
/**
* @return Type[]|null
*/
public function getTypes(...): ?array;
}
@matarld
interface PropertyTypeExtractorInterface
{
/**
* @return Type[]|null
*/
public function getTypes(...): ?array;
}
only properties
no union/intersection difference
no list/dict difference
no generics
...
@matarld
/**
* @template T of Animal
*/
final class FlyingAnimal
{
public function __construct(
public object $type,
private int $speed,
) {
}
/**
* @return class-string<T>
*/
public function getTypeClass(): string
{
return $this->type::class;
}
public function setSpeed(int $speed): void
{
$this->speed = $speed;
}
}
PropertyInfo
@matarld
ft. @Korbeil_
@matarld
/**
* @template T of Animal
*/
final class FlyingAnimal
{
public function __construct(
public object $type,
private int $speed,
) {
}
/**
* @return class-string<T>
*/
public function getTypeClass(): string
{
return $this->type::class;
}
public function setSpeed(int $speed): void
{
$this->speed = $speed;
}
}
TypeInfo
@matarld
PropertyInfo
TypeInfo
@matarld
object
array
format
huge collection of objects
super big array
@matarld
format
huge collection of objects
Streaming!
@matarld
@matarld
class Cat
{
public string $name;
public bool $flying;
}
PHP
Possible JSON
{ "name": "any_string", "flying": true|false }
any string
true/false
@matarld
[ { "name": "
[ { "name": "Undercover
[ { "name": "Undercover", "flying":
[ { "name": "Undercover", "flying": true
[ { "name": "Undercover", "flying": true }, { "name": "
[ { "name": "Undercover", "flying": true }, { "name": "Black mustache
[ { "name": "Undercover", "flying": true }, { "name": "Black mustache", "flying":
[ { "name": "Undercover", "flying": true }, { "name": "Black mustache", "flying": false
[ { "name": "Undercover", "flying": true }, { "name": "Black mustache", "flying": false } ]
in memory
in memory
@matarld
Encoder
return static function ($cats, $stream, $config) {
$stream->write('[');
$prefix_0 = '';
foreach ($cats as $cat) {
$stream->write($prefix_0);
$stream->write('{"name":');
$stream->write(json_encode($cat->name));
$stream->write(',"flying":');
$stream->write($cat->flying ? 'true' : 'false');
$stream->write('}');
$prefix_0 = ',';
}
$stream->write(']');
};
@matarld
Does an encoder exist?
Yep 👌
Encode the cat
/\_____/\
/ o o \
( == ^ == )
) (
( )
( ( ) ( ) )
(__(__)___(__)__)
Compute the cat JSON shape
Not yet 😬
Generate and store the encoder
@matarld
GET /api/cats/1
Accept: application/json
Response
@matarld
GET /api/cats/1
Accept: application/json
StreamedResponse
SerializeAndRespondProcessor
@matarld
Read only needed
JSON part lazily
Blazing fast!
Flat memory usage
Stream ready
Generics ready
Simple API
Edit data on-the-fly
@matarld
__
o-''|\_____/)
\_/|_) )
\ __ /
(_/ (_/
@matarld
class Cat
{
#[Groups('read')]
public int $id;
#[Groups(['read', 'write'])]
public string $nickname;
#[Groups(['admin'])]
public bool $isAlive;
}
-
read
write
admin
read + write
read + admin
write + admin
read + write + admin
@matarld
#[ORM\Entity]
#[ApiResource(
normalizationContext: ['groups' => 'read'],
denormalizationContext: ['groups' => 'write'],
)]
class Cat
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups('read')]
public int $id;
#[ORM\Column]
#[SerializedName('name')]
#[Groups(['read', 'write'])]
public string $nickname;
}
@matarld
#[ORM\Entity]
#[ApiResource(
normalizationContext: ['groups' => 'read'],
denormalizationContext: ['groups' => 'write'],
)]
class Cat
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups('read')]
public int $id;
#[ORM\Column]
#[SerializedName('name')]
#[Groups(['read', 'write'])]
public string $nickname;
}
@matarld
the takeaway
@matarld
#[ORM\Entity]
class CatEntity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
public int $id;
#[ORM\Column]
public string $nickname;
}
#[Get]
class CatGetResource
{
public int $id;
public string $name;
}
#[Post]
class CatPostResource
{
public string $name;
}
@matarld
class CatGetResourceProvider implements ProviderInterface
{
public function __construct(
#[Autowire(service: ItemProvider::class)]
private ProviderInterface $provider
) {
}
public function provide(Operation $operation, array $uriVariables, array $context)
{
$catEntity = $this->provider->provide($operation, $uriVariables, $context);
$catGetResource = new CatGetResource();
$catGetResource->id = $catEntity->id;
$catGetResource->name = ucfirst($catEntity->nickname);
return $catGetResource;
}
}
@matarld
@matarld
#[Map(CatGetResource::class)]
class CatEntity
{
#[Map(target: 'id')]
public int $id;
#[Map(
target: 'name',
transform: 'ucfirst',
)]
public string $nickname;
}
#[Get]
class CatGetResource
{
public int $id;
public string $name;
}
@matarld
class CatGetResourceProvider implements ProviderInterface
{
public function __construct(
#[Autowire(service: ItemProvider::class)]
private ProviderInterface $provider,
private ObjectMapperInterface $mapper,
) {
}
public function provide(Operation $operation, array $uriVariables, array $context)
{
return $this->mapper->map(
source: $this->provider->provide($operation, $uriVariables, $context),
target: CatGetResource::class,
);
}
}
@matarld
JsonEncoder
+ Mapper
Serializer
@matarld
JsonEncoder
+ Mapper
Serializer
@matarld
@matarld
@matarld
{ "name": "Jose", "aggressivity": "none", "mustaches": 4 }
normalization
encoding
@matarld
{ "name": "Jose", "aggressivity": "none", "mustaches": 4 }
decoding
denormalization
@matarld
@matarld
BC policy
AbstractNormalizer
AbstractObjectNormalizer
AbstractItemNormalizer
ItemNormalizer