Completed
Push — master ( 0a1e1d...ccf235 )
by Song
02:31
created

ExtendCommand::putFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Console;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Filesystem\Filesystem;
7
8
class ExtendCommand extends Command
9
{
10
    /**
11
     * The console command name.
12
     *
13
     * @var string
14
     */
15
    protected $signature = 'admin:extend {extension} {--namespace=}';
16
17
    /**
18
     * The console command description.
19
     *
20
     * @var string
21
     */
22
    protected $description = 'Build a Laravel-admin extension';
23
24
    /**
25
     * @var string
26
     */
27
    protected $basePath = '';
28
29
    /**
30
     * @var Filesystem
31
     */
32
    protected $filesystem;
33
34
    /**
35
     * @var string
36
     */
37
    protected $namespace;
38
39
    /**
40
     * @var string
41
     */
42
    protected $className;
43
44
    /**
45
     * @var string
46
     */
47
    protected $package;
48
49
    /**
50
     * @var string
51
     */
52
    protected $extensionDir;
53
54
    /**
55
     * @var array
56
     */
57
    protected $dirs = [
58
        'database/migrations',
59
        'database/seeds',
60
        'resources/assets',
61
        'resources/views',
62
        'src/Http/Controllers',
63
        'routes',
64
    ];
65
66
    /**
67
     * Execute the console command.
68
     *
69
     * @return void
70
     */
71
    public function handle(Filesystem $filesystem)
72
    {
73
        $this->filesystem = $filesystem;
74
75
        $this->extensionDir = config('admin.extension_dir');
76
77
        InputExtensionDir:
78
        if (empty($this->extensionDir)) {
79
            $this->extensionDir = $this->ask('Please input a directory to store your extension:');
80
        }
81
82
        if (!file_exists($this->extensionDir)) {
83
            goto InputExtensionDir;
84
        }
85
86
        $this->package = $this->argument('extension');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->argument('extension') can also be of type array. However, the property $package is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
87
88
        InputExtensionName:
89
        if (!$this->validateExtensionName($this->package)) {
0 ignored issues
show
Bug introduced by
It seems like $this->package can also be of type array or null; however, Encore\Admin\Console\Ext...validateExtensionName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
90
91
            $this->package = $this->ask("[$this->package] is not a valid package name, please input a name like (<vendor>/<name>)");
92
            goto InputExtensionName;
93
        }
94
95
        $this->makeDirs();
96
        $this->makeFiles();
97
    }
98
99
    /**
100
     * Make extension files.
101
     */
102
    protected function makeFiles()
103
    {
104
        $this->namespace = $this->getRootNameSpace();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getRootNameSpace() can also be of type array. However, the property $namespace is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
105
106
        $this->className = $this->getClassName();
107
108
        // copy files
109
        $this->copy([
110
            __DIR__ . '/stubs/extension/view.stub'       => 'resources/views/index.blade.php',
111
            __DIR__ . '/stubs/extension/.gitignore.stub' => '.gitignore',
112
            __DIR__ . '/stubs/extension/README.md.stub'  => 'README.md',
113
            __DIR__ . '/stubs/extension/LICENSE.stub'    => 'LICENSE',
114
        ]);
115
116
        // make composer.json
117
        $composerContents = str_replace(
118
            [':package', ':namespace', ':class_name'],
119
            [$this->package, str_replace('\\', '\\\\', $this->namespace).'\\\\', $this->className],
120
            file_get_contents(__DIR__ . '/stubs/extension/composer.json.stub')
121
        );
122
        $this->putFile('composer.json', $composerContents);
123
124
        // make class
125
        $classContents = str_replace(
126
            [':namespace', ':class_name', ':title', ':path', ':base_package'],
127
            [$this->namespace, $this->className, title_case($this->className), basename($this->package), basename($this->package)],
128
            file_get_contents(__DIR__ . '/stubs/extension/extension.stub')
129
        );
130
        $this->putFile("src/{$this->className}.php", $classContents);
131
132
        // make service provider
133
        $providerContents = str_replace(
134
            [':namespace', ':class_name', ':base_package', ':package'],
135
            [$this->namespace, $this->className, basename($this->package), $this->package],
136
            file_get_contents(__DIR__ . '/stubs/extension/service-provider.stub')
137
        );
138
        $this->putFile("src/{$this->className}ServiceProvider.php", $providerContents);
139
140
        // make controller
141
        $controllerContent = str_replace(
142
            [':namespace', ':class_name', ':base_package'],
143
            [$this->namespace, $this->className, basename($this->package)],
144
            file_get_contents(__DIR__ . '/stubs/extension/controller.stub')
145
        );
146
        $this->putFile("src/Http/Controllers/{$this->className}Controller.php", $controllerContent);
147
148
        // make routes
149
        $routesContent = str_replace(
150
            [':namespace', ':class_name', ':path'],
151
            [$this->namespace, $this->className, basename($this->package)],
152
            file_get_contents(__DIR__ . '/stubs/extension/routes.stub')
153
        );
154
        $this->putFile("routes/web.php", $routesContent);
155
    }
156
157
    /**
158
     * Get root namespace for this package.
159
     *
160
     * @return array|null|string
161
     */
162
    protected function getRootNameSpace()
163
    {
164
        if (!$namespace = $this->option('namespace')) {
165
            list($vendor, $name) = explode('/', $this->package);
166
167
            $default = str_replace(['-', '-'], '', title_case($vendor) . '\\' . title_case($name));
168
169
            $namespace = $this->ask('Root namespace', $default);
170
        }
171
172
        return $namespace;
173
    }
174
175
    /**
176
     * Get extension class name.
177
     *
178
     * @return string
179
     */
180
    protected function getClassName()
181
    {
182
        return class_basename($this->namespace);
183
    }
184
185
    /**
186
     * Create package dirs
187
     */
188
    protected function makeDirs()
189
    {
190
        $this->basePath = rtrim($this->extensionDir, '/') . '/' . ltrim($this->package, '/');
191
192
        $this->makeDir($this->dirs);
193
    }
194
195
    /**
196
     * Validate extension name.
197
     *
198
     * @param string $name
199
     * @return int
200
     */
201
    protected function validateExtensionName($name)
202
    {
203
        return preg_match('/^[\w-_]+\/[\w-_]+$/', $name);
204
    }
205
206
    /**
207
     * Extension path.
208
     *
209
     * @param string $path
210
     * @return string
211
     */
212
    protected function extensionPath($path = '')
213
    {
214
        $path = rtrim($path, '/');
215
216
        if (empty($path)) {
217
            return rtrim($this->basePath, '/');
218
        }
219
220
        return rtrim($this->basePath, '/') . '/' . ltrim($path, '/');
221
    }
222
223
    /**
224
     * Put contents to file.
225
     *
226
     * @param string $to
227
     * @param string $content
228
     */
229
    protected function putFile($to, $content)
230
    {
231
        $to = $this->extensionPath($to);
232
233
        $this->filesystem->put($to, $content);
234
    }
235
236
    /**
237
     * Copy files to extension path.
238
     *
239
     * @param string|array $from
240
     * @param string|null $to
241
     */
242
    protected function copy($from, $to = null)
243
    {
244
        if (is_array($from) && is_null($to)) {
245
            foreach ($from as $key => $value) {
246
                $this->copy($key, $value);
247
            }
248
249
            return;
250
        }
251
252
253
        if (!file_exists($from)) {
254
            return;
255
        }
256
257
        $to = $this->extensionPath($to);
258
259
        $this->filesystem->copy($from, $to);
0 ignored issues
show
Bug introduced by
It seems like $from defined by parameter $from on line 242 can also be of type array; however, Illuminate\Filesystem\Filesystem::copy() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and 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...
260
    }
261
262
    /**
263
     * Make new directory.
264
     *
265
     * @param array|string $paths
266
     */
267
    protected function makeDir($paths)
268
    {
269
        foreach ((array)$paths as $path) {
270
            $path = $this->extensionPath($path);
271
272
            $this->filesystem->makeDirectory($path, 0755, true, true);
273
        }
274
    }
275
}