Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 43772a4

Browse files
tobias-trozowskiweierophinney
authored andcommitted
added maxDepth for RouteBasedResource to prevent endless nesting
1 parent abdcefb commit 43772a4

16 files changed

+102
-35
lines changed

docs/book/cookbook/generating-custom-resources.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ interface StrategyInterface
2222
$instance,
2323
Metadata\AbstractMetadata $metadata,
2424
ResourceGenerator $resourceGenerator,
25-
ServerRequestInterface $request
25+
ServerRequestInterface $request,
26+
int $depth = 0
2627
) : HalResource;
2728
}
2829
```

docs/book/factories.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ The additional pairs are as follows:
109109
represents the resource identifier; defaults to "id".
110110
- `route_params`: an array of additional routing parameters to use when
111111
generating the self relational link for the resource.
112+
- `max_depth`: the number of nesting levels processed. Defaults to 10.
112113
- For `RouteBasedCollectionMetadata`:
113114
- `collection_class`: the collection class the metadata describes.
114115
- `collection_relation`: the embedded relation for the collection in the

docs/book/resource-generator.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ following information:
5050
that maps to the resource identifier)
5151
- array `$routeParams = []` (associative array of additional routing
5252
parameters to substitute when generating the URI)
53+
- int `$maxDepth = 10` max allowed nesting levels.
5354
- `Zend\Expressive\Hal\Metadata\UrlBasedCollectionMetadata`:
5455
- string `$class`
5556
- string `$collectionRelation`

src/Metadata/RouteBasedResourceMetadata.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,25 @@ class RouteBasedResourceMetadata extends AbstractResourceMetadata
2121
/** @var array */
2222
private $routeParams;
2323

24+
/** @var int */
25+
private $maxDepth;
26+
2427
public function __construct(
2528
string $class,
2629
string $route,
2730
string $extractor,
2831
string $resourceIdentifier = 'id',
2932
string $routeIdentifierPlaceholder = 'id',
30-
array $routeParams = []
33+
array $routeParams = [],
34+
int $maxDepth = 10
3135
) {
3236
$this->class = $class;
3337
$this->route = $route;
3438
$this->extractor = $extractor;
3539
$this->resourceIdentifier = $resourceIdentifier;
3640
$this->routeIdentifierPlaceholder = $routeIdentifierPlaceholder;
3741
$this->routeParams = $routeParams;
42+
$this->maxDepth = $maxDepth;
3843
}
3944

4045
public function getRoute() : string
@@ -61,4 +66,9 @@ public function setRouteParams(array $routeParams) : void
6166
{
6267
$this->routeParams = $routeParams;
6368
}
69+
70+
public function getMaxDepth(): int
71+
{
72+
return $this->maxDepth;
73+
}
6474
}

src/Metadata/RouteBasedResourceMetadataFactory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ class RouteBasedResourceMetadataFactory implements MetadataFactoryInterface
4646
* // generating the self relational link for the collection
4747
* // resource. Defaults to an empty array.
4848
* 'route_params' => [],
49+
*
50+
* // Max depth to render
51+
* // Defaults to 10.
52+
* 'max_depth' => 10,
4953
* ]
5054
* </code>
5155
* @return AbstractMetadata
@@ -72,7 +76,8 @@ public function createMetadata(string $requestedName, array $metadata) : Abstrac
7276
$metadata['extractor'],
7377
$metadata['resource_identifier'] ?? 'id',
7478
$metadata['route_identifier_placeholder'] ?? 'id',
75-
$metadata['route_params'] ?? []
79+
$metadata['route_params'] ?? [],
80+
$metadata['max_depth'] ?? 10
7681
);
7782
}
7883
}

src/ResourceGenerator.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function fromArray(array $data, string $uri = null) : HalResource
123123
* against types registered in the metadata map.
124124
* @param ServerRequestInterface $request
125125
*/
126-
public function fromObject($instance, ServerRequestInterface $request) : HalResource
126+
public function fromObject($instance, ServerRequestInterface $request, int $depth = 0) : HalResource
127127
{
128128
if (! is_object($instance)) {
129129
throw Exception\InvalidObjectException::forNonObject($instance);
@@ -146,7 +146,8 @@ public function fromObject($instance, ServerRequestInterface $request) : HalReso
146146
$instance,
147147
$metadata,
148148
$this,
149-
$request
149+
$request,
150+
$depth
150151
);
151152
}
152153
}

src/ResourceGenerator/ExtractCollectionTrait.php

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,22 @@ private function extractCollection(
4646
Traversable $collection,
4747
AbstractCollectionMetadata $metadata,
4848
ResourceGenerator $resourceGenerator,
49-
ServerRequestInterface $request
49+
ServerRequestInterface $request,
50+
int $depth = 0
5051
) : HalResource {
5152
if (! $metadata instanceof AbstractCollectionMetadata) {
5253
throw Exception\UnexpectedMetadataTypeException::forCollection($metadata, get_class($this));
5354
}
5455

5556
if ($collection instanceof Paginator) {
56-
return $this->extractPaginator($collection, $metadata, $resourceGenerator, $request);
57+
return $this->extractPaginator($collection, $metadata, $resourceGenerator, $request, $depth);
5758
}
5859

5960
if ($collection instanceof DoctrinePaginator) {
60-
return $this->extractDoctrinePaginator($collection, $metadata, $resourceGenerator, $request);
61+
return $this->extractDoctrinePaginator($collection, $metadata, $resourceGenerator, $request, $depth);
6162
}
6263

63-
return $this->extractIterator($collection, $metadata, $resourceGenerator, $request);
64+
return $this->extractIterator($collection, $metadata, $resourceGenerator, $request, $depth);
6465
}
6566

6667
/**
@@ -77,7 +78,8 @@ private function extractPaginator(
7778
Paginator $collection,
7879
AbstractCollectionMetadata $metadata,
7980
ResourceGenerator $resourceGenerator,
80-
ServerRequestInterface $request
81+
ServerRequestInterface $request,
82+
int $depth = 0
8183
) : HalResource {
8284
$data = ['_total_items' => $collection->getTotalItemCount()];
8385
$pageCount = $collection->count();
@@ -91,7 +93,8 @@ function (int $page) use ($collection) {
9193
$collection,
9294
$metadata,
9395
$resourceGenerator,
94-
$request
96+
$request,
97+
$depth
9598
);
9699
}
97100

@@ -106,7 +109,8 @@ private function extractDoctrinePaginator(
106109
DoctrinePaginator $collection,
107110
AbstractCollectionMetadata $metadata,
108111
ResourceGenerator $resourceGenerator,
109-
ServerRequestInterface $request
112+
ServerRequestInterface $request,
113+
int $depth = 0
110114
) : HalResource {
111115
$query = $collection->getQuery();
112116
$totalItems = count($collection);
@@ -124,22 +128,24 @@ function (int $page) use ($query, $perPage) {
124128
$collection,
125129
$metadata,
126130
$resourceGenerator,
127-
$request
131+
$request,
132+
$depth
128133
);
129134
}
130135

131136
private function extractIterator(
132137
Traversable $collection,
133138
AbstractCollectionMetadata $metadata,
134139
ResourceGenerator $resourceGenerator,
135-
ServerRequestInterface $request
140+
ServerRequestInterface $request,
141+
int $depth = 0
136142
) : HalResource {
137143
$isCountable = $collection instanceof Countable;
138144
$count = $isCountable ? $collection->count() : 0;
139145

140146
$resources = [];
141147
foreach ($collection as $item) {
142-
$resources[] = $resourceGenerator->fromObject($item, $request);
148+
$resources[] = $resourceGenerator->fromObject($item, $request, $depth + 1);
143149
$count = $isCountable ? $count : $count + 1;
144150
}
145151

@@ -184,7 +190,8 @@ private function createPaginatedCollectionResource(
184190
iterable $collection,
185191
AbstractCollectionMetadata $metadata,
186192
ResourceGenerator $resourceGenerator,
187-
ServerRequestInterface $request
193+
ServerRequestInterface $request,
194+
int $depth = 0
188195
) : HalResource {
189196
$links = [];
190197
$paginationParamType = $metadata->getPaginationParamType();
@@ -197,7 +204,8 @@ private function createPaginatedCollectionResource(
197204
$collection,
198205
$metadata,
199206
$resourceGenerator,
200-
$request
207+
$request,
208+
$depth
201209
);
202210
}
203211

@@ -236,7 +244,8 @@ private function createPaginatedCollectionResource(
236244
$collection,
237245
$metadata,
238246
$resourceGenerator,
239-
$request
247+
$request,
248+
$depth
240249
);
241250
}
242251

@@ -255,11 +264,12 @@ private function createCollectionResource(
255264
iterable $collection,
256265
AbstractCollectionMetadata $metadata,
257266
ResourceGenerator $resourceGenerator,
258-
ServerRequestInterface $request
267+
ServerRequestInterface $request,
268+
int $depth = 0
259269
) : HalResource {
260270
$resources = [];
261271
foreach ($collection as $item) {
262-
$resources[] = $resourceGenerator->fromObject($item, $request);
272+
$resources[] = $resourceGenerator->fromObject($item, $request, $depth + 1);
263273
}
264274

265275
return new HalResource($data, $links, [

src/ResourceGenerator/ExtractInstanceTrait.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Psr\Http\Message\ServerRequestInterface;
1111
use Zend\Expressive\Hal\Metadata\AbstractCollectionMetadata;
1212
use Zend\Expressive\Hal\Metadata\AbstractMetadata;
13+
use Zend\Expressive\Hal\Metadata\RouteBasedResourceMetadata;
1314
use Zend\Expressive\Hal\ResourceGenerator;
1415
use Zend\Hydrator\ExtractionInterface;
1516

@@ -27,7 +28,8 @@ private function extractInstance(
2728
$instance,
2829
AbstractMetadata $metadata,
2930
ResourceGenerator $resourceGenerator,
30-
ServerRequestInterface $request
31+
ServerRequestInterface $request,
32+
int $depth = 0
3133
) : array {
3234
$hydrators = $resourceGenerator->getHydrators();
3335
$extractor = $hydrators->get($metadata->getExtractor());
@@ -37,6 +39,16 @@ private function extractInstance(
3739

3840
$array = $extractor->extract($instance);
3941

42+
if ($metadata instanceof RouteBasedResourceMetadata) {
43+
$maxDepth = $metadata->getMaxDepth();
44+
if ($depth > $maxDepth) {
45+
$resourceIdentifier = $metadata->getResourceIdentifier();
46+
return [
47+
$resourceIdentifier => $array[$resourceIdentifier]
48+
];
49+
}
50+
}
51+
4052
// Extract nested resources if present in metadata map
4153
$metadataMap = $resourceGenerator->getMetadataMap();
4254
foreach ($array as $key => $value) {
@@ -49,7 +61,7 @@ private function extractInstance(
4961
continue;
5062
}
5163

52-
$childData = $resourceGenerator->fromObject($value, $request);
64+
$childData = $resourceGenerator->fromObject($value, $request, $depth + 1);
5365

5466
// Nested collections need to be merged.
5567
$childMetadata = $metadataMap->get($childClass);

src/ResourceGenerator/RouteBasedCollectionStrategy.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public function createResource(
2525
$instance,
2626
Metadata\AbstractMetadata $metadata,
2727
ResourceGenerator $resourceGenerator,
28-
ServerRequestInterface $request
28+
ServerRequestInterface $request,
29+
int $depth = 0
2930
) : HalResource {
3031
if (! $metadata instanceof Metadata\RouteBasedCollectionMetadata) {
3132
throw Exception\UnexpectedMetadataTypeException::forMetadata(

src/ResourceGenerator/RouteBasedResourceStrategy.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public function createResource(
2020
$instance,
2121
Metadata\AbstractMetadata $metadata,
2222
ResourceGenerator $resourceGenerator,
23-
ServerRequestInterface $request
23+
ServerRequestInterface $request,
24+
int $depth = 0
2425
) : HalResource {
2526
if (! $metadata instanceof Metadata\RouteBasedResourceMetadata) {
2627
throw Exception\UnexpectedMetadataTypeException::forMetadata(
@@ -34,7 +35,8 @@ public function createResource(
3435
$instance,
3536
$metadata,
3637
$resourceGenerator,
37-
$request
38+
$request,
39+
$depth
3840
);
3941

4042
$routeParams = $metadata->getRouteParams();
@@ -45,6 +47,11 @@ public function createResource(
4547
$routeParams[$routeIdentifier] = $data[$resourceIdentifier];
4648
}
4749

50+
$maxDepth = $metadata->getMaxDepth();
51+
if ($depth > $maxDepth) {
52+
$data = [];
53+
}
54+
4855
return new HalResource($data, [
4956
$resourceGenerator->getLinkGenerator()->fromRoute(
5057
'self',

0 commit comments

Comments
 (0)