Completed
Push — master ( cb6b70...4e63d8 )
by Nikola
04:46
created

GenerateBuilderCommand::getBuilderClassLocation()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 0
cts 11
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 2
nop 1
crap 12
1
<?php
2
/*
3
 * This file is part of the Abstract builder package, an RunOpenCode project.
4
 *
5
 * (c) 2017 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\AbstractBuilder\Command;
11
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\Console\Question\ChoiceQuestion;
16
use Symfony\Component\Console\Question\Question;
17
use Symfony\Component\Console\Style\SymfonyStyle;
18
19
/**
20
 * Class GenerateBuilderCommand
21
 *
22
 * @package RunOpenCode\AbstractBuilder\Command
23
 */
24
class GenerateBuilderCommand extends Command
25
{
26
    /**
27
     * @var SymfonyStyle
28
     */
29
    private $style;
30
31
    private $input;
32
33
    private $output;
34
35
    /**
36
     * {@inheritdoc}
37
     */
38
    protected function configure()
39
    {
40
        $this
41
            ->setName('runopencode:generate:builder')
42
            ->setDescription('Generates builder class skeleton for provided class.');
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function execute(InputInterface $input, OutputInterface $output)
49
    {
50
        $this->input = $input;
51
        $this->output = $output;
52
        $this->style = new SymfonyStyle($input, $output);
53
54
        $this->displayLogo();
55
56
        $this->style->title('Generate builder class');
57
58
        if (false === ($class = $this->getClass())) {
59
            return 0;
60
        }
61
62
        $gettersAndSetters = $this->getGettersAndSetters(array_map(function(\ReflectionParameter $parameter) {
0 ignored issues
show
Unused Code introduced by
$gettersAndSetters is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
63
            return $parameter->getName();
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
64
        }, (new \ReflectionClass($class))->getConstructor()->getParameters()));
65
66
        if (false === ($builderClass = $this->getBuilderClassName(sprintf('%sBuilder', $class)))) {
67
            return 0;
68
        }
69
70
        if (false === ($location = $this->getBuilderClassLocation((new \ReflectionClass(class_exists($builderClass) ? $builderClass : $class))->getFileName()))) {
71
            return 0;
72
        }
73
    }
74
75
    /**
76
     * Get class name for which skeleton should be built.
77
     *
78
     * @return string|bool
79
     */
80
    private function getClass()
81
    {
82
        $helper = $this->getHelper('question');
83
        $question = new Question('Enter full qualified class name for which you want to generate builder class: ', null);
84
85
        $class = $helper->ask($this->input, $this->output, $question);
86
87
        if (!class_exists($class, true)) {
88
            $this->style->error(sprintf('Unable to autoload class "%s". Does this class exists? Can it be autoloaded?', $class));
89
            return false;
90
        }
91
92
        return ltrim(str_replace('\\\\', '\\', $class), '\\');
93
    }
94
95
    /**
96
     * Get getters and setters that will be generated.
97
     *
98
     * @param string[] $parameters
99
     * @return string[]
100
     */
101
    private function getGettersAndSetters($parameters)
102
    {
103
        $helper = $this->getHelper('question');
104
105
        $question = new ChoiceQuestion(
106
            'Choose for which constructor arguments you want to generate getters and setters (separate choices with coma, enter none for all choices):',
107
            $parameters,
108
            implode(',', array_keys($parameters))
109
        );
110
111
        $question->setMultiselect(true);
112
113
        $selected = $helper->ask($this->input, $this->output, $question);
114
115
        if (0 === count($selected)) {
116
            $this->style->error('You have to choose at least one constructor argument.');
117
        }
118
119
        return
120
            array_merge(
121
                array_map(function($parameter) {
122
                    return sprintf('get%s()', ucfirst($parameter));
123
                }, $selected),
124
                array_map(function($parameter) {
125
                    return sprintf('set%s($%s)', ucfirst($parameter), $parameter);
126
                }, $selected)
127
            );
128
    }
129
130
    /**
131
     * Get class name for builder class.
132
     *
133
     * @param string $suggest
134
     * @return bool|string
135
     */
136
    private function getBuilderClassName($suggest)
137
    {
138
        $helper = $this->getHelper('question');
139
        $question = new Question(sprintf('Enter full qualified class name of your builder class (default: "%s"): ', $suggest), $suggest);
140
141
        $class = trim($helper->ask($this->input, $this->output, $question));
142
143
        if (!$class) {
144
            $this->style->error('You have to provide builder class name.');
145
            return false;
146
        }
147
148
        $class = ltrim(str_replace('\\\\', '\\', $class), '\\');
149
150
        foreach (explode('\\', $class) as $part) {
151
152
            if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $part)) {
153
                $this->style->error(sprintf('Provided builder class name "%s" is not valid PHP class name.', $class));
154
                return false;
155
            }
156
        }
157
158
        return $class;
159
    }
160
161
    /**
162
     * Get builder class location
163
     *
164
     * @param string $suggest
165
     * @return bool|string
166
     */
167
    private function getBuilderClassLocation($suggest)
168
    {
169
        $helper = $this->getHelper('question');
170
        $question = new Question(sprintf('Where do you want to generate your builder class code (default: "%s"): ', $suggest), $suggest);
171
172
        $location = trim($helper->ask($this->input, $this->output, $question));
173
174
        if (file_exists($location) && !is_writable($location)) {
175
            $this->style->error(sprintf('File on location "%s" already exists, but it is not writeable.', $location));
176
            return false;
177
        }
178
179
        // TODO - check if it si possible to create file at all...?
180
181
        return $location;
182
    }
183
184
    /**
185
     * Display logo.
186
     *
187
     * @return void
188
     */
189
    private function displayLogo()
190
    {
191
        $resource = fopen(__DIR__.'/../../../../LOGO', 'rb');
192
193
        while (($line = fgets($resource)) !== false) {
194
            $this->style->write('<fg=green>'.$line.'</>');
195
        }
196
197
        fclose($resource);
198
    }
199
}
200