SerializedToJson.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\DB\DataConverter;
  7. use Magento\Framework\Serialize\Serializer\Json;
  8. use Magento\Framework\Serialize\Serializer\Serialize;
  9. /**
  10. * Convert from serialized to JSON format
  11. */
  12. class SerializedToJson implements DataConverterInterface
  13. {
  14. /**
  15. * @var Serialize
  16. */
  17. private $serialize;
  18. /**
  19. * @var Json
  20. */
  21. private $json;
  22. /**
  23. * Constructor
  24. *
  25. * @param Serialize $serialize
  26. * @param Json $json
  27. */
  28. public function __construct(
  29. Serialize $serialize,
  30. Json $json
  31. ) {
  32. $this->serialize = $serialize;
  33. $this->json = $json;
  34. }
  35. /**
  36. * Convert from serialized to JSON format
  37. *
  38. * @param string $value
  39. * @return string
  40. * @throws DataConversionException
  41. */
  42. public function convert($value)
  43. {
  44. if ($this->isValidJsonValue($value)) {
  45. return $value;
  46. }
  47. return $this->encodeJson($this->unserializeValue($value));
  48. }
  49. /**
  50. * Is a valid JSON serialized value
  51. *
  52. * @param string $value
  53. * @return bool
  54. */
  55. protected function isValidJsonValue($value)
  56. {
  57. if (in_array($value, ['null', 'false', '0', '""', '[]'])
  58. || (json_decode($value) !== null && json_last_error() === JSON_ERROR_NONE)
  59. ) {
  60. return true;
  61. }
  62. //JSON last error reset
  63. json_encode([]);
  64. return false;
  65. }
  66. /**
  67. * Unserialize value
  68. *
  69. * @param string $value
  70. * @return mixed
  71. * @throws DataConversionException
  72. */
  73. protected function unserializeValue($value)
  74. {
  75. try {
  76. set_error_handler(function ($errorNumber, $errorString) {
  77. throw new DataConversionException($errorString, $errorNumber);
  78. });
  79. $value = $this->serialize->unserialize($value);
  80. } catch (\Throwable $throwable) {
  81. throw new DataConversionException($throwable->getMessage());
  82. } finally {
  83. restore_error_handler();
  84. }
  85. return $value;
  86. }
  87. /**
  88. * Encode value with json encoder.
  89. *
  90. * In PHP version < 7.1.0 serialize() uses PG(serialize_precision) which set to 17 be default.
  91. * Since json_encode() uses EG(precision) which set to 14 be default, json_encode() removes lower digits of
  92. * fraction parts and destroys original value even if PHP's float could hold more precise float value.
  93. * To prevent this issue EG(precision) is set to 17 to be equal with PG(serialize_precision) during
  94. * data converting from serialized format to JSON.
  95. *
  96. * In PHP version >= 7.1.0 serialize() and json_encode() use PG(serialize_precision) which set to -1 be default.
  97. * Setting -1 uses better algorithm for rounding float numbers.
  98. * But for data consistency during converting process PG(serialize_precision) is set to 17.
  99. *
  100. * @param string $value
  101. * @return string
  102. * @throws DataConversionException
  103. */
  104. protected function encodeJson($value)
  105. {
  106. $storedPrecision = ini_get('precision');
  107. $storedSerializePrecision = ini_get('serialize_precision');
  108. if (PHP_VERSION_ID < 70100) {
  109. // In PHP version < 7.1.0 json_encode() uses EG(precision).
  110. ini_set('precision', 17);
  111. } else {
  112. // In PHP version >= 7.1.0 json_encode() uses PG(serialize_precision).
  113. ini_set('serialize_precision', 17);
  114. }
  115. $value = $this->json->serialize($value);
  116. ini_set('precision', $storedPrecision);
  117. ini_set('serialize_precision', $storedSerializePrecision);
  118. if (json_last_error()) {
  119. throw new DataConversionException(json_last_error_msg());
  120. }
  121. return $value;
  122. }
  123. }