Skip to content

Commit 1c01a8b

Browse files
committed
feat(symfony): stop watch system provider/processor
1 parent 6491bfc commit 1c01a8b

11 files changed

+149
-9
lines changed

src/State/Processor/AddLinkHeaderProcessor.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
use ApiPlatform\Metadata\Operation;
1717
use ApiPlatform\State\ProcessorInterface;
18+
use ApiPlatform\State\StopwatchAwareInterface;
19+
use ApiPlatform\State\StopwatchAwareTrait;
1820
use Symfony\Component\HttpFoundation\Response;
1921
use Symfony\Component\WebLink\HttpHeaderSerializer;
2022

@@ -24,8 +26,10 @@
2426
*
2527
* @implements ProcessorInterface<T1, T2>
2628
*/
27-
final class AddLinkHeaderProcessor implements ProcessorInterface
29+
final class AddLinkHeaderProcessor implements ProcessorInterface, StopwatchAwareInterface
2830
{
31+
use StopwatchAwareTrait;
32+
2933
/**
3034
* @param ProcessorInterface<T1, T2> $decorated
3135
*/
@@ -44,11 +48,13 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
4448
return $response;
4549
}
4650

51+
$this->stopwatch?->start('api_platform.processor.add_link_header');
4752
// We add our header here as Symfony does it only for the main Request and we want it to be done on errors (sub-request) as well
4853
$linksProvider = $request->attributes->get('_api_platform_links');
4954
if ($this->serializer && ($links = $linksProvider?->getLinks())) {
5055
$response->headers->set('Link', $this->serializer->serialize($links));
5156
}
57+
$this->stopwatch?->stop('api_platform.processor.add_link_header');
5258

5359
return $response;
5460
}

src/State/Processor/RespondProcessor.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
use ApiPlatform\Metadata\Util\ClassInfoTrait;
2828
use ApiPlatform\Metadata\Util\CloneTrait;
2929
use ApiPlatform\State\ProcessorInterface;
30+
use ApiPlatform\State\StopwatchAwareInterface;
31+
use ApiPlatform\State\StopwatchAwareTrait;
3032
use Symfony\Component\HttpFoundation\Response;
3133
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface as SymfonyHttpExceptionInterface;
3234

@@ -35,10 +37,11 @@
3537
*
3638
* @author Kévin Dunglas <[email protected]>
3739
*/
38-
final class RespondProcessor implements ProcessorInterface
40+
final class RespondProcessor implements ProcessorInterface, StopwatchAwareInterface
3941
{
4042
use ClassInfoTrait;
4143
use CloneTrait;
44+
use StopwatchAwareTrait;
4245

4346
public const METHOD_TO_CODE = [
4447
'POST' => Response::HTTP_CREATED,
@@ -62,6 +65,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
6265
return $data;
6366
}
6467

68+
$this->stopwatch?->start('api_platform.processor.respond');
69+
6570
$headers = [
6671
'Content-Type' => \sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())),
6772
'Vary' => 'Accept',
@@ -144,6 +149,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
144149
}
145150
}
146151

152+
$this->stopwatch?->stop('api_platform.processor.respond');
153+
147154
return new Response(
148155
$data,
149156
$status,

src/State/Processor/SerializeProcessor.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use ApiPlatform\State\ProcessorInterface;
1818
use ApiPlatform\State\ResourceList;
1919
use ApiPlatform\State\SerializerContextBuilderInterface;
20+
use ApiPlatform\State\StopwatchAwareInterface;
21+
use ApiPlatform\State\StopwatchAwareTrait;
2022
use Symfony\Component\HttpFoundation\Response;
2123
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
2224
use Symfony\Component\Serializer\SerializerInterface;
@@ -33,8 +35,10 @@
3335
*
3436
* @author Kévin Dunglas <[email protected]>
3537
*/
36-
final class SerializeProcessor implements ProcessorInterface
38+
final class SerializeProcessor implements ProcessorInterface, StopwatchAwareInterface
3739
{
40+
use StopwatchAwareTrait;
41+
3842
/**
3943
* @param ProcessorInterface<mixed, mixed>|null $processor
4044
*/
@@ -48,6 +52,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
4852
return $this->processor ? $this->processor->process($data, $operation, $uriVariables, $context) : $data;
4953
}
5054

55+
$this->stopwatch?->start('api_platform.processor.serialize');
56+
5157
// @see ApiPlatform\State\Processor\RespondProcessor
5258
$context['original_data'] = $data;
5359

@@ -60,6 +66,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
6066
$serializerContext['uri_variables'] = $uriVariables;
6167

6268
if (isset($serializerContext['output']) && \array_key_exists('class', $serializerContext['output']) && null === $serializerContext['output']['class']) {
69+
$this->stopwatch?->stop('api_platform.processor.serialize');
70+
6371
return $this->processor ? $this->processor->process(null, $operation, $uriVariables, $context) : null;
6472
}
6573

@@ -81,6 +89,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
8189
$request->attributes->set('_api_platform_links', $linkProvider);
8290
}
8391

92+
$this->stopwatch?->stop('api_platform.processor.serialize');
93+
8494
return $this->processor ? $this->processor->process($serialized, $operation, $uriVariables, $context) : $serialized;
8595
}
8696
}

src/State/Processor/WriteProcessor.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use ApiPlatform\Metadata\Operation;
1717
use ApiPlatform\Metadata\Util\ClassInfoTrait;
1818
use ApiPlatform\State\ProcessorInterface;
19+
use ApiPlatform\State\StopwatchAwareInterface;
20+
use ApiPlatform\State\StopwatchAwareTrait;
1921
use Symfony\Component\HttpFoundation\Response;
2022

2123
/**
@@ -29,9 +31,10 @@
2931
* @author Kévin Dunglas <[email protected]>
3032
* @author Baptiste Meyer <[email protected]>
3133
*/
32-
final class WriteProcessor implements ProcessorInterface
34+
final class WriteProcessor implements ProcessorInterface, StopwatchAwareInterface
3335
{
3436
use ClassInfoTrait;
37+
use StopwatchAwareTrait;
3538

3639
/**
3740
* @param ProcessorInterface<mixed, mixed> $processor
@@ -51,7 +54,9 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
5154
return $this->processor ? $this->processor->process($data, $operation, $uriVariables, $context) : $data;
5255
}
5356

57+
$this->stopwatch?->start('api_platform.processor.write');
5458
$data = $this->callableProcessor->process($data, $operation, $uriVariables, $context);
59+
$this->stopwatch?->stop('api_platform.processor.write');
5560

5661
return $this->processor ? $this->processor->process($data, $operation, $uriVariables, $context) : $data;
5762
}

src/State/Provider/ContentNegotiationProvider.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
use ApiPlatform\Metadata\Operation;
1919
use ApiPlatform\Metadata\Util\ContentNegotiationTrait;
2020
use ApiPlatform\State\ProviderInterface;
21+
use ApiPlatform\State\StopwatchAwareInterface;
22+
use ApiPlatform\State\StopwatchAwareTrait;
2123
use Negotiation\Negotiator;
2224
use Symfony\Component\HttpFoundation\Request;
2325
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
2426

25-
final class ContentNegotiationProvider implements ProviderInterface
27+
final class ContentNegotiationProvider implements ProviderInterface, StopwatchAwareInterface
2628
{
2729
use ContentNegotiationTrait;
30+
use StopwatchAwareTrait;
2831

2932
/**
3033
* @param array<string, string[]> $formats
@@ -41,12 +44,14 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
4144
return $this->decorated?->provide($operation, $uriVariables, $context);
4245
}
4346

47+
$this->stopwatch?->start('api_platform.provider.content_negotiation');
4448
$isErrorOperation = $operation instanceof ErrorOperation;
4549

4650
$formats = $operation->getOutputFormats() ?? ($isErrorOperation ? $this->errorFormats : $this->formats);
4751
$this->addRequestFormats($request, $formats);
4852
$request->attributes->set('input_format', $this->getInputFormat($operation, $request));
4953
$request->setRequestFormat($this->getRequestFormat($request, $formats, !$isErrorOperation));
54+
$this->stopwatch?->stop('api_platform.provider.content_negotiation');
5055

5156
return $this->decorated?->provide($operation, $uriVariables, $context);
5257
}

src/State/Provider/DeserializeProvider.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use ApiPlatform\Metadata\Operation;
1818
use ApiPlatform\State\ProviderInterface;
1919
use ApiPlatform\State\SerializerContextBuilderInterface;
20+
use ApiPlatform\State\StopwatchAwareInterface;
21+
use ApiPlatform\State\StopwatchAwareTrait;
2022
use ApiPlatform\Validator\Exception\ValidationException;
2123
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
2224
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
@@ -30,8 +32,10 @@
3032
use Symfony\Contracts\Translation\TranslatorInterface;
3133
use Symfony\Contracts\Translation\TranslatorTrait;
3234

33-
final class DeserializeProvider implements ProviderInterface
35+
final class DeserializeProvider implements ProviderInterface, StopwatchAwareInterface
3436
{
37+
use StopwatchAwareTrait;
38+
3539
public function __construct(
3640
private readonly ?ProviderInterface $decorated,
3741
private readonly SerializerInterface $serializer,
@@ -59,6 +63,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
5963
return $data;
6064
}
6165

66+
$this->stopwatch?->start('api_platform.provider.deserialize');
67+
6268
$contentType = $request->headers->get('CONTENT_TYPE');
6369
if (null === $contentType || '' === $contentType) {
6470
throw new UnsupportedMediaTypeHttpException('The "Content-Type" header must exist.');
@@ -94,7 +100,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
94100
unset($serializerContext[SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE]);
95101

96102
try {
97-
return $this->serializer->deserialize((string) $request->getContent(), $serializerContext['deserializer_type'] ?? $operation->getClass(), $format, $serializerContext);
103+
$data = $this->serializer->deserialize((string) $request->getContent(), $serializerContext['deserializer_type'] ?? $operation->getClass(), $format, $serializerContext);
98104
} catch (PartialDenormalizationException $e) {
99105
if (!class_exists(ConstraintViolationList::class)) {
100106
throw $e;
@@ -118,6 +124,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
118124
}
119125
}
120126

127+
$this->stopwatch?->stop('api_platform.provider.deserialize');
128+
121129
return $data;
122130
}
123131

src/State/Provider/ParameterProvider.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
use ApiPlatform\State\ParameterNotFound;
2222
use ApiPlatform\State\ParameterProvider\ReadLinkParameterProvider;
2323
use ApiPlatform\State\ProviderInterface;
24+
use ApiPlatform\State\StopwatchAwareInterface;
25+
use ApiPlatform\State\StopwatchAwareTrait;
2426
use ApiPlatform\State\Util\ParameterParserTrait;
2527
use ApiPlatform\State\Util\RequestParser;
2628
use Psr\Container\ContainerInterface;
@@ -32,16 +34,18 @@
3234
*
3335
* @experimental
3436
*/
35-
final class ParameterProvider implements ProviderInterface
37+
final class ParameterProvider implements ProviderInterface, StopwatchAwareInterface
3638
{
3739
use ParameterParserTrait;
40+
use StopwatchAwareTrait;
3841

3942
public function __construct(private readonly ?ProviderInterface $decorated = null, private readonly ?ContainerInterface $locator = null)
4043
{
4144
}
4245

4346
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
4447
{
48+
$this->stopwatch?->start('api_platform.provider.parameter');
4549
$request = $context['request'] ?? null;
4650
if ($request && null === $request->attributes->get('_api_query_parameters')) {
4751
$queryString = RequestParser::getQueryString($request);
@@ -95,6 +99,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
9599

96100
$request?->attributes->set('_api_operation', $operation);
97101
$context['operation'] = $operation;
102+
$this->stopwatch?->stop('api_platform.provider.parameter');
98103

99104
return $this->decorated?->provide($operation, $uriVariables, $context);
100105
}

src/State/Provider/ReadProvider.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use ApiPlatform\State\Exception\ProviderNotFoundException;
2121
use ApiPlatform\State\ProviderInterface;
2222
use ApiPlatform\State\SerializerContextBuilderInterface;
23+
use ApiPlatform\State\StopwatchAwareInterface;
24+
use ApiPlatform\State\StopwatchAwareTrait;
2325
use ApiPlatform\State\UriVariablesResolverTrait;
2426
use ApiPlatform\State\Util\OperationRequestInitiatorTrait;
2527
use ApiPlatform\State\Util\RequestParser;
@@ -31,10 +33,11 @@
3133
*
3234
* @author Kévin Dunglas <[email protected]>
3335
*/
34-
final class ReadProvider implements ProviderInterface
36+
final class ReadProvider implements ProviderInterface, StopwatchAwareInterface
3537
{
3638
use CloneTrait;
3739
use OperationRequestInitiatorTrait;
40+
use StopwatchAwareTrait;
3841
use UriVariablesResolverTrait;
3942

4043
public function __construct(
@@ -55,6 +58,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
5558
return null;
5659
}
5760

61+
$this->stopwatch?->start('api_platform.provider.read');
62+
5863
if (null === ($filters = $request?->attributes->get('_api_filters')) && $request) {
5964
$queryString = RequestParser::getQueryString($request);
6065
$filters = $queryString ? RequestParser::parseRequestParams($queryString) : null;
@@ -96,6 +101,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
96101
$request?->attributes->set('data', $data);
97102
$request?->attributes->set('previous_data', $this->clone($data));
98103

104+
$this->stopwatch?->stop('api_platform.provider.read');
105+
99106
return $data;
100107
}
101108
}

src/State/StopwatchAwareInterface.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\State;
15+
16+
use Symfony\Component\Stopwatch\Stopwatch;
17+
18+
/**
19+
* Interface for classes that can be injected with a Stopwatch instance.
20+
*
21+
* @internal
22+
*
23+
* @author Kévin Dunglas <[email protected]>
24+
*/
25+
interface StopwatchAwareInterface
26+
{
27+
/**
28+
* Sets the Stopwatch instance.
29+
*/
30+
public function setStopwatch(Stopwatch $stopwatch): void;
31+
}

src/State/StopwatchAwareTrait.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\State;
15+
16+
use Symfony\Component\Stopwatch\Stopwatch;
17+
18+
/**
19+
* @internal
20+
*/
21+
trait StopwatchAwareTrait
22+
{
23+
private ?Stopwatch $stopwatch = null;
24+
25+
public function setStopwatch(Stopwatch $stopwatch): void
26+
{
27+
$this->stopwatch = $stopwatch;
28+
}
29+
}

0 commit comments

Comments
 (0)