Skip to content

Commit

Permalink
Merge pull request #40 from formal-php/reduce-circular-references
Browse files Browse the repository at this point in the history
Reduce circular references
  • Loading branch information
Baptouuuu authored Oct 26, 2024
2 parents 076be6f + 0a2d5a2 commit b248646
Show file tree
Hide file tree
Showing 21 changed files with 309 additions and 148 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [Unreleased]

### Changed

- Use `static` closures as much as possible to reduce the probability of creating circular references by capturing `$this` as it can lead to memory root buffer exhaustion.

## 3.4.0 - 2024-10-02

### Added
Expand Down
15 changes: 10 additions & 5 deletions src/Adapter/Elasticsearch/Decode.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,17 @@ public function __invoke(Aggregate\Id $id = null): callable
default => Of::callable(static fn() => Validation::success($id)),
};

return fn(mixed $content) => Maybe::all(
$properties = $this->properties;
$entities = $this->entities;
$optionals = $this->optionals;
$collections = $this->collections;

return static fn(mixed $content) => Maybe::all(
Is::array()->and($id)($content)->maybe(),
($this->properties)($content)->maybe(),
Is::array()->and($this->entities)($content)->maybe(),
Is::array()->and($this->optionals)($content)->maybe(),
Is::array()->and($this->collections)($content)->maybe(),
$properties($content)->maybe(),
Is::array()->and($entities)($content)->maybe(),
Is::array()->and($optionals)($content)->maybe(),
Is::array()->and($collections)($content)->maybe(),
)->map(Aggregate::of(...));
}

Expand Down
14 changes: 7 additions & 7 deletions src/Adapter/Elasticsearch/Encode.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public function __invoke(Diff|Aggregate $data): Content
$entities = $data
->entities()
->exclude(static fn($entity) => $entity->properties()->empty())
->map(fn($entity) => [
$entity->name() => $this->properties($entity->properties()),
->map(static fn($entity) => [
$entity->name() => self::properties($entity->properties()),
])
->toList();
$optionals = $data
Expand All @@ -36,20 +36,20 @@ public function __invoke(Diff|Aggregate $data): Content
static fn($properties) => $properties->empty(),
static fn() => false, // force setting the property to null below
))
->map(fn($optional) => [
->map(static fn($optional) => [
$optional->name() => $optional->properties()->match(
$this->properties(...),
self::properties(...),
static fn() => null,
),
])
->toList();
$collections = $data
->collections()
->map(fn($collection) => [
->map(static fn($collection) => [
$collection->name() => $collection
->entities()
->unsorted()
->map(fn($entity) => $this->properties($entity->properties()))
->map(static fn($entity) => self::properties($entity->properties()))
->toList(),
])
->toList();
Expand Down Expand Up @@ -77,7 +77,7 @@ public static function new(): self
/**
* @param Sequence<Aggregate\Property> $properties
*/
private function properties(Sequence $properties): array
private static function properties(Sequence $properties): array
{
return \array_merge(
...$properties
Expand Down
124 changes: 103 additions & 21 deletions src/Adapter/Elasticsearch/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ public static function of(
public function get(Aggregate\Id $id): Maybe
{
return ($this->http)(Request::of(
$this->url('_source', $id->value()),
self::url(
$this->url,
$this->path,
'_source',
$id->value(),
),
Method::get,
ProtocolVersion::v11,
))
Expand All @@ -149,7 +154,12 @@ public function get(Aggregate\Id $id): Maybe
public function contains(Aggregate\Id $id): bool
{
return ($this->http)(Request::of(
$this->url('_doc', $id->value()),
self::url(
$this->url,
$this->path,
'_doc',
$id->value(),
),
Method::head,
ProtocolVersion::v11,
))->match(
Expand All @@ -161,7 +171,12 @@ public function contains(Aggregate\Id $id): bool
public function add(Aggregate $data): void
{
$_ = ($this->http)(Request::of(
$this->url('_doc', $data->id()->value()),
self::url(
$this->url,
$this->path,
'_doc',
$data->id()->value(),
),
Method::put,
ProtocolVersion::v11,
Headers::of(
Expand All @@ -177,7 +192,12 @@ public function add(Aggregate $data): void
public function update(Diff $data): void
{
$_ = ($this->http)(Request::of(
$this->url('_update', $data->id()->value()),
self::url(
$this->url,
$this->path,
'_update',
$data->id()->value(),
),
Method::post,
ProtocolVersion::v11,
Headers::of(
Expand All @@ -193,7 +213,12 @@ public function update(Diff $data): void
public function remove(Aggregate\Id $id): void
{
$_ = ($this->http)(Request::of(
$this->url('_doc', $id->value()),
self::url(
$this->url,
$this->path,
'_doc',
$id->value(),
),
Method::delete,
ProtocolVersion::v11,
))->match(
Expand All @@ -205,7 +230,11 @@ public function remove(Aggregate\Id $id): void
public function removeAll(Specification $specification): void
{
$_ = ($this->http)(Request::of(
$this->url('_delete_by_query'),
self::url(
$this->url,
$this->path,
'_delete_by_query',
),
Method::post,
ProtocolVersion::v11,
Headers::of(
Expand Down Expand Up @@ -258,7 +287,19 @@ public function fetch(
return $this->stream($drop ?? 0, $normalizedSort, $query);
}

return $this->search($drop ?? 0, $take, $normalizedSort, $query);
$decode = ($this->decode)();

return self::search(
$this->http,
$decode,
$this->pluckHits,
$this->url,
$this->path,
$drop ?? 0,
$take,
$normalizedSort,
$query,
);
}

public function size(Specification $specification = null): int
Expand All @@ -271,7 +312,11 @@ public function size(Specification $specification = null): int
}

return ($this->http)(Request::of(
$this->url('_count'),
self::url(
$this->url,
$this->path,
'_count',
),
match ($content) {
null => Method::get,
default => Method::post,
Expand Down Expand Up @@ -309,14 +354,17 @@ public function any(Specification $specification = null): bool
* @param non-empty-string $action
* @param non-empty-string|null $id
*/
private function url(string $action, string $id = null): Url
{
private static function url(
Url $url,
Template $path,
string $action,
string $id = null,
): Url {
/** @var Map<non-empty-string, non-empty-string> */
$map = Map::of(['action', $action]);

return $this->url->withPath(
$this
->path
return $url->withPath(
$path
->expand(match ($id) {
null => $map,
default => ($map)('id', $id),
Expand All @@ -331,15 +379,40 @@ private function url(string $action, string $id = null): Url
*/
private function stream(int $drop, ?array $sort, ?array $query): Sequence
{
return Sequence::lazy(function() use ($drop, $sort, $query) {
$http = $this->http;
$decode = ($this->decode)();
$pluckHits = $this->pluckHits;
$url = $this->url;
$path = $this->path;

return Sequence::lazy(static function() use (
$http,
$decode,
$pluckHits,
$url,
$path,
$drop,
$sort,
$query,
) {
// This loop will break when reaching 10k documents (see
// self::search()). The user SHOULD be aware of this limitation
// after reading the documentation.
// In the case the user is not aware of this limitation, streaming
// more than 10k documents will crash the app (thus making the user
// aware of this limitation).
while (true) {
$hits = $this->search($drop, 100, $sort, $query);
$hits = self::search(
$http,
$decode,
$pluckHits,
$url,
$path,
$drop,
100,
$sort,
$query,
);

yield $hits;

Expand All @@ -356,12 +429,19 @@ private function stream(int $drop, ?array $sort, ?array $query): Sequence
}

/**
* @param callable(mixed): Maybe<Aggregate> $decode
* @param Constraint<mixed, Sequence<array>> $pluckHits
* @param 0|positive-int $drop
* @param positive-int $take
*
* @return Sequence<Aggregate>
*/
private function search(
private static function search(
Transport $http,
callable $decode,
Constraint $pluckHits,
Url $url,
Template $path,
int $drop,
int $take,
?array $sort,
Expand All @@ -385,10 +465,12 @@ private function search(
$payload['query'] = $query;
}

$decode = ($this->decode)();

return ($this->http)(Request::of(
$this->url('_search'),
return $http(Request::of(
self::url(
$url,
$path,
'_search',
),
Method::post,
ProtocolVersion::v11,
Headers::of(
Expand All @@ -399,7 +481,7 @@ private function search(
->map(static fn($success) => $success->response()->body()->toString())
->map(Json::decode(...))
->maybe()
->flatMap(fn($content) => ($this->pluckHits)($content)->maybe())
->flatMap(static fn($content) => $pluckHits($content)->maybe())
->toSequence()
->flatMap(static fn($hits) => $hits)
->flatMap(static fn($hit) => $decode($hit)->toSequence());
Expand Down
16 changes: 14 additions & 2 deletions src/Adapter/Filesystem/Decode.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ private function __construct(Definition $definition)
*/
public function __invoke(Aggregate\Id $id = null): callable
{
$property = $this->definition->id()->property();
/** @psalm-suppress ArgumentTypeCoercion */
$id = match ($id) {
null => fn(Directory $directory) => Aggregate\Id::of(
$this->definition->id()->property(),
null => static fn(Directory $directory) => Aggregate\Id::of(
$property,
$directory->name()->toString(),
),
default => static fn(Directory $directory) => $id,
Expand All @@ -59,10 +60,12 @@ public function __invoke(Aggregate\Id $id = null): callable
$directory
->get(Name::of('properties'))
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($properties) => $properties
->all()
->keep(Instance::of(File::class))
->memoize()
->map(static fn($file) => Aggregate\Property::of(
$file->name()->toString(),
Json::decode($file->content()->toString()),
Expand All @@ -71,16 +74,19 @@ public function __invoke(Aggregate\Id $id = null): callable
$directory
->get(Name::of('entities'))
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($entities) => $entities
->all()
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($entity) => Aggregate\Entity::of(
$entity->name()->toString(),
$entity
->all()
->keep(Instance::of(File::class))
->memoize()
->map(static fn($property) => Aggregate\Property::of(
$property->name()->toString(),
Json::decode($property->content()->toString()),
Expand All @@ -91,20 +97,24 @@ public function __invoke(Aggregate\Id $id = null): callable
$directory
->get(Name::of('optionals'))
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($optionals) => $optionals
->all()
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($optional) => Aggregate\Optional::of(
$optional->name()->toString(),
$optional
->get(Name::of('just'))
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($just) => $just
->all()
->keep(Instance::of(File::class))
->memoize()
->map(static fn($property) => Aggregate\Property::of(
$property->name()->toString(),
Json::decode($property->content()->toString()),
Expand All @@ -116,10 +126,12 @@ public function __invoke(Aggregate\Id $id = null): callable
$directory
->get(Name::of('collections'))
->keep(Instance::of(Directory::class))
->memoize()
->map(
static fn($collections) => $collections
->all()
->keep(Instance::of(File::class))
->memoize()
->map(
static fn($collection) => Aggregate\Collection::of(
$collection->name()->toString(),
Expand Down
Loading

0 comments on commit b248646

Please sign in to comment.