vendor/jms/serializer/src/JsonSerializationVisitor.php line 90

  1. <?php
  2. declare(strict_types=1);
  3. namespace JMS\Serializer;
  4. use JMS\Serializer\Exception\NotAcceptableException;
  5. use JMS\Serializer\Exception\RuntimeException;
  6. use JMS\Serializer\Metadata\ClassMetadata;
  7. use JMS\Serializer\Metadata\PropertyMetadata;
  8. use JMS\Serializer\Visitor\SerializationVisitorInterface;
  9. final class JsonSerializationVisitor extends AbstractVisitor implements SerializationVisitorInterface
  10. {
  11. /**
  12. * @var int
  13. */
  14. private $options;
  15. /**
  16. * @var array
  17. */
  18. private $dataStack;
  19. /**
  20. * @var \ArrayObject|array
  21. */
  22. private $data;
  23. public function __construct(
  24. int $options = JSON_PRESERVE_ZERO_FRACTION
  25. ) {
  26. $this->dataStack = [];
  27. $this->options = $options;
  28. }
  29. /**
  30. * {@inheritdoc}
  31. */
  32. public function visitNull($data, array $type)
  33. {
  34. return null;
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function visitString(string $data, array $type)
  40. {
  41. return $data;
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function visitBoolean(bool $data, array $type)
  47. {
  48. return $data;
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. public function visitInteger(int $data, array $type)
  54. {
  55. return $data;
  56. }
  57. /**
  58. * {@inheritdoc}
  59. */
  60. public function visitDouble(float $data, array $type)
  61. {
  62. $precision = $type['params'][0] ?? null;
  63. if (!is_int($precision)) {
  64. return $data;
  65. }
  66. $roundMode = $type['params'][1] ?? null;
  67. $roundMode = $this->mapRoundMode($roundMode);
  68. return round($data, $precision, $roundMode);
  69. }
  70. public function visitArray(array $data, array $type)
  71. {
  72. \array_push($this->dataStack, $data);
  73. $rs = isset($type['params'][1]) ? new \ArrayObject() : [];
  74. $isList = isset($type['params'][0]) && !isset($type['params'][1]);
  75. $elType = $this->getElementType($type);
  76. foreach ($data as $k => $v) {
  77. try {
  78. $v = $this->navigator->accept($v, $elType);
  79. } catch (NotAcceptableException $e) {
  80. continue;
  81. }
  82. if ($isList) {
  83. $rs[] = $v;
  84. } else {
  85. $rs[$k] = $v;
  86. }
  87. }
  88. \array_pop($this->dataStack);
  89. return $rs;
  90. }
  91. public function startVisitingObject(ClassMetadata $metadata, object $data, array $type): void
  92. {
  93. \array_push($this->dataStack, $this->data);
  94. $this->data = true === $metadata->isMap ? new \ArrayObject() : [];
  95. }
  96. /**
  97. * @return array|\ArrayObject
  98. */
  99. public function endVisitingObject(ClassMetadata $metadata, object $data, array $type)
  100. {
  101. $rs = $this->data;
  102. $this->data = \array_pop($this->dataStack);
  103. if (true !== $metadata->isList && empty($rs)) {
  104. return new \ArrayObject();
  105. }
  106. return $rs;
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function visitProperty(PropertyMetadata $metadata, $v): void
  112. {
  113. try {
  114. $v = $this->navigator->accept($v, $metadata->type);
  115. } catch (NotAcceptableException $e) {
  116. return;
  117. }
  118. if (true === $metadata->skipWhenEmpty && ($v instanceof \ArrayObject || \is_array($v)) && 0 === count($v)) {
  119. return;
  120. }
  121. if ($metadata->inline) {
  122. if (\is_array($v) || ($v instanceof \ArrayObject)) {
  123. // concatenate the two array-like structures
  124. // is there anything faster?
  125. foreach ($v as $key => $value) {
  126. $this->data[$key] = $value;
  127. }
  128. }
  129. } else {
  130. $this->data[$metadata->serializedName] = $v;
  131. }
  132. }
  133. /**
  134. * Checks if some data key exists.
  135. */
  136. public function hasData(string $key): bool
  137. {
  138. return isset($this->data[$key]);
  139. }
  140. /**
  141. * @deprecated Use `::visitProperty(new StaticPropertyMetadata('', 'name', 'value'), 'value')` instead
  142. *
  143. * Allows you to replace existing data on the current object element.
  144. *
  145. * @param mixed $value This value must either be a regular scalar, or an array.
  146. * It must not contain any objects anymore.
  147. */
  148. public function setData(string $key, $value): void
  149. {
  150. $this->data[$key] = $value;
  151. }
  152. /**
  153. * {@inheritdoc}
  154. */
  155. public function getResult($data)
  156. {
  157. $this->navigator = null;
  158. $result = @json_encode($data, $this->options);
  159. switch (json_last_error()) {
  160. case JSON_ERROR_NONE:
  161. return $result;
  162. case JSON_ERROR_UTF8:
  163. throw new RuntimeException('Your data could not be encoded because it contains invalid UTF8 characters.');
  164. default:
  165. throw new RuntimeException(sprintf('An error occurred while encoding your data (error code %d).', json_last_error()));
  166. }
  167. }
  168. }