/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php
182 строки
7 KB
soyuka
[JsonStreamer] Add a "cache_variant" option to the generated cache file name
27 июл 2026, 15:49
27 июл 2026, 15:49
626a2fb
Код
Авторство
О чём код?
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\JsonStreamer\Read; use Psr\Container\ContainerInterface; use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\DataModel\Read\BackedEnumNode; use Symfony\Component\JsonStreamer\DataModel\Read\CollectionNode; use Symfony\Component\JsonStreamer\DataModel\Read\CompositeNode; use Symfony\Component\JsonStreamer\DataModel\Read\DataModelNodeInterface; use Symfony\Component\JsonStreamer\DataModel\Read\ObjectNode; use Symfony\Component\JsonStreamer\DataModel\Read\ScalarNode; use Symfony\Component\JsonStreamer\Exception\InvalidArgumentException; use Symfony\Component\JsonStreamer\Exception\RuntimeException; use Symfony\Component\JsonStreamer\Exception\UnsupportedException; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BackedEnumType; use Symfony\Component\TypeInfo\Type\BuiltinType; use Symfony\Component\TypeInfo\Type\CollectionType; use Symfony\Component\TypeInfo\Type\EnumType; use Symfony\Component\TypeInfo\Type\GenericType; use Symfony\Component\TypeInfo\Type\IntersectionType; use Symfony\Component\TypeInfo\Type\ObjectType; use Symfony\Component\TypeInfo\Type\UnionType; /** * Generates and writes stream readers PHP files. * * @author Mathias Arlaud <mathias.arlaud@gmail.com> * * @internal */ final class StreamReaderGenerator { private StreamerDumper $dumper; private ?PhpGenerator $phpGenerator = null; public function __construct( private PropertyMetadataLoaderInterface $propertyMetadataLoader, private ContainerInterface $transformers, private string $streamReadersDir, ?ConfigCacheFactoryInterface $cacheFactory = null, ) { $this->dumper = new StreamerDumper($propertyMetadataLoader, $streamReadersDir, $cacheFactory); } /** * Generates and writes a stream reader PHP file and return its path. * * @param array<string, mixed> $options */ public function generate(Type $type, bool $decodeFromStream, array $options = []): string { $path = \sprintf('%s%s%s.%s%s.php', $this->streamReadersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type), self::cacheVariant($options), $decodeFromStream ? '.stream' : ''); $generateContent = function () use ($type, $decodeFromStream, $options): string { $this->phpGenerator ??= new PhpGenerator($this->transformers); return $this->phpGenerator->generate($this->createDataModel($type, $options), $decodeFromStream, $options); }; $this->dumper->dump($type, $path, $generateContent); return $path; } /** * @param array<string, mixed> $options */ private static function cacheVariant(array $options): string { $variant = $options['cache_variant'] ?? 'json'; // the value ends up in the cache file name, before the ".stream" marker, // so it must neither reach the filesystem unchecked nor shadow that marker if (!\is_string($variant) || !preg_match('/^[a-zA-Z0-9_-]++$/', $variant)) { throw new InvalidArgumentException(\sprintf('The "cache_variant" option must match "[a-zA-Z0-9_-]+", "%s" given.', \is_string($variant) ? $variant : get_debug_type($variant))); } return $variant; } /** * @param array<string, mixed> $options * @param array<string, mixed> $context */ private function createDataModel(Type $type, array $options = [], array &$context = []): DataModelNodeInterface { $context['original_type'] ??= $type; if ($type instanceof IntersectionType) { throw new UnsupportedException(\sprintf('Intersection types are not supported ("%s").', (string) $type)); } if ($type instanceof UnionType) { return new CompositeNode(array_map(fn (Type $t): DataModelNodeInterface => $this->createDataModel($t, $options, $context), $type->getTypes())); } if ($type instanceof BuiltinType) { return new ScalarNode($type); } if ($type instanceof BackedEnumType) { return new BackedEnumNode($type); } if ($type instanceof GenericType) { $type = $type->getWrappedType(); } if ($type instanceof ObjectType && !$type instanceof EnumType) { $typeString = (string) $type; $className = $type->getClassName(); if ($context['generated_classes'][$typeString] ??= false) { return ObjectNode::createMock($type); } $propertiesNodes = []; $context['generated_classes'][$typeString] = true; $propertiesMetadata = $this->propertyMetadataLoader->load($className, $options, $context); foreach ($propertiesMetadata as $streamedName => $propertyMetadata) { if (!$propertyMetadata->getName()) { continue; } $propertiesNodes[$streamedName] = [ 'name' => $propertyMetadata->getName(), 'value' => $this->createDataModel($propertyMetadata->getType(), $options, $context), 'accessor' => static function (string $accessor) use ($propertyMetadata): string { foreach ($propertyMetadata->getValueTransformers() as $valueTransformer) { if (\is_string($valueTransformer)) { $accessor = '$transformers->get('.var_export($valueTransformer, true).")->transform($accessor, \$options)"; continue; } try { $functionReflection = new \ReflectionFunction($valueTransformer); } catch (\ReflectionException $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } if ($functionReflection->isAnonymous()) { throw new RuntimeException(\sprintf('Cannot generate accessor for anonymous function "%s".', $functionReflection->getName())); } $functionName = !$functionReflection->getClosureCalledClass() ? $functionReflection->getName() : \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName()); $arguments = $functionReflection->isUserDefined() ? "$accessor, \$options" : $accessor; $accessor = "$functionName($arguments)"; } return $accessor; }, ]; } return new ObjectNode($type, $propertiesNodes); } if ($type instanceof CollectionType) { return new CollectionNode($type, $this->createDataModel($type->getCollectionValueType(), $options, $context)); } throw new UnsupportedException(\sprintf('"%s" type is not supported.', (string) $type)); } }