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 RunOpenCode\AbstractBuilder\Exception\RuntimeException; |
13
|
|
|
use RunOpenCode\AbstractBuilder\Helper\Style; |
14
|
|
|
use RunOpenCode\AbstractBuilder\Helper\Tokenizer; |
15
|
|
|
use Symfony\Component\Console\Command\Command; |
16
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
17
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
18
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
19
|
|
|
use Symfony\Component\Console\Question\ChoiceQuestion; |
20
|
|
|
use Symfony\Component\Console\Question\Question; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Class GenerateBuilderCommand |
24
|
|
|
* |
25
|
|
|
* @package RunOpenCode\AbstractBuilder\Command |
26
|
|
|
*/ |
27
|
|
|
class GenerateBuilderCommand extends Command |
28
|
|
|
{ |
29
|
|
|
/** |
30
|
|
|
* @var Style |
31
|
|
|
*/ |
32
|
|
|
private $style; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var InputInterface |
36
|
|
|
*/ |
37
|
|
|
private $input; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var OutputInterface |
41
|
|
|
*/ |
42
|
|
|
private $output; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* {@inheritdoc} |
46
|
|
|
*/ |
47
|
|
|
protected function configure() |
48
|
|
|
{ |
49
|
|
|
$this |
50
|
|
|
->setName('runopencode:generate:builder') |
51
|
|
|
->setDescription('Generates builder class skeleton for provided class.') |
52
|
|
|
->addArgument('class', InputArgument::OPTIONAL, 'Full qualified class name of building object that can be autoloaded, or path to file with class definition.') |
53
|
|
|
->addArgument('builder', InputArgument::OPTIONAL, 'Full qualified class name of builder class can be autoloaded, or it will be autoloaded, or path to file with class definition.') |
54
|
|
|
->addArgument('location', InputArgument::OPTIONAL, 'Path to location of file where builder class will be saved.'); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* {@inheritdoc} |
59
|
|
|
*/ |
60
|
|
|
public function execute(InputInterface $input, OutputInterface $output) |
61
|
|
|
{ |
62
|
|
|
$this->input = $input; |
63
|
|
|
$this->output = $output; |
64
|
|
|
$this->style = new Style($input, $output); |
65
|
|
|
|
66
|
|
|
$this->style->displayLogo(); |
67
|
|
|
|
68
|
|
|
$this->style->title('Generate builder class'); |
69
|
|
|
|
70
|
|
|
try { |
71
|
|
|
$buildingClass = $this->getBuildingClass(); |
72
|
|
|
$this->style->info(sprintf('Builder class for class "%s" will be generated.', $buildingClass)); |
73
|
|
|
|
74
|
|
|
$builderClass = $this->getBuilderClass(sprintf('%sBuilder', $buildingClass)); |
75
|
|
|
$this->style->info(sprintf('Full qualified namespace for builder class is "%s".', $builderClass)); |
76
|
|
|
} catch (\Exception $e) { |
77
|
|
|
$this->style->error($e->getMessage()); |
78
|
|
|
return 0; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Get class name for which skeleton should be built. |
85
|
|
|
* |
86
|
|
|
* @return string|bool |
87
|
|
|
*/ |
88
|
|
|
private function getBuildingClass() |
89
|
|
|
{ |
90
|
|
|
$class = $this->input->getArgument('class'); |
91
|
|
|
|
92
|
|
View Code Duplication |
if (null === $class) { |
|
|
|
|
93
|
|
|
$helper = $this->getHelper('question'); |
94
|
|
|
$question = new Question('Enter full qualified class name, or path to file with class, for which you want to generate builder class: ', null); |
95
|
|
|
|
96
|
|
|
$class = $helper->ask($this->input, $this->output, $question); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
if (!class_exists($class, true)) { |
100
|
|
|
$class = Tokenizer::findClass($class); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
if (!class_exists($class, true)) { |
104
|
|
|
throw new RuntimeException(sprintf('Unable to autoload class "%s". Does this class exists? Can it be autoloaded?', $class)); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
return ltrim(str_replace('\\\\', '\\', $class), '\\'); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Get class name for builder class. |
112
|
|
|
* |
113
|
|
|
* @param string $suggest |
114
|
|
|
* @return bool|string |
115
|
|
|
*/ |
116
|
|
|
private function getBuilderClass($suggest) |
117
|
|
|
{ |
118
|
|
|
$class = $this->input->getArgument('builder'); |
119
|
|
|
|
120
|
|
View Code Duplication |
if (null === $class) { |
|
|
|
|
121
|
|
|
$helper = $this->getHelper('question'); |
122
|
|
|
$question = new Question(sprintf('Enter full qualified class name of your builder class (default: "%s"): ', $suggest), $suggest); |
123
|
|
|
|
124
|
|
|
$class = $helper->ask($this->input, $this->output, $question); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
if (file_exists($class) && !class_exists($class, true)) { |
128
|
|
|
$class = Tokenizer::findClass($class); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
$class = ltrim(str_replace('\\\\', '\\', $class), '\\'); |
132
|
|
|
|
133
|
|
|
if ('' === $class) { |
134
|
|
|
throw new RuntimeException('Builder class name must be provided.'); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
foreach (explode('\\', $class) as $part) { |
138
|
|
|
|
139
|
|
|
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $part)) { |
140
|
|
|
throw new RuntimeException(sprintf('Provided builder class name "%s" is not valid PHP class name.', $class)); |
141
|
|
|
} |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $class; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Get getters and setters that will be generated. |
149
|
|
|
* |
150
|
|
|
* @param string[] $parameters |
151
|
|
|
* @return string[] |
152
|
|
|
*/ |
153
|
|
|
private function getGettersAndSetters($parameters) |
154
|
|
|
{ |
155
|
|
|
$helper = $this->getHelper('question'); |
156
|
|
|
|
157
|
|
|
$question = new ChoiceQuestion( |
158
|
|
|
'Choose for which constructor arguments you want to generate getters and setters (separate choices with coma, enter none for all choices):', |
159
|
|
|
$parameters, |
160
|
|
|
implode(',', array_keys($parameters)) |
161
|
|
|
); |
162
|
|
|
|
163
|
|
|
$question->setMultiselect(true); |
164
|
|
|
|
165
|
|
|
$selected = $helper->ask($this->input, $this->output, $question); |
166
|
|
|
|
167
|
|
|
if (0 === count($selected)) { |
168
|
|
|
$this->style->error('You have to choose at least one constructor argument.'); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return |
172
|
|
|
array_merge( |
173
|
|
|
array_map(function($parameter) { |
174
|
|
|
return sprintf('get%s()', ucfirst($parameter)); |
175
|
|
|
}, $selected), |
176
|
|
|
array_map(function($parameter) { |
177
|
|
|
return sprintf('set%s($%s)', ucfirst($parameter), $parameter); |
178
|
|
|
}, $selected) |
179
|
|
|
); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Get builder class location |
185
|
|
|
* |
186
|
|
|
* @param string $suggest |
187
|
|
|
* @return bool|string |
188
|
|
|
*/ |
189
|
|
|
private function getBuilderClassLocation($suggest) |
190
|
|
|
{ |
191
|
|
|
$helper = $this->getHelper('question'); |
192
|
|
|
$question = new Question(sprintf('Where do you want to generate your builder class code (default: "%s"): ', $suggest), $suggest); |
193
|
|
|
|
194
|
|
|
$location = trim($helper->ask($this->input, $this->output, $question)); |
195
|
|
|
|
196
|
|
|
if (file_exists($location) && !is_writable($location)) { |
197
|
|
|
$this->style->error(sprintf('File on location "%s" already exists, but it is not writeable.', $location)); |
198
|
|
|
return false; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
// TODO - check if it si possible to create file at all...? |
202
|
|
|
|
203
|
|
|
return $location; |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.