Skip to content

Commit 7e3ea1b

Browse files
author
Tommy Quissens
committed
added json hex tag serializer and use it.
code formatting Backwards compatibility fix use JSON_HEX_TAG option for not breaking things use new serializer instead of json_encode use correct serializer in test declare return type use serializer updated unit testing, now uses serializer removed space declare strict type improved comment Test new serializer Test for greater than and lesser than symbol, required by JSON_HEX_TAG option split comment for readability Declare strict type on test Use di to inject cleanup curly brackets code cleanup removed empty phpdoc Fix BC
1 parent 56b9c6c commit 7e3ea1b

File tree

7 files changed

+180
-13
lines changed

7 files changed

+180
-13
lines changed

app/code/Magento/Checkout/Block/Onepage.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Onepage extends \Magento\Framework\View\Element\Template
3838
protected $layoutProcessors;
3939

4040
/**
41-
* @var \Magento\Framework\Serialize\Serializer\Json
41+
* @var \Magento\Framework\Serialize\SerializerInterface
4242
*/
4343
private $serializer;
4444

@@ -48,25 +48,26 @@ class Onepage extends \Magento\Framework\View\Element\Template
4848
* @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider
4949
* @param array $layoutProcessors
5050
* @param array $data
51-
* @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
52-
* @throws \RuntimeException
51+
* @param \Magento\Framework\Serialize\Serializer\Json $serializer
52+
* @param \Magento\Framework\Serialize\SerializerInterface $serializerInterface
5353
*/
5454
public function __construct(
5555
\Magento\Framework\View\Element\Template\Context $context,
5656
\Magento\Framework\Data\Form\FormKey $formKey,
5757
\Magento\Checkout\Model\CompositeConfigProvider $configProvider,
5858
array $layoutProcessors = [],
5959
array $data = [],
60-
\Magento\Framework\Serialize\Serializer\Json $serializer = null
60+
\Magento\Framework\Serialize\Serializer\Json $serializer = null,
61+
\Magento\Framework\Serialize\SerializerInterface $serializerInterface = null
6162
) {
6263
parent::__construct($context, $data);
6364
$this->formKey = $formKey;
6465
$this->_isScopePrivate = true;
6566
$this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : [];
6667
$this->configProvider = $configProvider;
6768
$this->layoutProcessors = $layoutProcessors;
68-
$this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
69-
->get(\Magento\Framework\Serialize\Serializer\Json::class);
69+
$this->serializer = $serializerInterface ?: \Magento\Framework\App\ObjectManager::getInstance()
70+
->get(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
7071
}
7172

7273
/**
@@ -78,7 +79,7 @@ public function getJsLayout()
7879
$this->jsLayout = $processor->process($this->jsLayout);
7980
}
8081

81-
return json_encode($this->jsLayout, JSON_HEX_TAG);
82+
return $this->serializer->serialize($this->jsLayout);
8283
}
8384

8485
/**
@@ -120,6 +121,6 @@ public function getBaseUrl()
120121
*/
121122
public function getSerializedCheckoutConfig()
122123
{
123-
return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG);
124+
return $this->serializer->serialize($this->getCheckoutConfig());
124125
}
125126
}

app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class OnepageTest extends \PHPUnit\Framework\TestCase
3535
/**
3636
* @var \PHPUnit_Framework_MockObject_MockObject
3737
*/
38-
private $serializer;
38+
private $serializerMock;
3939

4040
protected function setUp()
4141
{
@@ -49,15 +49,16 @@ protected function setUp()
4949
\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class
5050
);
5151

52-
$this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class);
52+
$this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
5353

5454
$this->model = new \Magento\Checkout\Block\Onepage(
5555
$contextMock,
5656
$this->formKeyMock,
5757
$this->configProviderMock,
5858
[$this->layoutProcessorMock],
5959
[],
60-
$this->serializer
60+
$this->serializerMock,
61+
$this->serializerMock
6162
);
6263
}
6364

@@ -93,6 +94,7 @@ public function testGetJsLayout()
9394
$processedLayout = ['layout' => ['processed' => true]];
9495
$jsonLayout = '{"layout":{"processed":true}}';
9596
$this->layoutProcessorMock->expects($this->once())->method('process')->with([])->willReturn($processedLayout);
97+
$this->serializerMock->expects($this->once())->method('serialize')->willReturn($jsonLayout);
9698

9799
$this->assertEquals($jsonLayout, $this->model->getJsLayout());
98100
}
@@ -101,6 +103,7 @@ public function testGetSerializedCheckoutConfig()
101103
{
102104
$checkoutConfig = ['checkout', 'config'];
103105
$this->configProviderMock->expects($this->once())->method('getConfig')->willReturn($checkoutConfig);
106+
$this->serializerMock->expects($this->once())->method('serialize')->willReturn(json_encode($checkoutConfig));
104107

105108
$this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig());
106109
}

app/code/Magento/Checkout/etc/frontend/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<item name="totalsSortOrder" xsi:type="object">Magento\Checkout\Block\Checkout\TotalsProcessor</item>
6060
<item name="directoryData" xsi:type="object">Magento\Checkout\Block\Checkout\DirectoryDataProcessor</item>
6161
</argument>
62+
<argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\JsonHexTag</argument>
6263
</arguments>
6364
</type>
6465
<type name="Magento\Checkout\Block\Cart\Totals">

app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
namespace Magento\Ui\TemplateEngine\Xhtml;
77

8+
use Magento\Framework\App\ObjectManager;
9+
use Magento\Framework\Serialize\Serializer\JsonHexTag;
810
use Magento\Framework\View\Layout\Generator\Structure;
911
use Magento\Framework\View\Element\UiComponentInterface;
1012
use Magento\Framework\View\TemplateEngine\Xhtml\Template;
@@ -42,25 +44,33 @@ class Result implements ResultInterface
4244
*/
4345
protected $logger;
4446

47+
/**
48+
* @var JsonHexTag
49+
*/
50+
private $jsonSerializer;
51+
4552
/**
4653
* @param Template $template
4754
* @param CompilerInterface $compiler
4855
* @param UiComponentInterface $component
4956
* @param Structure $structure
5057
* @param LoggerInterface $logger
58+
* @param JsonHexTag $jsonSerializer
5159
*/
5260
public function __construct(
5361
Template $template,
5462
CompilerInterface $compiler,
5563
UiComponentInterface $component,
5664
Structure $structure,
57-
LoggerInterface $logger
65+
LoggerInterface $logger,
66+
JsonHexTag $jsonSerializer = null
5867
) {
5968
$this->template = $template;
6069
$this->compiler = $compiler;
6170
$this->component = $component;
6271
$this->structure = $structure;
6372
$this->logger = $logger;
73+
$this->jsonSerializer = $jsonSerializer ?? ObjectManager::getInstance()->get(JsonHexTag::class);
6474
}
6575

6676
/**
@@ -81,7 +91,7 @@ public function getDocumentElement()
8191
public function appendLayoutConfiguration()
8292
{
8393
$layoutConfiguration = $this->wrapContent(
84-
json_encode($this->structure->generate($this->component), JSON_HEX_TAG)
94+
$this->jsonSerializer->serialize($this->structure->generate($this->component))
8595
);
8696
$this->template->append($layoutConfiguration);
8797
}

lib/internal/Magento/Framework/Serialize/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
**Serialize** library provides interface *SerializerInterface* and multiple implementations:
44

55
* *Json* - default implementation. Uses PHP native json_encode/json_decode functions;
6+
* *JsonHexTag* - default implementation. Uses PHP native json_encode/json_decode functions with `JSON_HEX_TAG` option enabled;
67
* *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.
78

89
Using *Serialize* implementation directly is discouraged, always use *SerializerInterface*, using *Serialize* implementation may lead to security vulnerabilities.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\Serialize\Serializer;
10+
11+
use Magento\Framework\Serialize\SerializerInterface;
12+
13+
/**
14+
* Serialize data to JSON with the JSON_HEX_TAG option enabled
15+
* (All < and > are converted to \u003C and \u003E),
16+
* unserialize JSON encoded data
17+
*
18+
* @api
19+
* @since 100.2.0
20+
*/
21+
class JsonHexTag extends Json implements SerializerInterface
22+
{
23+
/**
24+
* @inheritDoc
25+
* @since 100.2.0
26+
*/
27+
public function serialize($data): string
28+
{
29+
$result = json_encode($data, JSON_HEX_TAG);
30+
if (false === $result) {
31+
throw new \InvalidArgumentException('Unable to serialize value.');
32+
}
33+
return $result;
34+
}
35+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\Serialize\Test\Unit\Serializer;
10+
11+
use Magento\Framework\DataObject;
12+
use Magento\Framework\Serialize\Serializer\JsonHexTag;
13+
14+
class JsonHexTagTest extends \PHPUnit\Framework\TestCase
15+
{
16+
/**
17+
* @var \Magento\Framework\Serialize\Serializer\Json
18+
*/
19+
private $json;
20+
21+
protected function setUp()
22+
{
23+
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
24+
$this->json = $objectManager->getObject(JsonHexTag::class);
25+
}
26+
27+
/**
28+
* @param string|int|float|bool|array|null $value
29+
* @param string $expected
30+
* @dataProvider serializeDataProvider
31+
*/
32+
public function testSerialize($value, $expected)
33+
{
34+
$this->assertEquals(
35+
$expected,
36+
$this->json->serialize($value)
37+
);
38+
}
39+
40+
public function serializeDataProvider()
41+
{
42+
$dataObject = new DataObject(['something']);
43+
return [
44+
['', '""'],
45+
['string', '"string"'],
46+
[null, 'null'],
47+
[false, 'false'],
48+
[['a' => 'b', 'd' => 123], '{"a":"b","d":123}'],
49+
[123, '123'],
50+
[10.56, '10.56'],
51+
[$dataObject, '{}'],
52+
['< >', '"\u003C \u003E"'],
53+
];
54+
}
55+
56+
/**
57+
* @param string $value
58+
* @param string|int|float|bool|array|null $expected
59+
* @dataProvider unserializeDataProvider
60+
*/
61+
public function testUnserialize($value, $expected)
62+
{
63+
$this->assertEquals(
64+
$expected,
65+
$this->json->unserialize($value)
66+
);
67+
}
68+
69+
/**
70+
* @return array
71+
*/
72+
public function unserializeDataProvider(): array {
73+
return [
74+
['""', ''],
75+
['"string"', 'string'],
76+
['null', null],
77+
['false', false],
78+
['{"a":"b","d":123}', ['a' => 'b', 'd' => 123]],
79+
['123', 123],
80+
['10.56', 10.56],
81+
['{}', []],
82+
['"\u003C \u003E"', '< >'],
83+
];
84+
}
85+
86+
/**
87+
* @expectedException \InvalidArgumentException
88+
* @expectedExceptionMessage Unable to serialize value.
89+
*/
90+
public function testSerializeException()
91+
{
92+
$this->json->serialize(STDOUT);
93+
}
94+
95+
/**
96+
* @expectedException \InvalidArgumentException
97+
* @expectedExceptionMessage Unable to unserialize value.
98+
* @dataProvider unserializeExceptionDataProvider
99+
*/
100+
public function testUnserializeException($value)
101+
{
102+
$this->json->unserialize($value);
103+
}
104+
105+
/**
106+
* @return array
107+
*/
108+
public function unserializeExceptionDataProvider(): array {
109+
return [
110+
[''],
111+
[false],
112+
[null],
113+
['{']
114+
];
115+
}
116+
}

0 commit comments

Comments
 (0)