Skip to content

Commit 51a0caa

Browse files
ENGCOM-8884: Github #31984: fix for array offset bug during some 404s (PHP 7.4) #31985
- Merge Pull Request #31985 from erfanimani/magento2:github-31984-fix-array-offset-bug - Merged commits: 1. 6316620 2. 6da61df 3. ae62eef
2 parents 3f697e0 + ae62eef commit 51a0caa

File tree

2 files changed

+332
-2
lines changed

2 files changed

+332
-2
lines changed

app/code/Magento/CatalogUrlRewrite/Model/Storage/DynamicStorage.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,16 @@ private function findProductRewriteByRequestPath(array $data): ?array
179179
unset($data[UrlRewrite::IS_AUTOGENERATED]);
180180
$categoryFromDb = $this->connection->fetchRow($this->prepareSelect($data));
181181

182+
if ($categoryFromDb === false) {
183+
return null;
184+
}
185+
182186
if ($categoryFromDb[UrlRewrite::REDIRECT_TYPE]) {
183187
$productFromDb[UrlRewrite::REDIRECT_TYPE] = OptionProvider::PERMANENT;
184188
$categoryPath = str_replace($categorySuffix, '', $categoryFromDb[UrlRewrite::TARGET_PATH]);
185189
}
186190

187-
if ($categoryFromDb === false
188-
|| !$productResource->canBeShowInCategory(
191+
if (!$productResource->canBeShowInCategory(
189192
$productFromDb[UrlRewrite::ENTITY_ID],
190193
$categoryFromDb[UrlRewrite::ENTITY_ID]
191194
)
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
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\CatalogUrlRewrite\Test\Unit\Model\Storage;
9+
10+
use Magento\Catalog\Model\ResourceModel\Product;
11+
use Magento\Catalog\Model\ResourceModel\ProductFactory;
12+
use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator;
13+
use Magento\CatalogUrlRewrite\Model\Storage\DynamicStorage;
14+
use Magento\Framework\Api\DataObjectHelper;
15+
use Magento\Framework\App\Config\ScopeConfigInterface;
16+
use Magento\Framework\App\ResourceConnection;
17+
use Magento\Framework\DB\Adapter\AdapterInterface;
18+
use Magento\Framework\DB\Select;
19+
use Magento\UrlRewrite\Model\OptionProvider;
20+
use Magento\Store\Model\ScopeInterface;
21+
use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory;
22+
use PHPUnit\Framework\MockObject\MockObject;
23+
use PHPUnit\Framework\TestCase;
24+
use ReflectionMethod;
25+
26+
class DynamicStorageTest extends TestCase
27+
{
28+
/**
29+
* @var DynamicStorage
30+
*/
31+
private $object;
32+
33+
/**
34+
* @var UrlRewriteFactory|MockObject
35+
*/
36+
private $urlRewriteFactoryMock;
37+
38+
/**
39+
* @var DataObjectHelper|MockObject
40+
*/
41+
private $dataObjectHelperMock;
42+
43+
/**
44+
* @var AdapterInterface|MockObject
45+
*/
46+
private $connectionMock;
47+
48+
/**
49+
* @var Select|MockObject
50+
*/
51+
private $selectMock;
52+
53+
/**
54+
* @var ResourceConnection|MockObject
55+
*/
56+
private $resourceConnectionMock;
57+
58+
/**
59+
* @var ScopeConfigInterface|MockObject
60+
*/
61+
private $scopeConfigMock;
62+
63+
/**
64+
* @var Product|MockObject
65+
*/
66+
private $productResourceMock;
67+
68+
/**
69+
* @var ProductFactory|MockObject
70+
*/
71+
private $productFactoryMock;
72+
73+
/**
74+
* @inheritdoc
75+
*/
76+
protected function setUp(): void
77+
{
78+
$this->urlRewriteFactoryMock = $this->getMockBuilder(UrlRewriteFactory::class)
79+
->disableOriginalConstructor()
80+
->getMock();
81+
82+
$this->dataObjectHelperMock = $this->getMockBuilder(DataObjectHelper::class)
83+
->disableOriginalConstructor()
84+
->getMock();
85+
86+
$this->connectionMock = $this->getMockBuilder(AdapterInterface::class)
87+
->disableOriginalConstructor()
88+
->getMock();
89+
90+
$this->selectMock = $this->getMockBuilder(Select::class)
91+
->disableOriginalConstructor()
92+
->getMock();
93+
94+
$this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class)
95+
->disableOriginalConstructor()
96+
->getMock();
97+
98+
$this->connectionMock
99+
->method('select')
100+
->willReturn($this->selectMock);
101+
102+
$this->resourceConnectionMock
103+
->method('getConnection')
104+
->willReturn($this->connectionMock);
105+
106+
$this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)
107+
->getMock();
108+
109+
$this->productResourceMock = $this->getMockBuilder(Product::class)
110+
->disableOriginalConstructor()
111+
->getMock();
112+
113+
$this->productFactoryMock = $this->getMockBuilder(ProductFactory::class)
114+
->disableOriginalConstructor()
115+
->getMock();
116+
117+
$this->productFactoryMock
118+
->method('create')
119+
->willReturn($this->productResourceMock);
120+
121+
$this->object = new DynamicStorage(
122+
$this->urlRewriteFactoryMock,
123+
$this->dataObjectHelperMock,
124+
$this->resourceConnectionMock,
125+
$this->scopeConfigMock,
126+
$this->productFactoryMock
127+
);
128+
}
129+
130+
/**
131+
* @dataProvider findProductRewriteByRequestPathDataProvider
132+
* @param array $data
133+
* @param array|false $productFromDb
134+
* @param string $categorySuffix
135+
* @param array|false $categoryFromDb
136+
* @param bool $canBeShownInCategory
137+
* @param array|null $expectedProductRewrite
138+
* @throws \ReflectionException
139+
*/
140+
public function testFindProductRewriteByRequestPath(
141+
array $data,
142+
$productFromDb,
143+
string $categorySuffix,
144+
$categoryFromDb,
145+
bool $canBeShownInCategory,
146+
?array $expectedProductRewrite
147+
): void {
148+
$this->connectionMock->expects($this->any())
149+
->method('fetchRow')
150+
->will($this->onConsecutiveCalls($productFromDb, $categoryFromDb));
151+
152+
$scopeConfigMap = [
153+
[
154+
CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX,
155+
ScopeInterface::SCOPE_STORE,
156+
$data['store_id'],
157+
$categorySuffix
158+
]
159+
];
160+
161+
$this->scopeConfigMock
162+
->method('getValue')
163+
->willReturnMap($scopeConfigMap);
164+
165+
$this->productResourceMock
166+
->method('canBeShowInCategory')
167+
->willReturn($canBeShownInCategory);
168+
169+
$method = new ReflectionMethod($this->object, 'findProductRewriteByRequestPath');
170+
$method->setAccessible(true);
171+
172+
$this->assertSame($expectedProductRewrite, $method->invoke($this->object, $data));
173+
}
174+
175+
/**
176+
* @return array
177+
*/
178+
public function findProductRewriteByRequestPathDataProvider(): array
179+
{
180+
return [
181+
[
182+
// Non-existing product
183+
[
184+
'request_path' => 'test.html',
185+
'store_id' => 1
186+
],
187+
false,
188+
'',
189+
null,
190+
true,
191+
null
192+
],
193+
[
194+
// Non-existing category
195+
[
196+
'request_path' => 'a/test.html',
197+
'store_id' => 1
198+
],
199+
[
200+
'entity_type' => 'product',
201+
'entity_id' => '1',
202+
'request_path' => 'test.html',
203+
'target_path' => 'catalog/product/view/id/1',
204+
'redirect_type' => '0',
205+
],
206+
'.html',
207+
false,
208+
true,
209+
null
210+
],
211+
[
212+
// Existing category
213+
[
214+
'request_path' => 'shop/test.html',
215+
'store_id' => 1
216+
],
217+
[
218+
'entity_type' => 'product',
219+
'entity_id' => '1',
220+
'request_path' => 'test.html',
221+
'target_path' => 'catalog/product/view/id/1',
222+
'redirect_type' => '0',
223+
],
224+
'.html',
225+
[
226+
'entity_type' => 'category',
227+
'entity_id' => '3',
228+
'request_path' => 'shop.html',
229+
'target_path' => 'catalog/category/view/id/3',
230+
'redirect_type' => '0',
231+
],
232+
true,
233+
[
234+
'entity_type' => 'product',
235+
'entity_id' => '1',
236+
'request_path' => 'shop/test.html',
237+
'target_path' => 'catalog/product/view/id/1/category/3',
238+
'redirect_type' => '0',
239+
]
240+
],
241+
[
242+
// Existing category, but can't be shown in category
243+
[
244+
'request_path' => 'shop/test.html',
245+
'store_id' => 1
246+
],
247+
[
248+
'entity_type' => 'product',
249+
'entity_id' => '1',
250+
'request_path' => 'test.html',
251+
'target_path' => 'catalog/product/view/id/1',
252+
'redirect_type' => '0',
253+
],
254+
'.html',
255+
[
256+
'entity_type' => 'category',
257+
'entity_id' => '3',
258+
'request_path' => 'shop.html',
259+
'target_path' => 'catalog/category/view/id/3',
260+
'redirect_type' => '0',
261+
],
262+
false,
263+
null
264+
],
265+
[
266+
// Existing category, with product 301 redirect type
267+
[
268+
'request_path' => 'shop/test.html',
269+
'store_id' => 1
270+
],
271+
[
272+
'entity_type' => 'product',
273+
'entity_id' => '1',
274+
'request_path' => 'test.html',
275+
'target_path' => 'test-new.html',
276+
'redirect_type' => OptionProvider::PERMANENT,
277+
],
278+
'.html',
279+
[
280+
'entity_type' => 'category',
281+
'entity_id' => '3',
282+
'request_path' => 'shop.html',
283+
'target_path' => 'catalog/category/view/id/3',
284+
'redirect_type' => '0',
285+
],
286+
true,
287+
[
288+
'entity_type' => 'product',
289+
'entity_id' => '1',
290+
'request_path' => 'shop/test.html',
291+
'target_path' => 'shop/test-new.html',
292+
'redirect_type' => OptionProvider::PERMANENT,
293+
]
294+
],
295+
[
296+
// Existing category, with category 301 redirect type
297+
[
298+
'request_path' => 'shop/test.html',
299+
'store_id' => 1
300+
],
301+
[
302+
'entity_type' => 'product',
303+
'entity_id' => '1',
304+
'request_path' => 'test.html',
305+
'target_path' => 'catalog/product/view/id/1',
306+
'redirect_type' => '0',
307+
],
308+
'.html',
309+
[
310+
'entity_type' => 'category',
311+
'entity_id' => '3',
312+
'request_path' => 'shop.html',
313+
'target_path' => 'shop-new.html',
314+
'redirect_type' => OptionProvider::PERMANENT,
315+
],
316+
true,
317+
[
318+
'entity_type' => 'product',
319+
'entity_id' => '1',
320+
'request_path' => 'shop/test.html',
321+
'target_path' => 'shop-new/test.html',
322+
'redirect_type' => OptionProvider::PERMANENT,
323+
]
324+
],
325+
];
326+
}
327+
}

0 commit comments

Comments
 (0)