Skip to content

Commit 599d674

Browse files
committed
[JsonEncoder] Introduce component
1 parent 73d4904 commit 599d674

File tree

196 files changed

+8451
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+8451
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77
/src/Symfony/Component/Translation/Bridge export-ignore
88
/src/Symfony/Component/Emoji/Resources/data/* linguist-generated=true
99
/src/Symfony/Component/Intl/Resources/data/*/* linguist-generated=true
10+
/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/* linguist-generated=true
11+
/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/* linguist-generated=true
1012
/src/Symfony/**/.github/workflows/close-pull-request.yml linguist-generated=true
1113
/src/Symfony/**/.github/PULL_REQUEST_TEMPLATE.md linguist-generated=true

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"symfony/http-foundation": "self.version",
8484
"symfony/http-kernel": "self.version",
8585
"symfony/intl": "self.version",
86+
"symfony/json-encoder": "self.version",
8687
"symfony/ldap": "self.version",
8788
"symfony/lock": "self.version",
8889
"symfony/mailer": "self.version",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\JsonEncoder\Attribute;
13+
14+
/**
15+
* Defines a callable or a {@see \Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface} service id
16+
* that will be used to denormalize the property data during decoding.
17+
*
18+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
19+
*
20+
* @experimental
21+
*/
22+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
23+
class Denormalizer
24+
{
25+
private string|\Closure $denormalizer;
26+
27+
/**
28+
* @param string|(callable(mixed, array<string, mixed>?): mixed)|(callable(mixed): mixed) $denormalizer
29+
*/
30+
public function __construct(mixed $denormalizer)
31+
{
32+
if (\is_callable($denormalizer)) {
33+
$denormalizer = \Closure::fromCallable($denormalizer);
34+
}
35+
36+
$this->denormalizer = $denormalizer;
37+
}
38+
39+
public function getDenormalizer(): string|\Closure
40+
{
41+
return $this->denormalizer;
42+
}
43+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\JsonEncoder\Attribute;
13+
14+
/**
15+
* Defines the encoded property name.
16+
*
17+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
18+
*
19+
* @experimental
20+
*/
21+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
22+
final class EncodedName
23+
{
24+
public function __construct(
25+
private string $name,
26+
) {
27+
}
28+
29+
public function getName(): string
30+
{
31+
return $this->name;
32+
}
33+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\JsonEncoder\Attribute;
13+
14+
/**
15+
* Defines a callable or a {@see \Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface} service id
16+
* that will be used to normalize the property data during encoding.
17+
*
18+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
19+
*
20+
* @experimental
21+
*/
22+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
23+
class Normalizer
24+
{
25+
private string|\Closure $normalizer;
26+
27+
/**
28+
* @param string|(callable(mixed, array<string, mixed>?): mixed)|(callable(mixed): mixed) $normalizer
29+
*/
30+
public function __construct(mixed $normalizer)
31+
{
32+
if (\is_callable($normalizer)) {
33+
$normalizer = \Closure::fromCallable($normalizer);
34+
}
35+
36+
$this->normalizer = $normalizer;
37+
}
38+
39+
public function getNormalizer(): string|\Closure
40+
{
41+
return $this->normalizer;
42+
}
43+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
7.3
5+
---
6+
7+
* Introduce the component as experimental
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\JsonEncoder\CacheWarmer;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Psr\Log\NullLogger;
16+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
17+
use Symfony\Component\JsonEncoder\Decode\DecoderGenerator;
18+
use Symfony\Component\JsonEncoder\Encode\EncoderGenerator;
19+
use Symfony\Component\JsonEncoder\Exception\ExceptionInterface;
20+
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface;
21+
use Symfony\Component\TypeInfo\Type;
22+
23+
/**
24+
* Generates encoders and decoders PHP files.
25+
*
26+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
27+
*
28+
* @internal
29+
*/
30+
final class EncoderDecoderCacheWarmer implements CacheWarmerInterface
31+
{
32+
private EncoderGenerator $encoderGenerator;
33+
private DecoderGenerator $decoderGenerator;
34+
35+
/**
36+
* @param iterable<class-string> $encodableClassNames
37+
*/
38+
public function __construct(
39+
private iterable $encodableClassNames,
40+
PropertyMetadataLoaderInterface $encodePropertyMetadataLoader,
41+
PropertyMetadataLoaderInterface $decodePropertyMetadataLoader,
42+
string $encodersDir,
43+
string $decodersDir,
44+
bool $forceEncodeChunks = false,
45+
private LoggerInterface $logger = new NullLogger(),
46+
) {
47+
$this->encoderGenerator = new EncoderGenerator($encodePropertyMetadataLoader, $encodersDir, $forceEncodeChunks);
48+
$this->decoderGenerator = new DecoderGenerator($decodePropertyMetadataLoader, $decodersDir);
49+
}
50+
51+
public function warmUp(string $cacheDir, ?string $buildDir = null): array
52+
{
53+
foreach ($this->encodableClassNames as $className) {
54+
$type = Type::object($className);
55+
56+
$this->warmUpEncoder($type);
57+
$this->warmUpDecoders($type);
58+
}
59+
60+
return [];
61+
}
62+
63+
public function isOptional(): bool
64+
{
65+
return true;
66+
}
67+
68+
private function warmUpEncoder(Type $type): void
69+
{
70+
try {
71+
$this->encoderGenerator->generate($type);
72+
} catch (ExceptionInterface $e) {
73+
$this->logger->debug('Cannot generate "json" encoder for "{type}": {exception}', ['type' => (string) $type, 'exception' => $e]);
74+
}
75+
}
76+
77+
private function warmUpDecoders(Type $type): void
78+
{
79+
try {
80+
$this->decoderGenerator->generate($type, decodeFromStream: false);
81+
} catch (ExceptionInterface $e) {
82+
$this->logger->debug('Cannot generate "json" decoder for "{type}": {exception}', ['type' => (string) $type, 'exception' => $e]);
83+
}
84+
85+
try {
86+
$this->decoderGenerator->generate($type, decodeFromStream: true);
87+
} catch (ExceptionInterface $e) {
88+
$this->logger->debug('Cannot generate "json" streaming decoder for "{type}": {exception}', ['type' => (string) $type, 'exception' => $e]);
89+
}
90+
}
91+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\JsonEncoder\CacheWarmer;
13+
14+
use Symfony\Component\Filesystem\Filesystem;
15+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer;
16+
use Symfony\Component\JsonEncoder\Exception\RuntimeException;
17+
use Symfony\Component\VarExporter\ProxyHelper;
18+
19+
/**
20+
* Generates lazy ghost {@see \Symfony\Component\VarExporter\LazyGhostTrait}
21+
* PHP files for $encodable types.
22+
*
23+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
24+
*
25+
* @internal
26+
*/
27+
final class LazyGhostCacheWarmer extends CacheWarmer
28+
{
29+
private Filesystem $fs;
30+
31+
/**
32+
* @param iterable<class-string> $encodableClassNames
33+
*/
34+
public function __construct(
35+
private iterable $encodableClassNames,
36+
private string $lazyGhostsDir,
37+
) {
38+
$this->fs = new Filesystem();
39+
}
40+
41+
public function warmUp(string $cacheDir, ?string $buildDir = null): array
42+
{
43+
if (!$this->fs->exists($this->lazyGhostsDir)) {
44+
$this->fs->mkdir($this->lazyGhostsDir);
45+
}
46+
47+
foreach ($this->encodableClassNames as $className) {
48+
$this->warmClassLazyGhost($className);
49+
}
50+
51+
return [];
52+
}
53+
54+
public function isOptional(): bool
55+
{
56+
return true;
57+
}
58+
59+
/**
60+
* @param class-string $className
61+
*/
62+
private function warmClassLazyGhost(string $className): void
63+
{
64+
$path = \sprintf('%s%s%s.php', $this->lazyGhostsDir, \DIRECTORY_SEPARATOR, hash('xxh128', $className));
65+
66+
try {
67+
$classReflection = new \ReflectionClass($className);
68+
} catch (\ReflectionException $e) {
69+
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
70+
}
71+
72+
$this->writeCacheFile($path, \sprintf(
73+
'class %s%s',
74+
\sprintf('%sGhost', preg_replace('/\\\\/', '', $className)),
75+
ProxyHelper::generateLazyGhost($classReflection),
76+
));
77+
}
78+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy