Skip to content

Commit 22472df

Browse files
authored
Merge pull request #2 from wubinworks/bugfix/geturl-incorrect-scope-and-dataobject-deserialization-infinite-loop
Fixed 2 critical bugs and extended compatibility to Magento 2.3 Fixed bugs: - getUrl method returns URL with incorrect scope - DataObject deserialization infinite loop
2 parents b8332c9 + f54f713 commit 22472df

12 files changed

+252
-155
lines changed

Model/Escaper.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function __construct(
8181
* Escape array input
8282
*
8383
* @param array $input
84-
* @param bool $strictMode For <= 2.4.3-p1. Needs to be the same with template filter
84+
* @param bool $strictMode
8585
* @return array
8686
*/
8787
public function escape(array $input, bool $strictMode = true): array

Model/Escaper/Debugger.php

+35-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
*/
1616
class Debugger
1717
{
18+
/**
19+
* @var int
20+
*/
21+
protected $maxDataObjectDebugDepth = 2;
22+
1823
/**
1924
* Debug mixed input
2025
*
@@ -77,12 +82,38 @@ protected function debugObject($input)
7782
*/
7883
protected function debugDataObject(DataObject $input): array
7984
{
80-
$data = $this->debugArray($input->getData());
85+
return $this->_debugDataObject($input);
86+
}
87+
88+
/**
89+
* Debug DataObject
90+
*
91+
* @param DataObject $dataObj
92+
* @param int $currDepth
93+
* @return array
94+
*/
95+
protected function _debugDataObject(DataObject $dataObj, int $currDepth = 1): array
96+
{
97+
$data = $dataObj->getData();
98+
$result = [];
99+
foreach ($data as $key => $item) {
100+
if ($item instanceof DataObject) {
101+
if ($currDepth < $this->maxDataObjectDebugDepth) {
102+
$result[$key] = $this->_debugDataObject($item, $currDepth + 1);
103+
} else {
104+
$result[$key] = get_class($item);
105+
}
106+
} else {
107+
$result[$key] = $this->debug($item);
108+
}
109+
}
110+
81111
$typeKey = '__TYPE__DataObject__';
82-
if ($input instanceof AbstractEmailTemplate) {
112+
if ($dataObj instanceof AbstractEmailTemplate) {
83113
$typeKey = '__TYPE__AbstractTemplate__';
84114
}
85-
$data[$typeKey] = get_class($input);
86-
return $data;
115+
$data[$typeKey] = get_class($dataObj);
116+
117+
return $result;
87118
}
88119
}

Model/Escaper/Filter.php

+24-19
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Magento\Framework\DataObject;
1111
use Magento\Email\Model\AbstractTemplate as AbstractEmailTemplate;
1212
use Wubinworks\TemplateFilterPatch\Model\Utils\SafeStringReplace;
13+
use Wubinworks\TemplateFilterPatch\Model\Utils\DataObjectDeserializer;
1314
use Wubinworks\TemplateFilterPatch\Model\SafeDataObject;
1415
use Wubinworks\TemplateFilterPatch\Model\SafeDataObjectFactory;
1516
use Wubinworks\TemplateFilterPatch\Model\SafeEmailTemplate;
@@ -32,6 +33,11 @@ class Filter
3233
*/
3334
protected $safeStringReplace;
3435

36+
/**
37+
* @var DataObjectDeserializer
38+
*/
39+
protected $dataObjectDeserializer;
40+
3541
/**
3642
* @var bool
3743
*/
@@ -51,17 +57,20 @@ class Filter
5157
* Constructor
5258
*
5359
* @param SafeStringReplace $safeStringReplace
60+
* @param DataObjectDeserializer $dataObjectDeserializer
5461
* @param SafeDataObjectFactory $safeDataObjectFactory
5562
* @param SafeEmailTemplateFactory $safeEmailTemplateFactory
5663
* @param string[] $prohibitedTypes
5764
*/
5865
public function __construct(
5966
SafeStringReplace $safeStringReplace,
67+
DataObjectDeserializer $dataObjectDeserializer,
6068
SafeDataObjectFactory $safeDataObjectFactory,
6169
SafeEmailTemplateFactory $safeEmailTemplateFactory,
6270
array $prohibitedTypes = []
6371
) {
6472
$this->safeStringReplace = $safeStringReplace;
73+
$this->dataObjectDeserializer = $dataObjectDeserializer;
6574
$this->safeDataObjectFactory = $safeDataObjectFactory;
6675
$this->safeEmailTemplateFactory = $safeEmailTemplateFactory;
6776
$this->prohibitedTypes = $prohibitedTypes;
@@ -182,40 +191,36 @@ protected function processObject($input, string $search, string $replace)
182191
*/
183192
protected function processDataObject(DataObject $input, string $search, string $replace): SafeDataObject
184193
{
185-
$data = $this->processArray($input->getData(), $search, $replace);
194+
$data = $this->dataObjectDeserializer->deserialize($input, 2);
195+
$data = $this->processArray($data, $search, $replace);
186196
return $this->safeDataObjectFactory->create(['data' => $data]);
187197
}
188198

189199
/**
190-
* Create safe email template
200+
* Create safe email template. A wrapper for calling `getUrl` method.
191201
*
192-
* @param DataObject|array $input
202+
* @param AbstractEmailTemplate $input
193203
*
194204
* @return SafeDataObject|AbstractEmailTemplate
195205
*
196206
* @throws \InvalidArgumentException
197207
*/
198-
protected function createSafeEmailTemplate($input): DataObject
208+
protected function createSafeEmailTemplate(AbstractEmailTemplate $input): DataObject
199209
{
200-
if ($input instanceof DataObject) {
201-
$data = $input->getData();
202-
} elseif (is_array($input)) {
203-
$data = $input;
204-
} else {
205-
throw new \InvalidArgumentException('$input must be DataObject or array.');
206-
}
207-
208-
$data = array_filter($data, function ($value) {
209-
return is_scalar($value) || $value === null;
210-
});
210+
$data = $this->dataObjectDeserializer->deserialize($input, 1);
211211

212212
if (class_exists(\Magento\Framework\Filter\VariableResolver\LegacyResolver::class)
213213
&& !$this->isStrictMode()) {
214-
// <= 2.4.3-p1
215-
return $this->safeDataObjectFactory->create(['data' => $data]);
214+
// LegacyResolver will be used. Need return DataObject instead of AbstractEmailTemplate
215+
return $this->safeDataObjectFactory->create([
216+
'_emailTemplate' => $input,
217+
'data' => $data
218+
]);
216219
} else {
217-
// Just for getUrl method
218-
return $this->safeEmailTemplateFactory->create(['data' => $data]);
220+
return $this->safeEmailTemplateFactory->create([
221+
'_emailTemplate' => $input,
222+
'data' => $data
223+
]);
219224
}
220225
}
221226

Model/SafeDataObject.php

+18-20
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,44 @@
1111
use Magento\Framework\ObjectManager\NoninterceptableInterface;
1212
use Magento\Email\Model\AbstractTemplate as AbstractEmailTemplate;
1313
use Magento\Store\Model\Store;
14-
use Wubinworks\TemplateFilterPatch\Model\StoreUrl;
14+
use Magento\Store\Model\StoreManagerInterface;
15+
use Wubinworks\TemplateFilterPatch\Traits\GetUrlTrait;
1516

1617
/**
1718
* Safe DataObject with getUrl method
1819
*
1920
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
21+
* @SuppressWarnings(PHPMD.FinalImplementation)
2022
*/
2123
// phpcs:ignore Magento2.PHP.FinalImplementation.FoundFinal
2224
final class SafeDataObject extends DataObject implements NoninterceptableInterface
2325
{
26+
use GetUrlTrait;
27+
28+
/**
29+
* @var StoreManagerInterface
30+
*/
31+
protected $storeManager;
32+
2433
/**
25-
* @var StoreUrl
34+
* @var AbstractEmailTemplate
2635
*/
27-
protected $storeUrlBuilder;
36+
protected $_emailTemplate;
2837

2938
/**
3039
* Constructor
3140
*
32-
* @param StoreUrl $storeUrlBuilder
41+
* @param StoreManagerInterface $storeManager
42+
* @param ?AbstractEmailTemplate $_emailTemplate
3343
* @param array $data
3444
*/
3545
public function __construct(
36-
StoreUrl $storeUrlBuilder,
46+
StoreManagerInterface $storeManager,
47+
?AbstractEmailTemplate $_emailTemplate = null,
3748
array $data = []
3849
) {
39-
$this->storeUrlBuilder= $storeUrlBuilder;
50+
$this->storeManager = $storeManager;
51+
$this->_emailTemplate= $_emailTemplate;
4052
$this->_data = $data;
4153
}
4254

@@ -95,18 +107,4 @@ public function offsetUnset($offset)
95107
{
96108
}
97109
// phpcs:enable
98-
99-
/**
100-
* Get URL by store
101-
*
102-
* @param Store|DataObject|int|string|null $store
103-
* @param string $route
104-
* @param array $params
105-
*
106-
* @return string|null
107-
*/
108-
public function getUrl($store, $route = '', $params = []): ?string
109-
{
110-
return $this->storeUrlBuilder->getUrl($store, $route, $params);
111-
}
112110
}

Model/SafeEmailTemplate.php

+19-20
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,43 @@
1111
use Magento\Framework\ObjectManager\NoninterceptableInterface;
1212
use Magento\Email\Model\AbstractTemplate as AbstractEmailTemplate;
1313
use Magento\Store\Model\Store;
14-
use Wubinworks\TemplateFilterPatch\Model\StoreUrl;
14+
use Magento\Store\Model\StoreManagerInterface;
15+
use Wubinworks\TemplateFilterPatch\Traits\GetUrlTrait;
1516

1617
/**
1718
* Safe email template object
19+
*
20+
* @SuppressWarnings(PHPMD.FinalImplementation)
1821
*/
1922
// phpcs:ignore Magento2.PHP.FinalImplementation.FoundFinal
2023
final class SafeEmailTemplate extends AbstractEmailTemplate implements NoninterceptableInterface
2124
{
25+
use GetUrlTrait;
26+
27+
/**
28+
* @var StoreManagerInterface
29+
*/
30+
protected $storeManager;
31+
2232
/**
23-
* @var StoreUrl
33+
* @var AbstractEmailTemplate
2434
*/
25-
protected $storeUrlBuilder;
35+
protected $_emailTemplate;
2636

2737
/**
2838
* Constructor
2939
*
30-
* @param StoreUrl $storeUrlBuilder
40+
* @param StoreManagerInterface $storeManager
41+
* @param ?AbstractEmailTemplate $_emailTemplate
3142
* @param array $data
3243
*/
3344
public function __construct(
34-
StoreUrl $storeUrlBuilder,
45+
StoreManagerInterface $storeManager,
46+
?AbstractEmailTemplate $_emailTemplate = null,
3547
array $data = []
3648
) {
37-
$this->storeUrlBuilder= $storeUrlBuilder;
49+
$this->storeManager = $storeManager;
50+
$this->_emailTemplate= $_emailTemplate;
3851
$this->_data = $data;
3952
}
4053

@@ -47,18 +60,4 @@ public function getType()
4760
{
4861
}
4962
// phpcs:enable
50-
51-
/**
52-
* Get URL by store
53-
*
54-
* @param Store|DataObject|int|string|null $store
55-
* @param string $route
56-
* @param array $params
57-
*
58-
* @return string|null
59-
*/
60-
public function getUrl($store, $route = '', $params = []): ?string
61-
{
62-
return $this->storeUrlBuilder->getUrl($store, $route, $params);
63-
}
6463
}

Model/StoreUrl.php

-62
This file was deleted.

0 commit comments

Comments
 (0)