Skip to content

Commit dc07ec7

Browse files
Merge branch '2.4.6-develop' into AC-5933
2 parents f39118a + 0e82160 commit dc07ec7

File tree

8 files changed

+345
-20
lines changed

8 files changed

+345
-20
lines changed

app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCashOnDeliveryPaymentForSpecificCountryTest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
<group value="checkout"/>
2121
</annotations>
2222
<before>
23+
<!--Enable flat rate shipping-->
24+
<magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/>
25+
2326
<!--Enable Cash On Delivery payment method-->
2427
<actionGroup ref="CashOnDeliverySpecificCountryActionGroup" stepKey="enableCashOnDelivery">
2528
<argument name="path" value="{{CashOnDeliveryEnableConfigData.path}}"/>

app/code/Magento/Cms/Controller/Noroute/Index.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@
44
* Copyright © Magento, Inc. All rights reserved.
55
* See COPYING.txt for license details.
66
*/
7+
declare(strict_types=1);
8+
79
namespace Magento\Cms\Controller\Noroute;
810

11+
use Magento\Framework\Controller\Result\ForwardFactory;
12+
913
/**
1014
* @SuppressWarnings(PHPMD.AllPurposeAction)
1115
*/
1216
class Index extends \Magento\Framework\App\Action\Action
1317
{
1418
/**
15-
* @var \Magento\Framework\Controller\Result\ForwardFactory
19+
* @var ForwardFactory
1620
*/
17-
protected $resultForwardFactory;
21+
protected ForwardFactory $resultForwardFactory;
1822

1923
/**
2024
* @param \Magento\Framework\App\Action\Context $context
@@ -48,6 +52,7 @@ public function execute()
4852
if ($resultPage) {
4953
$resultPage->setStatusHeader(404, '1.1', 'Not Found');
5054
$resultPage->setHeader('Status', '404 File not found');
55+
$resultPage->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0', true);
5156
return $resultPage;
5257
} else {
5358
/** @var \Magento\Framework\Controller\Result\Forward $resultForward */

app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@ class IndexTest extends TestCase
2929
/**
3030
* @var Index
3131
*/
32-
protected $_controller;
32+
protected Index $_controller;
3333

3434
/**
3535
* @var MockObject
3636
*/
37-
protected $_cmsHelperMock;
37+
protected MockObject $_cmsHelperMock;
3838

3939
/**
4040
* @var MockObject
4141
*/
42-
protected $_requestMock;
42+
protected MockObject $_requestMock;
4343

4444
/**
4545
* @var ForwardFactory|MockObject
@@ -119,10 +119,14 @@ public function testExecuteResultPage(): void
119119
->method('setStatusHeader')
120120
->with(404, '1.1', 'Not Found')
121121
->willReturn($this->resultPageMock);
122+
122123
$this->resultPageMock
123124
->method('setHeader')
124-
->with('Status', '404 File not found')
125-
->willReturn($this->resultPageMock);
125+
->withConsecutive(
126+
['Status', '404 File not found'],
127+
['Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0']
128+
)->willReturn($this->resultPageMock);
129+
126130
$this->_cmsHelperMock->expects(
127131
$this->once()
128132
)->method(

lib/internal/Magento/Framework/Filter/Template.php

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use Magento\Framework\Filter\DirectiveProcessor\TemplateDirective;
1919
use Magento\Framework\Filter\DirectiveProcessor\VarDirective;
2020
use Magento\Framework\Stdlib\StringUtils;
21+
use Magento\Framework\Filter\Template\SignatureProvider;
22+
use Magento\Framework\Filter\Template\FilteringDepthMeter;
2123

2224
/**
2325
* Template filter
@@ -100,24 +102,44 @@ class Template implements \Zend_Filter_Interface
100102
*/
101103
private $variableResolver;
102104

105+
/**
106+
* @var SignatureProvider|null
107+
*/
108+
private $signatureProvider;
109+
110+
/**
111+
* @var FilteringDepthMeter|null
112+
*/
113+
private $filteringDepthMeter;
114+
103115
/**
104116
* @param StringUtils $string
105117
* @param array $variables
106118
* @param DirectiveProcessorInterface[] $directiveProcessors
107119
* @param VariableResolverInterface|null $variableResolver
120+
* @param SignatureProvider|null $signatureProvider
121+
* @param FilteringDepthMeter|null $filteringDepthMeter
108122
*/
109123
public function __construct(
110124
StringUtils $string,
111125
$variables = [],
112126
$directiveProcessors = [],
113-
VariableResolverInterface $variableResolver = null
127+
VariableResolverInterface $variableResolver = null,
128+
SignatureProvider $signatureProvider = null,
129+
FilteringDepthMeter $filteringDepthMeter = null
114130
) {
115131
$this->string = $string;
116132
$this->setVariables($variables);
117133
$this->directiveProcessors = $directiveProcessors;
118134
$this->variableResolver = $variableResolver ?? ObjectManager::getInstance()
119135
->get(VariableResolverInterface::class);
120136

137+
$this->signatureProvider = $signatureProvider ?? ObjectManager::getInstance()
138+
->get(SignatureProvider::class);
139+
140+
$this->filteringDepthMeter = $filteringDepthMeter ?? ObjectManager::getInstance()
141+
->get(FilteringDepthMeter::class);
142+
121143
if (empty($directiveProcessors)) {
122144
$this->directiveProcessors = [
123145
'depend' => ObjectManager::getInstance()->get(DependDirective::class),
@@ -180,22 +202,118 @@ public function filter($value)
180202
)->render());
181203
}
182204

205+
$this->filteringDepthMeter->descend();
206+
207+
// Processing of template directives.
208+
$templateDirectivesResults = $this->processDirectives($value);
209+
210+
foreach ($templateDirectivesResults as $result) {
211+
$value = str_replace($result['directive'], $result['output'], $value);
212+
}
213+
214+
// Processing of deferred directives received from child templates
215+
// or nested directives.
216+
$deferredDirectivesResults = $this->processDirectives($value, true);
217+
218+
foreach ($deferredDirectivesResults as $result) {
219+
$value = str_replace($result['directive'], $result['output'], $value);
220+
}
221+
222+
if ($this->filteringDepthMeter->showMark() > 1) {
223+
// Signing own deferred directives (if any).
224+
$signature = $this->signatureProvider->get();
225+
226+
foreach ($templateDirectivesResults as $result) {
227+
if ($result['directive'] === $result['output']) {
228+
$value = str_replace(
229+
$result['output'],
230+
$signature . $result['output'] . $signature,
231+
$value
232+
);
233+
}
234+
}
235+
}
236+
237+
$value = $this->afterFilter($value);
238+
239+
$this->filteringDepthMeter->ascend();
240+
241+
return $value;
242+
}
243+
244+
/**
245+
* Processes template directives and returns an array that contains results produced by each directive.
246+
*
247+
* @param string $value
248+
* @param bool $isSigned
249+
*
250+
* @return array
251+
*
252+
* @throws InvalidArgumentException
253+
* @throws \Magento\Framework\Exception\LocalizedException
254+
*/
255+
private function processDirectives($value, $isSigned = false): array
256+
{
257+
$results = [];
258+
183259
foreach ($this->directiveProcessors as $directiveProcessor) {
184260
if (!$directiveProcessor instanceof DirectiveProcessorInterface) {
185261
throw new InvalidArgumentException(
186262
'Directive processors must implement ' . DirectiveProcessorInterface::class
187263
);
188264
}
189265

190-
if (preg_match_all($directiveProcessor->getRegularExpression(), $value, $constructions, PREG_SET_ORDER)) {
266+
$pattern = $directiveProcessor->getRegularExpression();
267+
268+
if ($isSigned) {
269+
$pattern = $this->embedSignatureIntoPattern($pattern);
270+
}
271+
272+
if (preg_match_all($pattern, $value, $constructions, PREG_SET_ORDER)) {
191273
foreach ($constructions as $construction) {
192274
$replacedValue = $directiveProcessor->process($construction, $this, $this->templateVars);
193-
$value = str_replace($construction[0], $replacedValue, $value);
275+
276+
$results[] = [
277+
'directive' => $construction[0],
278+
'output' => $replacedValue
279+
];
194280
}
195281
}
196282
}
197283

198-
return $this->afterFilter($value);
284+
return $results;
285+
}
286+
287+
/**
288+
* Modifies given regular expression pattern to be able to recognize signed directives.
289+
*
290+
* @param string $pattern
291+
*
292+
* @return string
293+
*
294+
* @throws \Magento\Framework\Exception\LocalizedException
295+
*/
296+
private function embedSignatureIntoPattern(string $pattern): string
297+
{
298+
$signature = $this->signatureProvider->get();
299+
300+
$closingDelimiters = [
301+
'(' => ')',
302+
'{' => '}',
303+
'[' => ']',
304+
'<' => '>'
305+
];
306+
307+
$closingDelimiter = $openingDelimiter = substr(trim($pattern), 0, 1);
308+
309+
if (array_key_exists($openingDelimiter, $closingDelimiters)) {
310+
$closingDelimiter = $closingDelimiters[$openingDelimiter];
311+
}
312+
313+
$pattern = substr_replace($pattern, $signature, strpos($pattern, $openingDelimiter) + 1, 0);
314+
$pattern = substr_replace($pattern, $signature, strrpos($pattern, $closingDelimiter), 0);
315+
316+
return $pattern;
199317
}
200318

201319
/**
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Filter\Template;
9+
10+
/**
11+
* Meter of template filtering depth.
12+
*
13+
* Records and provides template/directive filtering depth (filtering recursion).
14+
* Filtering depth 1 means that template or directive is root and has no parents.
15+
*/
16+
class FilteringDepthMeter
17+
{
18+
/**
19+
* @var int
20+
*/
21+
private $depth = 0;
22+
23+
/**
24+
* Increases filtering depth.
25+
*
26+
* @return void
27+
*/
28+
public function descend()
29+
{
30+
$this->depth++;
31+
}
32+
33+
/**
34+
* Decreases filtering depth.
35+
*
36+
* @return void
37+
*/
38+
public function ascend()
39+
{
40+
$this->depth--;
41+
}
42+
43+
/**
44+
* Shows current filtering depth.
45+
*
46+
* @return int
47+
*/
48+
public function showMark(): int
49+
{
50+
return $this->depth;
51+
}
52+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Filter\Template;
9+
10+
/**
11+
* Provider of a signature.
12+
*
13+
* Provides a signature which should be used to sign deferred directives
14+
* (directives that should be processed in scope of a parent template
15+
* instead of own scope, e.g. {{inlinecss}}).
16+
*/
17+
class SignatureProvider
18+
{
19+
/**
20+
* @var string|null
21+
*/
22+
private $signature;
23+
24+
/**
25+
* @var \Magento\Framework\Math\Random
26+
*/
27+
private $random;
28+
29+
/**
30+
* @param \Magento\Framework\Math\Random $random
31+
*/
32+
public function __construct(
33+
\Magento\Framework\Math\Random $random
34+
) {
35+
$this->random = $random;
36+
}
37+
38+
/**
39+
* Generates a random string which will be used as a signature during runtime.
40+
*
41+
* @return string
42+
*
43+
* @throws \Magento\Framework\Exception\LocalizedException
44+
*/
45+
public function get(): string
46+
{
47+
if ($this->signature === null) {
48+
$this->signature = $this->random->getRandomString(32);
49+
}
50+
51+
return $this->signature;
52+
}
53+
}

0 commit comments

Comments
 (0)