Plugin   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Importance

Changes 4
Bugs 3 Features 0
Metric Value
eloc 70
c 4
b 3
f 0
dl 0
loc 178
rs 10
wmc 11

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getSubscribedEvents() 0 4 1
A activate() 0 4 1
A write() 0 9 2
A extractDocBlock() 0 9 1
A resolvePlugins() 0 18 1
A extractPluginData() 0 33 2
A discover() 0 32 3
1
<?php
2
3
namespace Helick\MUPluginsDiscovery;
4
5
use Composer\Composer;
6
use Composer\EventDispatcher\EventSubscriberInterface;
7
use Composer\IO\IOInterface;
8
use Composer\Plugin\PluginInterface;
9
use Symfony\Component\Finder\Finder;
10
use Symfony\Component\Finder\SplFileInfo;
11
12
final class Plugin implements PluginInterface, EventSubscriberInterface
13
{
14
    /**
15
     * The composer instance.
16
     *
17
     * @var Composer
18
     */
19
    private $composer;
20
21
    /**
22
     * The IO instance.
23
     *
24
     * @var IOInterface
25
     */
26
    private $io;
27
28
    /**
29
     * @inheritDoc
30
     */
31
    public function activate(Composer $composer, IOInterface $io)
32
    {
33
        $this->composer = $composer;
34
        $this->io       = $io;
35
    }
36
37
    /**
38
     * @inheritDoc
39
     */
40
    public static function getSubscribedEvents()
41
    {
42
        return [
43
            'post-autoload-dump' => ['discover'],
44
        ];
45
    }
46
47
    /**
48
     * Rebuild the cached must-use plugins manifest.
49
     *
50
     * @return void
51
     */
52
    public function discover(): void
53
    {
54
        if (!is_dir($discoveryPath = getcwd() . '/web/content/mu-plugins')) {
55
            return;
56
        }
57
58
        $finder = new Finder();
59
        $finder->in($discoveryPath)
60
               ->files()->name('*.php')
61
               ->sortByName();
62
63
        $pluginsFinder   = (clone $finder)->depth(1);
64
        $muPluginsFinder = (clone $finder)->depth(0);
65
66
        $plugins   = $this->resolvePlugins($pluginsFinder);
67
        $muPlugins = $this->resolvePlugins($muPluginsFinder);
68
69
        foreach (array_column($plugins, 'Name') as $pluginName) {
70
            $this->io->write('Discovered plugin: ' . $pluginName, true, IOInterface::DEBUG);
71
        }
72
73
        $this->write(
74
            getcwd() . '/bootstrap/cache/mu-plugins.php',
75
            array_merge($plugins, $muPlugins)
76
        );
77
78
        $this->write(
79
            getcwd() . '/bootstrap/cache/plugins.php',
80
            array_keys($plugins)
81
        );
82
83
        $this->io->write('The must-use plugins manifest generated successfully.');
84
    }
85
86
    /**
87
     * Resolve plugins from a given finder.
88
     *
89
     * @param Finder $finder
90
     *
91
     * @return array
92
     */
93
    private function resolvePlugins(Finder $finder): array
94
    {
95
        $results = iterator_to_array($finder, false);
96
97
        $files = array_map(function (SplFileInfo $file) {
98
            return $file->getRelativePathname();
99
        }, $results);
100
101
        $data = array_map(function (SplFileInfo $file) {
102
            return $this->extractPluginData($this->extractDocBlock($file->getContents()));
103
        }, $results);
104
105
        $plugins = array_combine($files, $data);
106
        $plugins = array_filter($plugins, function (array $data) {
0 ignored issues
show
Bug introduced by
It seems like $plugins can also be of type false; however, parameter $input of array_filter() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

106
        $plugins = array_filter(/** @scrutinizer ignore-type */ $plugins, function (array $data) {
Loading history...
107
            return !empty($data['Name']);
108
        });
109
110
        return $plugins;
111
    }
112
113
    /**
114
     * Extract the doc block from a given source.
115
     *
116
     * @param string $source
117
     *
118
     * @return string
119
     */
120
    private function extractDocBlock(string $source): string
121
    {
122
        $comments = array_filter(token_get_all($source), function ($token) {
123
            return in_array($token[0], [T_COMMENT, T_DOC_COMMENT], true);
124
        });
125
126
        $comment = array_shift($comments);
127
128
        return $comment[1];
129
    }
130
131
    /**
132
     * Extract the plugin data from a given source.
133
     *
134
     * @param string $source
135
     *
136
     * @return array
137
     */
138
    private function extractPluginData(string $source): array
139
    {
140
        $headers = [
141
            'Name'        => 'Plugin Name',
142
            'PluginURI'   => 'Plugin URI',
143
            'Version'     => 'Version',
144
            'Description' => 'Description',
145
            'Author'      => 'Author',
146
            'AuthorURI'   => 'Author URI',
147
            'TextDomain'  => 'Text Domain',
148
            'DomainPath'  => 'Domain Path',
149
            'Network'     => 'Network',
150
        ];
151
152
        $patterns = array_map(function (string $regex) {
153
            return '/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi';
154
        }, $headers);
155
156
        $matches = array_map(function (string $pattern) use ($source) {
157
            return preg_match($pattern, $source, $match) ? $match[1] : '';
158
        }, $patterns);
159
160
        $matches = array_map(function (string $match) {
161
            return trim(preg_replace('/\s*(?:\*\/|\?>).*/', '', $match));
162
        }, $matches);
163
164
        $data = array_combine(array_keys($headers), $matches);
165
166
        $data['Network']    = ('true' === strtolower($data['Network']));
167
        $data['Title']      = $data['Name'];
168
        $data['AuthorName'] = $data['Author'];
169
170
        return $data;
171
    }
172
173
    /**
174
     * Write the given manifest array to disk.
175
     *
176
     * @param string $path
177
     * @param array  $manifest
178
     *
179
     * @return void
180
     */
181
    private function write(string $path, array $manifest): void
182
    {
183
        if (!is_writable($directory = dirname($path))) {
184
            $this->io->writeError("The {$directory} directory must be present and writable.");
185
        }
186
187
        file_put_contents(
188
            $path,
189
            '<?php return ' . var_export($manifest, true) . ';'
190
        );
191
    }
192
}
193