Installer::inflectPackageVars()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 19
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SunshineCMS\Installers;
4
5
use Composer\Composer;
6
use Composer\Installer\BinaryInstaller;
7
use Composer\Installer\LibraryInstaller;
8
use Composer\IO\IOInterface;
9
use Composer\Package\PackageInterface;
10
use Composer\Repository\InstalledRepositoryInterface;
11
use Composer\Util\Filesystem;
12
use InvalidArgumentException;
13
14
/**
15
 * Composer Library Installer Core for SunshineCMS.
16
 *
17
 * Handles the installation of SunshineCMS core, plugins,
18
 * themes and other extensions.
19
 *
20
 * @since 1.0.0
21
 *
22
 * @author  SunshineCMS Authors & Developers
23
 * @license GPL-3.0-or-later
24
 *
25
 * @package SunshineCMS\Installers
26
 */
27
class Installer extends LibraryInstaller
28
{
29
    /**
30
     * Stores base name.
31
     *
32
     * @var string
33
     */
34
    protected $name = 'sunshinecms';
35
36
    /**
37
     * Stores installation locations.
38
     *
39
     * @var array
40
     */
41
    protected $locations = [
42
        // SunshineCMS Core
43
        'core' => '',
44
45
        // SunshineCMS Packs
46
        '.*(?=-pack)' => 'vendor/{$vendor}/{$name}/',
47
48
        // SunshineCMS Plugins
49
        'plugin' => 'plugins/{$vendor}/{$name}/',
50
51
        // SunshineCMS Themes
52
        'theme' => 'themes/public/{$vendor}/{$name}/',
53
        '.*(?=-theme)' => 'themes/{$type}/{$vendor}/{$name}/',
54
    ];
55
56
    /**
57
     * Constructs installer.
58
     *
59
     * Constructs the installer core for SunshineCMS or disables it when
60
     * specified in main composer extra installer disable list.
61
     *
62
     * @param IOInterface          $io
63
     * @param Composer             $composer
64
     * @param string               $type
65
     * @param Filesystem|null      $filesystem
66
     * @param BinaryInstaller|null $binaryInstaller
67
     */
68
    public function __construct(
69
        IOInterface $io,
70
        Composer $composer,
71
        $type = 'library',
72
        Filesystem $filesystem = null,
73
        BinaryInstaller $binaryInstaller = null
74
    ) {
75
        parent::__construct($io, $composer, $type, $filesystem, $binaryInstaller);
76
        $this->removeDisabledInstallers();
77
    }
78
79
    /**
80
     * {@inheritDoc}
81
     */
82
    public function getInstallPath(PackageInterface $package)
83
    {
84
        // Get type
85
        $type = $package->getType();
86
87
        // Get vendor and name
88
        $prettyName = $package->getPrettyName();
89
        if (strpos($prettyName, '/') !== false) {
90
            list($vendor, $name) = explode('/', $prettyName);
91
        } else {
92
            $vendor = '';
93
            $name = $prettyName;
94
        }
95
96
        // Check if supported
97
        if (!$this->supports($type)) {
98
            throw new InvalidArgumentException('Package type of this package is not supported');
99
        }
100
101
        // Inflect package vars
102
        $availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type'));
103
104
        // Get different name if available
105
        $extra = $package->getExtra();
106
        if (!empty($extra['installer-name'])) {
107
            $availableVars['name'] = $extra['installer-name'];
108
        }
109
110
        // Return custom location
111
        if ($this->composer->getPackage()) {
112
            $extra = $this->composer->getPackage()->getExtra();
113
            if (!empty($extra['installer-paths'])) {
114
                $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type, $vendor);
115
                if ($customPath !== false) {
0 ignored issues
show
introduced by
The condition $customPath !== false is always true.
Loading history...
116
                    return $this->templatePath($availableVars, $customPath);
117
                }
118
            }
119
        }
120
121
        // Return location
122
        return $this->templatePath($availableVars);
123
    }
124
125
    /**
126
     * {@inheritDoc}
127
     */
128
    public function supports($type)
129
    {
130
        // Check base name
131
        if ($this->name !== substr($type, 0, strlen($this->name))) {
132
            return false;
133
        }
134
135
        // Check type name
136
        foreach ($this->locations as $key => $value) {
137
            preg_match('/' . $key . '/', $type, $matches, PREG_OFFSET_CAPTURE, 0);
138
139
            if (!empty($matches)) {
140
                return true;
141
            }
142
        }
143
144
        // Return if not supported
145
        return false;
146
    }
147
148
    /**
149
     * Removes disabled installers.
150
     *
151
     * Looks for disabled installers in composer's extra config
152
     * and remove them.
153
     *
154
     * @return void
155
     */
156
    protected function removeDisabledInstallers()
157
    {
158
        // Get extra config
159
        $extra = $this->composer->getPackage()->getExtra();
160
161
        // Check if installers need to be disabled
162
        if (!isset($extra['installer-disable']) || $extra['installer-disable'] === false) {
163
            return;
164
        }
165
166
        // Get installers to disable
167
        $disable = $extra['installer-disable'];
168
169
        // Ensure $disabled is an array
170
        if (!is_array($disable)) {
171
            $disable = [$disable];
172
        }
173
174
        // Check which installers should be disabled
175
        $all = [
176
            true,
177
            'all',
178
            '*',
179
        ];
180
        $intersect = array_intersect($all, $disable);
181
182
        if (!empty($intersect)) {
183
            // Disable all installers
184
            $this->locations = [];
185
        } else {
186
            // Disable specified installers
187
            foreach ($disable as $key => $installer) {
188
                if (is_string($installer) && key_exists($installer, $this->locations)) {
189
                    unset($this->locations[$installer]);
190
                }
191
            }
192
        }
193
    }
194
195
    /**
196
     * Formats package name.
197
     *
198
     * Lowercases name and changes underscores to hyphens. Also
199
     * cuts off leading or trailing `sunshinecms`, `plugin`,
200
     * `theme` or `pack` from name depending on type.
201
     *
202
     * @param array $vars
203
     *
204
     * @return array
205
     */
206
    protected function inflectPackageVars($vars)
207
    {
208
        // Lowercase and change underscores to hyphens
209
        $vars['name'] = strtolower(str_replace('_', '-', $vars['name']));
210
211
        // Remove base name
212
        $vars['name'] = preg_replace('/-' . $this->name . '/', '', $vars['name']);
213
        $vars['name'] = preg_replace('/' . $this->name . '-/', '', $vars['name']);
214
215
        // Remove type name
216
        foreach ($this->locations as $key => $value) {
217
            if ($vars['type'] === $this->name . '-' . $key) {
218
                $vars['name'] = preg_replace('/[-]?' . $key . '[-]?/', '', $vars['name']);
219
                break;
220
            }
221
        }
222
223
        // Return formatted name
224
        return $vars;
225
    }
226
227
    /**
228
     * Searchs for install path.
229
     *
230
     * Searchs through a passed paths array for a custom install path.
231
     *
232
     * @param array  $paths
233
     * @param string $name
234
     * @param string $type
235
     * @param string $vendor = null
236
     *
237
     * @return string
238
     */
239
    protected function mapCustomInstallPaths(array $paths, $name, $type, $vendor = null)
240
    {
241
        // Search in paths array
242
        foreach ($paths as $path => $names) {
243
            if (in_array($name, $names) || in_array('type:' . $type, $names) || in_array('vendor:' . $vendor, $names)) {
244
                return $path;
245
            }
246
        }
247
248
        // Return if not found
249
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
250
    }
251
252
    /**
253
     * Replaces vars in a path.
254
     *
255
     * Replaces vars for type, vendor and name with their calculated
256
     * values from package details.
257
     *
258
     * @param array $vars
259
     *
260
     * @return string
261
     */
262
    protected function templatePath(array $vars, $path = '')
263
    {
264
        // Export vars
265
        extract($vars);
266
267
        if (!empty($path)) {
268
            // Insert vendor and name
269
            preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches);
270
            if (!empty($matches[1])) {
271
                foreach ($matches[1] as $var) {
272
                    $path = str_replace('{$' . $var . '}', $$var, $path);
273
                }
274
            }
275
276
            // Return template path
277
            return preg_replace('#/+#', '/', $path);
278
        }
279
280
        // Search for path
281
        foreach ($this->locations as $key => $value) {
282
            if (preg_match('/(?<=' . $this->name . '-)' . $key . '/', $type)) {
283
                if (strpos($value, '{') !== false) {
284
                    // Match type
285
                    preg_match(
286
                        '/(?<=' . $this->name . '-)' . $key . '/',
287
                        $type,
288
                        $type,
289
                        PREG_OFFSET_CAPTURE,
290
                        0
291
                    );
292
                    $type = $type[0][0];
293
294
                    // Insert vendor and name
295
                    preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $value, $matches);
296
                    if (!empty($matches[1])) {
297
                        foreach ($matches[1] as $var) {
298
                            $value = str_replace('{$' . $var . '}', $$var, $value);
299
                        }
300
                    }
301
                }
302
303
                // Return template path
304
                return preg_replace('#/+#', '/', $value);
305
            }
306
        }
307
    }
308
}
309