diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 5001cfd..a10b535 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -13,4 +13,5 @@ src/Component/Serializer/Generator/* src/Component/Serializer/Parser/* + src/Component/Serializer/ArraySupportDecorator.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 47a94db..d4b5787 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,4 +1,6 @@ parameters: + excludePaths: + - src/Component/Serializer/ArraySupportDecorator.php ignoreErrors: - message: "#^Unsafe call to private method RetailCrm\\\\Api\\\\Builder\\\\ClientBuilder\\:\\:buildHandlersChain\\(\\) through static\\:\\:\\.$#" diff --git a/src/Component/Serializer/ArraySupportDecorator.php b/src/Component/Serializer/ArraySupportDecorator.php index e94de20..4d9c980 100644 --- a/src/Component/Serializer/ArraySupportDecorator.php +++ b/src/Component/Serializer/ArraySupportDecorator.php @@ -16,211 +16,422 @@ use Liip\Serializer\Exception\UnsupportedFormatException; use Liip\Serializer\SerializerInterface; use Pnz\JsonException\Json; -/** - * Class ArraySupportDecorator - * - * @category ArraySupportDecorator - * @package RetailCrm\Api\Component\Serializer - */ -class ArraySupportDecorator implements SerializerInterface -{ - private SerializerInterface $serializer; - +if (PHP_VERSION_ID >= 80000) { /** - * ArraySupportDecorator constructor. + * Class ArraySupportDecorator * - * @param \Liip\Serializer\SerializerInterface $serializer + * @category ArraySupportDecorator + * @package RetailCrm\Api\Component\Serializer */ - public function __construct(SerializerInterface $serializer) + class ArraySupportDecorator implements SerializerInterface { - $this->serializer = $serializer; - } + private SerializerInterface $serializer; - /** - * @inheritDoc - * @throws \JsonException - */ - public function serialize($data, string $format, ?Context $context = null): string - { - if ('json' !== $format) { - throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); + /** + * ArraySupportDecorator constructor. + * + * @param \Liip\Serializer\SerializerInterface $serializer + */ + public function __construct(SerializerInterface $serializer) + { + $this->serializer = $serializer; } - if (is_array($data)) { - try { - return Json::encode($this->encodeArray($data, $context), JSON_UNESCAPED_SLASHES); - } catch (JsonException $exception) { - throw new Exception( - sprintf( - 'Failed to JSON encode data for %s. This is not supposed to happen.', - // @phpstan-ignore-next-line - is_object($data) ? get_class($data) : gettype($data) - ), - 0, - $exception - ); - } - } - - return $this->serializer->serialize($data, $format, $context); - } - - /** - * @inheritDoc - */ - public function deserialize(string $data, string $type, string $format, ?Context $context = null): mixed - { - if ('json' !== $format) { - throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); - } - - if (static::isArrayType($type)) { - try { - $array = Json::decode($data, true); - } catch (JsonException $exception) { - throw new Exception('Failed to JSON decode data. This is not supposed to happen.', 0, $exception); + /** + * @inheritDoc + * @throws \JsonException + */ + public function serialize($data, string $format, ?Context $context = null): string + { + if ('json' !== $format) { + throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); } - return $this->serializer->fromArray($this->decodeArray($array, $type, $context), $type, $context); - } - - return $this->serializer->deserialize($data, $type, $format, $context); - } - - /** - * @inheritDoc - * - * @return array - */ - public function toArray($data, ?Context $context = null): array - { - if (is_array($data)) { - return $this->encodeArray($data, $context); - } - - return $this->serializer->toArray($data, $context); - } - - /** - * @inheritDoc - * - * @param array $data - * - * @return array|object - */ - public function fromArray(array $data, string $type, ?Context $context = null): mixed - { - if (static::isArrayType($type)) { - return $this->decodeArray($data, $type, $context); - } - - return $this->serializer->fromArray($data, $type, $context); - } - - /** - * Encodes array of objects into simple multidimensional array. - * - * @param mixed[] $data - * @param \Liip\Serializer\Context|null $context - * - * @return mixed[] - * @throws \Liip\Serializer\Exception\Exception - * @throws \Liip\Serializer\Exception\UnsupportedTypeException - */ - private function encodeArray(array $data, ?Context $context = null): array - { - $result = []; - - foreach ($data as $key => $value) { - switch (gettype($value)) { - case 'array': - $result[$key] = $this->encodeArray($value, $context); - break; - case 'object': - $result[$key] = $this->serializer->toArray($value, $context); - break; - default: - $result[$key] = $value; - break; + if (is_array($data)) { + try { + return Json::encode($this->encodeArray($data, $context), JSON_UNESCAPED_SLASHES); + } catch (JsonException $exception) { + throw new Exception( + sprintf( + 'Failed to JSON encode data for %s. This is not supposed to happen.', + // @phpstan-ignore-next-line + is_object($data) ? get_class($data) : gettype($data) + ), + 0, + $exception + ); + } } + + return $this->serializer->serialize($data, $format, $context); } - return $result; - } + /** + * @inheritDoc + */ + public function deserialize(string $data, string $type, string $format, ?Context $context = null): mixed + { + if ('json' !== $format) { + throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); + } - /** - * Decodes array of arrays to array of objects. - * - * @param mixed[] $data - * @param string $type - * @param \Liip\Serializer\Context|null $context - * - * @return array - * @throws \Liip\Serializer\Exception\Exception - * @throws \Liip\Serializer\Exception\UnsupportedTypeException - */ - private function decodeArray(array $data, string $type, ?Context $context = null): array - { - $result = []; - $subtype = static::getArrayValueType($type); - - if (class_exists($subtype)) { - foreach ($data as $key => $item) { - if (is_array($item)) { - $result[$key] = $this->decodeArray($item, $subtype, $context); - continue; + if (static::isArrayType($type)) { + try { + $array = Json::decode($data, true); + } catch (JsonException $exception) { + throw new Exception('Failed to JSON decode data. This is not supposed to happen.', 0, $exception); } - $result[$key] = $item; + return $this->serializer->fromArray($this->decodeArray($array, $type, $context), $type, $context); + } + + return $this->serializer->deserialize($data, $type, $format, $context); + } + + /** + * @inheritDoc + * + * @return array + */ + public function toArray($data, ?Context $context = null): array + { + if (is_array($data)) { + return $this->encodeArray($data, $context); + } + + return $this->serializer->toArray($data, $context); + } + + /** + * @inheritDoc + * + * @param array $data + * + * @return array|object + */ + public function fromArray(array $data, string $type, ?Context $context = null): mixed + { + if (static::isArrayType($type)) { + return $this->decodeArray($data, $type, $context); + } + + return $this->serializer->fromArray($data, $type, $context); + } + + /** + * Encodes array of objects into simple multidimensional array. + * + * @param mixed[] $data + * @param \Liip\Serializer\Context|null $context + * + * @return mixed[] + * @throws \Liip\Serializer\Exception\Exception + * @throws \Liip\Serializer\Exception\UnsupportedTypeException + */ + private function encodeArray(array $data, ?Context $context = null): array + { + $result = []; + + foreach ($data as $key => $value) { + switch (gettype($value)) { + case 'array': + $result[$key] = $this->encodeArray($value, $context); + break; + case 'object': + $result[$key] = $this->serializer->toArray($value, $context); + break; + default: + $result[$key] = $value; + break; + } } return $result; } - return $data; - } + /** + * Decodes array of arrays to array of objects. + * + * @param mixed[] $data + * @param string $type + * @param \Liip\Serializer\Context|null $context + * + * @return array + * @throws \Liip\Serializer\Exception\Exception + * @throws \Liip\Serializer\Exception\UnsupportedTypeException + */ + private function decodeArray(array $data, string $type, ?Context $context = null): array + { + $result = []; + $subtype = static::getArrayValueType($type); - /** - * Returns true if provided type is an array. - * - * @param string $type - * - * @return bool - */ - private static function isArrayType(string $type): bool - { - return false !== strpos($type, 'array'); - } + if (class_exists($subtype)) { + foreach ($data as $key => $item) { + if (is_array($item)) { + $result[$key] = $this->decodeArray($item, $subtype, $context); + continue; + } - /** - * Returns array value type from types like 'array' or 'array'. - * - * @param string $type - * - * @return string - */ - private static function getArrayValueType(string $type): string - { - $matches = []; + $result[$key] = $item; + } - preg_match_all( - '/array(\s+)?\<([\w\|\\\\]+)\s+\,\s+([\w\|\\\\]+)\>/m', - $type, - $matches, - PREG_SET_ORDER, - 0 - ); + return $result; + } - if (count($matches) > 0) { - return $matches[count($matches) - 1]; + return $data; } - preg_match_all('/array(\s+)?\<([\w\|\\\\]+)\>/m', $type, $matches, PREG_SET_ORDER, 0); - - if (count($matches) > 0) { - return $matches[count($matches) - 1]; + /** + * Returns true if provided type is an array. + * + * @param string $type + * + * @return bool + */ + private static function isArrayType(string $type): bool + { + return false !== strpos($type, 'array'); } - return 'mixed'; + /** + * Returns array value type from types like 'array' or 'array'. + * + * @param string $type + * + * @return string + */ + private static function getArrayValueType(string $type): string + { + $matches = []; + + preg_match_all( + '/array(\s+)?\<([\w\|\\\\]+)\s+\,\s+([\w\|\\\\]+)\>/m', + $type, + $matches, + PREG_SET_ORDER, + 0 + ); + + if (count($matches) > 0) { + return $matches[count($matches) - 1]; + } + + preg_match_all('/array(\s+)?\<([\w\|\\\\]+)\>/m', $type, $matches, PREG_SET_ORDER, 0); + + if (count($matches) > 0) { + return $matches[count($matches) - 1]; + } + + return 'mixed'; + } + } +} else { + /** + * Class ArraySupportDecorator + * + * @category ArraySupportDecorator + * @package RetailCrm\Api\Component\Serializer + */ + class ArraySupportDecorator implements SerializerInterface + { + private SerializerInterface $serializer; + + /** + * ArraySupportDecorator constructor. + * + * @param \Liip\Serializer\SerializerInterface $serializer + */ + public function __construct(SerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + /** + * @inheritDoc + * @throws \JsonException + */ + public function serialize($data, string $format, ?Context $context = null): string + { + if ('json' !== $format) { + throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); + } + + if (is_array($data)) { + try { + return Json::encode($this->encodeArray($data, $context), JSON_UNESCAPED_SLASHES); + } catch (JsonException $exception) { + throw new Exception( + sprintf( + 'Failed to JSON encode data for %s. This is not supposed to happen.', + // @phpstan-ignore-next-line + is_object($data) ? get_class($data) : gettype($data) + ), + 0, + $exception + ); + } + } + + return $this->serializer->serialize($data, $format, $context); + } + + /** + * @inheritDoc + */ + public function deserialize(string $data, string $type, string $format, ?Context $context = null) + { + if ('json' !== $format) { + throw new UnsupportedFormatException('Liip serializer only supports JSON for now'); + } + + if (static::isArrayType($type)) { + try { + $array = Json::decode($data, true); + } catch (JsonException $exception) { + throw new Exception('Failed to JSON decode data. This is not supposed to happen.', 0, $exception); + } + + return $this->serializer->fromArray($this->decodeArray($array, $type, $context), $type, $context); + } + + return $this->serializer->deserialize($data, $type, $format, $context); + } + + /** + * @inheritDoc + * + * @return array + */ + public function toArray($data, ?Context $context = null): array + { + if (is_array($data)) { + return $this->encodeArray($data, $context); + } + + return $this->serializer->toArray($data, $context); + } + + /** + * @inheritDoc + * + * @param array $data + * + * @return array|object + */ + public function fromArray(array $data, string $type, ?Context $context = null) + { + if (static::isArrayType($type)) { + return $this->decodeArray($data, $type, $context); + } + + return $this->serializer->fromArray($data, $type, $context); + } + + /** + * Encodes array of objects into simple multidimensional array. + * + * @param mixed[] $data + * @param \Liip\Serializer\Context|null $context + * + * @return mixed[] + * @throws \Liip\Serializer\Exception\Exception + * @throws \Liip\Serializer\Exception\UnsupportedTypeException + */ + private function encodeArray(array $data, ?Context $context = null): array + { + $result = []; + + foreach ($data as $key => $value) { + switch (gettype($value)) { + case 'array': + $result[$key] = $this->encodeArray($value, $context); + break; + case 'object': + $result[$key] = $this->serializer->toArray($value, $context); + break; + default: + $result[$key] = $value; + break; + } + } + + return $result; + } + + /** + * Decodes array of arrays to array of objects. + * + * @param mixed[] $data + * @param string $type + * @param \Liip\Serializer\Context|null $context + * + * @return array + * @throws \Liip\Serializer\Exception\Exception + * @throws \Liip\Serializer\Exception\UnsupportedTypeException + */ + private function decodeArray(array $data, string $type, ?Context $context = null): array + { + $result = []; + $subtype = static::getArrayValueType($type); + + if (class_exists($subtype)) { + foreach ($data as $key => $item) { + if (is_array($item)) { + $result[$key] = $this->decodeArray($item, $subtype, $context); + continue; + } + + $result[$key] = $item; + } + + return $result; + } + + return $data; + } + + /** + * Returns true if provided type is an array. + * + * @param string $type + * + * @return bool + */ + private static function isArrayType(string $type): bool + { + return false !== strpos($type, 'array'); + } + + /** + * Returns array value type from types like 'array' or 'array'. + * + * @param string $type + * + * @return string + */ + private static function getArrayValueType(string $type): string + { + $matches = []; + + preg_match_all( + '/array(\s+)?\<([\w\|\\\\]+)\s+\,\s+([\w\|\\\\]+)\>/m', + $type, + $matches, + PREG_SET_ORDER, + 0 + ); + + if (count($matches) > 0) { + return $matches[count($matches) - 1]; + } + + preg_match_all('/array(\s+)?\<([\w\|\\\\]+)\>/m', $type, $matches, PREG_SET_ORDER, 0); + + if (count($matches) > 0) { + return $matches[count($matches) - 1]; + } + + return 'mixed'; + } } }