Skip to content

Commit 5dfd1d3

Browse files
author
Oleksii Korshenko
committed
MAGETWO-70798: Fall back to parent directory if composer.json not found in module directory #8990
- Merge Pull Request #8990 from schmengler/magento2:component-registrar - Merged commits: 1. eb1fa97 2. 479a443 3. 9768fc6 4. 29bc089 5. e45250a 6. 4355d78 7. 3319ea0 8. 42044b9 9. bd53786 10. c053bcc
2 parents c955c32 + c053bcc commit 5dfd1d3

File tree

3 files changed

+238
-21
lines changed

3 files changed

+238
-21
lines changed

app/code/Magento/SampleData/Model/Dependency.php

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
*/
66
namespace Magento\SampleData\Model;
77

8+
use Magento\Framework\App\ObjectManager;
89
use Magento\Framework\Component\ComponentRegistrar;
10+
use Magento\Framework\Component\ComponentRegistrarInterface;
911
use Magento\Framework\Composer\ComposerInformation;
10-
use Magento\Framework\Filesystem;
1112
use Magento\Framework\Config\Composer\Package;
1213
use Magento\Framework\Config\Composer\PackageFactory;
14+
use Magento\Framework\Filesystem;
1315

1416
/**
1517
* Sample Data dependency
@@ -26,43 +28,54 @@ class Dependency
2628
*/
2729
protected $composerInformation;
2830

29-
/**
30-
* @var Filesystem
31-
*/
32-
private $filesystem;
33-
3431
/**
3532
* @var PackageFactory
3633
*/
3734
private $packageFactory;
3835

3936
/**
40-
* @var ComponentRegistrar
37+
* @var ComponentRegistrarInterface
4138
*/
4239
private $componentRegistrar;
4340

41+
/**
42+
* @var Filesystem\Directory\ReadInterfaceFactory
43+
*/
44+
private $directoryReadFactory;
45+
46+
//@codingStandardsIgnoreStart
4447
/**
4548
* @param ComposerInformation $composerInformation
4649
* @param Filesystem $filesystem
4750
* @param PackageFactory $packageFactory
48-
* @param ComponentRegistrar $componentRegistrar
51+
* @param ComponentRegistrarInterface $componentRegistrar
52+
* @param Filesystem\Directory\ReadInterfaceFactory $directoryReadFactory
53+
* @throws \RuntimeException
4954
*/
5055
public function __construct(
5156
ComposerInformation $composerInformation,
57+
// $filesystem kept for BC
5258
Filesystem $filesystem,
5359
PackageFactory $packageFactory,
54-
ComponentRegistrar $componentRegistrar
60+
ComponentRegistrarInterface $componentRegistrar,
61+
// $directoryReadFactory optional for BC
62+
Filesystem\Directory\ReadInterfaceFactory $directoryReadFactory = null
5563
) {
5664
$this->composerInformation = $composerInformation;
57-
$this->filesystem = $filesystem;
5865
$this->packageFactory = $packageFactory;
5966
$this->componentRegistrar = $componentRegistrar;
67+
if ($directoryReadFactory === null) {
68+
$directoryReadFactory = ObjectManager::getInstance()->get(Filesystem\Directory\ReadInterfaceFactory::class);
69+
}
70+
$this->directoryReadFactory = $directoryReadFactory;
6071
}
72+
//@codingStandardsIgnoreEnd
6173

6274
/**
6375
* Retrieve list of sample data packages from suggests
6476
*
6577
* @return array
78+
* @throws \Magento\Framework\Exception\FileSystemException
6679
*/
6780
public function getSampleDataPackages()
6881
{
@@ -81,19 +94,13 @@ public function getSampleDataPackages()
8194
* Retrieve suggested sample data packages from modules composer.json
8295
*
8396
* @return array
97+
* @throws \Magento\Framework\Exception\FileSystemException
8498
*/
8599
protected function getSuggestsFromModules()
86100
{
87101
$suggests = [];
88102
foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleDir) {
89-
$file = $moduleDir . '/composer.json';
90-
91-
if (!file_exists($file) || !is_readable($file)) {
92-
continue;
93-
}
94-
95-
/** @var Package $package */
96-
$package = $this->getModuleComposerPackage($file);
103+
$package = $this->getModuleComposerPackage($moduleDir);
97104
$suggest = json_decode(json_encode($package->get('suggest')), true);
98105
if (!empty($suggest)) {
99106
$suggests += $suggest;
@@ -105,11 +112,26 @@ protected function getSuggestsFromModules()
105112
/**
106113
* Load package
107114
*
108-
* @param string $file
115+
* @param string $moduleDir
109116
* @return Package
117+
* @throws \Magento\Framework\Exception\FileSystemException
110118
*/
111-
protected function getModuleComposerPackage($file)
119+
private function getModuleComposerPackage($moduleDir)
112120
{
113-
return $this->packageFactory->create(['json' => json_decode(file_get_contents($file))]);
121+
/*
122+
* Also look in parent directory of registered module directory to allow modules to follow the pds/skeleton
123+
* standard and have their source code in a "src" subdirectory of the repository
124+
*
125+
* see: https://github.com/php-pds/skeleton
126+
*/
127+
foreach ([$moduleDir, $moduleDir . DIRECTORY_SEPARATOR . '..'] as $dir) {
128+
/** @var Filesystem\Directory\ReadInterface $directory */
129+
$directory = $this->directoryReadFactory->create(['path' => $dir]);
130+
if ($directory->isExist('composer.json') && $directory->isReadable('composer.json')) {
131+
/** @var Package $package */
132+
return $this->packageFactory->create(['json' => json_decode($directory->readFile('composer.json'))]);
133+
}
134+
}
135+
return $this->packageFactory->create(['json' => new \stdClass]);
114136
}
115137
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\SampleData\Test\Unit\Model;
7+
8+
use Magento\Framework\Component\ComponentRegistrar;
9+
use Magento\Framework\Component\ComponentRegistrarInterface;
10+
use Magento\Framework\Composer\ComposerInformation;
11+
use Magento\Framework\Config\Composer\Package;
12+
use Magento\Framework\Config\Composer\PackageFactory;
13+
use Magento\Framework\Exception\FileSystemException;
14+
use Magento\Framework\Filesystem;
15+
use Magento\Framework\Phrase;
16+
use Magento\SampleData\Model\Dependency;
17+
18+
class DependencyTest extends \PHPUnit_Framework_TestCase
19+
{
20+
/**
21+
* @dataProvider dataPackagesFromComposerSuggest
22+
* @param string[] $moduleDirectories
23+
* @param callable $composerJsonGenerator
24+
* @param string[] $suggestionsFromLockFile
25+
* @param string[] $expectedPackages
26+
*/
27+
public function testPackagesFromComposerSuggest(
28+
array $moduleDirectories,
29+
callable $composerJsonGenerator,
30+
array $suggestionsFromLockFile,
31+
array $expectedPackages
32+
) {
33+
/** @var ComposerInformation|\PHPUnit_Framework_MockObject_MockObject $composerInformation */
34+
$composerInformation = $this->getMockBuilder(ComposerInformation::class)
35+
->disableOriginalConstructor()
36+
->getMock();
37+
$composerInformation->method('getSuggestedPackages')
38+
->willReturn($suggestionsFromLockFile);
39+
40+
/** @var Filesystem|\PHPUnit_Framework_MockObject_MockObject $filesystem */
41+
$filesystem = $this->getMockBuilder(Filesystem::class)->disableOriginalConstructor()->getMock();
42+
43+
/** @var PackageFactory|\PHPUnit_Framework_MockObject_MockObject $packageFactory */
44+
$packageFactory = $this->getMockBuilder(PackageFactory::class)
45+
->disableOriginalConstructor()
46+
->setMethods(['create'])
47+
->getMock();
48+
$packageFactory->method('create')
49+
->willReturnCallback(function ($args) {
50+
return new Package($args['json']);
51+
});
52+
53+
/** @var ComponentRegistrarInterface|\PHPUnit_Framework_MockObject_MockObject $componentRegistrar */
54+
$componentRegistrar = $this->getMockBuilder(ComponentRegistrarInterface::class)
55+
->getMockForAbstractClass();
56+
$componentRegistrar->method('getPaths')
57+
->with(ComponentRegistrar::MODULE)
58+
->willReturn(
59+
$moduleDirectories
60+
);
61+
62+
$directoryReadFactory = $this->getMockBuilder(Filesystem\Directory\ReadInterfaceFactory::class)
63+
->disableOriginalConstructor()
64+
->setMethods(['create'])
65+
->getMock();
66+
$directoryReadFactory->method('create')
67+
->willReturnMap($composerJsonGenerator($this));
68+
69+
$dependency = new Dependency(
70+
$composerInformation,
71+
$filesystem,
72+
$packageFactory,
73+
$componentRegistrar,
74+
$directoryReadFactory
75+
);
76+
$this->assertEquals($expectedPackages, $dependency->getSampleDataPackages());
77+
}
78+
public static function dataPackagesFromComposerSuggest()
79+
{
80+
return [
81+
[
82+
'moduleDirectories' => [
83+
'app/code/LocalModule',
84+
'app/code/LocalModuleWithoutComposerJson',
85+
'vendor/company/module',
86+
'vendor/company2/module/src'
87+
],
88+
'composerJsonGenerator' => function (DependencyTest $test) {
89+
return [
90+
[
91+
['path' => 'app/code/LocalModule'],
92+
$test->stubComposerJsonReader(
93+
[
94+
'name' => 'local/module',
95+
'suggest' => [
96+
'local/module-sample-data' => Dependency::SAMPLE_DATA_SUGGEST . '0.1.0'
97+
]
98+
]
99+
)
100+
],
101+
[
102+
['path' => 'app/code/LocalModuleWithoutComposerJson'],
103+
$test->stubFileNotFoundReader()
104+
],
105+
[
106+
['path' => 'vendor/company/module'],
107+
$test->stubComposerJsonReader(
108+
[
109+
'name' => 'company/module',
110+
'suggest' => [
111+
'company/module-sample-data' => Dependency::SAMPLE_DATA_SUGGEST . '1.0.0-beta'
112+
]
113+
]
114+
)
115+
],
116+
[
117+
['path' => 'vendor/company2/module/src/..'],
118+
$test->stubComposerJsonReader(
119+
[
120+
'name' => 'company2/module',
121+
'suggest' => [
122+
'company2/module-sample-data' => Dependency::SAMPLE_DATA_SUGGEST . '1.10'
123+
]
124+
]
125+
)
126+
],
127+
[
128+
['path' => 'vendor/company2/module/src'],
129+
$test->stubFileNotFoundReader()
130+
],
131+
[
132+
['path' => 'vendor/company/module/..'],
133+
$test->stubFileNotFoundReader()
134+
],
135+
[
136+
['path' => 'app/code/LocalModuleWithoutComposerJson/..'],
137+
$test->stubFileNotFoundReader()
138+
],
139+
[
140+
['path' => 'app/code/LocalModule/..'],
141+
$test->stubFileNotFoundReader()
142+
],
143+
];
144+
},
145+
'suggestions' => [
146+
'magento/foo-sample-data' => Dependency::SAMPLE_DATA_SUGGEST . '100.0.0',
147+
'thirdparty/bar-sample-data' => Dependency::SAMPLE_DATA_SUGGEST . '1.2.3',
148+
'thirdparty/something-else' => 'Just a suggested package',
149+
],
150+
'expectedPackages' => [
151+
'magento/foo-sample-data' => '100.0.0',
152+
'thirdparty/bar-sample-data' => '1.2.3',
153+
'local/module-sample-data' => '0.1.0',
154+
'company/module-sample-data' => '1.0.0-beta',
155+
'company2/module-sample-data' => '1.10',
156+
]
157+
]
158+
];
159+
}
160+
161+
public function stubComposerJsonReader(array $composerJsonContent)
162+
{
163+
$stub = $this->getMockBuilder(Filesystem\Directory\ReadInterface::class)
164+
->getMockForAbstractClass();
165+
$stub->method('isExist')
166+
->with('composer.json')
167+
->willReturn(true);
168+
$stub->method('isReadable')
169+
->with('composer.json')
170+
->willReturn(true);
171+
$stub->method('readFile')
172+
->with('composer.json')
173+
->willReturn(json_encode($composerJsonContent));
174+
return $stub;
175+
}
176+
177+
public function stubFileNotFoundReader()
178+
{
179+
$stub = $this->getMockBuilder(Filesystem\Directory\ReadInterface::class)
180+
->getMockForAbstractClass();
181+
$stub->method('isExist')
182+
->with('composer.json')
183+
->willReturn(false);
184+
$stub->method('readFile')
185+
->with('composer.json')
186+
->willThrowException(new FileSystemException(new Phrase('File not found')));
187+
return $stub;
188+
}
189+
}

app/code/Magento/SampleData/etc/di.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@
1515
</argument>
1616
</arguments>
1717
</type>
18+
<virtualType name="Magento\SampleData\Filesystem\Directory\Read" type="Magento\Framework\Filesystem\Directory\Read">
19+
<arguments>
20+
<argument name="driver" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
21+
</arguments>
22+
</virtualType>
23+
<preference for="Magento\Framework\Filesystem\Directory\ReadInterface" type="Magento\SampleData\Filesystem\Directory\Read" />
1824
</config>

0 commit comments

Comments
 (0)