diff --git a/app/code/Magento/Checkout/Block/Onepage.php b/app/code/Magento/Checkout/Block/Onepage.php
index ca6b045ddbb5d..e01d5835b4cf0 100644
--- a/app/code/Magento/Checkout/Block/Onepage.php
+++ b/app/code/Magento/Checkout/Block/Onepage.php
@@ -38,7 +38,7 @@ class Onepage extends \Magento\Framework\View\Element\Template
protected $layoutProcessors;
/**
- * @var \Magento\Framework\Serialize\Serializer\Json
+ * @var \Magento\Framework\Serialize\SerializerInterface
*/
private $serializer;
@@ -48,8 +48,9 @@ class Onepage extends \Magento\Framework\View\Element\Template
* @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider
* @param array $layoutProcessors
* @param array $data
- * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
- * @throws \RuntimeException
+ * @param \Magento\Framework\Serialize\Serializer\Json $serializer
+ * @param \Magento\Framework\Serialize\SerializerInterface $serializerInterface
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
@@ -57,7 +58,8 @@ public function __construct(
\Magento\Checkout\Model\CompositeConfigProvider $configProvider,
array $layoutProcessors = [],
array $data = [],
- \Magento\Framework\Serialize\Serializer\Json $serializer = null
+ \Magento\Framework\Serialize\Serializer\Json $serializer = null,
+ \Magento\Framework\Serialize\SerializerInterface $serializerInterface = null
) {
parent::__construct($context, $data);
$this->formKey = $formKey;
@@ -65,12 +67,12 @@ public function __construct(
$this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : [];
$this->configProvider = $configProvider;
$this->layoutProcessors = $layoutProcessors;
- $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
- ->get(\Magento\Framework\Serialize\Serializer\Json::class);
+ $this->serializer = $serializerInterface ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
}
/**
- * @return string
+ * @inheritdoc
*/
public function getJsLayout()
{
@@ -78,7 +80,7 @@ public function getJsLayout()
$this->jsLayout = $processor->process($this->jsLayout);
}
- return json_encode($this->jsLayout, JSON_HEX_TAG);
+ return $this->serializer->serialize($this->jsLayout);
}
/**
@@ -115,11 +117,13 @@ public function getBaseUrl()
}
/**
+ * Retrieve serialized checkout config.
+ *
* @return bool|string
* @since 100.2.0
*/
public function getSerializedCheckoutConfig()
{
- return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG);
+ return $this->serializer->serialize($this->getCheckoutConfig());
}
}
diff --git a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
index 54f77c95148ac..b54339aa2c1d8 100644
--- a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
+++ b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
@@ -35,7 +35,7 @@ class OnepageTest extends \PHPUnit\Framework\TestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
- private $serializer;
+ private $serializerMock;
protected function setUp()
{
@@ -49,7 +49,7 @@ protected function setUp()
\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class
);
- $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class);
+ $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
$this->model = new \Magento\Checkout\Block\Onepage(
$contextMock,
@@ -57,7 +57,8 @@ protected function setUp()
$this->configProviderMock,
[$this->layoutProcessorMock],
[],
- $this->serializer
+ $this->serializerMock,
+ $this->serializerMock
);
}
@@ -93,6 +94,7 @@ public function testGetJsLayout()
$processedLayout = ['layout' => ['processed' => true]];
$jsonLayout = '{"layout":{"processed":true}}';
$this->layoutProcessorMock->expects($this->once())->method('process')->with([])->willReturn($processedLayout);
+ $this->serializerMock->expects($this->once())->method('serialize')->willReturn($jsonLayout);
$this->assertEquals($jsonLayout, $this->model->getJsLayout());
}
@@ -101,6 +103,7 @@ public function testGetSerializedCheckoutConfig()
{
$checkoutConfig = ['checkout', 'config'];
$this->configProviderMock->expects($this->once())->method('getConfig')->willReturn($checkoutConfig);
+ $this->serializerMock->expects($this->once())->method('serialize')->willReturn(json_encode($checkoutConfig));
$this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig());
}
diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml
index d80f88786c87b..00bcd2a27005a 100644
--- a/app/code/Magento/Checkout/etc/frontend/di.xml
+++ b/app/code/Magento/Checkout/etc/frontend/di.xml
@@ -59,6 +59,7 @@
- Magento\Checkout\Block\Checkout\TotalsProcessor
- Magento\Checkout\Block\Checkout\DirectoryDataProcessor
+ Magento\Framework\Serialize\Serializer\JsonHexTag
diff --git a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php
index e249a64861d43..9304d25cc8a98 100644
--- a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php
+++ b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Ui\TemplateEngine\Xhtml;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Serialize\Serializer\JsonHexTag;
use Magento\Framework\View\Layout\Generator\Structure;
use Magento\Framework\View\Element\UiComponentInterface;
use Magento\Framework\View\TemplateEngine\Xhtml\Template;
@@ -42,25 +44,33 @@ class Result implements ResultInterface
*/
protected $logger;
+ /**
+ * @var JsonHexTag
+ */
+ private $jsonSerializer;
+
/**
* @param Template $template
* @param CompilerInterface $compiler
* @param UiComponentInterface $component
* @param Structure $structure
* @param LoggerInterface $logger
+ * @param JsonHexTag $jsonSerializer
*/
public function __construct(
Template $template,
CompilerInterface $compiler,
UiComponentInterface $component,
Structure $structure,
- LoggerInterface $logger
+ LoggerInterface $logger,
+ JsonHexTag $jsonSerializer = null
) {
$this->template = $template;
$this->compiler = $compiler;
$this->component = $component;
$this->structure = $structure;
$this->logger = $logger;
+ $this->jsonSerializer = $jsonSerializer ?? ObjectManager::getInstance()->get(JsonHexTag::class);
}
/**
@@ -81,7 +91,7 @@ public function getDocumentElement()
public function appendLayoutConfiguration()
{
$layoutConfiguration = $this->wrapContent(
- json_encode($this->structure->generate($this->component), JSON_HEX_TAG)
+ $this->jsonSerializer->serialize($this->structure->generate($this->component))
);
$this->template->append($layoutConfiguration);
}
diff --git a/lib/internal/Magento/Framework/Serialize/README.md b/lib/internal/Magento/Framework/Serialize/README.md
index 5af8fb7f71b6b..d900f89208a54 100644
--- a/lib/internal/Magento/Framework/Serialize/README.md
+++ b/lib/internal/Magento/Framework/Serialize/README.md
@@ -3,6 +3,7 @@
**Serialize** library provides interface *SerializerInterface* and multiple implementations:
* *Json* - default implementation. Uses PHP native json_encode/json_decode functions;
+ * *JsonHexTag* - default implementation. Uses PHP native json_encode/json_decode functions with `JSON_HEX_TAG` option enabled;
* *Serialize* - less secure than *Json*, but gives higher performance on big arrays. Uses PHP native serialize/unserialize functions, does not unserialize objects on PHP 7.
Using *Serialize* implementation directly is discouraged, always use *SerializerInterface*, using *Serialize* implementation may lead to security vulnerabilities.
\ No newline at end of file
diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php b/lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php
new file mode 100644
index 0000000000000..4a5406ff3fd99
--- /dev/null
+++ b/lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php
@@ -0,0 +1,35 @@
+ are converted to \u003C and \u003E),
+ * unserialize JSON encoded data
+ *
+ * @api
+ * @since 100.2.0
+ */
+class JsonHexTag extends Json implements SerializerInterface
+{
+ /**
+ * @inheritDoc
+ * @since 100.2.0
+ */
+ public function serialize($data): string
+ {
+ $result = json_encode($data, JSON_HEX_TAG);
+ if (false === $result) {
+ throw new \InvalidArgumentException('Unable to serialize value.');
+ }
+ return $result;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php
new file mode 100644
index 0000000000000..c867dced0fc6e
--- /dev/null
+++ b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php
@@ -0,0 +1,118 @@
+json = $objectManager->getObject(JsonHexTag::class);
+ }
+
+ /**
+ * @param string|int|float|bool|array|null $value
+ * @param string $expected
+ * @dataProvider serializeDataProvider
+ */
+ public function testSerialize($value, $expected)
+ {
+ $this->assertEquals(
+ $expected,
+ $this->json->serialize($value)
+ );
+ }
+
+ public function serializeDataProvider()
+ {
+ $dataObject = new DataObject(['something']);
+ return [
+ ['', '""'],
+ ['string', '"string"'],
+ [null, 'null'],
+ [false, 'false'],
+ [['a' => 'b', 'd' => 123], '{"a":"b","d":123}'],
+ [123, '123'],
+ [10.56, '10.56'],
+ [$dataObject, '{}'],
+ ['< >', '"\u003C \u003E"'],
+ ];
+ }
+
+ /**
+ * @param string $value
+ * @param string|int|float|bool|array|null $expected
+ * @dataProvider unserializeDataProvider
+ */
+ public function testUnserialize($value, $expected)
+ {
+ $this->assertEquals(
+ $expected,
+ $this->json->unserialize($value)
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function unserializeDataProvider(): array
+ {
+ return [
+ ['""', ''],
+ ['"string"', 'string'],
+ ['null', null],
+ ['false', false],
+ ['{"a":"b","d":123}', ['a' => 'b', 'd' => 123]],
+ ['123', 123],
+ ['10.56', 10.56],
+ ['{}', []],
+ ['"\u003C \u003E"', '< >'],
+ ];
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Unable to serialize value.
+ */
+ public function testSerializeException()
+ {
+ $this->json->serialize(STDOUT);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Unable to unserialize value.
+ * @dataProvider unserializeExceptionDataProvider
+ */
+ public function testUnserializeException($value)
+ {
+ $this->json->unserialize($value);
+ }
+
+ /**
+ * @return array
+ */
+ public function unserializeExceptionDataProvider(): array
+ {
+ return [
+ [''],
+ [false],
+ [null],
+ ['{']
+ ];
+ }
+}