.gitattributes000064400000000054151360552330007441 0ustar00/tests export-ignore /.github export-ignore LICENSE000064400000002054151360552330005555 0ustar00The MIT License (MIT) Copyright (c) Hyperf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. composer.json000064400000002355151360552330007276 0ustar00{ "name": "hyperf/serializer", "description": "A serializer component for Hyperf.", "license": "MIT", "keywords": [ "php", "swoole", "hyperf", "tappable" ], "homepage": "https://hyperf.io", "support": { "docs": "https://hyperf.wiki", "issues": "https://github.com/hyperf/hyperf/issues", "pull-request": "https://github.com/hyperf/hyperf/pulls", "source": "https://github.com/hyperf/hyperf" }, "require": { "php": ">=8.1", "hyperf/contract": "~3.1.0" }, "suggest": { "symfony/serializer": "Required to use SymfonyNormalizer (^5.0|^6.0)", "symfony/property-access": "Required to use SymfonyNormalizer (^5.0|^6.0)", "hyperf/di": "Required to use ExceptionNormalizer" }, "autoload": { "psr-4": { "Hyperf\\Serializer\\": "src/" } }, "autoload-dev": { "psr-4": { "HyperfTest\\Serializer\\": "tests/" } }, "config": { "sort-packages": true }, "extra": { "branch-alias": { "dev-master": "3.0-dev" }, "hyperf": { "config": "Hyperf\\Serializer\\ConfigProvider" } } } src/ConfigProvider.php000064400000001170151360552330010766 0ustar00 [ Serializer::class => SerializerFactory::class, NormalizerInterface::class => SimpleNormalizer::class, ], ]; } } src/ExceptionNormalizer.php000064400000006456151360552330012063 0ustar00getInstantiator()->instantiate($type); foreach (['code', 'message', 'file', 'line'] as $attribute) { if (isset($data[$attribute])) { $property = ReflectionManager::reflectProperty($type, $attribute); $property->setValue($exception, $data[$attribute]); } } return $exception; } catch (\ReflectionException) { return new RuntimeException(sprintf( 'Bad data %s: %s', $data['class'], $data['message'] ), $data['code']); } catch (\TypeError) { return new RuntimeException(sprintf( 'Uncaught data %s: %s', $data['class'], $data['message'] ), $data['code']); } } return new RuntimeException('Bad data data: ' . json_encode($data)); } public function supportsDenormalization($data, $type, $format = null) { return class_exists($type) && is_a($type, Throwable::class, true); } public function normalize($object, string $format = null, array $context = []) { if ($object instanceof Serializable) { return serialize($object); } /* @var \Throwable $object */ return [ 'message' => $object->getMessage(), 'code' => $object->getCode(), 'file' => $object->getFile(), 'line' => $object->getLine(), ]; } public function supportsNormalization($data, string $format = null) { return $data instanceof Throwable; } public function hasCacheableSupportsMethod(): bool { return get_class($this) === __CLASS__; } protected function getInstantiator(): Instantiator { if ($this->instantiator instanceof Instantiator) { return $this->instantiator; } return $this->instantiator = new Instantiator(); } } src/JsonDeNormalizer.php000064400000002012151360552330011267 0ustar00 (int) $data, 'string' => (string) $data, 'float' => (float) $data, 'array' => (array) $data, 'bool' => (bool) $data, 'mixed' => $data, default => $this->from($data, $class), }; } private function from(mixed $data, string $class): mixed { if (method_exists($class, 'jsonDeSerialize')) { return $class::jsonDeSerialize($data); } return $data; } } src/ScalarNormalizer.php000064400000003262151360552330011322 0ustar00 (int) $data, 'string' => (string) $data, 'float' => (float) $data, 'bool' => (bool) $data, default => $data, }; } public function supportsDenormalization($data, $type, string $format = null) { return in_array($type, [ 'int', 'string', 'float', 'bool', 'mixed', 'array', // TODO: Symfony\Component\Serializer\Normalizer\ArrayDenormalizer not support array, so it denormalized in ScalarNormalizer. ]); } public function normalize($object, string $format = null, array $context = []) { return $object; } public function supportsNormalization($data, string $format = null) { return is_scalar($data); } } src/Serializer.php000064400000030071151360552330010161 0ustar00serialize($obj, 'xml') * $serializer->decode($data, 'xml') * $serializer->denormalize($data, 'Class', 'xml') */ class Serializer implements Normalizer, SerializerInterface, ContextAwareNormalizerInterface, ContextAwareDenormalizerInterface, ContextAwareEncoderInterface, ContextAwareDecoderInterface { protected const SCALAR_TYPES = [ 'int' => true, 'bool' => true, 'float' => true, 'string' => true, ]; /** * @var Encoder\ChainEncoder */ protected $encoder; /** * @var Encoder\ChainDecoder */ protected $decoder; protected $normalizers = []; protected $denormalizerCache = []; protected $normalizerCache = []; /** * @param (NormalizerInterface|DenormalizerInterface|mixed)[] $normalizers * @param (EncoderInterface|DecoderInterface|mixed)[] $encoders */ public function __construct(array $normalizers = [], array $encoders = []) { foreach ($normalizers as $normalizer) { if ($normalizer instanceof SerializerAwareInterface) { $normalizer->setSerializer($this); } if ($normalizer instanceof DenormalizerAwareInterface) { $normalizer->setDenormalizer($this); } if ($normalizer instanceof NormalizerAwareInterface) { $normalizer->setNormalizer($this); } if (! ($normalizer instanceof NormalizerInterface || $normalizer instanceof DenormalizerInterface)) { throw new InvalidArgumentException(sprintf('The class "%s" neither implements "%s" nor "%s".', get_debug_type($normalizer), NormalizerInterface::class, DenormalizerInterface::class)); } } $this->normalizers = $normalizers; $decoders = []; $realEncoders = []; foreach ($encoders as $encoder) { if ($encoder instanceof SerializerAwareInterface) { $encoder->setSerializer($this); } if ($encoder instanceof DecoderInterface) { $decoders[] = $encoder; } if ($encoder instanceof EncoderInterface) { $realEncoders[] = $encoder; } if (! ($encoder instanceof EncoderInterface || $encoder instanceof DecoderInterface)) { throw new InvalidArgumentException(sprintf('The class "%s" neither implements "%s" nor "%s".', get_debug_type($encoder), EncoderInterface::class, DecoderInterface::class)); } } $this->encoder = new ChainEncoder($realEncoders); $this->decoder = new ChainDecoder($decoders); } final public function serialize($data, string $format, array $context = []): string { if (! $this->supportsEncoding($format, $context)) { throw new NotEncodableValueException(sprintf('Serialization for the format "%s" is not supported.', $format)); } if ($this->encoder->needsNormalization($format, $context)) { $data = $this->normalize($data, $format, $context); } return $this->encode($data, $format, $context); } /** * @phpstan-ignore-next-line * @param mixed $data */ final public function deserialize($data, string $type, string $format, array $context = []): mixed { if (! $this->supportsDecoding($format, $context)) { throw new NotEncodableValueException(sprintf('Deserialization for the format "%s" is not supported.', $format)); } $data = $this->decode($data, $format, $context); return $this->denormalize($data, $type, $format, $context); } public function normalize($object, string $format = null, array $context = []) { // If a normalizer supports the given data, use it if ($normalizer = $this->getNormalizer($object, $format, $context)) { return $normalizer->normalize($object, $format, $context); } if ($object === null || is_scalar($object)) { return $object; } if (is_array($object) || $object instanceof Traversable) { if ($object instanceof Countable && $object->count() === 0) { return $object; } $normalized = []; foreach ($object as $key => $val) { $normalized[$key] = $this->normalize($val, $format, $context); } return $normalized; } if (is_object($object)) { if (! $this->normalizers) { throw new LogicException('You must register at least one normalizer to be able to normalize objects.'); } throw new NotNormalizableValueException(sprintf('Could not normalize object of type "%s", no supporting normalizer found.', get_debug_type($object))); } throw new NotNormalizableValueException('An unexpected value could not be normalized: ' . (! is_resource($object) ? var_export($object, true) : sprintf('%s resource', get_resource_type($object)))); } /** * @param mixed $data * @throws NotNormalizableValueException */ public function denormalize($data, string $type, string $format = null, array $context = []) { if (isset(self::SCALAR_TYPES[$type])) { if (is_scalar($data)) { switch ($type) { case 'int': return (int) $data; case 'bool': return (bool) $data; case 'float': return (float) $data; case 'string': return (string) $data; } } } if (! $this->normalizers) { throw new LogicException('You must register at least one normalizer to be able to denormalize objects.'); } if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) { return $normalizer->denormalize($data, $type, $format, $context); } throw new NotNormalizableValueException(sprintf('Could not denormalize object of type "%s", no supporting normalizer found.', $type)); } public function supportsNormalization($data, string $format = null, array $context = []): bool { return $this->getNormalizer($data, $format, $context) !== null; } public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool { return isset(self::SCALAR_TYPES[$type]) || $this->getDenormalizer($data, $type, $format, $context) !== null; } final public function encode($data, string $format, array $context = []): string { return $this->encoder->encode($data, $format, $context); } final public function decode(string $data, string $format, array $context = []) { return $this->decoder->decode($data, $format, $context); } public function supportsEncoding(string $format, array $context = []): bool { return $this->encoder->supportsEncoding($format, $context); } public function supportsDecoding(string $format, array $context = []): bool { return $this->decoder->supportsDecoding($format, $context); } /** * Returns a matching normalizer. * * @param mixed $data Data to get the serializer for * @param string $format Format name, present to give the option to normalizers to act differently based on formats * @param array $context Options available to the normalizer */ private function getNormalizer($data, ?string $format, array $context): ?NormalizerInterface { $type = is_object($data) ? get_class($data) : 'native-' . gettype($data); if (! isset($this->normalizerCache[$format][$type])) { $this->normalizerCache[$format][$type] = []; foreach ($this->normalizers as $k => $normalizer) { if (! $normalizer instanceof NormalizerInterface) { continue; } if (! $normalizer instanceof CacheableSupportsMethodInterface || ! $normalizer->hasCacheableSupportsMethod()) { $this->normalizerCache[$format][$type][$k] = false; } elseif ($normalizer->supportsNormalization($data, $format)) { $this->normalizerCache[$format][$type][$k] = true; break; } } } foreach ($this->normalizerCache[$format][$type] as $k => $cached) { $normalizer = $this->normalizers[$k]; if ($cached || $normalizer->supportsNormalization($data, $format, $context)) { return $normalizer; } } return null; } /** * Returns a matching denormalizer. * * @param mixed $data Data to restore * @param string $class The expected class to instantiate * @param string $format Format name, present to give the option to normalizers to act differently based on formats * @param array $context Options available to the denormalizer */ private function getDenormalizer($data, string $class, ?string $format, array $context): ?DenormalizerInterface { if (! isset($this->denormalizerCache[$format][$class])) { $this->denormalizerCache[$format][$class] = []; foreach ($this->normalizers as $k => $normalizer) { if (! $normalizer instanceof DenormalizerInterface) { continue; } if (! $normalizer instanceof CacheableSupportsMethodInterface || ! $normalizer->hasCacheableSupportsMethod()) { $this->denormalizerCache[$format][$class][$k] = false; } elseif ($normalizer->supportsDenormalization(null, $class, $format)) { $this->denormalizerCache[$format][$class][$k] = true; break; } } } foreach ($this->denormalizerCache[$format][$class] as $k => $cached) { $normalizer = $this->normalizers[$k]; if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) { return $normalizer; } } return null; } } src/SerializerFactory.php000064400000001444151360552330011513 0ustar00serializer([ new ExceptionNormalizer(), new ObjectNormalizer(), new ArrayDenormalizer(), new ScalarNormalizer(), ]); } } src/SimpleNormalizer.php000064400000001401151360552330011337 0ustar00 (int) $data, 'string' => (string) $data, 'float' => (float) $data, 'array' => (array) $data, 'bool' => (bool) $data, default => $data, }; } } src/SymfonyNormalizer.php000064400000001327151360552330011561 0ustar00serializer->normalize($object); } public function denormalize($data, string $class) { return $this->serializer->denormalize($data, $class); } }