Skip to content

Commit

Permalink
added json hex tag serializer and use it.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Tommy Quissens committed Jan 14, 2019
1 parent 56b9c6c commit 7e3ea1b
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 13 deletions.
17 changes: 9 additions & 8 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,25 +48,26 @@ 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
*/
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);
}

/**
Expand All @@ -78,7 +79,7 @@ public function getJsLayout()
$this->jsLayout = $processor->process($this->jsLayout);
}

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

/**
Expand Down Expand Up @@ -120,6 +121,6 @@ public function getBaseUrl()
*/
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,116 @@
<?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],
['{']
];
}
}

0 comments on commit 7e3ea1b

Please sign in to comment.