Completed
Push — master ( b3b6a4...b2f402 )
by Sergi Tur
03:43
created

LlumCommand::sqlite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
namespace Acacha\Llum\Console;
4
5
use Illuminate\Config\Repository;
6
use Symfony\Component\Console\Command\Command;
7
use Symfony\Component\Console\Input\InputArgument;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\Process\Process;
11
12
/**
13
 * Class LlumCommand.
14
 */
15
abstract class LlumCommand extends Command
16
{
17
    /**
18
     * The output interface.
19
     *
20
     * @var OutputInterface
21
     */
22
    protected $output;
23
24
    /**
25
     * Command name.
26
     *
27
     * @var string
28
     */
29
    protected $commandName;
30
31
    /**
32
     * Command description.
33
     *
34
     * @var string
35
     */
36
    protected $commandDescription;
37
38
    /**
39
     * Command argument.
40
     *
41
     * @var string
42
     */
43
    protected $argument;
44
45
    /**
46
     * Argument type.
47
     *
48
     * @var int
49
     */
50
    protected $argumentType = InputArgument::REQUIRED;
51
52
    /**
53
     * Command argument description.
54
     *
55
     * @var string
56
     */
57
    protected $argumentDescription;
58
59
    /**
60
     * Method to execute.
61
     *
62
     * @var string
63
     */
64
    protected $method;
65
66
    /**
67
     * Laravel config file (config/app.php).
68
     *
69
     * @var string
70
     */
71
    protected $laravel_config_file;
72
73
    /**
74
     * Path to config folder.
75
     *
76
     * @var string
77
     */
78
    protected $configPath;
79
80
    /**
81
     * Avoids using bash using stubs instead to modify config/app.php file.
82
     *
83
     * @var bool
84
     */
85
    protected $noBash = false;
86
87
    /**
88
     * Config repository.
89
     *
90
     * @var Repository
91
     */
92
    protected $config;
93
94
    /**
95
     * LlumCommand constructor.
96
     */
97
    public function __construct()
98
    {
99
        parent::__construct();
100
        $this->configPath = __DIR__.'/../config/';
101
        $this->laravel_config_file = getcwd().'/config/app.php';
102
        $this->config = $this->obtainConfig();
103
    }
104
105
    /**
106
     * Check if port is in use.
107
     *
108
     * @param int    $port
109
     * @param string $host
110
     * @param int    $timeout
111
     *
112
     * @return bool
113
     */
114
    protected function check_port($port = 8000, $host = '127.0.0.1', $timeout = 3)
115
    {
116
        $fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
117
        if (! $fp) {
118
            return true;
119
        } else {
120
            fclose($fp);
121
122
            return false;
123
        }
124
    }
125
126
    /**
127
     * Install /config/app.php file using bash script.
128
     */
129
    protected function installConfigAppFileWithBash()
130
    {
131
        passthru(__DIR__.'/../bash_scripts/iluminar.sh '.$this->laravel_config_file);
132
    }
133
134
    /**
135
     * Install /stubs/app.php into /config/app.php.
136
     */
137
    protected function installConfigAppFileWithStubs()
138
    {
139
        copy(__DIR__.'/stubs/app.php', $this->laravel_config_file);
140
    }
141
142
    /**
143
     * Check if Laravel config file exists.
144
     *
145
     * @return bool
146
     */
147
    protected function checkIfLaravelConfigFileExists()
148
    {
149
        return file_exists($this->laravel_config_file);
150
    }
151
152
    /**
153
     * Install llum custom config/app.php file.
154
     *
155
     * @return int
156
     */
157
    protected function installConfigAppFile()
158
    {
159
        if (! $this->checkIfLaravelConfigFileExists()) {
160
            $this->output->writeln('<error>File '.$this->laravel_config_file.' doesn\'t exists');
161
162
            return -1;
163
        }
164
165
        if ($this->configAppFileAlreadyInstalled()) {
166
            $this->output->writeln('<info>File '.$this->laravel_config_file.' already supports llum.</info>');
167
168
            return 0;
169
        }
170
171
        if ($this->isNoBashActive()) {
172
            $this->installConfigAppFileWithStubs();
173
            $this->output->writeln('<info>File '.$this->laravel_config_file.' overwrited correctly with and stub.</info>');
174
        } else {
175
            $this->installConfigAppFileWithBash();
176
        }
177
178
        return 0;
179
    }
180
181
    /**
182
     * Check if config/app.php stub file is already installed.
183
     *
184
     * @return bool
185
     */
186
    protected function configAppFileAlreadyInstalled()
187
    {
188
        if (strpos(file_get_contents($this->laravel_config_file), '#llum_providers') !== false) {
189
            return true;
190
        }
191
192
        return false;
193
    }
194
195
    /**
196
     * Add Laravel IDE Helper provider to config/app.php file.
197
     *
198
     * @return int|null
199
     */
200
    protected function addLaravelIdeHelperProvider()
201
    {
202
        return $this->addProvider('Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class');
203
    }
204
205
    /**
206
     *  Add provider to config/app.php file.
207
     *
208
     * @param $provider
209
     *
210
     * @return int|null
211
     */
212
    private function addProvider($provider)
213
    {
214
        return $this->addTextIntoMountPoint('#llum_providers', $provider);
215
    }
216
217
    /**
218
     * Add alias to config/app.php file.
219
     *
220
     * @param string $alias
221
     *
222
     * @return int|null
223
     */
224
    private function addAlias($alias)
225
    {
226
        return $this->addTextIntoMountPoint('#llum_aliases', $alias);
227
    }
228
229
    /**
230
     * Insert text into file using mountpoint. Mountpoint is maintained at file.
231
     *
232
     * @param string $mountpoint
233
     * @param $textToAdd
234
     *
235
     * @return int|null
236
     */
237
    private function addTextIntoMountPoint($mountpoint, $textToAdd)
238
    {
239
        passthru(
240
            'sed -i \'s/.*'.$mountpoint.'.*/ \ \ \ \ \ \ \ '.$this->scapeSingleQuotes(preg_quote($textToAdd)).',\n \ \ \ \ \ \ \ '.$mountpoint.'/\' '.$this->laravel_config_file, $error);
241
242
        return $error;
243
    }
244
245
    /**
246
     * scape single quotes for sed using \x27.
247
     *
248
     * @param string $str
249
     *
250
     * @return string
251
     */
252
    private function scapeSingleQuotes($str)
253
    {
254
        return str_replace("'", '\\x27', $str);
255
    }
256
257
    /**
258
     * Require composer package.
259
     *
260
     * @param $package
261
     */
262
    private function requireComposerPackage($package)
263
    {
264
        $composer = $this->findComposer();
265
266
        $process = new Process($composer.' require '.$package.'', null, null, null, null);
267
        $this->output->writeln('<info>Running composer require '.$package.'</info>');
268
        $process->run(function ($type, $line) {
269
            $this->output->write($line);
270
        });
271
    }
272
273
    /**
274
     * Get the composer command for the environment.
275
     *
276
     * @return string
277
     */
278
    private function findComposer()
279
    {
280
        if (file_exists(getcwd().'/composer.phar')) {
281
            return '"'.PHP_BINARY.'" composer.phar"';
282
        }
283
284
        return 'composer';
285
    }
286
287
    /**
288
     * Installs provider in laravel config/app.php file.
289
     *
290
     * @param $provider
291
     */
292
    protected function provider($provider)
293
    {
294
        if ($this->installConfigAppFile() == -1) {
295
            return;
296
        }
297
        $this->addProvider($provider);
298
    }
299
300
    /**
301
     * Installs alias/facade in laravel config/app.php file.
302
     *
303
     * @param $aliasName
304
     * @param $aliasClass
305
     */
306
    protected function alias($aliasName, $aliasClass)
307
    {
308
        if ($this->installConfigAppFile() == -1) {
309
            return;
310
        }
311
        $this->addAlias("'".$aliasName."' => ".$aliasClass);
312
    }
313
314
    /**
315
     * get package from config.
316
     *
317
     * @param $name
318
     *
319
     * @return array
320
     */
321
    private function getPackageFromConfig($name)
322
    {
323
        //Check if package name is a composer package name
324
        if (str_contains($name, '/')) {
325
            return $this->config->get($this->getPackageNameByComposerName($name));
326
        }
327
328
        return $this->config->get($name);
329
    }
330
331
    /**
332
     * Add providers to Laravel config file.
333
     *
334
     * @param $providers
335
     */
336
    protected function addProviders($providers)
337
    {
338
        foreach ($providers as $provider) {
339
            $this->output->writeln('<info>Adding '.$provider.' to Laravel config app.php file</info>');
340
            $this->addProvider($provider);
341
        }
342
    }
343
344
    /**
345
     * Add aliases to Laravel config file.
346
     *
347
     * @param $aliases
348
     */
349
    protected function addAliases($aliases)
350
    {
351
        foreach ($aliases as $alias => $aliasClass) {
352
            $this->output->writeln('<info>Adding '.$aliasClass.' to Laravel config app.php file</info>');
353
            $this->addAlias("'$alias' => ".$aliasClass);
354
        }
355
    }
356
357
    /**
358
     * Installs laravel package form config/packages.php file.
359
     *
360
     * @param string $name
361
     * @return int -1 if error occurred
362
     */
363
    protected function package($name)
364
    {
365
        $package = $this->getPackageFromConfig($name);
366
367
        if ($package == null) {
368
            $this->showPackageNotFoundError($name);
369
370
            return -1;
371
        }
372
373
        list($name, $providers, $aliases, $after) = array_fill(0, 4, null);
374
        extract($package, EXTR_IF_EXISTS);
375
376
        $this->requireComposerPackage($name);
377
378
        if ($this->installConfigAppFile() == -1) {
379
            return -1;
380
        }
381
382
        $this->addProviders($providers);
383
384
        $this->addAliases($aliases);
385
386
        if ($after != null) {
387
            passthru($after);
388
        }
389
    }
390
391
    /**
392
     * Get config repository.
393
     *
394
     * @return Repository
395
     */
396
    protected function obtainConfig()
397
    {
398
        return new Repository(require $this->configPath.'packages.php');
399
    }
400
401
    /**
402
     * @param InputInterface  $input
403
     * @param OutputInterface $output
404
     */
405
    protected function initialize(InputInterface $input, OutputInterface $output)
406
    {
407
        parent::initialize($input, $output);
408
        if ($input->hasOption('no-bash')) {
409
            $this->noBash = $input->getOption('no-bash');
410
        }
411
    }
412
413
    /**
414
     * Check is --no-bash option is active.
415
     *
416
     * @return bool
417
     */
418
    private function isNoBashActive()
419
    {
420
        return $this->noBash;
421
    }
422
423
    /**
424
     * Get package name by composer package name.
425
     * 
426
     * @param $composerPackageName
427
     *
428
     * @return string
429
     */
430
    private function getPackageNameByComposerName($composerPackageName)
431
    {
432
        foreach ($this->config->all() as $key => $configItem) {
433
            if ($configItem[ 'name' ] == $composerPackageName) {
434
                return $key;
435
            }
436
        }
437
438
        return;
439
    }
440
441
    /**
442
     * Show package not found error.
443
     *
444
     * @param $name
445
     */
446
    protected function showPackageNotFoundError($name)
447
    {
448
        $this->output->writeln('<error>Package '.$name.' not found in file '.$this->configPath.'packages.php</error>');
449
450
        return;
451
    }
452
453
    /**
454
     * Configure the command options.
455
     *
456
     * @param ConsoleCommand $command
457
     */
458
    protected function configureCommand(ConsoleCommand $command)
459
    {
460
        $this->ignoreValidationErrors();
461
462
        $this->setName($command->name())
0 ignored issues
show
Bug introduced by
It seems like $command->name() targeting Acacha\Llum\Console\ConsoleCommand::name() can also be of type object<Acacha\Llum\Traits\GetSetable>; however, Symfony\Component\Consol...mand\Command::setName() does only seem to accept string, 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...
463
            ->setDescription($command->description());
0 ignored issues
show
Bug introduced by
It seems like $command->description() targeting Acacha\Llum\Console\ConsoleCommand::description() can also be of type object<Acacha\Llum\Traits\GetSetable>; however, Symfony\Component\Consol...mmand::setDescription() does only seem to accept string, 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...
464
        if ($command->argument() != null) {
465
            $this->addArgument($command->argument()[ 'name' ],
466
                $command->argument()[ 'type' ],
467
                $command->argument()[ 'description' ]
468
            );
469
        }
470
    }
471
472
    /**
473
     * Execute the command.
474
     *
475
     * @param InputInterface  $input
476
     * @param OutputInterface $output
477
     *
478
     * @return int|null|void
479
     */
480
    protected function execute(InputInterface $input, OutputInterface $output)
481
    {
482
        $this->output = $output;
483
        $method = $this->method;
484
        if ($this->argument != null) {
485
            $argument = $input->getArgument($this->argument);
486
            $this->$method($argument);
487
488
            return;
489
        }
490
491
        $this->$method();
492
    }
493
494
    /**
495
     * Configure the command options.
496
     */
497
    protected function configure()
498
    {
499
        $command = new ConsoleCommand();
500
501
        $command->name($this->commandName)
1 ignored issue
show
Bug introduced by
It seems like description() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
502
                ->description($this->commandDescription);
503
504
        if ($this->argument != null) {
505
            $command->argument([
506
                'name' => $this->argument,
507
                'description' => $this->argumentDescription,
508
                'type' => $this->argumentType,
509
            ]);
510
        }
511
        $this->configureCommand($command);
512
    }
513
}
514