Passed
Pull Request — master (#8)
by Jitendra
02:11
created

InitCommand::interact()   C

Complexity

Conditions 13
Paths 1

Size

Total Lines 64
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 39
nc 1
nop 2
dl 0
loc 64
rs 6.0391
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
namespace Ahc\Phint\Console;
4
5
use Ahc\Phint\Generator\CollisionHandler;
6
use Ahc\Phint\Generator\TwigGenerator;
7
use Ahc\Phint\Util\Composer;
8
use Ahc\Phint\Util\Git;
9
use Ahc\Phint\Util\Inflector;
10
use Ahc\Phint\Util\Path;
11
use Symfony\Component\Console\Input\InputArgument;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
16
class InitCommand extends BaseCommand
17
{
18
    /** @var Git */
19
    protected $git;
20
21
    /**
22
     * Configure the command options.
23
     *
24
     * @return void
25
     */
26
    protected function configure()
27
    {
28
        $this
29
            ->setName('init')
30
            ->setDescription('Scaffold a bare new PHP project')
31
            ->addArgument('project', InputArgument::REQUIRED, 'The project name without slashes')
32
            ->addOption('path', null, InputOption::VALUE_NONE, 'The project path (Auto resolved)')
33
            ->addOption('force', 'f', InputOption::VALUE_NONE, 'Run even if the project exists')
34
            ->addOption('description', 'd', InputOption::VALUE_OPTIONAL, 'Project description')
35
            ->addOption('name', 'm', InputOption::VALUE_OPTIONAL, 'Vendor full name, defaults to git name')
36
            ->addOption('username', 'u', InputOption::VALUE_OPTIONAL, 'Vendor handle/username')
37
            ->addOption('email', 'e', InputOption::VALUE_OPTIONAL, 'Vendor email, defaults to git email')
38
            ->addOption('namespace', 's', InputOption::VALUE_OPTIONAL, 'Root namespace')
39
            ->addOption('year', 'y', InputOption::VALUE_OPTIONAL, 'License Year', date('Y'))
40
            ->addOption('type', 't', InputOption::VALUE_OPTIONAL, 'Project type')
41
            ->addOption('using', 'z', InputOption::VALUE_OPTIONAL, 'Packagist name of reference project (eg: laravel/lumen)')
42
            ->addOption('keywords', 'l', InputOption::VALUE_OPTIONAL, 'Project Keywords')
43
            ->addOption('php', 'p', InputOption::VALUE_OPTIONAL, 'Minimum PHP version project needs')
44
            ->setHelp(<<<EOT
45
The <info>init</info> command creates a new project with all basic files and
46
structures in the <project-name> directory. See some examples below:
47
48
<info>phint init</info> project-name <comment>--force --description "My awesome project" --name "Your Name" --email "[email protected]"</comment>
49
<info>phint init</info> project-name <comment>--using laravel/lumen --namespace Project/Api --type project</comment>
50
51
EOT
52
            );
53
    }
54
55
    /**
56
     * Execute the command.
57
     *
58
     * @param InputInterface  $input
59
     * @param OutputInterface $output
60
     *
61
     * @return void
62
     */
63
    protected function execute(InputInterface $input, OutputInterface $output)
64
    {
65
        $output->writeln('<info>Preparing ...</info>');
66
67
        $composer   = new Composer;
68
        $parameters = $this->input->getOptions() + $this->input->getArguments();
69
70
        if (null !== $using = $this->input->getOption('using')) {
71
            $this->output->writeln('Using <comment>' . $using . '</comment> to create project');
72
73
            $composer->withOutput($this->output)->createProject($projectPath, $using);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $projectPath seems to be never defined.
Loading history...
74
        }
75
76
        $this->output->writeln('<comment>Generating files ...</comment>');
77
78
        $this->generate($parameters['path'], $parameters);
79
80
        $this->output->writeln('Setting up <info>git</info>');
81
82
        $this->git->withWorkDir($parameters['path'])->init()
83
            ->addRemote($parameters['username'], $parameters['project']);
84
85
        $this->output->writeln('Setting up <info>composer</info>');
86
87
        $composer->withWorkDir($parameters['path'])->install();
88
89
        $output->writeln('<comment>Done</comment>');
90
    }
91
92
    protected function prepareProjectPath()
93
    {
94
        $path = $this->input->getArgument('project');
95
96
        if (!(new Path)->isAbsolute($path)) {
97
            $path = \getcwd() . '/' . $path;
98
        }
99
100
        if (\file_exists($path)) {
101
            if (!$this->input->getOption('force')) {
102
                throw new \InvalidArgumentException('Something with the same name already exists!');
103
            }
104
105
            if (!$this->input->getOption('using')) {
106
                $this->output->writeln('<error>You have set force flag, existing files will be overwritten</error>');
107
            }
108
        } else {
109
            \mkdir(\rtrim($path, '/') . '/src', 0777, true);
110
        }
111
112
        return $path;
113
    }
114
115
    protected function interact(InputInterface $input, OutputInterface $output)
116
    {
117
        $this->input  = $input;
118
        $this->output = $output;
1 ignored issue
show
Documentation Bug introduced by
It seems like $output of type Symfony\Component\Console\Output\OutputInterface is incompatible with the declared type Ahc\Phint\Console\OutputInterface of property $output.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
119
120
        $project = $input->getArgument('project');
121
122
        if (empty($project) || !preg_match('/[a-z0-9_-]/i', $project)) {
123
            throw new \InvalidArgumentException('Project argument is required and should only contain [a-z0-9_-]');
124
        }
125
126
        $this->input->setOption('path', $this->prepareProjectPath());
127
128
        $this->git = new Git;
129
        $inflector = new Inflector;
130
131
        $this->output->writeln('<info>Phint Setup</info>');
132
        $this->output->writeln('<comment>Just press ENTER if you want to use the [default] or skip<comment>');
133
        $this->output->writeln('');
134
135
        $this->input->setOption('type', $this->input->getOption('type') ?: $this->prompt(
136
            'Project type (project/library)',
137
            'library',
138
            ['project', 'library', 'composer-plugin']
139
        ));
140
141
        $this->input->setOption('name', $this->input->getOption('name') ?: $this->prompt(
142
            'Vendor full name',
143
            $this->git->getConfig('user.name')
144
        ));
145
146
        $this->input->setOption('email', $this->input->getOption('email') ?: $this->prompt(
147
            'Vendor email',
148
             $this->git->getConfig('user.email')
149
        ));
150
151
        $this->input->setOption('description', $this->input->getOption('description') ?: $this->prompt(
152
            'Brief project description'
153
        ));
154
155
        $this->input->setOption('username', $username = $this->input->getOption('username') ?: $this->prompt(
156
            'Vendor handle (often github username)',
157
            getenv('VENDOR_USERNAME') ?: null
158
        ));
159
160
        $namespace = $this->input->getOption('namespace') ?: $this->prompt(
161
            'Project root namespace (forward slashes are auto fixed)',
162
            (getenv('VENDOR_NAMESPACE') ?: $inflector->stuldyCase($username))
163
                . '/' . $inflector->stuldyCase($project)
164
        );
165
166
        $this->input->setOption('namespace', \str_replace('/', '\\\\', $namespace));
167
168
        $keywords = $this->input->getOption('keywords') ?: $this->prompt(
169
            'Project keywords (CSV)',
170
            "php, $project"
171
        );
172
173
        $this->input->setOption('keywords', array_map('trim', explode(',', $keywords)));
1 ignored issue
show
Bug introduced by
array_map('trim', explode(',', $keywords)) of type array is incompatible with the type string|boolean expected by parameter $value of Symfony\Component\Consol...tInterface::setOption(). ( Ignorable by Annotation )

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

173
        $this->input->setOption('keywords', /** @scrutinizer ignore-type */ array_map('trim', explode(',', $keywords)));
Loading history...
174
175
        $this->input->setOption('php', $this->input->getOption('php') ?: $this->prompt(
176
            'Minimum PHP version project needs',
177
            PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
178
            ['5.4', '5.5', '5.6', '7.0', '7.1']
179
        ));
180
    }
181
182
    protected function generate($projectPath, array $parameters)
183
    {
184
        $templatePath = __DIR__ . '/../../resources';
185
        $cachePath    = __DIR__ . '/../../.cache';
186
187
        $generator = new TwigGenerator($templatePath, $cachePath);
188
189
        $generator->generate($projectPath, $parameters, new CollisionHandler);
190
    }
191
}
192