Skip to content

Commit 40bfecb

Browse files
authored
Merge pull request #80 from steinkel/feature/79-fix-vendor-plugins
Feature/79 fix vendor plugins
1 parent eb81b7e commit 40bfecb

File tree

2 files changed

+107
-31
lines changed

2 files changed

+107
-31
lines changed

src/Plugin.php

Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,20 @@ public function preAutoloadDump(Event $event): void
6565

6666
$root = dirname(realpath($event->getComposer()->getConfig()->get('vendor-dir'))) . '/';
6767
foreach ($extra['plugin-paths'] as $pluginsPath) {
68-
if (!is_dir($root . $pluginsPath)) {
68+
$pluginPath = $root . $pluginsPath;
69+
if (!is_dir($pluginPath)) {
6970
continue;
7071
}
71-
foreach (new DirectoryIterator($root . $pluginsPath) as $fileInfo) {
72-
if (!$fileInfo->isDir() || $fileInfo->isDot()) {
73-
continue;
74-
}
72+
foreach ($this->findAppPlugins($pluginPath) as $pluginName => $pluginPath) {
73+
$namespace = str_replace('/', '\\', $pluginName) . '\\';
74+
$testNamespace = $namespace . 'Test\\';
75+
$path = $this->getRelativePath($pluginPath, $root);
7576

76-
$folderName = $fileInfo->getFilename();
77-
if ($folderName[0] === '.') {
78-
continue;
77+
if (!isset($autoload['psr-4'][$namespace])) {
78+
$autoload['psr-4'][$namespace] = $path . '/src';
7979
}
80-
81-
$pluginNamespace = $folderName . '\\';
82-
$pluginTestNamespace = $folderName . '\\Test\\';
83-
$path = $pluginsPath . '/' . $folderName . '/';
84-
if (!isset($autoload['psr-4'][$pluginNamespace]) && is_dir($root . $path . '/src')) {
85-
$autoload['psr-4'][$pluginNamespace] = $path . 'src';
86-
}
87-
if (!isset($devAutoload['psr-4'][$pluginTestNamespace]) && is_dir($root . $path . '/tests')) {
88-
$devAutoload['psr-4'][$pluginTestNamespace] = $path . 'tests';
80+
if (!isset($devAutoload['psr-4'][$testNamespace]) && is_dir($pluginPath . '/tests')) {
81+
$devAutoload['psr-4'][$testNamespace] = $path . '/tests';
8982
}
9083
}
9184
}
@@ -154,28 +147,102 @@ public function findPlugins(
154147

155148
foreach ($pluginDirs as $path) {
156149
$path = $this->getFullPath($path, $vendorDir);
157-
if (is_dir($path)) {
158-
$dir = new DirectoryIterator($path);
159-
foreach ($dir as $info) {
160-
if (!$info->isDir() || $info->isDot()) {
161-
continue;
162-
}
163-
164-
$name = $info->getFilename();
165-
if ($name[0] === '.') {
166-
continue;
167-
}
168-
169-
$plugins[$name] = $path . DIRECTORY_SEPARATOR . $name;
170-
}
150+
if (!is_dir($path)) {
151+
continue;
171152
}
153+
$plugins += $this->findAppPlugins($path, true);
172154
}
173155

174156
ksort($plugins);
175157

176158
return $plugins;
177159
}
178160

161+
/**
162+
* Find application plugins in a plugin path.
163+
*
164+
* Supports both `plugins/MyPlugin/src` and `plugins/Vendor/Plugin/src`.
165+
* When requested, top-level directories with no plugin children are kept
166+
* for backward compatibility.
167+
*
168+
* @param string $path The absolute plugin path.
169+
* @param bool $keepLegacyDirectories Whether to keep legacy top-level entries.
170+
* @return array<string, string>
171+
*/
172+
protected function findAppPlugins(string $path, bool $keepLegacyDirectories = false): array
173+
{
174+
$plugins = [];
175+
176+
foreach (new DirectoryIterator($path) as $info) {
177+
if ($this->shouldSkipDirectory($info)) {
178+
continue;
179+
}
180+
181+
$name = $info->getFilename();
182+
$pluginPath = $info->getPathname();
183+
if ($this->isPluginDirectory($pluginPath)) {
184+
$plugins[$name] = $pluginPath;
185+
186+
continue;
187+
}
188+
189+
$vendorPlugins = [];
190+
foreach (new DirectoryIterator($pluginPath) as $subInfo) {
191+
if ($this->shouldSkipDirectory($subInfo)) {
192+
continue;
193+
}
194+
195+
$subName = $subInfo->getFilename();
196+
$subPluginPath = $subInfo->getPathname();
197+
if ($this->isPluginDirectory($subPluginPath)) {
198+
$vendorPlugins[$name . '/' . $subName] = $subPluginPath;
199+
}
200+
}
201+
202+
if ($vendorPlugins) {
203+
$plugins += $vendorPlugins;
204+
205+
continue;
206+
}
207+
if ($keepLegacyDirectories) {
208+
$plugins[$name] = $pluginPath;
209+
}
210+
}
211+
212+
return $plugins;
213+
}
214+
215+
/**
216+
* @param \DirectoryIterator $info Directory iterator entry.
217+
* @return bool
218+
*/
219+
protected function shouldSkipDirectory(DirectoryIterator $info): bool
220+
{
221+
return !$info->isDir() || $info->isDot() || $info->getFilename()[0] === '.';
222+
}
223+
224+
/**
225+
* @param string $path Directory path.
226+
* @return bool
227+
*/
228+
protected function isPluginDirectory(string $path): bool
229+
{
230+
return is_dir($path . DIRECTORY_SEPARATOR . 'src');
231+
}
232+
233+
/**
234+
* @param string $path Absolute plugin path.
235+
* @param string $root Absolute application root path.
236+
* @return string
237+
*/
238+
protected function getRelativePath(string $path, string $root): string
239+
{
240+
$path = str_replace('\\', '/', $path);
241+
$root = str_replace('\\', '/', $root);
242+
243+
return trim(substr($path, strlen($root)), '/');
244+
}
245+
179246
/**
180247
* Turns relative paths in full paths.
181248
*

tests/TestCase/PluginTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class PluginTest extends TestCase
3939
'plugins/Fee/tests',
4040
'plugins/Foe/src',
4141
'plugins/Fum',
42+
'plugins/LegacyVendor/Childless',
43+
'plugins/YourVendor/YourPlugin/src',
44+
'plugins/YourVendor/YourPlugin/tests',
4245
'app_plugins/Bar/src',
4346
'app_plugins/Bar/tests',
4447
];
@@ -148,6 +151,7 @@ public function testPreAutoloadDump()
148151
'Foo\\' => 'xyz/Foo/src',
149152
'Fee\\' => 'plugins/Fee/src',
150153
'Foe\\' => 'plugins/Foe/src',
154+
'YourVendor\\YourPlugin\\' => 'plugins/YourVendor/YourPlugin/src',
151155
'Bar\\' => 'app_plugins/Bar/src',
152156
],
153157
];
@@ -157,6 +161,7 @@ public function testPreAutoloadDump()
157161
'psr-4' => [
158162
'Foo\Test\\' => 'xyz/Foo/tests',
159163
'Fee\Test\\' => 'plugins/Fee/tests',
164+
'YourVendor\\YourPlugin\\Test\\' => 'plugins/YourVendor/YourPlugin/tests',
160165
'Bar\Test\\' => 'app_plugins/Bar/tests',
161166
],
162167
];
@@ -322,8 +327,10 @@ public function testFindPlugins()
322327
'Foe' => $this->path . '/plugins/Foe',
323328
'Foo' => $this->path . '/plugins/Foo',
324329
'Fum' => $this->path . '/plugins/Fum',
330+
'LegacyVendor' => $this->path . '/plugins/LegacyVendor',
325331
'Princess' => $this->path . '/vendor/cakephp/princess',
326332
'TheThing' => $this->path . '/vendor/cakephp/the-thing',
333+
'YourVendor/YourPlugin' => $this->path . '/plugins/YourVendor/YourPlugin',
327334
];
328335
$this->assertSame($expected, $return, 'Composer and application plugins should be listed');
329336

@@ -339,8 +346,10 @@ public function testFindPlugins()
339346
'Foe' => $this->path . '/plugins/Foe',
340347
'Foo' => $this->path . '/plugins/Foo',
341348
'Fum' => $this->path . '/plugins/Fum',
349+
'LegacyVendor' => $this->path . '/plugins/LegacyVendor',
342350
'Princess' => $this->path . '/vendor/cakephp/princess',
343351
'TheThing' => $this->path . '/vendor/cakephp/the-thing',
352+
'YourVendor/YourPlugin' => $this->path . '/plugins/YourVendor/YourPlugin',
344353
];
345354
$this->assertSame($expected, $return, 'Composer and application plugins should be listed');
346355
}

0 commit comments

Comments
 (0)