Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions app/code/Magento/Checkout/Block/Onepage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -48,37 +48,39 @@ 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,
\Magento\Framework\Data\Form\FormKey $formKey,
\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;
$this->_isScopePrivate = true;
$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()
{
foreach ($this->layoutProcessors as $processor) {
$this->jsLayout = $processor->process($this->jsLayout);
}

return json_encode($this->jsLayout, JSON_HEX_TAG);
return $this->serializer->serialize($this->jsLayout);
}

/**
Expand Down Expand Up @@ -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());
}
}
9 changes: 6 additions & 3 deletions app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class OnepageTest extends \PHPUnit\Framework\TestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $serializer;
private $serializerMock;

protected function setUp()
{
Expand All @@ -49,15 +49,16 @@ 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,
$this->formKeyMock,
$this->configProviderMock,
[$this->layoutProcessorMock],
[],
$this->serializer
$this->serializerMock,
$this->serializerMock
);
}

Expand Down Expand Up @@ -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());
}
Expand All @@ -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());
}
Expand Down
1 change: 1 addition & 0 deletions app/code/Magento/Checkout/etc/frontend/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<item name="totalsSortOrder" xsi:type="object">Magento\Checkout\Block\Checkout\TotalsProcessor</item>
<item name="directoryData" xsi:type="object">Magento\Checkout\Block\Checkout\DirectoryDataProcessor</item>
</argument>
<argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\JsonHexTag</argument>
</arguments>
</type>
<type name="Magento\Checkout\Block\Cart\Totals">
Expand Down
14 changes: 12 additions & 2 deletions app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions lib/internal/Magento/Framework/Serialize/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
35 changes: 35 additions & 0 deletions lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\Framework\Serialize\Serializer;

use Magento\Framework\Serialize\SerializerInterface;

/**
* Serialize data to JSON with the JSON_HEX_TAG option enabled
* (All < and > 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\Framework\Serialize\Test\Unit\Serializer;

use Magento\Framework\DataObject;
use Magento\Framework\Serialize\Serializer\JsonHexTag;

class JsonHexTagTest extends \PHPUnit\Framework\TestCase
{
/**
* @var \Magento\Framework\Serialize\Serializer\Json
*/
private $json;

protected function setUp()
{
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->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],
['{']
];
}
}