8000 [JsonEncoder] Introduce component · symfony/symfony@aed69bb · GitHub
[go: up one dir, main page]

Skip to content

Commit aed69bb

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

File tree

196 files changed

+8451
-0
lines changed
  • Enum
  • Model
  • Normalizer
  • decoder
  • encoder
  • Mapping
  • 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 3C7 +
    *
    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 7C51 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 <math 8F6F ias.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)
    0