Completed
Push — master ( cec761...c24643 )
by mark
07:01 queued 03:21
created

AssetsTask::_process()   C

Complexity

Conditions 11
Paths 10

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
nc 10
nop 3
dl 0
loc 51
rs 6.9224
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11
 * @link          https://cakephp.org CakePHP(tm) Project
12
 * @since         3.0.0
13
 * @license       https://opensource.org/licenses/mit-license.php MIT License
14
 */
15
namespace Cake\Shell\Task;
16
17
use Cake\Console\Shell;
18
use Cake\Core\Plugin;
19
use Cake\Filesystem\Folder;
20
use Cake\Utility\Inflector;
21
22
/**
23
 * Task for symlinking / copying plugin assets to app's webroot.
24
 */
25
class AssetsTask extends Shell
26
{
27
    /**
28
     * Attempt to symlink plugin assets to app's webroot. If symlinking fails it
29
     * fallbacks to copying the assets. For vendor namespaced plugin, parent folder
30
     * for vendor name are created if required.
31
     *
32
     * @param string|null $name Name of plugin for which to symlink assets.
33
     *   If null all plugins will be processed.
34
     * @return void
35
     */
36
    public function symlink($name = null)
37
    {
38
        $this->_process($this->_list($name));
39
    }
40
41
    /**
42
     * Copying plugin assets to app's webroot. For vendor namespaced plugin,
43
     * parent folder for vendor name are created if required.
44
     *
45
     * @param string|null $name Name of plugin for which to symlink assets.
46
     *   If null all plugins will be processed.
47
     * @return void
48
     */
49
    public function copy($name = null)
50
    {
51
        $this->_process($this->_list($name), true, $this->param('overwrite'));
0 ignored issues
show
Bug introduced by
It seems like $this->param('overwrite') targeting Cake\Console\Shell::param() can also be of type null or string; however, Cake\Shell\Task\AssetsTask::_process() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
52
    }
53
54
    /**
55
     * Remove plugin assets from app's webroot.
56
     *
57
     * @param string|null $name Name of plugin for which to remove assets.
58
     *   If null all plugins will be processed.
59
     * @return void
60
     * @since 3.5.12
61
     */
62
    public function remove($name = null)
63
    {
64
        $plugins = $this->_list($name);
65
66
        foreach ($plugins as $plugin => $config) {
67
            $this->out();
68
            $this->out('For plugin: ' . $plugin);
69
            $this->hr();
70
71
            $this->_remove($config);
72
        }
73
74
        $this->out();
75
        $this->out('Done');
76
    }
77
78
    /**
79
     * Get list of plugins to process. Plugins without a webroot directory are skipped.
80
     *
81
     * @param string|null $name Name of plugin for which to symlink assets.
82
     *   If null all plugins will be processed.
83
     * @return array List of plugins with meta data.
84
     */
85
    protected function _list($name = null)
86
    {
87
        if ($name === null) {
88
            $pluginsList = Plugin::loaded();
89
        } else {
90
            if (!Plugin::isLoaded($name)) {
91
                $this->err(sprintf('Plugin %s is not loaded.', $name));
92
93
                return [];
94
            }
95
            $pluginsList = [$name];
96
        }
97
98
        $plugins = [];
99
100
        foreach ($pluginsList as $plugin) {
101
            $path = Plugin::path($plugin) . 'webroot';
102
            if (!is_dir($path)) {
103
                $this->verbose('', 1);
104
                $this->verbose(
105
                    sprintf('Skipping plugin %s. It does not have webroot folder.', $plugin),
106
                    2
107
                );
108
                continue;
109
            }
110
111
            $link = Inflector::underscore($plugin);
112
            $dir = WWW_ROOT;
113
            $namespaced = false;
114
            if (strpos($link, '/') !== false) {
115
                $namespaced = true;
116
                $parts = explode('/', $link);
117
                $link = array_pop($parts);
118
                $dir = WWW_ROOT . implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR;
119
            }
120
121
            $plugins[$plugin] = [
122
                'srcPath' => Plugin::path($plugin) . 'webroot',
123
                'destDir' => $dir,
124
                'link' => $link,
125
                'namespaced' => $namespaced,
126
            ];
127
        }
128
129
        return $plugins;
130
    }
131
132
    /**
133
     * Process plugins
134
     *
135
     * @param array $plugins List of plugins to process
136
     * @param bool $copy Force copy mode. Default false.
137
     * @param bool $overwrite Overwrite existing files.
138
     * @return void
139
     */
140
    protected function _process($plugins, $copy = false, $overwrite = false)
141
    {
142
        $overwrite = (bool)$this->param('overwrite');
143
144
        foreach ($plugins as $plugin => $config) {
145
            $this->out();
146
            $this->out('For plugin: ' . $plugin);
147
            $this->hr();
148
149
            if (
150
                $config['namespaced'] &&
151
                !is_dir($config['destDir']) &&
152
                !$this->_createDirectory($config['destDir'])
153
            ) {
154
                continue;
155
            }
156
157
            $dest = $config['destDir'] . $config['link'];
158
159
            if (file_exists($dest)) {
160
                if ($overwrite && !$this->_remove($config)) {
161
                    continue;
162
                } elseif (!$overwrite) {
163
                    $this->verbose(
164
                        $dest . ' already exists',
165
                        1
166
                    );
167
168
                    continue;
169
                }
170
            }
171
172
            if (!$copy) {
173
                $result = $this->_createSymlink(
174
                    $config['srcPath'],
175
                    $dest
176
                );
177
                if ($result) {
178
                    continue;
179
                }
180
            }
181
182
            $this->_copyDirectory(
183
                $config['srcPath'],
184
                $dest
185
            );
186
        }
187
188
        $this->out();
189
        $this->out('Done');
190
    }
191
192
    /**
193
     * Remove folder/symlink.
194
     *
195
     * @param array $config Plugin config.
196
     * @return bool
197
     */
198
    protected function _remove($config)
199
    {
200
        if ($config['namespaced'] && !is_dir($config['destDir'])) {
201
            $this->verbose(
202
                $config['destDir'] . $config['link'] . ' does not exist',
203
                1
204
            );
205
206
            return false;
207
        }
208
209
        $dest = $config['destDir'] . $config['link'];
210
211
        if (!file_exists($dest)) {
212
            $this->verbose(
213
                $dest . ' does not exist',
214
                1
215
            );
216
217
            return false;
218
        }
219
220
        if (is_link($dest)) {
221
            // @codingStandardsIgnoreLine
222
            if (@unlink($dest)) {
223
                $this->out('Unlinked ' . $dest);
224
225
                return true;
226
            } else {
227
                $this->err('Failed to unlink  ' . $dest);
228
229
                return false;
230
            }
231
        }
232
233
        $folder = new Folder($dest);
234
        if ($folder->delete()) {
235
            $this->out('Deleted ' . $dest);
236
237
            return true;
238
        } else {
239
            $this->err('Failed to delete ' . $dest);
240
241
            return false;
242
        }
243
    }
244
245
    /**
246
     * Create directory
247
     *
248
     * @param string $dir Directory name
249
     * @return bool
250
     */
251
    protected function _createDirectory($dir)
252
    {
253
        $old = umask(0);
254
        // @codingStandardsIgnoreStart
255
        $result = @mkdir($dir, 0755, true);
256
        // @codingStandardsIgnoreEnd
257
        umask($old);
258
259
        if ($result) {
260
            $this->out('Created directory ' . $dir);
261
262
            return true;
263
        }
264
265
        $this->err('Failed creating directory ' . $dir);
266
267
        return false;
268
    }
269
270
    /**
271
     * Create symlink
272
     *
273
     * @param string $target Target directory
274
     * @param string $link Link name
275
     * @return bool
276
     */
277
    protected function _createSymlink($target, $link)
278
    {
279
        // @codingStandardsIgnoreStart
280
        $result = @symlink($target, $link);
281
        // @codingStandardsIgnoreEnd
282
283
        if ($result) {
284
            $this->out('Created symlink ' . $link);
285
286
            return true;
287
        }
288
289
        return false;
290
    }
291
292
    /**
293
     * Copy directory
294
     *
295
     * @param string $source Source directory
296
     * @param string $destination Destination directory
297
     * @return bool
298
     */
299
    protected function _copyDirectory($source, $destination)
300
    {
301
        $folder = new Folder($source);
302
        if ($folder->copy(['to' => $destination])) {
303
            $this->out('Copied assets to directory ' . $destination);
304
305
            return true;
306
        }
307
308
        $this->err('Error copying assets to directory ' . $destination);
309
310
        return false;
311
    }
312
313
    /**
314
     * Gets the option parser instance and configures it.
315
     *
316
     * @return \Cake\Console\ConsoleOptionParser
317
     */
318
    public function getOptionParser()
319
    {
320
        $parser = parent::getOptionParser();
321
322
        $parser->addSubcommand('symlink', [
323
            'help' => 'Symlink (copy as fallback) plugin assets to app\'s webroot.',
324
        ])->addSubcommand('copy', [
325
            'help' => 'Copy plugin assets to app\'s webroot.',
326
        ])->addSubcommand('remove', [
327
            'help' => 'Remove plugin assets from app\'s webroot.',
328
        ])->addArgument('name', [
329
            'help' => 'A specific plugin you want to symlink assets for.',
330
            'optional' => true,
331
        ])->addOption('overwrite', [
332
            'help' => 'Overwrite existing symlink / folder / files.',
333
            'default' => false,
334
            'boolean' => true,
335
        ]);
336
337
        return $parser;
338
    }
339
}
340